cmExtraCodeLiteGenerator.cxx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2004-2009 Kitware, Inc.
  4. Copyright 2004 Alexander Neundorf ([email protected])
  5. Copyright 2013 Eran Ifrah ([email protected])
  6. Distributed under the OSI-approved BSD License (the "License");
  7. see accompanying file Copyright.txt for details.
  8. This software is distributed WITHOUT ANY WARRANTY; without even the
  9. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. See the License for more information.
  11. ============================================================================*/
  12. #include "cmExtraCodeLiteGenerator.h"
  13. #include "cmGeneratedFileStream.h"
  14. #include "cmGlobalUnixMakefileGenerator3.h"
  15. #include "cmLocalUnixMakefileGenerator3.h"
  16. #include "cmMakefile.h"
  17. #include "cmSourceFile.h"
  18. #include "cmSystemTools.h"
  19. #include "cmake.h"
  20. #include "cmStandardIncludes.h"
  21. #include "cmXMLWriter.h"
  22. #include <cmsys/Directory.hxx>
  23. #include <cmsys/SystemInformation.hxx>
  24. #include <cmsys/SystemTools.hxx>
  25. void cmExtraCodeLiteGenerator::GetDocumentation(cmDocumentationEntry& entry,
  26. const std::string&) const
  27. {
  28. entry.Name = this->GetName();
  29. entry.Brief = "Generates CodeLite project files.";
  30. }
  31. cmExtraCodeLiteGenerator::cmExtraCodeLiteGenerator()
  32. : cmExternalMakefileProjectGenerator()
  33. , ConfigName("NoConfig")
  34. , CpuCount(2)
  35. {
  36. #if defined(_WIN32)
  37. this->SupportedGlobalGenerators.push_back("MinGW Makefiles");
  38. this->SupportedGlobalGenerators.push_back("NMake Makefiles");
  39. #endif
  40. this->SupportedGlobalGenerators.push_back("Ninja");
  41. this->SupportedGlobalGenerators.push_back("Unix Makefiles");
  42. }
  43. void cmExtraCodeLiteGenerator::Generate()
  44. {
  45. // Hold root tree information for creating the workspace
  46. std::string workspaceProjectName;
  47. std::string workspaceOutputDir;
  48. std::string workspaceFileName;
  49. std::string workspaceSourcePath;
  50. const std::map<std::string, std::vector<cmLocalGenerator*> >& projectMap =
  51. this->GlobalGenerator->GetProjectMap();
  52. // loop projects and locate the root project.
  53. // and extract the information for creating the worspace
  54. for (std::map<std::string, std::vector<cmLocalGenerator*> >::const_iterator
  55. it = projectMap.begin();
  56. it != projectMap.end(); ++it) {
  57. const cmMakefile* mf = it->second[0]->GetMakefile();
  58. this->ConfigName = GetConfigurationName(mf);
  59. if (strcmp(it->second[0]->GetCurrentBinaryDirectory(),
  60. it->second[0]->GetBinaryDirectory()) == 0) {
  61. workspaceOutputDir = it->second[0]->GetCurrentBinaryDirectory();
  62. workspaceProjectName = it->second[0]->GetProjectName();
  63. workspaceSourcePath = it->second[0]->GetSourceDirectory();
  64. workspaceFileName = workspaceOutputDir + "/";
  65. workspaceFileName += workspaceProjectName + ".workspace";
  66. this->WorkspacePath = it->second[0]->GetCurrentBinaryDirectory();
  67. ;
  68. break;
  69. }
  70. }
  71. cmGeneratedFileStream fout(workspaceFileName.c_str());
  72. cmXMLWriter xml(fout);
  73. xml.StartDocument("utf-8");
  74. xml.StartElement("CodeLite_Workspace");
  75. xml.Attribute("Name", workspaceProjectName);
  76. // for each sub project in the workspace create a codelite project
  77. for (std::map<std::string, std::vector<cmLocalGenerator*> >::const_iterator
  78. it = projectMap.begin();
  79. it != projectMap.end(); ++it) {
  80. // retrive project information
  81. std::string outputDir = it->second[0]->GetCurrentBinaryDirectory();
  82. std::string projectName = it->second[0]->GetProjectName();
  83. std::string filename = outputDir + "/" + projectName + ".project";
  84. // Make the project file relative to the workspace
  85. filename = cmSystemTools::RelativePath(this->WorkspacePath.c_str(),
  86. filename.c_str());
  87. // create a project file
  88. this->CreateProjectFile(it->second);
  89. xml.StartElement("Project");
  90. xml.Attribute("Name", projectName);
  91. xml.Attribute("Path", filename);
  92. xml.Attribute("Active", "No");
  93. xml.EndElement();
  94. }
  95. xml.StartElement("BuildMatrix");
  96. xml.StartElement("WorkspaceConfiguration");
  97. xml.Attribute("Name", this->ConfigName);
  98. xml.Attribute("Selected", "yes");
  99. for (std::map<std::string, std::vector<cmLocalGenerator*> >::const_iterator
  100. it = projectMap.begin();
  101. it != projectMap.end(); ++it) {
  102. // retrive project information
  103. std::string projectName = it->second[0]->GetProjectName();
  104. xml.StartElement("Project");
  105. xml.Attribute("Name", projectName);
  106. xml.Attribute("ConfigName", this->ConfigName);
  107. xml.EndElement();
  108. }
  109. xml.EndElement(); // WorkspaceConfiguration
  110. xml.EndElement(); // BuildMatrix
  111. xml.EndElement(); // CodeLite_Workspace
  112. }
  113. /* create the project file */
  114. void cmExtraCodeLiteGenerator::CreateProjectFile(
  115. const std::vector<cmLocalGenerator*>& lgs)
  116. {
  117. std::string outputDir = lgs[0]->GetCurrentBinaryDirectory();
  118. std::string projectName = lgs[0]->GetProjectName();
  119. std::string filename = outputDir + "/";
  120. filename += projectName + ".project";
  121. this->CreateNewProjectFile(lgs, filename);
  122. }
  123. void cmExtraCodeLiteGenerator::CreateNewProjectFile(
  124. const std::vector<cmLocalGenerator*>& lgs, const std::string& filename)
  125. {
  126. const cmMakefile* mf = lgs[0]->GetMakefile();
  127. cmGeneratedFileStream fout(filename.c_str());
  128. if (!fout) {
  129. return;
  130. }
  131. cmXMLWriter xml(fout);
  132. ////////////////////////////////////
  133. xml.StartDocument("utf-8");
  134. xml.StartElement("CodeLite_Project");
  135. xml.Attribute("Name", lgs[0]->GetProjectName());
  136. xml.Attribute("InternalType", "");
  137. // Collect all used source files in the project
  138. // Sort them into two containers, one for C/C++ implementation files
  139. // which may have an acompanying header, one for all other files
  140. std::string projectType;
  141. std::vector<std::string> srcExts =
  142. this->GlobalGenerator->GetCMakeInstance()->GetSourceExtensions();
  143. std::vector<std::string> headerExts =
  144. this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions();
  145. std::map<std::string, cmSourceFile*> cFiles;
  146. std::set<std::string> otherFiles;
  147. for (std::vector<cmLocalGenerator*>::const_iterator lg = lgs.begin();
  148. lg != lgs.end(); lg++) {
  149. cmMakefile* makefile = (*lg)->GetMakefile();
  150. std::vector<cmGeneratorTarget*> targets = (*lg)->GetGeneratorTargets();
  151. for (std::vector<cmGeneratorTarget*>::iterator ti = targets.begin();
  152. ti != targets.end(); ti++) {
  153. switch ((*ti)->GetType()) {
  154. case cmState::EXECUTABLE: {
  155. projectType = "Executable";
  156. } break;
  157. case cmState::STATIC_LIBRARY: {
  158. projectType = "Static Library";
  159. } break;
  160. case cmState::SHARED_LIBRARY: {
  161. projectType = "Dynamic Library";
  162. } break;
  163. case cmState::MODULE_LIBRARY: {
  164. projectType = "Dynamic Library";
  165. } break;
  166. default: // intended fallthrough
  167. break;
  168. }
  169. switch ((*ti)->GetType()) {
  170. case cmState::EXECUTABLE:
  171. case cmState::STATIC_LIBRARY:
  172. case cmState::SHARED_LIBRARY:
  173. case cmState::MODULE_LIBRARY: {
  174. std::vector<cmSourceFile*> sources;
  175. cmGeneratorTarget* gt = *ti;
  176. gt->GetSourceFiles(sources,
  177. makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
  178. for (std::vector<cmSourceFile*>::const_iterator si = sources.begin();
  179. si != sources.end(); si++) {
  180. // check whether it is a C/C++ implementation file
  181. bool isCFile = false;
  182. std::string lang = (*si)->GetLanguage();
  183. if (lang == "C" || lang == "CXX") {
  184. std::string srcext = (*si)->GetExtension();
  185. for (std::vector<std::string>::const_iterator ext =
  186. srcExts.begin();
  187. ext != srcExts.end(); ++ext) {
  188. if (srcext == *ext) {
  189. isCFile = true;
  190. break;
  191. }
  192. }
  193. }
  194. // then put it accordingly into one of the two containers
  195. if (isCFile) {
  196. cFiles[(*si)->GetFullPath()] = *si;
  197. } else {
  198. otherFiles.insert((*si)->GetFullPath());
  199. }
  200. }
  201. }
  202. default: // intended fallthrough
  203. break;
  204. }
  205. }
  206. }
  207. // The following loop tries to add header files matching to implementation
  208. // files to the project. It does that by iterating over all source files,
  209. // replacing the file name extension with ".h" and checks whether such a
  210. // file exists. If it does, it is inserted into the map of files.
  211. // A very similar version of that code exists also in the kdevelop
  212. // project generator.
  213. for (std::map<std::string, cmSourceFile*>::const_iterator sit =
  214. cFiles.begin();
  215. sit != cFiles.end(); ++sit) {
  216. std::string headerBasename = cmSystemTools::GetFilenamePath(sit->first);
  217. headerBasename += "/";
  218. headerBasename += cmSystemTools::GetFilenameWithoutExtension(sit->first);
  219. // check if there's a matching header around
  220. for (std::vector<std::string>::const_iterator ext = headerExts.begin();
  221. ext != headerExts.end(); ++ext) {
  222. std::string hname = headerBasename;
  223. hname += ".";
  224. hname += *ext;
  225. // if it's already in the set, don't check if it exists on disk
  226. std::set<std::string>::const_iterator headerIt = otherFiles.find(hname);
  227. if (headerIt != otherFiles.end()) {
  228. break;
  229. }
  230. if (cmSystemTools::FileExists(hname.c_str())) {
  231. otherFiles.insert(hname);
  232. break;
  233. }
  234. }
  235. }
  236. // Get the project path ( we need it later to convert files to
  237. // their relative path)
  238. std::string projectPath = cmSystemTools::GetFilenamePath(filename);
  239. // Create 2 virtual folders: src and include
  240. // and place all the implementation files into the src
  241. // folder, the rest goes to the include folder
  242. xml.StartElement("VirtualDirectory");
  243. xml.Attribute("Name", "src");
  244. // insert all source files in the codelite project
  245. // first the C/C++ implementation files, then all others
  246. for (std::map<std::string, cmSourceFile*>::const_iterator sit =
  247. cFiles.begin();
  248. sit != cFiles.end(); ++sit) {
  249. xml.StartElement("File");
  250. xml.Attribute("Name", cmSystemTools::RelativePath(projectPath.c_str(),
  251. sit->first.c_str()));
  252. xml.EndElement();
  253. }
  254. xml.EndElement(); // VirtualDirectory
  255. xml.StartElement("VirtualDirectory");
  256. xml.Attribute("Name", "include");
  257. for (std::set<std::string>::const_iterator sit = otherFiles.begin();
  258. sit != otherFiles.end(); ++sit) {
  259. xml.StartElement("File");
  260. xml.Attribute(
  261. "Name", cmSystemTools::RelativePath(projectPath.c_str(), sit->c_str()));
  262. xml.EndElement();
  263. }
  264. xml.EndElement(); // VirtualDirectory
  265. // Get the number of CPUs. We use this information for the make -jN
  266. // command
  267. cmsys::SystemInformation info;
  268. info.RunCPUCheck();
  269. this->CpuCount =
  270. info.GetNumberOfLogicalCPU() * info.GetNumberOfPhysicalCPU();
  271. std::string codeliteCompilerName = this->GetCodeLiteCompilerName(mf);
  272. xml.StartElement("Settings");
  273. xml.Attribute("Type", projectType);
  274. xml.StartElement("Configuration");
  275. xml.Attribute("Name", this->ConfigName);
  276. xml.Attribute("CompilerType", this->GetCodeLiteCompilerName(mf));
  277. xml.Attribute("DebuggerType", "GNU gdb debugger");
  278. xml.Attribute("Type", projectType);
  279. xml.Attribute("BuildCmpWithGlobalSettings", "append");
  280. xml.Attribute("BuildLnkWithGlobalSettings", "append");
  281. xml.Attribute("BuildResWithGlobalSettings", "append");
  282. xml.StartElement("Compiler");
  283. xml.Attribute("Options", "-g");
  284. xml.Attribute("Required", "yes");
  285. xml.Attribute("PreCompiledHeader", "");
  286. xml.StartElement("IncludePath");
  287. xml.Attribute("Value", ".");
  288. xml.EndElement(); // IncludePath
  289. xml.EndElement(); // Compiler
  290. xml.StartElement("Linker");
  291. xml.Attribute("Options", "");
  292. xml.Attribute("Required", "yes");
  293. xml.EndElement(); // Linker
  294. xml.StartElement("ResourceCompiler");
  295. xml.Attribute("Options", "");
  296. xml.Attribute("Required", "no");
  297. xml.EndElement(); // ResourceCompiler
  298. xml.StartElement("General");
  299. xml.Attribute("OutputFile", "$(IntermediateDirectory)/$(ProjectName)");
  300. xml.Attribute("IntermediateDirectory", "./");
  301. xml.Attribute("Command", "./$(ProjectName)");
  302. xml.Attribute("CommandArguments", "");
  303. xml.Attribute("WorkingDirectory", "$(IntermediateDirectory)");
  304. xml.Attribute("PauseExecWhenProcTerminates", "yes");
  305. xml.EndElement(); // General
  306. xml.StartElement("Debugger");
  307. xml.Attribute("IsRemote", "no");
  308. xml.Attribute("RemoteHostName", "");
  309. xml.Attribute("RemoteHostPort", "");
  310. xml.Attribute("DebuggerPath", "");
  311. xml.Element("PostConnectCommands");
  312. xml.Element("StartupCommands");
  313. xml.EndElement(); // Debugger
  314. xml.Element("PreBuild");
  315. xml.Element("PostBuild");
  316. xml.StartElement("CustomBuild");
  317. xml.Attribute("Enabled", "yes");
  318. xml.Element("RebuildCommand", GetRebuildCommand(mf));
  319. xml.Element("CleanCommand", GetCleanCommand(mf));
  320. xml.Element("BuildCommand", GetBuildCommand(mf));
  321. xml.Element("SingleFileCommand", GetSingleFileBuildCommand(mf));
  322. xml.Element("PreprocessFileCommand");
  323. xml.Element("WorkingDirectory", "$(WorkspacePath)");
  324. xml.EndElement(); // CustomBuild
  325. xml.StartElement("AdditionalRules");
  326. xml.Element("CustomPostBuild");
  327. xml.Element("CustomPreBuild");
  328. xml.EndElement(); // AdditionalRules
  329. xml.EndElement(); // Configuration
  330. xml.StartElement("GlobalSettings");
  331. xml.StartElement("Compiler");
  332. xml.Attribute("Options", "");
  333. xml.StartElement("IncludePath");
  334. xml.Attribute("Value", ".");
  335. xml.EndElement(); // IncludePath
  336. xml.EndElement(); // Compiler
  337. xml.StartElement("Linker");
  338. xml.Attribute("Options", "");
  339. xml.StartElement("LibraryPath");
  340. xml.Attribute("Value", ".");
  341. xml.EndElement(); // LibraryPath
  342. xml.EndElement(); // Linker
  343. xml.StartElement("ResourceCompiler");
  344. xml.Attribute("Options", "");
  345. xml.EndElement(); // ResourceCompiler
  346. xml.EndElement(); // GlobalSettings
  347. xml.EndElement(); // Settings
  348. xml.EndElement(); // CodeLite_Project
  349. }
  350. std::string cmExtraCodeLiteGenerator::GetCodeLiteCompilerName(
  351. const cmMakefile* mf) const
  352. {
  353. // figure out which language to use
  354. // for now care only for C and C++
  355. std::string compilerIdVar = "CMAKE_CXX_COMPILER_ID";
  356. if (!this->GlobalGenerator->GetLanguageEnabled("CXX")) {
  357. compilerIdVar = "CMAKE_C_COMPILER_ID";
  358. }
  359. std::string compilerId = mf->GetSafeDefinition(compilerIdVar);
  360. std::string compiler = "gnu g++"; // default to g++
  361. // Since we need the compiler for parsing purposes only
  362. // it does not matter if we use clang or clang++, same as
  363. // "gnu gcc" vs "gnu g++"
  364. if (compilerId == "MSVC") {
  365. compiler = "VC++";
  366. } else if (compilerId == "Clang") {
  367. compiler = "clang++";
  368. } else if (compilerId == "GNU") {
  369. compiler = "gnu g++";
  370. }
  371. return compiler;
  372. }
  373. std::string cmExtraCodeLiteGenerator::GetConfigurationName(
  374. const cmMakefile* mf) const
  375. {
  376. std::string confName = mf->GetSafeDefinition("CMAKE_BUILD_TYPE");
  377. // Trim the configuration name from whitespaces (left and right)
  378. confName.erase(0, confName.find_first_not_of(" \t\r\v\n"));
  379. confName.erase(confName.find_last_not_of(" \t\r\v\n") + 1);
  380. if (confName.empty()) {
  381. confName = "NoConfig";
  382. }
  383. return confName;
  384. }
  385. std::string cmExtraCodeLiteGenerator::GetBuildCommand(
  386. const cmMakefile* mf) const
  387. {
  388. std::string generator = mf->GetSafeDefinition("CMAKE_GENERATOR");
  389. std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
  390. std::string buildCommand = make; // Default
  391. if (generator == "NMake Makefiles" || generator == "Ninja") {
  392. buildCommand = make;
  393. } else if (generator == "MinGW Makefiles" || generator == "Unix Makefiles") {
  394. std::ostringstream ss;
  395. ss << make << " -j " << this->CpuCount;
  396. buildCommand = ss.str();
  397. }
  398. return buildCommand;
  399. }
  400. std::string cmExtraCodeLiteGenerator::GetCleanCommand(
  401. const cmMakefile* mf) const
  402. {
  403. return GetBuildCommand(mf) + " clean";
  404. }
  405. std::string cmExtraCodeLiteGenerator::GetRebuildCommand(
  406. const cmMakefile* mf) const
  407. {
  408. return GetCleanCommand(mf) + " && " + GetBuildCommand(mf);
  409. }
  410. std::string cmExtraCodeLiteGenerator::GetSingleFileBuildCommand(
  411. const cmMakefile* mf) const
  412. {
  413. std::string buildCommand;
  414. std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
  415. std::string generator = mf->GetSafeDefinition("CMAKE_GENERATOR");
  416. if (generator == "Unix Makefiles" || generator == "MinGW Makefiles") {
  417. std::ostringstream ss;
  418. ss << make << " -f$(ProjectPath)/Makefile $(CurrentFileName).cpp.o";
  419. buildCommand = ss.str();
  420. }
  421. return buildCommand;
  422. }