cmExtraCodeLiteGenerator.cxx 17 KB

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