cmExtraSublimeTextGenerator.cxx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  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. /*
  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. //----------------------------------------------------------------------------
  34. void cmExtraSublimeTextGenerator
  35. ::GetDocumentation(cmDocumentationEntry& entry, const char*) const
  36. {
  37. entry.Name = this->GetName();
  38. entry.Brief = "Generates Sublime Text 2 project files.";
  39. entry.Full =
  40. "Project files for Sublime Text 2 will be created in the top directory "
  41. "and in every subdirectory which features a CMakeLists.txt file "
  42. "containing a PROJECT() call. "
  43. "Additionally Makefiles (or build.ninja files) are generated into the "
  44. "build tree. The appropriate make program can build the project through "
  45. "the default make target. A \"make install\" target is also provided.";
  46. }
  47. cmExtraSublimeTextGenerator::cmExtraSublimeTextGenerator()
  48. :cmExternalMakefileProjectGenerator()
  49. {
  50. #if defined(_WIN32)
  51. this->SupportedGlobalGenerators.push_back("MinGW Makefiles");
  52. this->SupportedGlobalGenerators.push_back("NMake Makefiles");
  53. // disable until somebody actually tests it:
  54. // this->SupportedGlobalGenerators.push_back("MSYS Makefiles");
  55. #endif
  56. this->SupportedGlobalGenerators.push_back("Ninja");
  57. this->SupportedGlobalGenerators.push_back("Unix Makefiles");
  58. }
  59. void cmExtraSublimeTextGenerator::Generate()
  60. {
  61. // for each sub project in the project create a sublime text 2 project
  62. for (std::map<cmStdString, std::vector<cmLocalGenerator*> >::const_iterator
  63. it = this->GlobalGenerator->GetProjectMap().begin();
  64. it!= this->GlobalGenerator->GetProjectMap().end();
  65. ++it)
  66. {
  67. // create a project file
  68. this->CreateProjectFile(it->second);
  69. }
  70. }
  71. void cmExtraSublimeTextGenerator::CreateProjectFile(
  72. const std::vector<cmLocalGenerator*>& lgs)
  73. {
  74. const cmMakefile* mf=lgs[0]->GetMakefile();
  75. std::string outputDir=mf->GetStartOutputDirectory();
  76. std::string projectName=mf->GetProjectName();
  77. std::string filename=outputDir+"/";
  78. filename+=projectName+".sublime-project";
  79. this->CreateNewProjectFile(lgs, filename);
  80. }
  81. void cmExtraSublimeTextGenerator
  82. ::CreateNewProjectFile(const std::vector<cmLocalGenerator*>& lgs,
  83. const std::string& filename)
  84. {
  85. const cmMakefile* mf=lgs[0]->GetMakefile();
  86. cmGeneratedFileStream fout(filename.c_str());
  87. if(!fout)
  88. {
  89. return;
  90. }
  91. const std::string &sourceRootRelativeToOutput = cmSystemTools::RelativePath(
  92. mf->GetHomeOutputDirectory(),
  93. mf->GetHomeDirectory());
  94. // Write the folder entries to the project file
  95. fout << "{\n";
  96. fout << "\t\"folders\":\n\t[\n\t";
  97. if (!sourceRootRelativeToOutput.empty())
  98. {
  99. fout << "\t{\n\t\t\t\"path\": \"" << sourceRootRelativeToOutput << "\"";
  100. const std::string &outputRelativeToSourceRoot =
  101. cmSystemTools::RelativePath(mf->GetHomeDirectory(),
  102. mf->GetHomeOutputDirectory());
  103. if ((!outputRelativeToSourceRoot.empty()) &&
  104. ((outputRelativeToSourceRoot.length() < 3) ||
  105. (outputRelativeToSourceRoot.substr(0, 3) != "../")))
  106. {
  107. fout << ",\n\t\t\t\"folder_exclude_patterns\": [\"" <<
  108. outputRelativeToSourceRoot << "\"]";
  109. }
  110. }
  111. else
  112. {
  113. fout << "\t{\n\t\t\t\"path\": \"./\"";
  114. }
  115. fout << "\n\t\t}";
  116. // End of the folders section
  117. fout << "\n\t]";
  118. // Write the beginning of the build systems section to the project file
  119. fout << ",\n\t\"build_systems\":\n\t[\n\t";
  120. // Set of include directories over all targets (sublime text/sublimeclang
  121. // doesn't currently support these settings per build system, only project
  122. // wide
  123. MapSourceFileFlags sourceFileFlags;
  124. AppendAllTargets(lgs, mf, fout, sourceFileFlags);
  125. // End of build_systems
  126. fout << "\n\t]";
  127. // Write the settings section with sublimeclang options
  128. fout << ",\n\t\"settings\":\n\t{\n\t";
  129. // Check if the CMAKE_SUBLIMECLANG_DISABLED flag has been set. If it has
  130. // disable sublimeclang for this project.
  131. const char* sublimeclangDisabledValue =
  132. mf->GetSafeDefinition("CMAKE_SUBLIMECLANG_DISABLED");
  133. bool sublimeclangEnabled = cmSystemTools::IsOff(sublimeclangDisabledValue);
  134. // Per project sublimeclang settings override default and user settings,
  135. // so we only write the sublimeclang_enabled setting to the project file
  136. // if it is set to be disabled.
  137. if (!sublimeclangEnabled)
  138. {
  139. fout << "\t\"sublimeclang_enabled\": false,\n\t";
  140. }
  141. std::string outputDir = mf->GetStartOutputDirectory();
  142. std::string projectName = mf->GetProjectName();
  143. std::string sublimeClangOptionsFilename = outputDir+"/";
  144. sublimeClangOptionsFilename += projectName + ".sublimeclang-options";
  145. std::string sublimeClangOptionsScriptFilename = outputDir + "/"
  146. + projectName + "_sublimeclang_options_script.py";
  147. fout << "\t\"sublimeclang_options_script\": \"python "
  148. << sublimeClangOptionsScriptFilename << " "
  149. << sublimeClangOptionsFilename
  150. << "\"\n\t";
  151. // End of the settings section
  152. fout << "}\n";
  153. // End of file
  154. fout << "}";
  155. this->WriteSublimeClangOptionsFile(sourceFileFlags,
  156. sublimeClangOptionsFilename);
  157. this->WriteSublimeClangOptionsScript(sublimeClangOptionsScriptFilename);
  158. }
  159. void cmExtraSublimeTextGenerator::
  160. WriteSublimeClangOptionsScript(const std::string& filename)
  161. {
  162. cmGeneratedFileStream fout(filename.c_str());
  163. if(!fout)
  164. {
  165. return;
  166. }
  167. fout << "import json\n";
  168. fout << "import sys\n\n\n";
  169. fout << "if len(sys.argv) < 2:\n";
  170. fout << " sys.exit(1)\n";
  171. fout << "data = None\n";
  172. fout << "with open(sys.argv[1]) as f:\n";
  173. fout << " data = json.load(f)\n";
  174. fout << "if data is not None:\n";
  175. fout << " for arg in data.get(sys.argv[2], []):\n";
  176. fout << " print arg\n";
  177. }
  178. void cmExtraSublimeTextGenerator::
  179. WriteSublimeClangOptionsFile(const MapSourceFileFlags& sourceFileFlags,
  180. const std::string& filename)
  181. {
  182. cmGeneratedFileStream fout(filename.c_str());
  183. if(!fout)
  184. {
  185. return;
  186. }
  187. fout << "{\n";
  188. MapSourceFileFlags::const_iterator sourceFileFlagsEnd =
  189. sourceFileFlags.end();
  190. int totalFiles = sourceFileFlags.size();
  191. int fileIndex = 0;
  192. for (MapSourceFileFlags::const_iterator iter = sourceFileFlags.begin();
  193. iter != sourceFileFlagsEnd; ++iter)
  194. {
  195. const std::string& sourceFilePath = iter->first;
  196. const std::vector<std::string>& flags = iter->second;
  197. fout << "\t\"" << sourceFilePath << "\":\n\t[\n\t";
  198. std::vector<std::string>::const_iterator flagsEnd = flags.end();
  199. for (std::vector<std::string>::const_iterator flagsIter = flags.begin();
  200. flagsIter != flagsEnd; ++flagsIter)
  201. {
  202. fout << "\t\"" << *flagsIter << "\"";
  203. if (flagsIter + 1 != flagsEnd)
  204. {
  205. fout << ",";
  206. }
  207. fout << "\n\t";
  208. }
  209. fout << "]";
  210. if (fileIndex < totalFiles - 1)
  211. {
  212. fout << ",";
  213. }
  214. fout << "\n";
  215. fileIndex++;
  216. }
  217. fout << "}\n";
  218. }
  219. void cmExtraSublimeTextGenerator::
  220. AppendAllTargets(const std::vector<cmLocalGenerator*>& lgs,
  221. const cmMakefile* mf,
  222. cmGeneratedFileStream& fout,
  223. MapSourceFileFlags& sourceFileFlags)
  224. {
  225. std::string make = mf->GetRequiredDefinition("CMAKE_MAKE_PROGRAM");
  226. std::string compiler = "";
  227. if (!lgs.empty())
  228. {
  229. this->AppendTarget(fout, "all", lgs[0], 0, make.c_str(), mf,
  230. compiler.c_str(), sourceFileFlags, true);
  231. this->AppendTarget(fout, "clean", lgs[0], 0, make.c_str(), mf,
  232. compiler.c_str(), sourceFileFlags, false);
  233. }
  234. // add all executable and library targets and some of the GLOBAL
  235. // and UTILITY targets
  236. for (std::vector<cmLocalGenerator*>::const_iterator lg=lgs.begin();
  237. lg!=lgs.end(); lg++)
  238. {
  239. cmMakefile* makefile=(*lg)->GetMakefile();
  240. cmTargets& targets=makefile->GetTargets();
  241. for (cmTargets::iterator ti = targets.begin();
  242. ti != targets.end(); ti++)
  243. {
  244. switch(ti->second.GetType())
  245. {
  246. case cmTarget::GLOBAL_TARGET:
  247. {
  248. bool insertTarget = false;
  249. // Only add the global targets from CMAKE_BINARY_DIR,
  250. // not from the subdirs
  251. if (strcmp(makefile->GetStartOutputDirectory(),
  252. makefile->GetHomeOutputDirectory())==0)
  253. {
  254. insertTarget = true;
  255. // only add the "edit_cache" target if it's not ccmake, because
  256. // this will not work within the IDE
  257. if (ti->first == "edit_cache")
  258. {
  259. const char* editCommand = makefile->GetDefinition
  260. ("CMAKE_EDIT_COMMAND");
  261. if (editCommand == 0)
  262. {
  263. insertTarget = false;
  264. }
  265. else if (strstr(editCommand, "ccmake")!=NULL)
  266. {
  267. insertTarget = false;
  268. }
  269. }
  270. }
  271. if (insertTarget)
  272. {
  273. this->AppendTarget(fout, ti->first.c_str(), *lg, 0,
  274. make.c_str(), makefile, compiler.c_str(),
  275. sourceFileFlags, false);
  276. }
  277. }
  278. break;
  279. case cmTarget::UTILITY:
  280. // Add all utility targets, except the Nightly/Continuous/
  281. // Experimental-"sub"targets as e.g. NightlyStart
  282. if (((ti->first.find("Nightly")==0) &&(ti->first!="Nightly"))
  283. || ((ti->first.find("Continuous")==0)&&(ti->first!="Continuous"))
  284. || ((ti->first.find("Experimental")==0)
  285. && (ti->first!="Experimental")))
  286. {
  287. break;
  288. }
  289. this->AppendTarget(fout, ti->first.c_str(), *lg, 0,
  290. make.c_str(), makefile, compiler.c_str(),
  291. sourceFileFlags, false);
  292. break;
  293. case cmTarget::EXECUTABLE:
  294. case cmTarget::STATIC_LIBRARY:
  295. case cmTarget::SHARED_LIBRARY:
  296. case cmTarget::MODULE_LIBRARY:
  297. case cmTarget::OBJECT_LIBRARY:
  298. {
  299. this->AppendTarget(fout, ti->first.c_str(), *lg, &ti->second,
  300. make.c_str(), makefile, compiler.c_str(),
  301. sourceFileFlags, false);
  302. std::string fastTarget = ti->first;
  303. fastTarget += "/fast";
  304. this->AppendTarget(fout, fastTarget.c_str(), *lg, &ti->second,
  305. make.c_str(), makefile, compiler.c_str(),
  306. sourceFileFlags, false);
  307. }
  308. break;
  309. default:
  310. break;
  311. }
  312. }
  313. }
  314. }
  315. void cmExtraSublimeTextGenerator::
  316. AppendTarget(cmGeneratedFileStream& fout,
  317. const char* targetName,
  318. cmLocalGenerator* lg,
  319. cmTarget* target,
  320. const char* make,
  321. const cmMakefile* makefile,
  322. const char* compiler,
  323. MapSourceFileFlags& sourceFileFlags,
  324. bool firstTarget)
  325. {
  326. if (target != 0)
  327. {
  328. cmGeneratorTarget *gtgt = this->GlobalGenerator
  329. ->GetGeneratorTarget(target);
  330. std::vector<cmSourceFile*> const& sourceFiles = target->GetSourceFiles();
  331. std::vector<cmSourceFile*>::const_iterator sourceFilesEnd =
  332. sourceFiles.end();
  333. for (std::vector<cmSourceFile*>::const_iterator iter =
  334. sourceFiles.begin(); iter != sourceFilesEnd; ++iter)
  335. {
  336. cmSourceFile* sourceFile = *iter;
  337. MapSourceFileFlags::iterator sourceFileFlagsIter =
  338. sourceFileFlags.find(sourceFile->GetFullPath());
  339. if (sourceFileFlagsIter == sourceFileFlags.end())
  340. {
  341. sourceFileFlagsIter =
  342. sourceFileFlags.insert(MapSourceFileFlags::value_type(
  343. sourceFile->GetFullPath(), std::vector<std::string>())).first;
  344. }
  345. std::vector<std::string>& flags = sourceFileFlagsIter->second;
  346. std::string flagsString =
  347. this->ComputeFlagsForObject(*iter, lg, target, gtgt);
  348. std::string definesString =
  349. this->ComputeDefines(*iter, lg, target, gtgt);
  350. flags.clear();
  351. cmsys::RegularExpression flagRegex;
  352. // Regular expression to extract compiler flags from a string
  353. // https://gist.github.com/3944250
  354. const char* regexString =
  355. "(^|[ ])-[DIOUWfgs][^= ]+(=\\\"[^\"]+\\\"|=[^\"][^ ]+)?";
  356. flagRegex.compile(regexString);
  357. std::string workString = flagsString + " " + definesString;
  358. while (flagRegex.find(workString))
  359. {
  360. std::string::size_type start = flagRegex.start();
  361. if (workString[start] == ' ')
  362. {
  363. start++;
  364. }
  365. flags.push_back(workString.substr(start,
  366. flagRegex.end() - start));
  367. if (flagRegex.end() < workString.size())
  368. {
  369. workString = workString.substr(flagRegex.end());
  370. }
  371. else
  372. {
  373. workString = "";
  374. }
  375. }
  376. }
  377. }
  378. // Ninja uses ninja.build files (look for a way to get the output file name
  379. // from cmMakefile or something)
  380. std::string makefileName;
  381. if (strcmp(this->GlobalGenerator->GetName(), "Ninja")==0)
  382. {
  383. makefileName = "build.ninja";
  384. }
  385. else
  386. {
  387. makefileName = "Makefile";
  388. }
  389. if (!firstTarget)
  390. {
  391. fout << ",\n\t";
  392. }
  393. fout << "\t{\n\t\t\t\"name\": \"" << makefile->GetProjectName() << " - " <<
  394. targetName << "\",\n";
  395. fout << "\t\t\t\"cmd\": [" <<
  396. this->BuildMakeCommand(make, makefileName.c_str(), targetName) <<
  397. "],\n";
  398. fout << "\t\t\t\"working_dir\": \"${project_path}\",\n";
  399. fout << "\t\t\t\"file_regex\": \"^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$\"\n";
  400. fout << "\t\t}";
  401. }
  402. // Create the command line for building the given target using the selected
  403. // make
  404. std::string cmExtraSublimeTextGenerator::BuildMakeCommand(
  405. const std::string& make, const char* makefile, const char* target)
  406. {
  407. std::string command = "\"";
  408. command += make + "\"";
  409. if (strcmp(this->GlobalGenerator->GetName(), "NMake Makefiles")==0)
  410. {
  411. std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
  412. command += ", \"/NOLOGO\", \"/f\", \"";
  413. command += makefileName + "\"";
  414. command += ", \"VERBOSE=1\", \"";
  415. command += target;
  416. command += "\"";
  417. }
  418. else if (strcmp(this->GlobalGenerator->GetName(), "Ninja")==0)
  419. {
  420. std::string makefileName = cmSystemTools::ConvertToOutputPath(makefile);
  421. command += ", \"-f\", \"";
  422. command += makefileName + "\"";
  423. command += ", \"-v\", \"";
  424. command += target;
  425. command += "\"";
  426. }
  427. else
  428. {
  429. std::string makefileName;
  430. if (strcmp(this->GlobalGenerator->GetName(), "MinGW Makefiles")==0)
  431. {
  432. // no escaping of spaces in this case, see
  433. // http://public.kitware.com/Bug/view.php?id=10014
  434. makefileName = makefile;
  435. }
  436. else
  437. {
  438. makefileName = cmSystemTools::ConvertToOutputPath(makefile);
  439. }
  440. command += ", \"-f\", \"";
  441. command += makefileName + "\"";
  442. command += ", \"VERBOSE=1\", \"";
  443. command += target;
  444. command += "\"";
  445. }
  446. return command;
  447. }
  448. // TODO: Most of the code is picked up from the Ninja generator, refactor it.
  449. std::string
  450. cmExtraSublimeTextGenerator::ComputeFlagsForObject(cmSourceFile* source,
  451. cmLocalGenerator* lg,
  452. cmTarget *target,
  453. cmGeneratorTarget* gtgt)
  454. {
  455. std::string flags;
  456. cmMakefile *makefile = lg->GetMakefile();
  457. const char* language = source->GetLanguage();
  458. if (language == NULL)
  459. {
  460. language = "C";
  461. }
  462. const char* config = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
  463. // Add language-specific flags.
  464. lg->AddLanguageFlags(flags, language, config);
  465. lg->AddArchitectureFlags(flags, gtgt, language, config);
  466. // TODO: Fortran support.
  467. // // Fortran-specific flags computed for this target.
  468. // if(*l == "Fortran")
  469. // {
  470. // this->AddFortranFlags(flags);
  471. // }
  472. // Add shared-library flags if needed.
  473. lg->AddCMP0018Flags(flags, target, language);
  474. // Add include directory flags.
  475. {
  476. std::vector<std::string> includes;
  477. lg->GetIncludeDirectories(includes, gtgt, language, config);
  478. std::string includeFlags =
  479. lg->GetIncludeFlags(includes, language, true); // full include paths
  480. lg->AppendFlags(flags, includeFlags.c_str());
  481. }
  482. // Append old-style preprocessor definition flags.
  483. lg->AppendFlags(flags, makefile->GetDefineFlags());
  484. // Add target-specific flags.
  485. if(gtgt->GetProperty("COMPILE_FLAGS"))
  486. {
  487. std::string langIncludeExpr = "CMAKE_";
  488. langIncludeExpr += language;
  489. langIncludeExpr += "_FLAG_REGEX";
  490. const char* regex = makefile->GetDefinition(langIncludeExpr.c_str());
  491. if(regex)
  492. {
  493. cmsys::RegularExpression r(regex);
  494. std::vector<std::string> args;
  495. cmSystemTools::
  496. ParseWindowsCommandLine(gtgt->GetProperty("COMPILE_FLAGS"), args);
  497. for(std::vector<std::string>::iterator i = args.begin();
  498. i != args.end(); ++i)
  499. {
  500. if(r.find(i->c_str()))
  501. {
  502. lg->AppendFlags(flags, i->c_str());
  503. }
  504. }
  505. }
  506. else
  507. {
  508. lg->AppendFlags(flags, gtgt->GetProperty("COMPILE_FLAGS"));
  509. }
  510. }
  511. // Add source file specific flags.
  512. lg->AppendFlags(flags, source->GetProperty("COMPILE_FLAGS"));
  513. // TODO: Handle Apple frameworks.
  514. return flags;
  515. }
  516. // TODO: Refactor with
  517. // void cmMakefileTargetGenerator::WriteTargetLanguageFlags().
  518. std::string
  519. cmExtraSublimeTextGenerator::
  520. ComputeDefines(cmSourceFile *source, cmLocalGenerator* lg, cmTarget *target,
  521. cmGeneratorTarget* gtgt)
  522. {
  523. std::set<std::string> defines;
  524. cmMakefile *makefile = lg->GetMakefile();
  525. const char* language = source->GetLanguage();
  526. if (language == NULL)
  527. {
  528. language = "";
  529. }
  530. const char* config = makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
  531. // Add the export symbol definition for shared library objects.
  532. if(const char* exportMacro = target->GetExportMacro())
  533. {
  534. lg->AppendDefines(defines, exportMacro);
  535. }
  536. // Add preprocessor definitions for this target and configuration.
  537. lg->AppendDefines(defines, gtgt->GetCompileDefinitions());
  538. lg->AppendDefines(defines, source->GetProperty("COMPILE_DEFINITIONS"));
  539. {
  540. std::string defPropName = "COMPILE_DEFINITIONS_";
  541. defPropName += cmSystemTools::UpperCase(config);
  542. lg->AppendDefines(defines, gtgt->GetCompileDefinitions(config));
  543. lg->AppendDefines(defines, source->GetProperty(defPropName.c_str()));
  544. }
  545. std::string definesString;
  546. lg->JoinDefines(defines, definesString, language);
  547. return definesString;
  548. }