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