cmGlobalNinjaGenerator.cxx 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035
  1. /*============================================================================
  2. CMake - Cross Platform Makefile Generator
  3. Copyright 2011 Peter Collingbourne <[email protected]>
  4. Copyright 2011 Nicolas Despres <[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 "cmGlobalNinjaGenerator.h"
  12. #include "cmLocalNinjaGenerator.h"
  13. #include "cmMakefile.h"
  14. #include "cmGeneratedFileStream.h"
  15. #include "cmGeneratorTarget.h"
  16. #include "cmVersion.h"
  17. #include <algorithm>
  18. const char* cmGlobalNinjaGenerator::NINJA_BUILD_FILE = "build.ninja";
  19. const char* cmGlobalNinjaGenerator::NINJA_RULES_FILE = "rules.ninja";
  20. const char* cmGlobalNinjaGenerator::INDENT = " ";
  21. void cmGlobalNinjaGenerator::Indent(std::ostream& os, int count)
  22. {
  23. for(int i = 0; i < count; ++i)
  24. os << cmGlobalNinjaGenerator::INDENT;
  25. }
  26. void cmGlobalNinjaGenerator::WriteDivider(std::ostream& os)
  27. {
  28. os
  29. << "# ======================================"
  30. << "=======================================\n";
  31. }
  32. void cmGlobalNinjaGenerator::WriteComment(std::ostream& os,
  33. const std::string& comment)
  34. {
  35. if (comment.empty())
  36. return;
  37. std::string replace = comment;
  38. std::string::size_type lpos = 0;
  39. std::string::size_type rpos;
  40. os << "\n#############################################\n";
  41. while((rpos = replace.find('\n', lpos)) != std::string::npos)
  42. {
  43. os << "# " << replace.substr(lpos, rpos - lpos) << "\n";
  44. lpos = rpos + 1;
  45. }
  46. os << "# " << replace.substr(lpos) << "\n\n";
  47. }
  48. static bool IsIdentChar(char c)
  49. {
  50. return
  51. ('a' <= c && c <= 'z') ||
  52. ('+' <= c && c <= '9') || // +,-./ and numbers
  53. ('A' <= c && c <= 'Z') ||
  54. (c == '_') || (c == '$') || (c == '\\') ||
  55. (c == ' ') || (c == ':');
  56. }
  57. std::string cmGlobalNinjaGenerator::EncodeIdent(const std::string &ident,
  58. std::ostream &vars) {
  59. if (std::find_if(ident.begin(), ident.end(),
  60. std::not1(std::ptr_fun(IsIdentChar))) != ident.end()) {
  61. static unsigned VarNum = 0;
  62. cmOStringStream names;
  63. names << "ident" << VarNum++;
  64. vars << names.str() << " = " << ident << "\n";
  65. return "$" + names.str();
  66. } else {
  67. std::string result = ident;
  68. cmSystemTools::ReplaceString(result, " ", "$ ");
  69. cmSystemTools::ReplaceString(result, ":", "$:");
  70. return result;
  71. }
  72. }
  73. std::string cmGlobalNinjaGenerator::EncodeLiteral(const std::string &lit)
  74. {
  75. std::string result = lit;
  76. cmSystemTools::ReplaceString(result, "$", "$$");
  77. return result;
  78. }
  79. std::string cmGlobalNinjaGenerator::EncodePath(const std::string &path)
  80. {
  81. std::string result = path;
  82. #ifdef _WIN32
  83. if(UsingMinGW)
  84. cmSystemTools::ReplaceString(result, "\\", "/");
  85. else
  86. cmSystemTools::ReplaceString(result, "/", "\\");
  87. #endif
  88. return EncodeLiteral(result);
  89. }
  90. void cmGlobalNinjaGenerator::WriteBuild(std::ostream& os,
  91. const std::string& comment,
  92. const std::string& rule,
  93. const cmNinjaDeps& outputs,
  94. const cmNinjaDeps& explicitDeps,
  95. const cmNinjaDeps& implicitDeps,
  96. const cmNinjaDeps& orderOnlyDeps,
  97. const cmNinjaVars& variables,
  98. const std::string& rspfile,
  99. int cmdLineLimit)
  100. {
  101. // Make sure there is a rule.
  102. if(rule.empty())
  103. {
  104. cmSystemTools::Error("No rule for WriteBuildStatement! called "
  105. "with comment: ",
  106. comment.c_str());
  107. return;
  108. }
  109. // Make sure there is at least one output file.
  110. if(outputs.empty())
  111. {
  112. cmSystemTools::Error("No output files for WriteBuildStatement! called "
  113. "with comment: ",
  114. comment.c_str());
  115. return;
  116. }
  117. cmGlobalNinjaGenerator::WriteComment(os, comment);
  118. cmOStringStream arguments;
  119. // TODO: Better formatting for when there are multiple input/output files.
  120. // Write explicit dependencies.
  121. for(cmNinjaDeps::const_iterator i = explicitDeps.begin();
  122. i != explicitDeps.end();
  123. ++i)
  124. arguments << " " << EncodeIdent(EncodePath(*i), os);
  125. // Write implicit dependencies.
  126. if(!implicitDeps.empty())
  127. {
  128. arguments << " |";
  129. for(cmNinjaDeps::const_iterator i = implicitDeps.begin();
  130. i != implicitDeps.end();
  131. ++i)
  132. arguments << " " << EncodeIdent(EncodePath(*i), os);
  133. }
  134. // Write order-only dependencies.
  135. if(!orderOnlyDeps.empty())
  136. {
  137. arguments << " ||";
  138. for(cmNinjaDeps::const_iterator i = orderOnlyDeps.begin();
  139. i != orderOnlyDeps.end();
  140. ++i)
  141. arguments << " " << EncodeIdent(EncodePath(*i), os);
  142. }
  143. arguments << "\n";
  144. cmOStringStream build;
  145. // Write outputs files.
  146. build << "build";
  147. for(cmNinjaDeps::const_iterator i = outputs.begin();
  148. i != outputs.end(); ++i)
  149. build << " " << EncodeIdent(EncodePath(*i), os);
  150. build << ":";
  151. // Write the rule.
  152. build << " " << rule;
  153. // Write the variables bound to this build statement.
  154. cmOStringStream variable_assignments;
  155. for(cmNinjaVars::const_iterator i = variables.begin();
  156. i != variables.end(); ++i)
  157. cmGlobalNinjaGenerator::WriteVariable(variable_assignments,
  158. i->first, i->second, "", 1);
  159. // check if a response file rule should be used
  160. std::string buildstr = build.str();
  161. std::string assignments = variable_assignments.str();
  162. const std::string args = arguments.str();
  163. if (cmdLineLimit > 0
  164. && args.size() + buildstr.size() + assignments.size()
  165. > (size_t) cmdLineLimit) {
  166. buildstr += "_RSP_FILE";
  167. variable_assignments.clear();
  168. cmGlobalNinjaGenerator::WriteVariable(variable_assignments,
  169. "RSP_FILE", rspfile, "", 1);
  170. assignments += variable_assignments.str();
  171. }
  172. os << buildstr << args << assignments;
  173. }
  174. void cmGlobalNinjaGenerator::WritePhonyBuild(std::ostream& os,
  175. const std::string& comment,
  176. const cmNinjaDeps& outputs,
  177. const cmNinjaDeps& explicitDeps,
  178. const cmNinjaDeps& implicitDeps,
  179. const cmNinjaDeps& orderOnlyDeps,
  180. const cmNinjaVars& variables)
  181. {
  182. cmGlobalNinjaGenerator::WriteBuild(os,
  183. comment,
  184. "phony",
  185. outputs,
  186. explicitDeps,
  187. implicitDeps,
  188. orderOnlyDeps,
  189. variables);
  190. }
  191. void cmGlobalNinjaGenerator::AddCustomCommandRule()
  192. {
  193. this->AddRule("CUSTOM_COMMAND",
  194. "$COMMAND",
  195. "$DESC",
  196. "Rule for running custom commands.",
  197. /*depfile*/ "",
  198. /*rspfile*/ "",
  199. /*rspcontent*/ "",
  200. /*restat*/ true);
  201. }
  202. void
  203. cmGlobalNinjaGenerator::WriteCustomCommandBuild(const std::string& command,
  204. const std::string& description,
  205. const std::string& comment,
  206. const cmNinjaDeps& outputs,
  207. const cmNinjaDeps& deps,
  208. const cmNinjaDeps& orderOnlyDeps)
  209. {
  210. std::string cmd = command;
  211. #ifdef _WIN32
  212. if (cmd.empty())
  213. // TODO Shouldn't an empty command be handled by ninja?
  214. cmd = "cmd.exe /c";
  215. #endif
  216. this->AddCustomCommandRule();
  217. cmNinjaVars vars;
  218. vars["COMMAND"] = cmd;
  219. vars["DESC"] = EncodeLiteral(description);
  220. cmGlobalNinjaGenerator::WriteBuild(*this->BuildFileStream,
  221. comment,
  222. "CUSTOM_COMMAND",
  223. outputs,
  224. deps,
  225. cmNinjaDeps(),
  226. orderOnlyDeps,
  227. vars);
  228. }
  229. void
  230. cmGlobalNinjaGenerator::AddMacOSXContentRule()
  231. {
  232. cmLocalGenerator *lg = this->LocalGenerators[0];
  233. cmMakefile* mfRoot = lg->GetMakefile();
  234. cmOStringStream cmd;
  235. cmd << lg->ConvertToOutputFormat(
  236. mfRoot->GetRequiredDefinition("CMAKE_COMMAND"),
  237. cmLocalGenerator::SHELL)
  238. << " -E copy $in $out";
  239. this->AddRule("COPY_OSX_CONTENT",
  240. cmd.str(),
  241. "Copying OS X Content $out",
  242. "Rule for copying OS X bundle content file."
  243. /*depfile*/ "",
  244. /*rspfile*/ "");
  245. }
  246. void
  247. cmGlobalNinjaGenerator::WriteMacOSXContentBuild(const std::string& input,
  248. const std::string& output)
  249. {
  250. this->AddMacOSXContentRule();
  251. cmNinjaDeps outputs;
  252. outputs.push_back(output);
  253. cmNinjaDeps deps;
  254. deps.push_back(input);
  255. cmNinjaVars vars;
  256. cmGlobalNinjaGenerator::WriteBuild(*this->BuildFileStream,
  257. "",
  258. "COPY_OSX_CONTENT",
  259. outputs,
  260. deps,
  261. cmNinjaDeps(),
  262. cmNinjaDeps(),
  263. cmNinjaVars());
  264. }
  265. void cmGlobalNinjaGenerator::WriteRule(std::ostream& os,
  266. const std::string& name,
  267. const std::string& command,
  268. const std::string& description,
  269. const std::string& comment,
  270. const std::string& depfile,
  271. const std::string& rspfile,
  272. const std::string& rspcontent,
  273. bool restat,
  274. bool generator)
  275. {
  276. // Make sure the rule has a name.
  277. if(name.empty())
  278. {
  279. cmSystemTools::Error("No name given for WriteRuleStatement! called "
  280. "with comment: ",
  281. comment.c_str());
  282. return;
  283. }
  284. // Make sure a command is given.
  285. if(command.empty())
  286. {
  287. cmSystemTools::Error("No command given for WriteRuleStatement! called "
  288. "with comment: ",
  289. comment.c_str());
  290. return;
  291. }
  292. cmGlobalNinjaGenerator::WriteComment(os, comment);
  293. // Write the rule.
  294. os << "rule " << name << "\n";
  295. // Write the depfile if any.
  296. if(!depfile.empty())
  297. {
  298. cmGlobalNinjaGenerator::Indent(os, 1);
  299. os << "depfile = " << depfile << "\n";
  300. }
  301. // Write the command.
  302. cmGlobalNinjaGenerator::Indent(os, 1);
  303. os << "command = " << command << "\n";
  304. // Write the description if any.
  305. if(!description.empty())
  306. {
  307. cmGlobalNinjaGenerator::Indent(os, 1);
  308. os << "description = " << description << "\n";
  309. }
  310. if(!rspfile.empty())
  311. {
  312. if (rspcontent.empty())
  313. {
  314. cmSystemTools::Error("No rspfile_content given!", comment.c_str());
  315. return;
  316. }
  317. cmGlobalNinjaGenerator::Indent(os, 1);
  318. os << "rspfile = " << rspfile << "\n";
  319. cmGlobalNinjaGenerator::Indent(os, 1);
  320. os << "rspfile_content = " << rspcontent << "\n";
  321. }
  322. if(restat)
  323. {
  324. cmGlobalNinjaGenerator::Indent(os, 1);
  325. os << "restat = 1\n";
  326. }
  327. if(generator)
  328. {
  329. cmGlobalNinjaGenerator::Indent(os, 1);
  330. os << "generator = 1\n";
  331. }
  332. os << "\n";
  333. }
  334. void cmGlobalNinjaGenerator::WriteVariable(std::ostream& os,
  335. const std::string& name,
  336. const std::string& value,
  337. const std::string& comment,
  338. int indent)
  339. {
  340. // Make sure we have a name.
  341. if(name.empty())
  342. {
  343. cmSystemTools::Error("No name given for WriteVariable! called "
  344. "with comment: ",
  345. comment.c_str());
  346. return;
  347. }
  348. // Do not add a variable if the value is empty.
  349. std::string val = cmSystemTools::TrimWhitespace(value);
  350. if(val.empty())
  351. {
  352. return;
  353. }
  354. cmGlobalNinjaGenerator::WriteComment(os, comment);
  355. cmGlobalNinjaGenerator::Indent(os, indent);
  356. os << name << " = " << val << "\n";
  357. }
  358. void cmGlobalNinjaGenerator::WriteInclude(std::ostream& os,
  359. const std::string& filename,
  360. const std::string& comment)
  361. {
  362. cmGlobalNinjaGenerator::WriteComment(os, comment);
  363. os << "include " << filename << "\n";
  364. }
  365. void cmGlobalNinjaGenerator::WriteDefault(std::ostream& os,
  366. const cmNinjaDeps& targets,
  367. const std::string& comment)
  368. {
  369. cmGlobalNinjaGenerator::WriteComment(os, comment);
  370. os << "default";
  371. for(cmNinjaDeps::const_iterator i = targets.begin(); i != targets.end(); ++i)
  372. os << " " << *i;
  373. os << "\n";
  374. }
  375. cmGlobalNinjaGenerator::cmGlobalNinjaGenerator()
  376. : cmGlobalGenerator()
  377. , BuildFileStream(0)
  378. , RulesFileStream(0)
  379. , CompileCommandsStream(0)
  380. , Rules()
  381. , AllDependencies()
  382. {
  383. // // Ninja is not ported to non-Unix OS yet.
  384. // this->ForceUnixPaths = true;
  385. this->FindMakeProgramFile = "CMakeNinjaFindMake.cmake";
  386. }
  387. //----------------------------------------------------------------------------
  388. // Virtual public methods.
  389. cmLocalGenerator* cmGlobalNinjaGenerator::CreateLocalGenerator()
  390. {
  391. cmLocalGenerator* lg = new cmLocalNinjaGenerator;
  392. lg->SetGlobalGenerator(this);
  393. return lg;
  394. }
  395. void cmGlobalNinjaGenerator
  396. ::GetDocumentation(cmDocumentationEntry& entry)
  397. {
  398. entry.Name = cmGlobalNinjaGenerator::GetActualName();
  399. entry.Brief = "Generates build.ninja files (experimental).";
  400. entry.Full =
  401. "A build.ninja file is generated into the build tree. Recent "
  402. "versions of the ninja program can build the project through the "
  403. "\"all\" target. An \"install\" target is also provided.";
  404. }
  405. // Implemented in all cmGlobaleGenerator sub-classes.
  406. // Used in:
  407. // Source/cmLocalGenerator.cxx
  408. // Source/cmake.cxx
  409. void cmGlobalNinjaGenerator::Generate()
  410. {
  411. this->OpenBuildFileStream();
  412. this->OpenRulesFileStream();
  413. this->cmGlobalGenerator::Generate();
  414. this->WriteAssumedSourceDependencies();
  415. this->WriteTargetAliases(*this->BuildFileStream);
  416. this->WriteBuiltinTargets(*this->BuildFileStream);
  417. if (cmSystemTools::GetErrorOccuredFlag()) {
  418. this->RulesFileStream->setstate(std::ios_base::failbit);
  419. this->BuildFileStream->setstate(std::ios_base::failbit);
  420. }
  421. this->CloseCompileCommandsStream();
  422. this->CloseRulesFileStream();
  423. this->CloseBuildFileStream();
  424. }
  425. // Implemented in all cmGlobaleGenerator sub-classes.
  426. // Used in:
  427. // Source/cmMakefile.cxx:
  428. void cmGlobalNinjaGenerator
  429. ::EnableLanguage(std::vector<std::string>const& langs,
  430. cmMakefile* makefile,
  431. bool optional)
  432. {
  433. if (makefile->IsOn("CMAKE_COMPILER_IS_MINGW"))
  434. {
  435. UsingMinGW = true;
  436. this->EnableMinGWLanguage(makefile);
  437. }
  438. if (std::find(langs.begin(), langs.end(), "Fortran") != langs.end())
  439. {
  440. cmSystemTools::Error("The Ninja generator does not support Fortran yet.");
  441. }
  442. this->cmGlobalGenerator::EnableLanguage(langs, makefile, optional);
  443. }
  444. bool cmGlobalNinjaGenerator::UsingMinGW = false;
  445. // Implemented by:
  446. // cmGlobalUnixMakefileGenerator3
  447. // cmGlobalVisualStudio10Generator
  448. // cmGlobalVisualStudio6Generator
  449. // cmGlobalVisualStudio7Generator
  450. // cmGlobalXCodeGenerator
  451. // Called by:
  452. // cmGlobalGenerator::Build()
  453. std::string cmGlobalNinjaGenerator
  454. ::GenerateBuildCommand(const char* makeProgram,
  455. const char* projectName,
  456. const char* additionalOptions,
  457. const char* targetName,
  458. const char* config,
  459. bool ignoreErrors,
  460. bool fast)
  461. {
  462. // Project name and config are not used yet.
  463. (void)projectName;
  464. (void)config;
  465. // Ninja does not have -i equivalent option yet.
  466. (void)ignoreErrors;
  467. // We do not handle fast build yet.
  468. (void)fast;
  469. std::string makeCommand =
  470. cmSystemTools::ConvertToUnixOutputPath(makeProgram);
  471. if(additionalOptions)
  472. {
  473. makeCommand += " ";
  474. makeCommand += additionalOptions;
  475. }
  476. if(targetName)
  477. {
  478. if(strcmp(targetName, "clean") == 0)
  479. {
  480. makeCommand += " -t clean";
  481. }
  482. else
  483. {
  484. makeCommand += " ";
  485. makeCommand += targetName;
  486. }
  487. }
  488. return makeCommand;
  489. }
  490. //----------------------------------------------------------------------------
  491. // Non-virtual public methods.
  492. void cmGlobalNinjaGenerator::AddRule(const std::string& name,
  493. const std::string& command,
  494. const std::string& description,
  495. const std::string& comment,
  496. const std::string& depfile,
  497. const std::string& rspfile,
  498. const std::string& rspcontent,
  499. bool restat,
  500. bool generator)
  501. {
  502. // Do not add the same rule twice.
  503. if (this->HasRule(name))
  504. {
  505. return;
  506. }
  507. this->Rules.insert(name);
  508. cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream,
  509. name,
  510. command,
  511. description,
  512. comment,
  513. depfile,
  514. rspfile,
  515. rspcontent,
  516. restat,
  517. generator);
  518. this->RuleCmdLength[name] = (int) command.size();
  519. }
  520. bool cmGlobalNinjaGenerator::HasRule(const std::string &name)
  521. {
  522. RulesSetType::const_iterator rule = this->Rules.find(name);
  523. return (rule != this->Rules.end());
  524. }
  525. //----------------------------------------------------------------------------
  526. // Private virtual overrides
  527. // TODO: Refactor to combine with cmGlobalUnixMakefileGenerator3 impl.
  528. void cmGlobalNinjaGenerator::ComputeTargetObjects(cmGeneratorTarget* gt) const
  529. {
  530. cmTarget* target = gt->Target;
  531. // Compute full path to object file directory for this target.
  532. std::string dir_max;
  533. dir_max += gt->Makefile->GetCurrentOutputDirectory();
  534. dir_max += "/";
  535. dir_max += gt->LocalGenerator->GetTargetDirectory(*target);
  536. dir_max += "/";
  537. gt->ObjectDirectory = dir_max;
  538. // Compute the name of each object file.
  539. for(std::vector<cmSourceFile*>::iterator
  540. si = gt->ObjectSources.begin();
  541. si != gt->ObjectSources.end(); ++si)
  542. {
  543. cmSourceFile* sf = *si;
  544. std::string objectName = gt->LocalGenerator
  545. ->GetObjectFileNameWithoutTarget(*sf, dir_max);
  546. gt->Objects[sf] = objectName;
  547. }
  548. }
  549. //----------------------------------------------------------------------------
  550. // Private methods
  551. void cmGlobalNinjaGenerator::OpenBuildFileStream()
  552. {
  553. // Compute Ninja's build file path.
  554. std::string buildFilePath =
  555. this->GetCMakeInstance()->GetHomeOutputDirectory();
  556. buildFilePath += "/";
  557. buildFilePath += cmGlobalNinjaGenerator::NINJA_BUILD_FILE;
  558. // Get a stream where to generate things.
  559. if (!this->BuildFileStream)
  560. {
  561. this->BuildFileStream = new cmGeneratedFileStream(buildFilePath.c_str());
  562. if (!this->BuildFileStream)
  563. {
  564. // An error message is generated by the constructor if it cannot
  565. // open the file.
  566. return;
  567. }
  568. }
  569. // Write the do not edit header.
  570. this->WriteDisclaimer(*this->BuildFileStream);
  571. // Write a comment about this file.
  572. *this->BuildFileStream
  573. << "# This file contains all the build statements describing the\n"
  574. << "# compilation DAG.\n\n"
  575. ;
  576. }
  577. void cmGlobalNinjaGenerator::CloseBuildFileStream()
  578. {
  579. if (this->BuildFileStream)
  580. {
  581. delete this->BuildFileStream;
  582. this->BuildFileStream = 0;
  583. }
  584. else
  585. {
  586. cmSystemTools::Error("Build file stream was not open.");
  587. }
  588. }
  589. void cmGlobalNinjaGenerator::OpenRulesFileStream()
  590. {
  591. // Compute Ninja's build file path.
  592. std::string rulesFilePath =
  593. this->GetCMakeInstance()->GetHomeOutputDirectory();
  594. rulesFilePath += "/";
  595. rulesFilePath += cmGlobalNinjaGenerator::NINJA_RULES_FILE;
  596. // Get a stream where to generate things.
  597. if (!this->RulesFileStream)
  598. {
  599. this->RulesFileStream = new cmGeneratedFileStream(rulesFilePath.c_str());
  600. if (!this->RulesFileStream)
  601. {
  602. // An error message is generated by the constructor if it cannot
  603. // open the file.
  604. return;
  605. }
  606. }
  607. // Write the do not edit header.
  608. this->WriteDisclaimer(*this->RulesFileStream);
  609. // Write comment about this file.
  610. *this->RulesFileStream
  611. << "# This file contains all the rules used to get the outputs files\n"
  612. << "# built from the input files.\n"
  613. << "# It is included in the main '" << NINJA_BUILD_FILE << "'.\n\n"
  614. ;
  615. }
  616. void cmGlobalNinjaGenerator::CloseRulesFileStream()
  617. {
  618. if (this->RulesFileStream)
  619. {
  620. delete this->RulesFileStream;
  621. this->RulesFileStream = 0;
  622. }
  623. else
  624. {
  625. cmSystemTools::Error("Rules file stream was not open.");
  626. }
  627. }
  628. void cmGlobalNinjaGenerator::AddCXXCompileCommand(
  629. const std::string &commandLine,
  630. const std::string &sourceFile)
  631. {
  632. // Compute Ninja's build file path.
  633. std::string buildFileDir =
  634. this->GetCMakeInstance()->GetHomeOutputDirectory();
  635. if (!this->CompileCommandsStream)
  636. {
  637. std::string buildFilePath = buildFileDir + "/compile_commands.json";
  638. // Get a stream where to generate things.
  639. this->CompileCommandsStream =
  640. new cmGeneratedFileStream(buildFilePath.c_str());
  641. *this->CompileCommandsStream << "[";
  642. } else {
  643. *this->CompileCommandsStream << "," << std::endl;
  644. }
  645. std::string sourceFileName = sourceFile;
  646. if (!cmSystemTools::FileIsFullPath(sourceFileName.c_str()))
  647. {
  648. sourceFileName = cmSystemTools::CollapseFullPath(
  649. sourceFileName.c_str(),
  650. this->GetCMakeInstance()->GetHomeOutputDirectory());
  651. }
  652. *this->CompileCommandsStream << "\n{\n"
  653. << " \"directory\": \""
  654. << cmGlobalGenerator::EscapeJSON(buildFileDir) << "\",\n"
  655. << " \"command\": \""
  656. << cmGlobalGenerator::EscapeJSON(commandLine) << "\",\n"
  657. << " \"file\": \""
  658. << cmGlobalGenerator::EscapeJSON(sourceFileName) << "\"\n"
  659. << "}";
  660. }
  661. void cmGlobalNinjaGenerator::CloseCompileCommandsStream()
  662. {
  663. if (this->CompileCommandsStream)
  664. {
  665. *this->CompileCommandsStream << "\n]";
  666. delete this->CompileCommandsStream;
  667. this->CompileCommandsStream = 0;
  668. }
  669. }
  670. void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os)
  671. {
  672. os
  673. << "# CMAKE generated file: DO NOT EDIT!\n"
  674. << "# Generated by \"" << this->GetName() << "\""
  675. << " Generator, CMake Version "
  676. << cmVersion::GetMajorVersion() << "."
  677. << cmVersion::GetMinorVersion() << "\n\n";
  678. }
  679. void cmGlobalNinjaGenerator::AddDependencyToAll(cmTarget* target)
  680. {
  681. this->AppendTargetOutputs(target, this->AllDependencies);
  682. }
  683. void cmGlobalNinjaGenerator::AddDependencyToAll(const std::string& input)
  684. {
  685. this->AllDependencies.push_back(input);
  686. }
  687. void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies()
  688. {
  689. for (std::map<std::string, std::set<std::string> >::iterator
  690. i = this->AssumedSourceDependencies.begin();
  691. i != this->AssumedSourceDependencies.end(); ++i) {
  692. cmNinjaDeps deps;
  693. std::copy(i->second.begin(), i->second.end(), std::back_inserter(deps));
  694. WriteCustomCommandBuild(/*command=*/"", /*description=*/"",
  695. "Assume dependencies for generated source file.",
  696. cmNinjaDeps(1, i->first), deps);
  697. }
  698. }
  699. void
  700. cmGlobalNinjaGenerator
  701. ::AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs)
  702. {
  703. const char* configName =
  704. target->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE");
  705. cmLocalNinjaGenerator *ng =
  706. static_cast<cmLocalNinjaGenerator *>(this->LocalGenerators[0]);
  707. switch (target->GetType()) {
  708. case cmTarget::EXECUTABLE:
  709. case cmTarget::SHARED_LIBRARY:
  710. case cmTarget::STATIC_LIBRARY:
  711. case cmTarget::MODULE_LIBRARY:
  712. outputs.push_back(ng->ConvertToNinjaPath(
  713. target->GetFullPath(configName).c_str()));
  714. break;
  715. case cmTarget::OBJECT_LIBRARY:
  716. case cmTarget::UTILITY: {
  717. std::string path = ng->ConvertToNinjaPath(
  718. target->GetMakefile()->GetStartOutputDirectory());
  719. if (path.empty() || path == ".")
  720. outputs.push_back(target->GetName());
  721. else {
  722. path += "/";
  723. path += target->GetName();
  724. outputs.push_back(path);
  725. }
  726. break;
  727. }
  728. case cmTarget::GLOBAL_TARGET:
  729. // Always use the target in HOME instead of an unused duplicate in a
  730. // subdirectory.
  731. outputs.push_back(target->GetName());
  732. break;
  733. default:
  734. return;
  735. }
  736. }
  737. void
  738. cmGlobalNinjaGenerator
  739. ::AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs)
  740. {
  741. if (target->GetType() == cmTarget::GLOBAL_TARGET) {
  742. // Global targets only depend on other utilities, which may not appear in
  743. // the TargetDepends set (e.g. "all").
  744. std::set<cmStdString> const& utils = target->GetUtilities();
  745. std::copy(utils.begin(), utils.end(), std::back_inserter(outputs));
  746. } else {
  747. cmTargetDependSet const& targetDeps =
  748. this->GetTargetDirectDepends(*target);
  749. for (cmTargetDependSet::const_iterator i = targetDeps.begin();
  750. i != targetDeps.end(); ++i) {
  751. this->AppendTargetOutputs(*i, outputs);
  752. }
  753. }
  754. }
  755. void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias,
  756. cmTarget* target) {
  757. cmNinjaDeps outputs;
  758. this->AppendTargetOutputs(target, outputs);
  759. // Mark the target's outputs as ambiguous to ensure that no other target uses
  760. // the output as an alias.
  761. for (cmNinjaDeps::iterator i = outputs.begin(); i != outputs.end(); ++i)
  762. TargetAliases[*i] = 0;
  763. // Insert the alias into the map. If the alias was already present in the
  764. // map and referred to another target, mark it as ambiguous.
  765. std::pair<TargetAliasMap::iterator, bool> newAlias =
  766. TargetAliases.insert(std::make_pair(alias, target));
  767. if (newAlias.second && newAlias.first->second != target)
  768. newAlias.first->second = 0;
  769. }
  770. void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os)
  771. {
  772. cmGlobalNinjaGenerator::WriteDivider(os);
  773. os << "# Target aliases.\n\n";
  774. for (TargetAliasMap::iterator i = TargetAliases.begin();
  775. i != TargetAliases.end(); ++i) {
  776. // Don't write ambiguous aliases.
  777. if (!i->second)
  778. continue;
  779. cmNinjaDeps deps;
  780. this->AppendTargetOutputs(i->second, deps);
  781. cmGlobalNinjaGenerator::WritePhonyBuild(os,
  782. "",
  783. cmNinjaDeps(1, i->first),
  784. deps);
  785. }
  786. }
  787. void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os)
  788. {
  789. // Write headers.
  790. cmGlobalNinjaGenerator::WriteDivider(os);
  791. os << "# Built-in targets\n\n";
  792. this->WriteTargetAll(os);
  793. this->WriteTargetRebuildManifest(os);
  794. this->WriteTargetClean(os);
  795. this->WriteTargetHelp(os);
  796. }
  797. void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os)
  798. {
  799. cmNinjaDeps outputs;
  800. outputs.push_back("all");
  801. cmGlobalNinjaGenerator::WritePhonyBuild(os,
  802. "The main all target.",
  803. outputs,
  804. this->AllDependencies);
  805. cmGlobalNinjaGenerator::WriteDefault(os,
  806. outputs,
  807. "Make the all target the default.");
  808. }
  809. void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
  810. {
  811. cmLocalGenerator *lg = this->LocalGenerators[0];
  812. cmMakefile* mfRoot = lg->GetMakefile();
  813. cmOStringStream cmd;
  814. cmd << lg->ConvertToOutputFormat(
  815. mfRoot->GetRequiredDefinition("CMAKE_COMMAND"),
  816. cmLocalGenerator::SHELL)
  817. << " -H"
  818. << lg->ConvertToOutputFormat(mfRoot->GetHomeDirectory(),
  819. cmLocalGenerator::SHELL)
  820. << " -B"
  821. << lg->ConvertToOutputFormat(mfRoot->GetHomeOutputDirectory(),
  822. cmLocalGenerator::SHELL);
  823. WriteRule(*this->RulesFileStream,
  824. "RERUN_CMAKE",
  825. cmd.str(),
  826. "Re-running CMake...",
  827. "Rule for re-running cmake.",
  828. /*depfile=*/ "",
  829. /*rspfile=*/ "",
  830. /*rspcontent*/ "",
  831. /*restat=*/ false,
  832. /*generator=*/ true);
  833. cmNinjaDeps implicitDeps;
  834. for (std::vector<cmLocalGenerator *>::const_iterator i =
  835. this->LocalGenerators.begin(); i != this->LocalGenerators.end(); ++i) {
  836. const std::vector<std::string>& lf = (*i)->GetMakefile()->GetListFiles();
  837. implicitDeps.insert(implicitDeps.end(), lf.begin(), lf.end());
  838. }
  839. std::sort(implicitDeps.begin(), implicitDeps.end());
  840. implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()),
  841. implicitDeps.end());
  842. implicitDeps.push_back("CMakeCache.txt");
  843. WriteBuild(os,
  844. "Re-run CMake if any of its inputs changed.",
  845. "RERUN_CMAKE",
  846. /*outputs=*/ cmNinjaDeps(1, NINJA_BUILD_FILE),
  847. /*explicitDeps=*/ cmNinjaDeps(),
  848. implicitDeps,
  849. /*orderOnlyDeps=*/ cmNinjaDeps(),
  850. /*variables=*/ cmNinjaVars());
  851. WritePhonyBuild(os,
  852. "A missing CMake input file is not an error.",
  853. implicitDeps,
  854. cmNinjaDeps());
  855. }
  856. std::string cmGlobalNinjaGenerator::ninjaCmd() const
  857. {
  858. cmLocalGenerator* lgen = this->LocalGenerators[0];
  859. if (lgen) {
  860. return lgen->ConvertToOutputFormat(
  861. lgen->GetMakefile()->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"),
  862. cmLocalGenerator::SHELL);
  863. }
  864. return "ninja";
  865. }
  866. void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os)
  867. {
  868. WriteRule(*this->RulesFileStream,
  869. "CLEAN",
  870. (ninjaCmd() + " -t clean").c_str(),
  871. "Cleaning all built files...",
  872. "Rule for cleaning all built files.",
  873. /*depfile=*/ "",
  874. /*rspfile=*/ "",
  875. /*rspcontent*/ "",
  876. /*restat=*/ false,
  877. /*generator=*/ false);
  878. WriteBuild(os,
  879. "Clean all the built files.",
  880. "CLEAN",
  881. /*outputs=*/ cmNinjaDeps(1, "clean"),
  882. /*explicitDeps=*/ cmNinjaDeps(),
  883. /*implicitDeps=*/ cmNinjaDeps(),
  884. /*orderOnlyDeps=*/ cmNinjaDeps(),
  885. /*variables=*/ cmNinjaVars());
  886. }
  887. void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os)
  888. {
  889. WriteRule(*this->RulesFileStream,
  890. "HELP",
  891. (ninjaCmd() + " -t targets").c_str(),
  892. "All primary targets available:",
  893. "Rule for printing all primary targets available.",
  894. /*depfile=*/ "",
  895. /*rspfile=*/ "",
  896. /*rspcontent*/ "",
  897. /*restat=*/ false,
  898. /*generator=*/ false);
  899. WriteBuild(os,
  900. "Print all primary targets available.",
  901. "HELP",
  902. /*outputs=*/ cmNinjaDeps(1, "help"),
  903. /*explicitDeps=*/ cmNinjaDeps(),
  904. /*implicitDeps=*/ cmNinjaDeps(),
  905. /*orderOnlyDeps=*/ cmNinjaDeps(),
  906. /*variables=*/ cmNinjaVars());
  907. }