cmExtraCodeBlocksGenerator.cxx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. /*=========================================================================
  2. Program: CMake - Cross-Platform Makefile Generator
  3. Module: $RCSfile$
  4. Language: C++
  5. Date: $Date$
  6. Version: $Revision$
  7. Copyright (c) 2002 Kitware, Inc., Insight Consortium. All rights reserved.
  8. Copyright (c) 2004 Alexander Neundorf [email protected], All rights reserved.
  9. See Copyright.txt or http://www.cmake.org/HTML/Copyright.html for details.
  10. This software is distributed WITHOUT ANY WARRANTY; without even
  11. the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  12. PURPOSE. See the above copyright notices for more information.
  13. =========================================================================*/
  14. #include "cmExtraCodeBlocksGenerator.h"
  15. #include "cmGlobalUnixMakefileGenerator3.h"
  16. #include "cmLocalUnixMakefileGenerator3.h"
  17. #include "cmMakefile.h"
  18. #include "cmake.h"
  19. #include "cmSourceFile.h"
  20. #include "cmGeneratedFileStream.h"
  21. #include "cmTarget.h"
  22. #include "cmSystemTools.h"
  23. #include <cmsys/SystemTools.hxx>
  24. /* Some useful URLs:
  25. Homepage:
  26. http://www.codeblocks.org
  27. File format docs:
  28. http://wiki.codeblocks.org/index.php?title=File_formats_description
  29. http://wiki.codeblocks.org/index.php?title=Workspace_file
  30. http://wiki.codeblocks.org/index.php?title=Project_file
  31. Discussion:
  32. http://forums.codeblocks.org/index.php/topic,6789.0.html
  33. */
  34. //----------------------------------------------------------------------------
  35. void cmExtraCodeBlocksGenerator
  36. ::GetDocumentation(cmDocumentationEntry& entry, const char*) const
  37. {
  38. entry.Name = this->GetName();
  39. entry.Brief = "Generates CodeBlocks project files.";
  40. entry.Full =
  41. "Project files for CodeBlocks will be created in the top directory "
  42. "and in every subdirectory which features a CMakeLists.txt file "
  43. "containing a PROJECT() call. "
  44. "Additionally a hierarchy of makefiles is generated into the "
  45. "build tree. The appropriate make program can build the project through "
  46. "the default make target. A \"make install\" target is also provided.";
  47. }
  48. cmExtraCodeBlocksGenerator::cmExtraCodeBlocksGenerator()
  49. :cmExternalMakefileProjectGenerator()
  50. {
  51. #if defined(_WIN32)
  52. this->SupportedGlobalGenerators.push_back("MinGW Makefiles");
  53. // disable until somebody actually tests it:
  54. // this->SupportedGlobalGenerators.push_back("NMake Makefiles");
  55. // this->SupportedGlobalGenerators.push_back("MSYS Makefiles");
  56. #endif
  57. this->SupportedGlobalGenerators.push_back("Unix Makefiles");
  58. }
  59. void cmExtraCodeBlocksGenerator::SetGlobalGenerator(
  60. cmGlobalGenerator* generator)
  61. {
  62. cmExternalMakefileProjectGenerator::SetGlobalGenerator(generator);
  63. cmGlobalUnixMakefileGenerator3* mf = (cmGlobalUnixMakefileGenerator3*)
  64. generator;
  65. mf->SetToolSupportsColor(false);
  66. mf->SetForceVerboseMakefiles(true);
  67. }
  68. void cmExtraCodeBlocksGenerator::Generate()
  69. {
  70. // for each sub project in the project create a codeblocks project
  71. for (std::map<cmStdString, std::vector<cmLocalGenerator*> >::const_iterator
  72. it = this->GlobalGenerator->GetProjectMap().begin();
  73. it!= this->GlobalGenerator->GetProjectMap().end();
  74. ++it)
  75. {
  76. // create a project file
  77. this->CreateProjectFile(it->second);
  78. }
  79. }
  80. /* create the project file */
  81. void cmExtraCodeBlocksGenerator::CreateProjectFile(
  82. const std::vector<cmLocalGenerator*>& lgs)
  83. {
  84. const cmMakefile* mf=lgs[0]->GetMakefile();
  85. std::string outputDir=mf->GetStartOutputDirectory();
  86. std::string projectName=mf->GetProjectName();
  87. std::string filename=outputDir+"/";
  88. filename+=projectName+".cbp";
  89. std::string sessionFilename=outputDir+"/";
  90. sessionFilename+=projectName+".layout";
  91. this->CreateNewProjectFile(lgs, filename);
  92. }
  93. void cmExtraCodeBlocksGenerator
  94. ::CreateNewProjectFile(const std::vector<cmLocalGenerator*>& lgs,
  95. const std::string& filename)
  96. {
  97. const cmMakefile* mf=lgs[0]->GetMakefile();
  98. cmGeneratedFileStream fout(filename.c_str());
  99. if(!fout)
  100. {
  101. return;
  102. }
  103. // figure out the compiler
  104. std::string compiler = this->GetCBCompilerId(mf);
  105. std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
  106. fout<<"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>\n"
  107. "<CodeBlocks_project_file>\n"
  108. " <FileVersion major=\"1\" minor=\"6\" />\n"
  109. " <Project>\n"
  110. " <Option title=\"" << mf->GetProjectName()<<"\" />\n"
  111. " <Option makefile_is_custom=\"1\" />\n"
  112. " <Option compiler=\"" << compiler << "\" />\n"
  113. " <Build>\n";
  114. bool installTargetCreated = false;
  115. bool installStripTargetCreated = false;
  116. bool testTargetCreated = false;
  117. bool experimentalTargetCreated = false;
  118. bool nightlyTargetCreated = false;
  119. bool packageTargetCreated = false;
  120. bool packageSourceTargetCreated = false;
  121. bool rebuildCacheTargetCreated = false;
  122. this->AppendTarget(fout, "all", 0, make.c_str(), mf, compiler.c_str());
  123. // add all executable and library targets and some of the GLOBAL
  124. // and UTILITY targets
  125. for (std::vector<cmLocalGenerator*>::const_iterator lg=lgs.begin();
  126. lg!=lgs.end(); lg++)
  127. {
  128. cmMakefile* makefile=(*lg)->GetMakefile();
  129. cmTargets& targets=makefile->GetTargets();
  130. for (cmTargets::iterator ti = targets.begin();
  131. ti != targets.end(); ti++)
  132. {
  133. switch(ti->second.GetType())
  134. {
  135. case cmTarget::UTILITY:
  136. case cmTarget::GLOBAL_TARGET:
  137. // only add these targets once
  138. if ((ti->first=="install") && (installTargetCreated==false))
  139. {
  140. installTargetCreated=true;
  141. }
  142. else if ((ti->first=="install/strip")
  143. && (installStripTargetCreated==false))
  144. {
  145. installStripTargetCreated=true;
  146. }
  147. else if ((ti->first=="test") && (testTargetCreated==false))
  148. {
  149. testTargetCreated=true;
  150. }
  151. else if ((ti->first=="Experimental")
  152. && (experimentalTargetCreated==false))
  153. {
  154. experimentalTargetCreated=true;
  155. }
  156. else if ((ti->first=="Nightly") && (nightlyTargetCreated==false))
  157. {
  158. nightlyTargetCreated=true;
  159. }
  160. else if ((ti->first=="package") && (packageTargetCreated==false))
  161. {
  162. packageTargetCreated=true;
  163. }
  164. else if ((ti->first=="package_source")
  165. && (packageSourceTargetCreated==false))
  166. {
  167. packageSourceTargetCreated=true;
  168. }
  169. else if ((ti->first=="rebuild_cache")
  170. && (rebuildCacheTargetCreated==false))
  171. {
  172. rebuildCacheTargetCreated=true;
  173. }
  174. else
  175. {
  176. break;
  177. }
  178. this->AppendTarget(fout, ti->first.c_str(), 0,
  179. make.c_str(), makefile, compiler.c_str());
  180. break;
  181. case cmTarget::EXECUTABLE:
  182. case cmTarget::STATIC_LIBRARY:
  183. case cmTarget::SHARED_LIBRARY:
  184. case cmTarget::MODULE_LIBRARY:
  185. {
  186. this->AppendTarget(fout, ti->first.c_str(), &ti->second,
  187. make.c_str(), makefile, compiler.c_str());
  188. std::string fastTarget = ti->first;
  189. fastTarget += "/fast";
  190. this->AppendTarget(fout, fastTarget.c_str(), &ti->second,
  191. make.c_str(), makefile, compiler.c_str());
  192. }
  193. break;
  194. // ignore these:
  195. case cmTarget::INSTALL_FILES:
  196. case cmTarget::INSTALL_PROGRAMS:
  197. case cmTarget::INSTALL_DIRECTORY:
  198. default:
  199. break;
  200. }
  201. }
  202. }
  203. fout<<" </Build>\n";
  204. // Collect all used source files in the project
  205. // Sort them into two containers, one for C/C++ implementation files
  206. // which may have an acompanying header, one for all other files
  207. std::map<std::string, cmSourceFile*> cFiles;
  208. std::set<std::string> otherFiles;
  209. for (std::vector<cmLocalGenerator*>::const_iterator lg=lgs.begin();
  210. lg!=lgs.end(); lg++)
  211. {
  212. cmMakefile* makefile=(*lg)->GetMakefile();
  213. cmTargets& targets=makefile->GetTargets();
  214. for (cmTargets::iterator ti = targets.begin();
  215. ti != targets.end(); ti++)
  216. {
  217. switch(ti->second.GetType())
  218. {
  219. case cmTarget::EXECUTABLE:
  220. case cmTarget::STATIC_LIBRARY:
  221. case cmTarget::SHARED_LIBRARY:
  222. case cmTarget::MODULE_LIBRARY:
  223. {
  224. const std::vector<cmSourceFile*>&sources=ti->second.GetSourceFiles();
  225. for (std::vector<cmSourceFile*>::const_iterator si=sources.begin();
  226. si!=sources.end(); si++)
  227. {
  228. // check whether it is a C/C++ implementation file
  229. bool isCFile = false;
  230. if ((*si)->GetLanguage() && (*(*si)->GetLanguage() == 'C'))
  231. {
  232. for(std::vector<std::string>::const_iterator
  233. ext = mf->GetSourceExtensions().begin();
  234. ext != mf->GetSourceExtensions().end();
  235. ++ext)
  236. {
  237. if ((*si)->GetExtension() == *ext)
  238. {
  239. isCFile = true;
  240. break;
  241. }
  242. }
  243. }
  244. // then put it accordingly into one of the two containers
  245. if (isCFile)
  246. {
  247. cFiles[(*si)->GetFullPath()] = *si ;
  248. }
  249. else
  250. {
  251. otherFiles.insert((*si)->GetFullPath());
  252. }
  253. }
  254. }
  255. default: // intended fallthrough
  256. break;
  257. }
  258. }
  259. }
  260. // The following loop tries to add header files matching to implementation
  261. // files to the project. It does that by iterating over all source files,
  262. // replacing the file name extension with ".h" and checks whether such a
  263. // file exists. If it does, it is inserted into the map of files.
  264. // A very similar version of that code exists also in the kdevelop
  265. // project generator.
  266. for (std::map<std::string, cmSourceFile*>::const_iterator
  267. sit=cFiles.begin();
  268. sit!=cFiles.end();
  269. ++sit)
  270. {
  271. std::string headerBasename=cmSystemTools::GetFilenamePath(sit->first);
  272. headerBasename+="/";
  273. headerBasename+=cmSystemTools::GetFilenameWithoutExtension(sit->first);
  274. // check if there's a matching header around
  275. for(std::vector<std::string>::const_iterator
  276. ext = mf->GetHeaderExtensions().begin();
  277. ext != mf->GetHeaderExtensions().end();
  278. ++ext)
  279. {
  280. std::string hname=headerBasename;
  281. hname += ".";
  282. hname += *ext;
  283. // if it's already in the set, don't check if it exists on disk
  284. std::set<std::string>::const_iterator headerIt=otherFiles.find(hname);
  285. if (headerIt != otherFiles.end())
  286. {
  287. break;
  288. }
  289. if(cmSystemTools::FileExists(hname.c_str()))
  290. {
  291. otherFiles.insert(hname);
  292. break;
  293. }
  294. }
  295. }
  296. // insert all source files in the CodeBlocks project
  297. // first the C/C++ implementation files, then all others
  298. for (std::map<std::string, cmSourceFile*>::const_iterator
  299. sit=cFiles.begin();
  300. sit!=cFiles.end();
  301. ++sit)
  302. {
  303. fout<<" <Unit filename=\""<< sit->first <<"\">\n"
  304. " </Unit>\n";
  305. }
  306. for (std::set<std::string>::const_iterator
  307. sit=otherFiles.begin();
  308. sit!=otherFiles.end();
  309. ++sit)
  310. {
  311. fout<<" <Unit filename=\""<< sit->c_str() <<"\">\n"
  312. " </Unit>\n";
  313. }
  314. fout<<" </Project>\n"
  315. "</CodeBlocks_project_file>\n";
  316. }
  317. // Generate the xml code for one target.
  318. void cmExtraCodeBlocksGenerator::AppendTarget(cmGeneratedFileStream& fout,
  319. const char* targetName,
  320. cmTarget* target,
  321. const char* make,
  322. const cmMakefile* makefile,
  323. const char* compiler)
  324. {
  325. std::string makefileName = makefile->GetStartOutputDirectory();
  326. makefileName += "/Makefile";
  327. makefileName = cmSystemTools::ConvertToOutputPath(makefileName.c_str());
  328. fout<<" <Target title=\"" << targetName << "\">\n";
  329. if (target!=0)
  330. {
  331. int cbTargetType = this->GetCBTargetType(target);
  332. fout<<" <Option output=\"" << target->GetLocation(0)
  333. << "\" prefix_auto=\"0\" extension_auto=\"0\" />\n"
  334. " <Option working_dir=\""
  335. << makefile->GetStartOutputDirectory() << "\" />\n"
  336. " <Option object_output=\"./\" />\n"
  337. " <Option type=\"" << cbTargetType << "\" />\n"
  338. " <Option compiler=\"" << compiler << "\" />\n"
  339. " <Compiler>\n";
  340. // the include directories for this target
  341. const std::vector<std::string>& incDirs =
  342. target->GetMakefile()->GetIncludeDirectories();
  343. for(std::vector<std::string>::const_iterator dirIt=incDirs.begin();
  344. dirIt != incDirs.end();
  345. ++dirIt)
  346. {
  347. fout <<" <Add directory=\"" << dirIt->c_str() << "\" />\n";
  348. }
  349. fout<<" </Compiler>\n";
  350. }
  351. else // e.g. all and the GLOBAL and UTILITY targets
  352. {
  353. fout<<" <Option working_dir=\""
  354. << makefile->GetStartOutputDirectory() << "\" />\n"
  355. <<" <Option type=\"" << 4 << "\" />\n";
  356. }
  357. fout<<" <MakeCommands>\n"
  358. " <Build command=\""
  359. << this->BuildMakeCommand(make, makefileName.c_str(), targetName)
  360. << "\" />\n"
  361. " <CompileFile command=\""
  362. << this->BuildMakeCommand(make, makefileName.c_str(),"&quot;$file&quot;")
  363. << "\" />\n"
  364. " <Clean command=\""
  365. << this->BuildMakeCommand(make, makefileName.c_str(), "clean")
  366. << "\" />\n"
  367. " <DistClean command=\""
  368. << this->BuildMakeCommand(make, makefileName.c_str(), "clean")
  369. << "\" />\n"
  370. " </MakeCommands>\n"
  371. " </Target>\n";
  372. }
  373. // Translate the cmake compiler id into the CodeBlocks compiler id
  374. std::string cmExtraCodeBlocksGenerator::GetCBCompilerId(const cmMakefile* mf)
  375. {
  376. // figure out which language to use
  377. // for now care only for C and C++
  378. std::string compilerIdVar = "CMAKE_CXX_COMPILER_ID";
  379. if (this->GlobalGenerator->GetLanguageEnabled("CXX") == false)
  380. {
  381. compilerIdVar = "CMAKE_C_COMPILER_ID";
  382. }
  383. std::string hostSystemName = mf->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
  384. std::string systemName = mf->GetSafeDefinition("CMAKE_SYSTEM_NAME");
  385. std::string compilerId = mf->GetRequiredDefinition(compilerIdVar.c_str());
  386. std::string compiler = "gcc";
  387. if (compilerId == "MSVC")
  388. {
  389. compiler = "msvc";
  390. }
  391. else if (compilerId == "Borland")
  392. {
  393. compiler = "bcc";
  394. }
  395. else if (compilerId == "SDCC")
  396. {
  397. compiler = "sdcc";
  398. }
  399. else if (compilerId == "Intel")
  400. {
  401. compiler = "icc";
  402. }
  403. else if (compilerId == "Watcom")
  404. {
  405. compiler = "ow";
  406. }
  407. else if (compilerId == "GNU")
  408. {
  409. compiler = "gcc";
  410. }
  411. return compiler;
  412. }
  413. // Translate the cmake target type into the CodeBlocks target type id
  414. int cmExtraCodeBlocksGenerator::GetCBTargetType(cmTarget* target)
  415. {
  416. if ( target->GetType()==cmTarget::EXECUTABLE)
  417. {
  418. if ((target->GetPropertyAsBool("WIN32_EXECUTABLE"))
  419. || (target->GetPropertyAsBool("MACOSX_BUNDLE")))
  420. {
  421. return 0;
  422. }
  423. else
  424. {
  425. return 1;
  426. }
  427. }
  428. else if ( target->GetType()==cmTarget::STATIC_LIBRARY)
  429. {
  430. return 2;
  431. }
  432. else if ((target->GetType()==cmTarget::SHARED_LIBRARY)
  433. || (target->GetType()==cmTarget::MODULE_LIBRARY))
  434. {
  435. return 3;
  436. }
  437. return 4;
  438. }
  439. // Create the command line for building the given target using the selected
  440. // make
  441. std::string cmExtraCodeBlocksGenerator::BuildMakeCommand(
  442. const std::string& make, const char* makefile, const char* target)
  443. {
  444. std::string command = make;
  445. if (strcmp(this->GlobalGenerator->GetName(), "NMake Makefiles")==0)
  446. {
  447. command += " /NOLOGO /f &quot;";
  448. command += makefile;
  449. command += "&quot; ";
  450. command += target;
  451. }
  452. else if (strcmp(this->GlobalGenerator->GetName(), "MinGW Makefiles")==0)
  453. {
  454. command += " -f ";
  455. command += makefile;
  456. command += " ";
  457. command += target;
  458. }
  459. else
  460. {
  461. command += " -f &quot;";
  462. command += makefile;
  463. command += "&quot; ";
  464. command += target;
  465. }
  466. return command;
  467. }