cmGlobalNinjaGenerator.cxx 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082
  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. std::string path;
  433. for(std::vector<std::string>::const_iterator l = languages.begin();
  434. l != languages.end(); ++l)
  435. {
  436. std::vector<std::string> language;
  437. language.push_back(*l);
  438. if(*l == "NONE")
  439. {
  440. this->cmGlobalGenerator::EnableLanguage(language, mf, optional);
  441. continue;
  442. }
  443. else if(*l == "Fortran")
  444. {
  445. std::string message = "The \"";
  446. message += this->GetName();
  447. message += "\" generator does not support the language \"";
  448. message += *l;
  449. message += "\" yet.";
  450. cmSystemTools::Error(message.c_str());
  451. }
  452. else if(*l == "RC")
  453. {
  454. // check if mingw is used
  455. if(mf->IsOn("CMAKE_COMPILER_IS_MINGW"))
  456. {
  457. UsingMinGW = true;
  458. if(!mf->GetDefinition("CMAKE_RC_COMPILER"))
  459. {
  460. std::string windres = cmSystemTools::FindProgram("windres");
  461. if(windres.empty())
  462. {
  463. std::string compiler_path;
  464. std::string::size_type prefix = std::string::npos;
  465. if (mf->GetDefinition("CMAKE_C_COMPILER"))
  466. {
  467. compiler_path = mf->GetDefinition("CMAKE_C_COMPILER");
  468. prefix = compiler_path.rfind("gcc");
  469. }
  470. else if (mf->GetDefinition("CMAKE_CXX_COMPILER"))
  471. {
  472. compiler_path = mf->GetDefinition("CMAKE_CXX_COMPILER");
  473. prefix = compiler_path.rfind("++");
  474. prefix--;
  475. }
  476. if (prefix != std::string::npos)
  477. {
  478. windres = compiler_path.substr(0, prefix) + "windres";
  479. windres = cmSystemTools::FindProgram(windres.c_str());
  480. }
  481. }
  482. if(!windres.empty())
  483. mf->AddDefinition("CMAKE_RC_COMPILER", windres.c_str());
  484. }
  485. }
  486. }
  487. this->cmGlobalGenerator::EnableLanguage(language, mf, optional);
  488. this->ResolveLanguageCompiler(*l, mf, optional);
  489. }
  490. }
  491. bool cmGlobalNinjaGenerator::UsingMinGW = false;
  492. // Implemented by:
  493. // cmGlobalUnixMakefileGenerator3
  494. // cmGlobalVisualStudio10Generator
  495. // cmGlobalVisualStudio6Generator
  496. // cmGlobalVisualStudio7Generator
  497. // cmGlobalXCodeGenerator
  498. // Called by:
  499. // cmGlobalGenerator::Build()
  500. std::string cmGlobalNinjaGenerator
  501. ::GenerateBuildCommand(const char* makeProgram,
  502. const char* projectName,
  503. const char* additionalOptions,
  504. const char* targetName,
  505. const char* config,
  506. bool ignoreErrors,
  507. bool fast)
  508. {
  509. // Project name and config are not used yet.
  510. (void)projectName;
  511. (void)config;
  512. // Ninja does not have -i equivalent option yet.
  513. (void)ignoreErrors;
  514. // We do not handle fast build yet.
  515. (void)fast;
  516. std::string makeCommand =
  517. cmSystemTools::ConvertToUnixOutputPath(makeProgram);
  518. if(additionalOptions)
  519. {
  520. makeCommand += " ";
  521. makeCommand += additionalOptions;
  522. }
  523. if(targetName)
  524. {
  525. if(strcmp(targetName, "clean") == 0)
  526. {
  527. makeCommand += " -t clean";
  528. }
  529. else
  530. {
  531. makeCommand += " ";
  532. makeCommand += targetName;
  533. }
  534. }
  535. return makeCommand;
  536. }
  537. //----------------------------------------------------------------------------
  538. // Non-virtual public methods.
  539. void cmGlobalNinjaGenerator::AddRule(const std::string& name,
  540. const std::string& command,
  541. const std::string& description,
  542. const std::string& comment,
  543. const std::string& depfile,
  544. const std::string& rspfile,
  545. const std::string& rspcontent,
  546. bool restat,
  547. bool generator)
  548. {
  549. // Do not add the same rule twice.
  550. if (this->HasRule(name))
  551. {
  552. return;
  553. }
  554. this->Rules.insert(name);
  555. cmGlobalNinjaGenerator::WriteRule(*this->RulesFileStream,
  556. name,
  557. command,
  558. description,
  559. comment,
  560. depfile,
  561. rspfile,
  562. rspcontent,
  563. restat,
  564. generator);
  565. this->RuleCmdLength[name] = (int) command.size();
  566. }
  567. bool cmGlobalNinjaGenerator::HasRule(const std::string &name)
  568. {
  569. RulesSetType::const_iterator rule = this->Rules.find(name);
  570. return (rule != this->Rules.end());
  571. }
  572. //----------------------------------------------------------------------------
  573. // Private virtual overrides
  574. // TODO: Refactor to combine with cmGlobalUnixMakefileGenerator3 impl.
  575. void cmGlobalNinjaGenerator::ComputeTargetObjects(cmGeneratorTarget* gt) const
  576. {
  577. cmTarget* target = gt->Target;
  578. // Compute full path to object file directory for this target.
  579. std::string dir_max;
  580. dir_max += gt->Makefile->GetCurrentOutputDirectory();
  581. dir_max += "/";
  582. dir_max += gt->LocalGenerator->GetTargetDirectory(*target);
  583. dir_max += "/";
  584. gt->ObjectDirectory = dir_max;
  585. // Compute the name of each object file.
  586. for(std::vector<cmSourceFile*>::iterator
  587. si = gt->ObjectSources.begin();
  588. si != gt->ObjectSources.end(); ++si)
  589. {
  590. cmSourceFile* sf = *si;
  591. std::string objectName = gt->LocalGenerator
  592. ->GetObjectFileNameWithoutTarget(*sf, dir_max);
  593. gt->Objects[sf] = objectName;
  594. }
  595. }
  596. //----------------------------------------------------------------------------
  597. // Private methods
  598. void cmGlobalNinjaGenerator::OpenBuildFileStream()
  599. {
  600. // Compute Ninja's build file path.
  601. std::string buildFilePath =
  602. this->GetCMakeInstance()->GetHomeOutputDirectory();
  603. buildFilePath += "/";
  604. buildFilePath += cmGlobalNinjaGenerator::NINJA_BUILD_FILE;
  605. // Get a stream where to generate things.
  606. if (!this->BuildFileStream)
  607. {
  608. this->BuildFileStream = new cmGeneratedFileStream(buildFilePath.c_str());
  609. if (!this->BuildFileStream)
  610. {
  611. // An error message is generated by the constructor if it cannot
  612. // open the file.
  613. return;
  614. }
  615. }
  616. // Write the do not edit header.
  617. this->WriteDisclaimer(*this->BuildFileStream);
  618. // Write a comment about this file.
  619. *this->BuildFileStream
  620. << "# This file contains all the build statements describing the\n"
  621. << "# compilation DAG.\n\n"
  622. ;
  623. }
  624. void cmGlobalNinjaGenerator::CloseBuildFileStream()
  625. {
  626. if (this->BuildFileStream)
  627. {
  628. delete this->BuildFileStream;
  629. this->BuildFileStream = 0;
  630. }
  631. else
  632. {
  633. cmSystemTools::Error("Build file stream was not open.");
  634. }
  635. }
  636. void cmGlobalNinjaGenerator::OpenRulesFileStream()
  637. {
  638. // Compute Ninja's build file path.
  639. std::string rulesFilePath =
  640. this->GetCMakeInstance()->GetHomeOutputDirectory();
  641. rulesFilePath += "/";
  642. rulesFilePath += cmGlobalNinjaGenerator::NINJA_RULES_FILE;
  643. // Get a stream where to generate things.
  644. if (!this->RulesFileStream)
  645. {
  646. this->RulesFileStream = new cmGeneratedFileStream(rulesFilePath.c_str());
  647. if (!this->RulesFileStream)
  648. {
  649. // An error message is generated by the constructor if it cannot
  650. // open the file.
  651. return;
  652. }
  653. }
  654. // Write the do not edit header.
  655. this->WriteDisclaimer(*this->RulesFileStream);
  656. // Write comment about this file.
  657. *this->RulesFileStream
  658. << "# This file contains all the rules used to get the outputs files\n"
  659. << "# built from the input files.\n"
  660. << "# It is included in the main '" << NINJA_BUILD_FILE << "'.\n\n"
  661. ;
  662. }
  663. void cmGlobalNinjaGenerator::CloseRulesFileStream()
  664. {
  665. if (this->RulesFileStream)
  666. {
  667. delete this->RulesFileStream;
  668. this->RulesFileStream = 0;
  669. }
  670. else
  671. {
  672. cmSystemTools::Error("Rules file stream was not open.");
  673. }
  674. }
  675. void cmGlobalNinjaGenerator::AddCXXCompileCommand(
  676. const std::string &commandLine,
  677. const std::string &sourceFile)
  678. {
  679. // Compute Ninja's build file path.
  680. std::string buildFileDir =
  681. this->GetCMakeInstance()->GetHomeOutputDirectory();
  682. if (!this->CompileCommandsStream)
  683. {
  684. std::string buildFilePath = buildFileDir + "/compile_commands.json";
  685. // Get a stream where to generate things.
  686. this->CompileCommandsStream =
  687. new cmGeneratedFileStream(buildFilePath.c_str());
  688. *this->CompileCommandsStream << "[";
  689. } else {
  690. *this->CompileCommandsStream << "," << std::endl;
  691. }
  692. std::string sourceFileName = sourceFile;
  693. if (!cmSystemTools::FileIsFullPath(sourceFileName.c_str()))
  694. {
  695. sourceFileName = cmSystemTools::CollapseFullPath(
  696. sourceFileName.c_str(),
  697. this->GetCMakeInstance()->GetHomeOutputDirectory());
  698. }
  699. *this->CompileCommandsStream << "\n{\n"
  700. << " \"directory\": \""
  701. << cmGlobalGenerator::EscapeJSON(buildFileDir) << "\",\n"
  702. << " \"command\": \""
  703. << cmGlobalGenerator::EscapeJSON(commandLine) << "\",\n"
  704. << " \"file\": \""
  705. << cmGlobalGenerator::EscapeJSON(sourceFileName) << "\"\n"
  706. << "}";
  707. }
  708. void cmGlobalNinjaGenerator::CloseCompileCommandsStream()
  709. {
  710. if (this->CompileCommandsStream)
  711. {
  712. *this->CompileCommandsStream << "\n]";
  713. delete this->CompileCommandsStream;
  714. this->CompileCommandsStream = 0;
  715. }
  716. }
  717. void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os)
  718. {
  719. os
  720. << "# CMAKE generated file: DO NOT EDIT!\n"
  721. << "# Generated by \"" << this->GetName() << "\""
  722. << " Generator, CMake Version "
  723. << cmVersion::GetMajorVersion() << "."
  724. << cmVersion::GetMinorVersion() << "\n\n";
  725. }
  726. void cmGlobalNinjaGenerator::AddDependencyToAll(cmTarget* target)
  727. {
  728. this->AppendTargetOutputs(target, this->AllDependencies);
  729. }
  730. void cmGlobalNinjaGenerator::AddDependencyToAll(const std::string& input)
  731. {
  732. this->AllDependencies.push_back(input);
  733. }
  734. void cmGlobalNinjaGenerator::WriteAssumedSourceDependencies()
  735. {
  736. for (std::map<std::string, std::set<std::string> >::iterator
  737. i = this->AssumedSourceDependencies.begin();
  738. i != this->AssumedSourceDependencies.end(); ++i) {
  739. cmNinjaDeps deps;
  740. std::copy(i->second.begin(), i->second.end(), std::back_inserter(deps));
  741. WriteCustomCommandBuild(/*command=*/"", /*description=*/"",
  742. "Assume dependencies for generated source file.",
  743. cmNinjaDeps(1, i->first), deps);
  744. }
  745. }
  746. void
  747. cmGlobalNinjaGenerator
  748. ::AppendTargetOutputs(cmTarget* target, cmNinjaDeps& outputs)
  749. {
  750. const char* configName =
  751. target->GetMakefile()->GetDefinition("CMAKE_BUILD_TYPE");
  752. cmLocalNinjaGenerator *ng =
  753. static_cast<cmLocalNinjaGenerator *>(this->LocalGenerators[0]);
  754. switch (target->GetType()) {
  755. case cmTarget::EXECUTABLE:
  756. case cmTarget::SHARED_LIBRARY:
  757. case cmTarget::STATIC_LIBRARY:
  758. case cmTarget::MODULE_LIBRARY:
  759. outputs.push_back(ng->ConvertToNinjaPath(
  760. target->GetFullPath(configName).c_str()));
  761. break;
  762. case cmTarget::OBJECT_LIBRARY:
  763. case cmTarget::UTILITY: {
  764. std::string path = ng->ConvertToNinjaPath(
  765. target->GetMakefile()->GetStartOutputDirectory());
  766. if (path.empty() || path == ".")
  767. outputs.push_back(target->GetName());
  768. else {
  769. path += "/";
  770. path += target->GetName();
  771. outputs.push_back(path);
  772. }
  773. break;
  774. }
  775. case cmTarget::GLOBAL_TARGET:
  776. // Always use the target in HOME instead of an unused duplicate in a
  777. // subdirectory.
  778. outputs.push_back(target->GetName());
  779. break;
  780. default:
  781. return;
  782. }
  783. }
  784. void
  785. cmGlobalNinjaGenerator
  786. ::AppendTargetDepends(cmTarget* target, cmNinjaDeps& outputs)
  787. {
  788. if (target->GetType() == cmTarget::GLOBAL_TARGET) {
  789. // Global targets only depend on other utilities, which may not appear in
  790. // the TargetDepends set (e.g. "all").
  791. std::set<cmStdString> const& utils = target->GetUtilities();
  792. std::copy(utils.begin(), utils.end(), std::back_inserter(outputs));
  793. } else {
  794. cmTargetDependSet const& targetDeps =
  795. this->GetTargetDirectDepends(*target);
  796. for (cmTargetDependSet::const_iterator i = targetDeps.begin();
  797. i != targetDeps.end(); ++i) {
  798. this->AppendTargetOutputs(*i, outputs);
  799. }
  800. }
  801. }
  802. void cmGlobalNinjaGenerator::AddTargetAlias(const std::string& alias,
  803. cmTarget* target) {
  804. cmNinjaDeps outputs;
  805. this->AppendTargetOutputs(target, outputs);
  806. // Mark the target's outputs as ambiguous to ensure that no other target uses
  807. // the output as an alias.
  808. for (cmNinjaDeps::iterator i = outputs.begin(); i != outputs.end(); ++i)
  809. TargetAliases[*i] = 0;
  810. // Insert the alias into the map. If the alias was already present in the
  811. // map and referred to another target, mark it as ambiguous.
  812. std::pair<TargetAliasMap::iterator, bool> newAlias =
  813. TargetAliases.insert(std::make_pair(alias, target));
  814. if (newAlias.second && newAlias.first->second != target)
  815. newAlias.first->second = 0;
  816. }
  817. void cmGlobalNinjaGenerator::WriteTargetAliases(std::ostream& os)
  818. {
  819. cmGlobalNinjaGenerator::WriteDivider(os);
  820. os << "# Target aliases.\n\n";
  821. for (TargetAliasMap::iterator i = TargetAliases.begin();
  822. i != TargetAliases.end(); ++i) {
  823. // Don't write ambiguous aliases.
  824. if (!i->second)
  825. continue;
  826. cmNinjaDeps deps;
  827. this->AppendTargetOutputs(i->second, deps);
  828. cmGlobalNinjaGenerator::WritePhonyBuild(os,
  829. "",
  830. cmNinjaDeps(1, i->first),
  831. deps);
  832. }
  833. }
  834. void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os)
  835. {
  836. // Write headers.
  837. cmGlobalNinjaGenerator::WriteDivider(os);
  838. os << "# Built-in targets\n\n";
  839. this->WriteTargetAll(os);
  840. this->WriteTargetRebuildManifest(os);
  841. this->WriteTargetClean(os);
  842. this->WriteTargetHelp(os);
  843. }
  844. void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os)
  845. {
  846. cmNinjaDeps outputs;
  847. outputs.push_back("all");
  848. cmGlobalNinjaGenerator::WritePhonyBuild(os,
  849. "The main all target.",
  850. outputs,
  851. this->AllDependencies);
  852. cmGlobalNinjaGenerator::WriteDefault(os,
  853. outputs,
  854. "Make the all target the default.");
  855. }
  856. void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
  857. {
  858. cmLocalGenerator *lg = this->LocalGenerators[0];
  859. cmMakefile* mfRoot = lg->GetMakefile();
  860. cmOStringStream cmd;
  861. cmd << lg->ConvertToOutputFormat(
  862. mfRoot->GetRequiredDefinition("CMAKE_COMMAND"),
  863. cmLocalGenerator::SHELL)
  864. << " -H"
  865. << lg->ConvertToOutputFormat(mfRoot->GetHomeDirectory(),
  866. cmLocalGenerator::SHELL)
  867. << " -B"
  868. << lg->ConvertToOutputFormat(mfRoot->GetHomeOutputDirectory(),
  869. cmLocalGenerator::SHELL);
  870. WriteRule(*this->RulesFileStream,
  871. "RERUN_CMAKE",
  872. cmd.str(),
  873. "Re-running CMake...",
  874. "Rule for re-running cmake.",
  875. /*depfile=*/ "",
  876. /*rspfile=*/ "",
  877. /*rspcontent*/ "",
  878. /*restat=*/ false,
  879. /*generator=*/ true);
  880. cmNinjaDeps implicitDeps;
  881. for (std::vector<cmLocalGenerator *>::const_iterator i =
  882. this->LocalGenerators.begin(); i != this->LocalGenerators.end(); ++i) {
  883. const std::vector<std::string>& lf = (*i)->GetMakefile()->GetListFiles();
  884. implicitDeps.insert(implicitDeps.end(), lf.begin(), lf.end());
  885. }
  886. std::sort(implicitDeps.begin(), implicitDeps.end());
  887. implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()),
  888. implicitDeps.end());
  889. implicitDeps.push_back("CMakeCache.txt");
  890. WriteBuild(os,
  891. "Re-run CMake if any of its inputs changed.",
  892. "RERUN_CMAKE",
  893. /*outputs=*/ cmNinjaDeps(1, NINJA_BUILD_FILE),
  894. /*explicitDeps=*/ cmNinjaDeps(),
  895. implicitDeps,
  896. /*orderOnlyDeps=*/ cmNinjaDeps(),
  897. /*variables=*/ cmNinjaVars());
  898. WritePhonyBuild(os,
  899. "A missing CMake input file is not an error.",
  900. implicitDeps,
  901. cmNinjaDeps());
  902. }
  903. std::string cmGlobalNinjaGenerator::ninjaCmd() const
  904. {
  905. cmLocalGenerator* lgen = this->LocalGenerators[0];
  906. if (lgen) {
  907. return lgen->ConvertToOutputFormat(
  908. lgen->GetMakefile()->GetRequiredDefinition("CMAKE_MAKE_PROGRAM"),
  909. cmLocalGenerator::SHELL);
  910. }
  911. return "ninja";
  912. }
  913. void cmGlobalNinjaGenerator::WriteTargetClean(std::ostream& os)
  914. {
  915. WriteRule(*this->RulesFileStream,
  916. "CLEAN",
  917. (ninjaCmd() + " -t clean").c_str(),
  918. "Cleaning all built files...",
  919. "Rule for cleaning all built files.",
  920. /*depfile=*/ "",
  921. /*rspfile=*/ "",
  922. /*rspcontent*/ "",
  923. /*restat=*/ false,
  924. /*generator=*/ false);
  925. WriteBuild(os,
  926. "Clean all the built files.",
  927. "CLEAN",
  928. /*outputs=*/ cmNinjaDeps(1, "clean"),
  929. /*explicitDeps=*/ cmNinjaDeps(),
  930. /*implicitDeps=*/ cmNinjaDeps(),
  931. /*orderOnlyDeps=*/ cmNinjaDeps(),
  932. /*variables=*/ cmNinjaVars());
  933. }
  934. void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os)
  935. {
  936. WriteRule(*this->RulesFileStream,
  937. "HELP",
  938. (ninjaCmd() + " -t targets").c_str(),
  939. "All primary targets available:",
  940. "Rule for printing all primary targets available.",
  941. /*depfile=*/ "",
  942. /*rspfile=*/ "",
  943. /*rspcontent*/ "",
  944. /*restat=*/ false,
  945. /*generator=*/ false);
  946. WriteBuild(os,
  947. "Print all primary targets available.",
  948. "HELP",
  949. /*outputs=*/ cmNinjaDeps(1, "help"),
  950. /*explicitDeps=*/ cmNinjaDeps(),
  951. /*implicitDeps=*/ cmNinjaDeps(),
  952. /*orderOnlyDeps=*/ cmNinjaDeps(),
  953. /*variables=*/ cmNinjaVars());
  954. }