cmExtraSublimeTextGenerator.cxx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  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 "cmGeneratedFileStream.h"
  13. #include "cmGeneratorTarget.h"
  14. #include "cmGlobalUnixMakefileGenerator3.h"
  15. #include "cmLocalGenerator.h"
  16. #include "cmLocalUnixMakefileGenerator3.h"
  17. #include "cmMakefile.h"
  18. #include "cmSourceFile.h"
  19. #include "cmSystemTools.h"
  20. #include "cmake.h"
  21. #include <cmsys/SystemTools.hxx>
  22. /*
  23. Sublime Text 2 Generator
  24. Author: Morné Chamberlain
  25. This generator was initially based off of the CodeBlocks generator.
  26. Some useful URLs:
  27. Homepage:
  28. http://www.sublimetext.com/
  29. File format docs:
  30. http://www.sublimetext.com/docs/2/projects.html
  31. http://sublimetext.info/docs/en/reference/build_systems.html
  32. */
  33. void cmExtraSublimeTextGenerator::GetDocumentation(cmDocumentationEntry& entry,
  34. const std::string&) const
  35. {
  36. entry.Name = this->GetName();
  37. entry.Brief = "Generates Sublime Text 2 project files.";
  38. }
  39. cmExtraSublimeTextGenerator::cmExtraSublimeTextGenerator()
  40. : cmExternalMakefileProjectGenerator()
  41. {
  42. #if defined(_WIN32)
  43. this->SupportedGlobalGenerators.push_back("MinGW Makefiles");
  44. this->SupportedGlobalGenerators.push_back("NMake Makefiles");
  45. // disable until somebody actually tests it:
  46. // this->SupportedGlobalGenerators.push_back("MSYS Makefiles");
  47. #endif
  48. this->SupportedGlobalGenerators.push_back("Ninja");
  49. this->SupportedGlobalGenerators.push_back("Unix Makefiles");
  50. }
  51. void cmExtraSublimeTextGenerator::Generate()
  52. {
  53. // for each sub project in the project create a sublime text 2 project
  54. for (std::map<std::string, std::vector<cmLocalGenerator*> >::const_iterator
  55. it = this->GlobalGenerator->GetProjectMap().begin();
  56. it != this->GlobalGenerator->GetProjectMap().end(); ++it) {
  57. // create a project file
  58. this->CreateProjectFile(it->second);
  59. }
  60. }
  61. void cmExtraSublimeTextGenerator::CreateProjectFile(
  62. const std::vector<cmLocalGenerator*>& lgs)
  63. {
  64. std::string outputDir = lgs[0]->GetCurrentBinaryDirectory();
  65. std::string projectName = lgs[0]->GetProjectName();
  66. const std::string filename =
  67. outputDir + "/" + projectName + ".sublime-project";
  68. this->CreateNewProjectFile(lgs, filename);
  69. }
  70. void cmExtraSublimeTextGenerator::CreateNewProjectFile(
  71. const std::vector<cmLocalGenerator*>& lgs, const std::string& filename)
  72. {
  73. const cmMakefile* mf = lgs[0]->GetMakefile();
  74. cmGeneratedFileStream fout(filename.c_str());
  75. if (!fout) {
  76. return;
  77. }
  78. const std::string& sourceRootRelativeToOutput = cmSystemTools::RelativePath(
  79. lgs[0]->GetBinaryDirectory(), lgs[0]->GetSourceDirectory());
  80. // Write the folder entries to the project file
  81. fout << "{\n";
  82. fout << "\t\"folders\":\n\t[\n\t";
  83. if (!sourceRootRelativeToOutput.empty()) {
  84. fout << "\t{\n\t\t\t\"path\": \"" << sourceRootRelativeToOutput << "\"";
  85. const std::string& outputRelativeToSourceRoot =
  86. cmSystemTools::RelativePath(lgs[0]->GetSourceDirectory(),
  87. lgs[0]->GetBinaryDirectory());
  88. if ((!outputRelativeToSourceRoot.empty()) &&
  89. ((outputRelativeToSourceRoot.length() < 3) ||
  90. (outputRelativeToSourceRoot.substr(0, 3) != "../"))) {
  91. fout << ",\n\t\t\t\"folder_exclude_patterns\": [\""
  92. << outputRelativeToSourceRoot << "\"]";
  93. }
  94. } else {
  95. fout << "\t{\n\t\t\t\"path\": \"./\"";
  96. }
  97. fout << "\n\t\t}";
  98. // End of the folders section
  99. fout << "\n\t]";
  100. // Write the beginning of the build systems section to the project file
  101. fout << ",\n\t\"build_systems\":\n\t[\n\t";
  102. // Set of include directories over all targets (sublime text/sublimeclang
  103. // doesn't currently support these settings per build system, only project
  104. // wide
  105. MapSourceFileFlags sourceFileFlags;
  106. AppendAllTargets(lgs, mf, fout, sourceFileFlags);
  107. // End of build_systems
  108. fout << "\n\t]";
  109. fout << "\n\t}";
  110. }
  111. void cmExtraSublimeTextGenerator::AppendAllTargets(
  112. const std::vector<cmLocalGenerator*>& lgs, const cmMakefile* mf,
  113. cmGeneratedFileStream& fout, MapSourceFileFlags& sourceFileFlags)
  114. {
  115. std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
  116. std::string compiler = "";
  117. if (!lgs.empty()) {
  118. this->AppendTarget(fout, "all", lgs[0], 0, make.c_str(), mf,
  119. compiler.c_str(), sourceFileFlags, true);
  120. this->AppendTarget(fout, "clean", lgs[0], 0, make.c_str(), mf,
  121. compiler.c_str(), sourceFileFlags, false);
  122. }
  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. cmMakefile* makefile = (*lg)->GetMakefile();
  128. std::vector<cmGeneratorTarget*> targets = (*lg)->GetGeneratorTargets();
  129. for (std::vector<cmGeneratorTarget*>::iterator ti = targets.begin();
  130. ti != targets.end(); ti++) {
  131. std::string targetName = (*ti)->GetName();
  132. switch ((*ti)->GetType()) {
  133. case cmState::GLOBAL_TARGET: {
  134. // Only add the global targets from CMAKE_BINARY_DIR,
  135. // not from the subdirs
  136. if (strcmp((*lg)->GetCurrentBinaryDirectory(),
  137. (*lg)->GetBinaryDirectory()) == 0) {
  138. this->AppendTarget(fout, targetName, *lg, 0, make.c_str(),
  139. makefile, compiler.c_str(), sourceFileFlags,
  140. false);
  141. }
  142. } break;
  143. case cmState::UTILITY:
  144. // Add all utility targets, except the Nightly/Continuous/
  145. // Experimental-"sub"targets as e.g. NightlyStart
  146. if (((targetName.find("Nightly") == 0) &&
  147. (targetName != "Nightly")) ||
  148. ((targetName.find("Continuous") == 0) &&
  149. (targetName != "Continuous")) ||
  150. ((targetName.find("Experimental") == 0) &&
  151. (targetName != "Experimental"))) {
  152. break;
  153. }
  154. this->AppendTarget(fout, targetName, *lg, 0, make.c_str(), makefile,
  155. compiler.c_str(), sourceFileFlags, false);
  156. break;
  157. case cmState::EXECUTABLE:
  158. case cmState::STATIC_LIBRARY:
  159. case cmState::SHARED_LIBRARY:
  160. case cmState::MODULE_LIBRARY:
  161. case cmState::OBJECT_LIBRARY: {
  162. this->AppendTarget(fout, targetName, *lg, *ti, make.c_str(),
  163. makefile, compiler.c_str(), sourceFileFlags,
  164. false);
  165. std::string fastTarget = targetName;
  166. fastTarget += "/fast";
  167. this->AppendTarget(fout, fastTarget, *lg, *ti, make.c_str(),
  168. makefile, compiler.c_str(), sourceFileFlags,
  169. false);
  170. } break;
  171. default:
  172. break;
  173. }
  174. }
  175. }
  176. }
  177. void cmExtraSublimeTextGenerator::AppendTarget(
  178. cmGeneratedFileStream& fout, const std::string& targetName,
  179. cmLocalGenerator* lg, cmGeneratorTarget* target, const char* make,
  180. const cmMakefile* makefile,
  181. const char*, // compiler
  182. MapSourceFileFlags& sourceFileFlags, bool firstTarget)
  183. {
  184. if (target != 0) {
  185. std::vector<cmSourceFile*> sourceFiles;
  186. target->GetSourceFiles(sourceFiles,
  187. makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
  188. std::vector<cmSourceFile*>::const_iterator sourceFilesEnd =
  189. sourceFiles.end();
  190. for (std::vector<cmSourceFile*>::const_iterator iter = sourceFiles.begin();
  191. iter != sourceFilesEnd; ++iter) {
  192. cmSourceFile* sourceFile = *iter;
  193. MapSourceFileFlags::iterator sourceFileFlagsIter =
  194. sourceFileFlags.find(sourceFile->GetFullPath());
  195. if (sourceFileFlagsIter == sourceFileFlags.end()) {
  196. sourceFileFlagsIter =
  197. sourceFileFlags
  198. .insert(MapSourceFileFlags::value_type(sourceFile->GetFullPath(),
  199. std::vector<std::string>()))
  200. .first;
  201. }
  202. std::vector<std::string>& flags = sourceFileFlagsIter->second;
  203. std::string flagsString = this->ComputeFlagsForObject(*iter, lg, target);
  204. std::string definesString = this->ComputeDefines(*iter, lg, target);
  205. flags.clear();
  206. cmsys::RegularExpression flagRegex;
  207. // Regular expression to extract compiler flags from a string
  208. // https://gist.github.com/3944250
  209. const char* regexString =
  210. "(^|[ ])-[DIOUWfgs][^= ]+(=\\\"[^\"]+\\\"|=[^\"][^ ]+)?";
  211. flagRegex.compile(regexString);
  212. std::string workString = flagsString + " " + definesString;
  213. while (flagRegex.find(workString)) {
  214. std::string::size_type start = flagRegex.start();
  215. if (workString[start] == ' ') {
  216. start++;
  217. }
  218. flags.push_back(workString.substr(start, flagRegex.end() - start));
  219. if (flagRegex.end() < workString.size()) {
  220. workString = workString.substr(flagRegex.end());
  221. } else {
  222. workString = "";
  223. }
  224. }
  225. }
  226. }
  227. // Ninja uses ninja.build files (look for a way to get the output file name
  228. // from cmMakefile or something)
  229. std::string makefileName;
  230. if (this->GlobalGenerator->GetName() == "Ninja") {
  231. makefileName = "build.ninja";
  232. } else {
  233. makefileName = "Makefile";
  234. }
  235. if (!firstTarget) {
  236. fout << ",\n\t";
  237. }
  238. fout << "\t{\n\t\t\t\"name\": \"" << lg->GetProjectName() << " - "
  239. << targetName << "\",\n";
  240. fout << "\t\t\t\"cmd\": ["
  241. << this->BuildMakeCommand(make, makefileName.c_str(), targetName)
  242. << "],\n";
  243. fout << "\t\t\t\"working_dir\": \"${project_path}\",\n";
  244. fout << "\t\t\t\"file_regex\": \"^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$\"\n";
  245. fout << "\t\t}";
  246. }
  247. // Create the command line for building the given target using the selected
  248. // make
  249. std::string cmExtraSublimeTextGenerator::BuildMakeCommand(
  250. const std::string& make, const char* makefile, const std::string& target)
  251. {
  252. std::string command = "\"";
  253. command += make + "\"";
  254. std::string generator = this->GlobalGenerator->GetName();
  255. if (generator == "NMake Makefiles") {
  256. std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
  257. command += ", \"/NOLOGO\", \"/f\", \"";
  258. command += makefileName + "\"";
  259. command += ", \"VERBOSE=1\", \"";
  260. command += target;
  261. command += "\"";
  262. } else if (generator == "Ninja") {
  263. std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
  264. command += ", \"-f\", \"";
  265. command += makefileName + "\"";
  266. command += ", \"-v\", \"";
  267. command += target;
  268. command += "\"";
  269. } else {
  270. std::string makefileName;
  271. if (generator == "MinGW Makefiles") {
  272. // no escaping of spaces in this case, see
  273. // http://public.kitware.com/Bug/view.php?id=10014
  274. makefileName = makefile;
  275. } else {
  276. makefileName = cmSystemTools::ConvertToOutputPath(makefile);
  277. }
  278. command += ", \"-f\", \"";
  279. command += makefileName + "\"";
  280. command += ", \"VERBOSE=1\", \"";
  281. command += target;
  282. command += "\"";
  283. }
  284. return command;
  285. }
  286. // TODO: Most of the code is picked up from the Ninja generator, refactor it.
  287. std::string cmExtraSublimeTextGenerator::ComputeFlagsForObject(
  288. cmSourceFile* source, cmLocalGenerator* lg, cmGeneratorTarget* gtgt)
  289. {
  290. std::string flags;
  291. std::string language = source->GetLanguage();
  292. if (language.empty()) {
  293. language = "C";
  294. }
  295. std::string const& config =
  296. lg->GetMakefile()->GetSafeDefinition("CMAKE_BUILD_TYPE");
  297. lg->GetTargetCompileFlags(gtgt, config, language, flags);
  298. // Add include directory flags.
  299. {
  300. std::vector<std::string> includes;
  301. lg->GetIncludeDirectories(includes, gtgt, language, config);
  302. std::string includeFlags = lg->GetIncludeFlags(includes, gtgt, language,
  303. true); // full include paths
  304. lg->AppendFlags(flags, includeFlags);
  305. }
  306. // Add source file specific flags.
  307. lg->AppendFlags(flags, source->GetProperty("COMPILE_FLAGS"));
  308. return flags;
  309. }
  310. // TODO: Refactor with
  311. // void cmMakefileTargetGenerator::WriteTargetLanguageFlags().
  312. std::string cmExtraSublimeTextGenerator::ComputeDefines(
  313. cmSourceFile* source, cmLocalGenerator* lg, cmGeneratorTarget* target)
  314. {
  315. std::set<std::string> defines;
  316. cmMakefile* makefile = lg->GetMakefile();
  317. const std::string& language = source->GetLanguage();
  318. const std::string& config = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
  319. // Add the export symbol definition for shared library objects.
  320. if (const char* exportMacro = target->GetExportMacro()) {
  321. lg->AppendDefines(defines, exportMacro);
  322. }
  323. // Add preprocessor definitions for this target and configuration.
  324. lg->AddCompileDefinitions(defines, target, config, language);
  325. lg->AppendDefines(defines, source->GetProperty("COMPILE_DEFINITIONS"));
  326. {
  327. std::string defPropName = "COMPILE_DEFINITIONS_";
  328. defPropName += cmSystemTools::UpperCase(config);
  329. lg->AppendDefines(defines, source->GetProperty(defPropName));
  330. }
  331. std::string definesString;
  332. lg->JoinDefines(defines, definesString, language);
  333. return definesString;
  334. }