cmGlobalNinjaGenerator.cxx 32 KB

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