cmExtraCodeLiteGenerator.cxx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  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(); it!= projectMap.end(); ++it)
  56. {
  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. {
  62. workspaceOutputDir = it->second[0]->GetCurrentBinaryDirectory();
  63. workspaceProjectName = it->second[0]->GetProjectName();
  64. workspaceSourcePath = it->second[0]->GetSourceDirectory();
  65. workspaceFileName = workspaceOutputDir+"/";
  66. workspaceFileName += workspaceProjectName + ".workspace";
  67. this->WorkspacePath = it->second[0]->GetCurrentBinaryDirectory();;
  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(); it!= projectMap.end(); ++it)
  79. {
  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(); it!= projectMap.end(); ++it)
  101. {
  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
  124. ::CreateNewProjectFile(const std::vector<cmLocalGenerator*>& lgs,
  125. const std::string& filename)
  126. {
  127. const cmMakefile* mf=lgs[0]->GetMakefile();
  128. cmGeneratedFileStream fout(filename.c_str());
  129. if(!fout)
  130. {
  131. return;
  132. }
  133. cmXMLWriter xml(fout);
  134. ////////////////////////////////////
  135. xml.StartDocument("utf-8");
  136. xml.StartElement("CodeLite_Project");
  137. xml.Attribute("Name", lgs[0]->GetProjectName());
  138. xml.Attribute("InternalType", "");
  139. // Collect all used source files in the project
  140. // Sort them into two containers, one for C/C++ implementation files
  141. // which may have an acompanying header, one for all other files
  142. std::string projectType;
  143. std::vector<std::string> srcExts =
  144. this->GlobalGenerator->GetCMakeInstance()->GetSourceExtensions();
  145. std::vector<std::string> headerExts =
  146. this->GlobalGenerator->GetCMakeInstance()->GetHeaderExtensions();
  147. std::map<std::string, cmSourceFile*> cFiles;
  148. std::set<std::string> otherFiles;
  149. for (std::vector<cmLocalGenerator*>::const_iterator lg=lgs.begin();
  150. lg!=lgs.end(); lg++)
  151. {
  152. cmMakefile* makefile=(*lg)->GetMakefile();
  153. std::vector<cmGeneratorTarget*> targets=(*lg)->GetGeneratorTargets();
  154. for (std::vector<cmGeneratorTarget*>::iterator ti = targets.begin();
  155. ti != targets.end(); ti++)
  156. {
  157. switch((*ti)->GetType())
  158. {
  159. case cmState::EXECUTABLE:
  160. {
  161. projectType = "Executable";
  162. }
  163. break;
  164. case cmState::STATIC_LIBRARY:
  165. {
  166. projectType = "Static Library";
  167. }
  168. break;
  169. case cmState::SHARED_LIBRARY:
  170. {
  171. projectType = "Dynamic Library";
  172. }
  173. break;
  174. case cmState::MODULE_LIBRARY:
  175. {
  176. projectType = "Dynamic Library";
  177. }
  178. break;
  179. default: // intended fallthrough
  180. break;
  181. }
  182. switch((*ti)->GetType())
  183. {
  184. case cmState::EXECUTABLE:
  185. case cmState::STATIC_LIBRARY:
  186. case cmState::SHARED_LIBRARY:
  187. case cmState::MODULE_LIBRARY:
  188. {
  189. std::vector<cmSourceFile*> sources;
  190. cmGeneratorTarget* gt = *ti;
  191. gt->GetSourceFiles(sources,
  192. makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
  193. for (std::vector<cmSourceFile*>::const_iterator si=sources.begin();
  194. si!=sources.end(); si++)
  195. {
  196. // check whether it is a C/C++ implementation file
  197. bool isCFile = false;
  198. std::string lang = (*si)->GetLanguage();
  199. if (lang == "C" || lang == "CXX")
  200. {
  201. std::string srcext = (*si)->GetExtension();
  202. for(std::vector<std::string>::const_iterator
  203. ext = srcExts.begin(); ext != srcExts.end(); ++ext)
  204. {
  205. if (srcext == *ext)
  206. {
  207. isCFile = true;
  208. break;
  209. }
  210. }
  211. }
  212. // then put it accordingly into one of the two containers
  213. if (isCFile)
  214. {
  215. cFiles[(*si)->GetFullPath()] = *si ;
  216. }
  217. else
  218. {
  219. otherFiles.insert((*si)->GetFullPath());
  220. }
  221. }
  222. }
  223. default: // intended fallthrough
  224. break;
  225. }
  226. }
  227. }
  228. // The following loop tries to add header files matching to implementation
  229. // files to the project. It does that by iterating over all source files,
  230. // replacing the file name extension with ".h" and checks whether such a
  231. // file exists. If it does, it is inserted into the map of files.
  232. // A very similar version of that code exists also in the kdevelop
  233. // project generator.
  234. for (std::map<std::string, cmSourceFile*>::const_iterator
  235. sit=cFiles.begin();
  236. sit!=cFiles.end();
  237. ++sit)
  238. {
  239. std::string headerBasename=cmSystemTools::GetFilenamePath(sit->first);
  240. headerBasename+="/";
  241. headerBasename+=cmSystemTools::GetFilenameWithoutExtension(sit->first);
  242. // check if there's a matching header around
  243. for(std::vector<std::string>::const_iterator
  244. ext = headerExts.begin();
  245. ext != headerExts.end();
  246. ++ext)
  247. {
  248. std::string hname=headerBasename;
  249. hname += ".";
  250. hname += *ext;
  251. // if it's already in the set, don't check if it exists on disk
  252. std::set<std::string>::const_iterator headerIt=otherFiles.find(hname);
  253. if (headerIt != otherFiles.end())
  254. {
  255. break;
  256. }
  257. if(cmSystemTools::FileExists(hname.c_str()))
  258. {
  259. otherFiles.insert(hname);
  260. break;
  261. }
  262. }
  263. }
  264. // Get the project path ( we need it later to convert files to
  265. // their relative path)
  266. std::string projectPath = cmSystemTools::GetFilenamePath(filename);
  267. // Create 2 virtual folders: src and include
  268. // and place all the implementation files into the src
  269. // folder, the rest goes to the include folder
  270. xml.StartElement("VirtualDirectory");
  271. xml.Attribute("Name", "src");
  272. // insert all source files in the codelite project
  273. // first the C/C++ implementation files, then all others
  274. for (std::map<std::string, cmSourceFile*>::const_iterator
  275. sit=cFiles.begin();
  276. sit!=cFiles.end();
  277. ++sit)
  278. {
  279. xml.StartElement("File");
  280. xml.Attribute("Name",
  281. cmSystemTools::RelativePath(projectPath.c_str(), sit->first.c_str()));
  282. xml.EndElement();
  283. }
  284. xml.EndElement(); // VirtualDirectory
  285. xml.StartElement("VirtualDirectory");
  286. xml.Attribute("Name", "include");
  287. for (std::set<std::string>::const_iterator
  288. sit=otherFiles.begin();
  289. sit!=otherFiles.end();
  290. ++sit)
  291. {
  292. xml.StartElement("File");
  293. xml.Attribute("Name",
  294. cmSystemTools::RelativePath(projectPath.c_str(), sit->c_str()));
  295. xml.EndElement();
  296. }
  297. xml.EndElement(); // VirtualDirectory
  298. // Get the number of CPUs. We use this information for the make -jN
  299. // command
  300. cmsys::SystemInformation info;
  301. info.RunCPUCheck();
  302. this->CpuCount = info.GetNumberOfLogicalCPU() *
  303. info.GetNumberOfPhysicalCPU();
  304. std::string codeliteCompilerName = this->GetCodeLiteCompilerName(mf);
  305. xml.StartElement("Settings");
  306. xml.Attribute("Type", projectType);
  307. xml.StartElement("Configuration");
  308. xml.Attribute("Name", this->ConfigName);
  309. xml.Attribute("CompilerType", this->GetCodeLiteCompilerName(mf));
  310. xml.Attribute("DebuggerType", "GNU gdb debugger");
  311. xml.Attribute("Type", projectType);
  312. xml.Attribute("BuildCmpWithGlobalSettings", "append");
  313. xml.Attribute("BuildLnkWithGlobalSettings", "append");
  314. xml.Attribute("BuildResWithGlobalSettings", "append");
  315. xml.StartElement("Compiler");
  316. xml.Attribute("Options", "-g");
  317. xml.Attribute("Required", "yes");
  318. xml.Attribute("PreCompiledHeader", "");
  319. xml.StartElement("IncludePath");
  320. xml.Attribute("Value", ".");
  321. xml.EndElement(); // IncludePath
  322. xml.EndElement(); // Compiler
  323. xml.StartElement("Linker");
  324. xml.Attribute("Options", "");
  325. xml.Attribute("Required", "yes");
  326. xml.EndElement(); // Linker
  327. xml.StartElement("ResourceCompiler");
  328. xml.Attribute("Options", "");
  329. xml.Attribute("Required", "no");
  330. xml.EndElement(); // ResourceCompiler
  331. xml.StartElement("General");
  332. xml.Attribute("OutputFile", "$(IntermediateDirectory)/$(ProjectName)");
  333. xml.Attribute("IntermediateDirectory", "./");
  334. xml.Attribute("Command", "./$(ProjectName)");
  335. xml.Attribute("CommandArguments", "");
  336. xml.Attribute("WorkingDirectory", "$(IntermediateDirectory)");
  337. xml.Attribute("PauseExecWhenProcTerminates", "yes");
  338. xml.EndElement(); // General
  339. xml.StartElement("Debugger");
  340. xml.Attribute("IsRemote", "no");
  341. xml.Attribute("RemoteHostName", "");
  342. xml.Attribute("RemoteHostPort", "");
  343. xml.Attribute("DebuggerPath", "");
  344. xml.Element("PostConnectCommands");
  345. xml.Element("StartupCommands");
  346. xml.EndElement(); // Debugger
  347. xml.Element("PreBuild");
  348. xml.Element("PostBuild");
  349. xml.StartElement("CustomBuild");
  350. xml.Attribute("Enabled", "yes");
  351. xml.Element("RebuildCommand", GetRebuildCommand(mf));
  352. xml.Element("CleanCommand", GetCleanCommand(mf));
  353. xml.Element("BuildCommand", GetBuildCommand(mf));
  354. xml.Element("SingleFileCommand", GetSingleFileBuildCommand(mf));
  355. xml.Element("PreprocessFileCommand");
  356. xml.Element("WorkingDirectory", "$(WorkspacePath)");
  357. xml.EndElement(); // CustomBuild
  358. xml.StartElement("AdditionalRules");
  359. xml.Element("CustomPostBuild");
  360. xml.Element("CustomPreBuild");
  361. xml.EndElement(); // AdditionalRules
  362. xml.EndElement(); // Configuration
  363. xml.StartElement("GlobalSettings");
  364. xml.StartElement("Compiler");
  365. xml.Attribute("Options", "");
  366. xml.StartElement("IncludePath");
  367. xml.Attribute("Value", ".");
  368. xml.EndElement(); // IncludePath
  369. xml.EndElement(); // Compiler
  370. xml.StartElement("Linker");
  371. xml.Attribute("Options", "");
  372. xml.StartElement("LibraryPath");
  373. xml.Attribute("Value", ".");
  374. xml.EndElement(); // LibraryPath
  375. xml.EndElement(); // Linker
  376. xml.StartElement("ResourceCompiler");
  377. xml.Attribute("Options", "");
  378. xml.EndElement(); // ResourceCompiler
  379. xml.EndElement(); // GlobalSettings
  380. xml.EndElement(); // Settings
  381. xml.EndElement(); // CodeLite_Project
  382. }
  383. std::string
  384. cmExtraCodeLiteGenerator::GetCodeLiteCompilerName(const cmMakefile* mf) const
  385. {
  386. // figure out which language to use
  387. // for now care only for C and C++
  388. std::string compilerIdVar = "CMAKE_CXX_COMPILER_ID";
  389. if (this->GlobalGenerator->GetLanguageEnabled("CXX") == false)
  390. {
  391. compilerIdVar = "CMAKE_C_COMPILER_ID";
  392. }
  393. std::string compilerId = mf->GetSafeDefinition(compilerIdVar);
  394. std::string compiler = "gnu g++"; // default to g++
  395. // Since we need the compiler for parsing purposes only
  396. // it does not matter if we use clang or clang++, same as
  397. // "gnu gcc" vs "gnu g++"
  398. if (compilerId == "MSVC")
  399. {
  400. compiler = "VC++";
  401. }
  402. else if (compilerId == "Clang")
  403. {
  404. compiler = "clang++";
  405. }
  406. else if (compilerId == "GNU")
  407. {
  408. compiler = "gnu g++";
  409. }
  410. return compiler;
  411. }
  412. std::string
  413. cmExtraCodeLiteGenerator::GetConfigurationName(const cmMakefile* mf) const
  414. {
  415. std::string confName = mf->GetSafeDefinition("CMAKE_BUILD_TYPE");
  416. // Trim the configuration name from whitespaces (left and right)
  417. confName.erase(0, confName.find_first_not_of(" \t\r\v\n"));
  418. confName.erase(confName.find_last_not_of(" \t\r\v\n")+1);
  419. if ( confName.empty() )
  420. {
  421. confName = "NoConfig";
  422. }
  423. return confName;
  424. }
  425. std::string
  426. cmExtraCodeLiteGenerator::GetBuildCommand(const cmMakefile* mf) const
  427. {
  428. std::string generator = mf->GetSafeDefinition("CMAKE_GENERATOR");
  429. std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
  430. std::string buildCommand = make; // Default
  431. if ( generator == "NMake Makefiles" ||
  432. generator == "Ninja" )
  433. {
  434. buildCommand = make;
  435. }
  436. else if ( generator == "MinGW Makefiles" ||
  437. generator == "Unix Makefiles" )
  438. {
  439. std::ostringstream ss;
  440. ss << make << " -j " << this->CpuCount;
  441. buildCommand = ss.str();
  442. }
  443. return buildCommand;
  444. }
  445. std::string
  446. cmExtraCodeLiteGenerator::GetCleanCommand(const cmMakefile* mf) const
  447. {
  448. return GetBuildCommand(mf) + " clean";
  449. }
  450. std::string
  451. cmExtraCodeLiteGenerator::GetRebuildCommand(const cmMakefile* mf) const
  452. {
  453. return GetCleanCommand(mf) + " && " + GetBuildCommand(mf);
  454. }
  455. std::string
  456. cmExtraCodeLiteGenerator::GetSingleFileBuildCommand
  457. (const cmMakefile* mf) const
  458. {
  459. std::string buildCommand;
  460. std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
  461. std::string generator = mf->GetSafeDefinition("CMAKE_GENERATOR");
  462. if ( generator == "Unix Makefiles" || generator == "MinGW Makefiles" )
  463. {
  464. std::ostringstream ss;
  465. ss << make << " -f$(ProjectPath)/Makefile $(CurrentFileName).cpp.o";
  466. buildCommand = ss.str();
  467. }
  468. return buildCommand;
  469. }