cmExtraSublimeTextGenerator.cxx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2004-2009 Kitware, Inc.
  4. Copyright 2004 Alexander Neundorf ([email protected])
  5. Distributed under the OSI-approved BSD License (the "License");
  6. see accompanying file Copyright.txt for details.
  7. This software is distributed WITHOUT ANY WARRANTY; without even the
  8. implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. See the License for more information.
  10. ============================================================================*/
  11. #include "cmExtraSublimeTextGenerator.h"
  12. #include "cmGlobalUnixMakefileGenerator3.h"
  13. #include "cmLocalUnixMakefileGenerator3.h"
  14. #include "cmMakefile.h"
  15. #include "cmake.h"
  16. #include "cmSourceFile.h"
  17. #include "cmGeneratedFileStream.h"
  18. #include "cmTarget.h"
  19. #include "cmSystemTools.h"
  20. #include "cmXMLSafe.h"
  21. #include <cmsys/SystemTools.hxx>
  22. #if defined(_WIN32)
  23. # define PATH_SEP "\\"
  24. #else
  25. # define PATH_SEP "/"
  26. #endif
  27. /* Some useful URLs:
  28. Homepage:
  29. http://www.sublimetext.com/
  30. File format docs:
  31. http://www.sublimetext.com/docs/2/projects.html
  32. http://sublimetext.info/docs/en/reference/build_systems.html
  33. */
  34. //----------------------------------------------------------------------------
  35. void cmExtraSublimeTextGenerator
  36. ::GetDocumentation(cmDocumentationEntry& entry, const char*) const
  37. {
  38. entry.Name = this->GetName();
  39. entry.Brief = "Generates Sublime Text 2 project files.";
  40. entry.Full =
  41. "Project files for Sublime Text 2 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. cmExtraSublimeTextGenerator::cmExtraSublimeTextGenerator()
  49. :cmExternalMakefileProjectGenerator()
  50. {
  51. #if defined(_WIN32)
  52. this->SupportedGlobalGenerators.push_back("MinGW Makefiles");
  53. this->SupportedGlobalGenerators.push_back("NMake Makefiles");
  54. // disable until somebody actually tests it:
  55. // this->SupportedGlobalGenerators.push_back("MSYS Makefiles");
  56. #endif
  57. this->SupportedGlobalGenerators.push_back("Ninja");
  58. this->SupportedGlobalGenerators.push_back("Unix Makefiles");
  59. }
  60. void cmExtraSublimeTextGenerator::Generate()
  61. {
  62. // for each sub project in the project create a sublime text 2 project
  63. for (std::map<cmStdString, std::vector<cmLocalGenerator*> >::const_iterator
  64. it = this->GlobalGenerator->GetProjectMap().begin();
  65. it!= this->GlobalGenerator->GetProjectMap().end();
  66. ++it)
  67. {
  68. // create a project file
  69. this->CreateProjectFile(it->second);
  70. }
  71. }
  72. /* create the project file */
  73. void cmExtraSublimeTextGenerator::CreateProjectFile(
  74. const std::vector<cmLocalGenerator*>& lgs)
  75. {
  76. const cmMakefile* mf=lgs[0]->GetMakefile();
  77. std::string outputDir=mf->GetStartOutputDirectory();
  78. std::string projectName=mf->GetProjectName();
  79. std::string filename=outputDir+"/";
  80. filename+=projectName+".sublime-project";
  81. this->CreateNewProjectFile(lgs, filename);
  82. }
  83. void cmExtraSublimeTextGenerator
  84. ::CreateNewProjectFile(const std::vector<cmLocalGenerator*>& lgs,
  85. const std::string& filename)
  86. {
  87. const cmMakefile* mf=lgs[0]->GetMakefile();
  88. cmGeneratedFileStream fout(filename.c_str());
  89. if(!fout)
  90. {
  91. return;
  92. }
  93. // A set of folders to include in the project
  94. std::set<std::string> folderIncludePatternsSet;
  95. // Collect all files, this includes source files and list files
  96. std::vector<std::string> allFiles;
  97. std::stringstream fileIncludePatternsStream;
  98. for (std::vector<cmLocalGenerator *>::const_iterator
  99. it = lgs.begin();
  100. it != lgs.end();
  101. ++it)
  102. {
  103. cmMakefile* makefile=(*it)->GetMakefile();
  104. // Add list files
  105. const std::vector<std::string> & listFiles =
  106. makefile->GetListFiles();
  107. allFiles.insert(allFiles.end(), listFiles.begin(), listFiles.end());
  108. // Add source files
  109. cmTargets& targets=makefile->GetTargets();
  110. for (cmTargets::iterator ti = targets.begin();
  111. ti != targets.end(); ti++)
  112. {
  113. switch(ti->second.GetType())
  114. {
  115. case cmTarget::EXECUTABLE:
  116. case cmTarget::STATIC_LIBRARY:
  117. case cmTarget::SHARED_LIBRARY:
  118. case cmTarget::MODULE_LIBRARY:
  119. case cmTarget::OBJECT_LIBRARY:
  120. case cmTarget::UTILITY: // can have sources since 2.6.3
  121. {
  122. const std::vector<cmSourceFile*>&sources=ti->second.GetSourceFiles();
  123. for (std::vector<cmSourceFile*>::const_iterator si=sources.begin();
  124. si!=sources.end(); si++)
  125. {
  126. // don't add source files which have the GENERATED property set:
  127. if ((*si)->GetPropertyAsBool("GENERATED"))
  128. {
  129. continue;
  130. }
  131. allFiles.push_back((*si)->GetFullPath());
  132. }
  133. }
  134. default: // intended fallthrough
  135. break;
  136. }
  137. }
  138. }
  139. // Convert
  140. const char* cmakeRoot = mf->GetDefinition("CMAKE_ROOT");
  141. for (std::vector<std::string>::const_iterator jt = allFiles.begin();
  142. jt != allFiles.end();
  143. ++jt)
  144. {
  145. // don't put cmake's own files into the project (#12110):
  146. if (jt->find(cmakeRoot) == 0)
  147. {
  148. continue;
  149. }
  150. const std::string &relative = cmSystemTools::RelativePath(
  151. lgs[0]->GetMakefile()->GetHomeDirectory(),
  152. jt->c_str());
  153. // Split filename from path
  154. std::string fileName = cmSystemTools::GetFilenameName(relative);
  155. std::string path = "";
  156. if (fileName.length() < relative.length())
  157. {
  158. path = relative.substr(0, relative.length() - fileName.length() - 1);
  159. }
  160. // We don't want paths with CMakeFiles in them
  161. if (relative.find("CMakeFiles") == std::string::npos)
  162. {
  163. if (fileIncludePatternsStream.tellp() > 0)
  164. {
  165. fileIncludePatternsStream << ", ";
  166. }
  167. fileIncludePatternsStream << "\"" << relative << "\"";
  168. if ((!path.empty()) && (folderIncludePatternsSet.find(path) ==
  169. folderIncludePatternsSet.end()))
  170. {
  171. folderIncludePatternsSet.insert(path);
  172. std::string::size_type splitIndex = path.rfind(PATH_SEP);
  173. std::string splitPath = path;
  174. while (splitIndex != std::string::npos)
  175. {
  176. splitPath = splitPath.substr(0, splitIndex);
  177. if ((splitPath.empty()) ||
  178. (folderIncludePatternsSet.insert(splitPath).second == false))
  179. {
  180. // If the path is already in the set then all of its
  181. // parents are as well
  182. break;
  183. }
  184. splitIndex = splitPath.rfind(PATH_SEP);
  185. }
  186. }
  187. }
  188. }
  189. // Write the folder entries to the project file
  190. const std::string &homeRelative = cmSystemTools::RelativePath(
  191. lgs[0]->GetMakefile()->GetHomeOutputDirectory(),
  192. lgs[0]->GetMakefile()->GetHomeDirectory());
  193. fout << "{\n";
  194. fout << "\t\"folders\":\n\t[\n\t";
  195. fout << "\t{\n\t\t\t\"path\": \"" << homeRelative << "\",\n";
  196. fout << "\t\t\t\"folder_include_patterns\": [";
  197. std::set<std::string>::const_iterator folderIter =
  198. folderIncludePatternsSet.begin();
  199. while (folderIter != folderIncludePatternsSet.end())
  200. {
  201. fout << "\"" << *folderIter << "\"";
  202. folderIter++;
  203. if (folderIter != folderIncludePatternsSet.end())
  204. {
  205. fout << ", ";
  206. }
  207. }
  208. fout << "],\n";
  209. fout << "\t\t\t\"file_include_patterns\": [" <<
  210. fileIncludePatternsStream.str() << "]\n";
  211. fout << "\t\t}\n\t";
  212. // End of the folders section
  213. fout << "]";
  214. // Write the beginning of the build systems section to the project file
  215. fout << ",\n\t\"build_systems\":\n\t[\n\t";
  216. // Set of include directories over all targets (sublime text/sublimeclang
  217. // doesn't currently support these settings per build system, only project
  218. // wide
  219. std::set<std::string> includeDirs;
  220. std::set<std::string> defines;
  221. std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
  222. std::string compiler = "";
  223. this->AppendTarget(fout, "all", 0, make.c_str(), mf, compiler.c_str(),
  224. includeDirs, defines, true);
  225. this->AppendTarget(fout, "clean", 0, make.c_str(), mf, compiler.c_str(),
  226. includeDirs, defines, false);
  227. // add all executable and library targets and some of the GLOBAL
  228. // and UTILITY targets
  229. for (std::vector<cmLocalGenerator*>::const_iterator lg=lgs.begin();
  230. lg!=lgs.end(); lg++)
  231. {
  232. cmMakefile* makefile=(*lg)->GetMakefile();
  233. cmTargets& targets=makefile->GetTargets();
  234. for (cmTargets::iterator ti = targets.begin();
  235. ti != targets.end(); ti++)
  236. {
  237. switch(ti->second.GetType())
  238. {
  239. case cmTarget::GLOBAL_TARGET:
  240. {
  241. bool insertTarget = false;
  242. // Only add the global targets from CMAKE_BINARY_DIR,
  243. // not from the subdirs
  244. if (strcmp(makefile->GetStartOutputDirectory(),
  245. makefile->GetHomeOutputDirectory())==0)
  246. {
  247. insertTarget = true;
  248. // only add the "edit_cache" target if it's not ccmake, because
  249. // this will not work within the IDE
  250. if (ti->first == "edit_cache")
  251. {
  252. const char* editCommand = makefile->GetDefinition
  253. ("CMAKE_EDIT_COMMAND");
  254. if (editCommand == 0)
  255. {
  256. insertTarget = false;
  257. }
  258. else if (strstr(editCommand, "ccmake")!=NULL)
  259. {
  260. insertTarget = false;
  261. }
  262. }
  263. }
  264. if (insertTarget)
  265. {
  266. this->AppendTarget(fout, ti->first.c_str(), 0,
  267. make.c_str(), makefile, compiler.c_str(),
  268. includeDirs, defines, false);
  269. }
  270. }
  271. break;
  272. case cmTarget::UTILITY:
  273. // Add all utility targets, except the Nightly/Continuous/
  274. // Experimental-"sub"targets as e.g. NightlyStart
  275. if (((ti->first.find("Nightly")==0) &&(ti->first!="Nightly"))
  276. || ((ti->first.find("Continuous")==0)&&(ti->first!="Continuous"))
  277. || ((ti->first.find("Experimental")==0)
  278. && (ti->first!="Experimental")))
  279. {
  280. break;
  281. }
  282. this->AppendTarget(fout, ti->first.c_str(), 0,
  283. make.c_str(), makefile, compiler.c_str(),
  284. includeDirs, defines, false);
  285. break;
  286. case cmTarget::EXECUTABLE:
  287. case cmTarget::STATIC_LIBRARY:
  288. case cmTarget::SHARED_LIBRARY:
  289. case cmTarget::MODULE_LIBRARY:
  290. case cmTarget::OBJECT_LIBRARY:
  291. {
  292. this->AppendTarget(fout, ti->first.c_str(), &ti->second,
  293. make.c_str(), makefile, compiler.c_str(),
  294. includeDirs, defines, false);
  295. std::string fastTarget = ti->first;
  296. fastTarget += "/fast";
  297. this->AppendTarget(fout, fastTarget.c_str(), &ti->second,
  298. make.c_str(), makefile, compiler.c_str(),
  299. includeDirs, defines, false);
  300. }
  301. break;
  302. default:
  303. break;
  304. }
  305. }
  306. }
  307. // End of build_systems
  308. fout << "\n\t]";
  309. // Write the settings section with sublimeclang options
  310. fout << ",\n\t\"settings\":\n\t{\n\t";
  311. fout << "\t\"sublimeclang_options\":\n\t\t[\n\t\t";
  312. std::set<std::string>::const_iterator stringSetIter = includeDirs.begin();
  313. while (stringSetIter != includeDirs.end())
  314. {
  315. const std::string &includeDir = *stringSetIter;
  316. const std::string &relative = cmSystemTools::RelativePath(
  317. lgs[0]->GetMakefile()->GetHomeOutputDirectory(),
  318. includeDir.c_str());
  319. fout << "\t\"-I" << relative << "\"";
  320. stringSetIter++;
  321. if (stringSetIter != includeDirs.end())
  322. {
  323. fout << ",";
  324. }
  325. fout << "\n\t\t";
  326. }
  327. stringSetIter = defines.begin();
  328. while (stringSetIter != defines.end())
  329. {
  330. fout << "\t\"-D" << *stringSetIter << "\"";
  331. stringSetIter++;
  332. if (stringSetIter != defines.end())
  333. {
  334. fout << ",";
  335. }
  336. fout << "\n\t\t";
  337. }
  338. // End of the sublimeclang_options section
  339. fout << "]\n\t";
  340. // End of the settings section
  341. fout << "}\n";
  342. // End of file
  343. fout << "}";
  344. }
  345. // Generate the build_system entry for one target
  346. void cmExtraSublimeTextGenerator::AppendTarget(cmGeneratedFileStream& fout,
  347. const char* targetName,
  348. cmTarget* target,
  349. const char* make,
  350. const cmMakefile* makefile,
  351. const char* compiler,
  352. std::set<std::string>&
  353. includeDirs,
  354. std::set<std::string>& defines,
  355. bool firstTarget)
  356. {
  357. if (target != 0)
  358. {
  359. // the compilerdefines for this target
  360. cmGeneratorTarget *gtgt = this->GlobalGenerator
  361. ->GetGeneratorTarget(target);
  362. std::string cdefs = gtgt->GetCompileDefinitions();
  363. if(cdefs.empty())
  364. {
  365. // Expand the list.
  366. std::vector<std::string> defs;
  367. cmSystemTools::ExpandListArgument(cdefs.c_str(), defs);
  368. for(std::vector<std::string>::const_iterator di = defs.begin();
  369. di != defs.end(); ++di)
  370. {
  371. cmXMLSafe safedef(di->c_str());
  372. defines.insert(safedef.str());
  373. }
  374. }
  375. // the include directories for this target
  376. std::vector<std::string> includes;
  377. target->GetMakefile()->GetLocalGenerator()->
  378. GetIncludeDirectories(includes, gtgt);
  379. for(std::vector<std::string>::const_iterator dirIt=includes.begin();
  380. dirIt != includes.end();
  381. ++dirIt)
  382. {
  383. includeDirs.insert(*dirIt);
  384. }
  385. std::string systemIncludeDirs = makefile->GetSafeDefinition(
  386. "CMAKE_EXTRA_GENERATOR_C_SYSTEM_INCLUDE_DIRS");
  387. if (!systemIncludeDirs.empty())
  388. {
  389. std::vector<std::string> dirs;
  390. cmSystemTools::ExpandListArgument(systemIncludeDirs.c_str(), dirs);
  391. for(std::vector<std::string>::const_iterator dirIt=dirs.begin();
  392. dirIt != dirs.end();
  393. ++dirIt)
  394. {
  395. includeDirs.insert(*dirIt);
  396. }
  397. }
  398. systemIncludeDirs = makefile->GetSafeDefinition(
  399. "CMAKE_EXTRA_GENERATOR_CXX_SYSTEM_INCLUDE_DIRS");
  400. if (!systemIncludeDirs.empty())
  401. {
  402. std::vector<std::string> dirs;
  403. cmSystemTools::ExpandListArgument(systemIncludeDirs.c_str(), dirs);
  404. for(std::vector<std::string>::const_iterator dirIt=dirs.begin();
  405. dirIt != dirs.end();
  406. ++dirIt)
  407. {
  408. includeDirs.insert(*dirIt);
  409. }
  410. }
  411. }
  412. // Write out the build_system data for this target
  413. std::string makefileName = makefile->GetStartOutputDirectory();
  414. makefileName += "/Makefile";
  415. if (!firstTarget)
  416. {
  417. fout << ",\n\t";
  418. }
  419. fout << "\t{\n\t\t\t\"name\": \"" << makefile->GetProjectName() << " - " <<
  420. targetName << "\",\n";
  421. fout << "\t\t\t\"cmd\": [" <<
  422. this->BuildMakeCommand(make, makefileName.c_str(), targetName) <<
  423. "],\n";
  424. fout << "\t\t\t\"working_dir\": \"${project_path}\",\n";
  425. fout << "\t\t\t\"file_regex\": \"^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$\"\n";
  426. fout << "\t\t}";
  427. }
  428. // Create the command line for building the given target using the selected
  429. // make
  430. std::string cmExtraSublimeTextGenerator::BuildMakeCommand(
  431. const std::string& make, const char* makefile, const char* target)
  432. {
  433. std::string command = "\"";
  434. command += make + "\"";
  435. if (strcmp(this->GlobalGenerator->GetName(), "NMake Makefiles")==0)
  436. {
  437. std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
  438. command += ", \"/NOLOGO\", \"/f\", \"";
  439. command += makefileName + "\"";
  440. command += ", \"VERBOSE=1\", \"";
  441. command += target;
  442. command += "\"";
  443. }
  444. else if (strcmp(this->GlobalGenerator->GetName(), "MinGW Makefiles")==0)
  445. {
  446. // no escaping of spaces in this case, see
  447. // http://public.kitware.com/Bug/view.php?id=10014
  448. std::string makefileName = makefile;
  449. command += ", \"-f\", \"";
  450. command += makefileName + "\"";
  451. command += ", \"VERBOSE=1\", \"";
  452. command += target;
  453. command += "\"";
  454. }
  455. else
  456. {
  457. std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
  458. command += ", \"-f\", \"";
  459. command += makefileName + "\"";
  460. command += ", \"VERBOSE=1\", \"";
  461. command += target;
  462. command += "\"";
  463. }
  464. return command;
  465. }