cmFastbuildTargetGenerator.cxx 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file LICENSE.rst or https://cmake.org/licensing for details. */
  3. #include "cmFastbuildTargetGenerator.h"
  4. #include <algorithm>
  5. #include <cstddef>
  6. #include <unordered_map>
  7. #include <unordered_set>
  8. #include <cm/memory>
  9. #include <cm/optional>
  10. #include "cmCryptoHash.h"
  11. #include "cmCustomCommand.h"
  12. #include "cmCustomCommandGenerator.h"
  13. #include "cmCustomCommandLines.h"
  14. #include "cmFastbuildNormalTargetGenerator.h"
  15. #include "cmFastbuildUtilityTargetGenerator.h"
  16. #include "cmGeneratorExpression.h"
  17. #include "cmGeneratorTarget.h"
  18. #include "cmGlobalCommonGenerator.h"
  19. #include "cmGlobalFastbuildGenerator.h"
  20. #include "cmList.h"
  21. #include "cmListFileCache.h"
  22. #include "cmLocalCommonGenerator.h"
  23. #include "cmLocalFastbuildGenerator.h"
  24. #include "cmLocalGenerator.h"
  25. #include "cmMakefile.h"
  26. #include "cmOSXBundleGenerator.h"
  27. #include "cmOutputConverter.h"
  28. #include "cmRulePlaceholderExpander.h"
  29. #include "cmSourceFile.h"
  30. #include "cmState.h"
  31. #include "cmStateTypes.h"
  32. #include "cmStringAlgorithms.h"
  33. #include "cmSystemTools.h"
  34. #include "cmTarget.h"
  35. #include "cmValue.h"
  36. #define FASTBUILD_DOLLAR_TAG "FASTBUILD_DOLLAR_TAG"
  37. constexpr auto FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT =
  38. "CMAKE_FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT";
  39. constexpr auto FASTBUILD_DISABLE_OUTPUT_PRECHECK_EXEC =
  40. "CMAKE_FASTBUILD_DISABLE_OUTPUT_PRECHECK_EXEC";
  41. cmFastbuildTargetGenerator* cmFastbuildTargetGenerator::New(
  42. cmGeneratorTarget* target, std::string config)
  43. {
  44. switch (target->GetType()) {
  45. case cmStateEnums::EXECUTABLE:
  46. case cmStateEnums::SHARED_LIBRARY:
  47. case cmStateEnums::STATIC_LIBRARY:
  48. case cmStateEnums::MODULE_LIBRARY:
  49. case cmStateEnums::OBJECT_LIBRARY:
  50. return new cmFastbuildNormalTargetGenerator(target, std::move(config));
  51. case cmStateEnums::UTILITY:
  52. case cmStateEnums::GLOBAL_TARGET:
  53. case cmStateEnums::INTERFACE_LIBRARY:
  54. return new cmFastbuildUtilityTargetGenerator(target, std::move(config));
  55. default:
  56. return nullptr;
  57. }
  58. }
  59. cmFastbuildTargetGenerator::cmFastbuildTargetGenerator(
  60. cmGeneratorTarget* target, std::string configParam)
  61. : cmCommonTargetGenerator(target)
  62. , LocalGenerator(
  63. static_cast<cmLocalFastbuildGenerator*>(target->GetLocalGenerator()))
  64. , TargetDirectDependencies(
  65. this->GlobalCommonGenerator->GetTargetDirectDepends(GeneratorTarget))
  66. , Config(std::move(configParam))
  67. {
  68. this->MacOSXContentGenerator =
  69. cm::make_unique<MacOSXContentGeneratorType>(this, Config);
  70. }
  71. void cmFastbuildTargetGenerator::LogMessage(std::string const& m) const
  72. {
  73. this->GetGlobalGenerator()->LogMessage(m);
  74. }
  75. std::string cmFastbuildTargetGenerator::GetUtilityAliasFromBuildStep(
  76. FastbuildBuildStep step) const
  77. {
  78. if (step == FastbuildBuildStep::PRE_BUILD) {
  79. return GetTargetName() + FASTBUILD_PRE_BUILD_ALIAS_POSTFIX;
  80. }
  81. if (step == FastbuildBuildStep::PRE_LINK) {
  82. return GetTargetName() + FASTBUILD_PRE_LINK_ALIAS_POSTFIX;
  83. }
  84. if (step == FastbuildBuildStep::POST_BUILD) {
  85. return GetTargetName() + FASTBUILD_POST_BUILD_ALIAS_POSTFIX;
  86. }
  87. return GetTargetName() + FASTBUILD_CUSTOM_COMMAND_ALIAS_POSTFIX;
  88. }
  89. void cmFastbuildTargetGenerator::MacOSXContentGeneratorType::operator()(
  90. cmSourceFile const& source, char const* pkgloc,
  91. std::string const& configName)
  92. {
  93. // Skip OS X content when not building a Framework or Bundle.
  94. if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) {
  95. return;
  96. }
  97. // Get the input file location.
  98. std::string input = source.GetFullPath();
  99. input = this->Generator->GetGlobalGenerator()->ConvertToFastbuildPath(input);
  100. // Get the output file location.
  101. std::string output =
  102. this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(
  103. pkgloc, configName);
  104. output += "/";
  105. output += cmSystemTools::GetFilenameName(input);
  106. output =
  107. this->Generator->GetGlobalGenerator()->ConvertToFastbuildPath(output);
  108. FastbuildCopyNode node;
  109. node.Name = "Copy_" + output;
  110. node.Source = std::move(input);
  111. if (cmSystemTools::FileIsDirectory(node.Source)) {
  112. node.CopyDir = true;
  113. }
  114. node.Dest = std::move(output);
  115. // Just in case if "from" is generated by some custom command.
  116. // Tested in "BundleTest" test.
  117. node.PreBuildDependencies =
  118. this->Generator->GetTargetName() + FASTBUILD_CUSTOM_COMMAND_ALIAS_POSTFIX;
  119. this->Generator->CopyNodes.emplace_back(std::move(node));
  120. }
  121. std::string cmFastbuildTargetGenerator::GetCustomCommandTargetName(
  122. cmCustomCommand const& cc, FastbuildBuildStep step) const
  123. {
  124. std::string const extra = this->Makefile->GetCurrentBinaryDirectory();
  125. std::string targetName = "cc";
  126. std::string extras = extra;
  127. // Compute hash based on commands & args & output.
  128. for (cmCustomCommandLine const& commandLine : cc.GetCommandLines()) {
  129. extras += cmJoin(commandLine, "");
  130. }
  131. for (std::string const& output : cc.GetOutputs()) {
  132. extras += output;
  133. }
  134. extras += std::to_string(static_cast<int>(step));
  135. cmCryptoHash hash(cmCryptoHash::AlgoSHA256);
  136. targetName += "-" + hash.HashString(extras).substr(0, 7);
  137. return targetName;
  138. }
  139. std::vector<std::string> cmFastbuildTargetGenerator::GetInputFiles(
  140. cmCustomCommandGenerator const& ccg, FastbuildBuildStep step) const
  141. {
  142. std::vector<std::string> result;
  143. auto const& cc = ccg.GetCC();
  144. LogMessage("CC Name: " + GetCustomCommandTargetName(cc, step));
  145. for (std::string const& dep : ccg.GetDepends()) {
  146. LogMessage("Custom command dep: " + dep);
  147. // Tested in EmptyDepends test.
  148. std::string realDep;
  149. if (this->LocalCommonGenerator->GetRealDependency(dep, Config, realDep)) {
  150. auto list = cmList{ cmGeneratorExpression::Evaluate(
  151. this->ConvertToFastbuildPath(realDep), this->LocalGenerator, Config) };
  152. if (!realDep.empty()) {
  153. LogMessage("Custom command real dep: " + realDep);
  154. for (auto const& item : list) {
  155. result.emplace_back(item);
  156. }
  157. }
  158. }
  159. }
  160. if (cc.HasMainDependency()) {
  161. LogMessage("CC main dep: " + cc.GetMainDependency());
  162. }
  163. LogMessage("cc Target: " + cc.GetTarget());
  164. for (auto const& impDep : cc.GetImplicitDepends()) {
  165. LogMessage("CC imp dep: " + impDep.first + ", " + impDep.second);
  166. }
  167. for (std::string const& dep : cc.GetOutputs()) {
  168. LogMessage("Custom command output: " + this->ConvertToFastbuildPath(dep));
  169. }
  170. for (std::string const& dep : cc.GetByproducts()) {
  171. LogMessage("Custom command byproducts: " +
  172. this->ConvertToFastbuildPath(dep));
  173. }
  174. return result;
  175. }
  176. void cmFastbuildTargetGenerator::WriteScriptProlog(cmsys::ofstream& file) const
  177. {
  178. #ifdef _WIN32
  179. file << "@echo off\n";
  180. #else
  181. file << "set -e\n\n";
  182. #endif
  183. }
  184. void cmFastbuildTargetGenerator::WriteScriptEpilog(cmsys::ofstream& file) const
  185. {
  186. (void)file;
  187. #ifdef _WIN32
  188. file << "goto :EOF\n\n"
  189. ":ABORT\n"
  190. "set ERROR_CODE=%ERRORLEVEL%\n"
  191. "echo Batch file failed at line %FAIL_LINE% "
  192. "with errorcode %ERRORLEVEL%\n"
  193. "exit /b %ERROR_CODE%";
  194. #endif
  195. }
  196. std::string cmFastbuildTargetGenerator::GetScriptWorkingDir(
  197. cmCustomCommandGenerator const& ccg) const
  198. {
  199. std::string workingDirectory = ccg.GetWorkingDirectory();
  200. if (workingDirectory.empty()) {
  201. return this->LocalCommonGenerator->GetCurrentBinaryDirectory();
  202. }
  203. return workingDirectory;
  204. }
  205. std::string cmFastbuildTargetGenerator::GetScriptFilename(
  206. std::string const& utilityTargetName) const
  207. {
  208. std::string scriptFileName = Makefile->GetCurrentBinaryDirectory();
  209. scriptFileName += "/CMakeFiles/";
  210. scriptFileName += utilityTargetName;
  211. scriptFileName += FASTBUILD_SCRIPT_FILE_EXTENSION;
  212. return scriptFileName;
  213. }
  214. void cmFastbuildTargetGenerator::AddCommentPrinting(
  215. std::vector<std::string>& cmdLines,
  216. cmCustomCommandGenerator const& ccg) const
  217. {
  218. std::string cmakeCommand = this->GetLocalGenerator()->ConvertToOutputFormat(
  219. cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
  220. auto const comment = ccg.GetComment();
  221. if (comment) {
  222. // Comment printing should be first. Tested in
  223. // RunCMake.ExternalProject:EnvVars-build test.
  224. cmdLines.insert(
  225. cmdLines.begin(),
  226. cmakeCommand.append(" -E echo ")
  227. .append(LocalGenerator->EscapeForShell(cmGeneratorExpression::Evaluate(
  228. *comment, this->LocalGenerator, Config))));
  229. }
  230. }
  231. std::string cmFastbuildTargetGenerator::GetCdCommand(
  232. cmCustomCommandGenerator const& ccg) const
  233. {
  234. return cmStrCat(FASTBUILD_SCRIPT_CD,
  235. this->LocalGenerator->ConvertToOutputFormat(
  236. GetScriptWorkingDir(ccg), cmOutputConverter::SHELL));
  237. }
  238. void cmFastbuildTargetGenerator::WriteCmdsToFile(
  239. cmsys::ofstream& file, std::vector<std::string> const& cmds) const
  240. {
  241. #ifdef _WIN32
  242. int line = 1;
  243. for (auto cmd : cmds) {
  244. // On Windows batch, '%' is a special character that needs to be
  245. // doubled to be escaped
  246. cmSystemTools::ReplaceString(cmd, "%", "%%");
  247. file << cmd << " || (set FAIL_LINE=" << ++line << "& goto :ABORT)" << '\n';
  248. #else
  249. for (auto const& cmd : cmds) {
  250. file << cmd << '\n';
  251. #endif
  252. }
  253. }
  254. void cmFastbuildTargetGenerator::AddOutput(cmCustomCommandGenerator const& ccg,
  255. FastbuildExecNode& exec)
  256. {
  257. std::string dummyOutput = cmSystemTools::JoinPath(
  258. { LocalCommonGenerator->GetMakefile()->GetHomeOutputDirectory(),
  259. "/_fbuild_dummy" });
  260. this->GetGlobalGenerator()->AllFoldersToClean.insert(dummyOutput);
  261. dummyOutput.append("/").append(exec.Name).append(
  262. FASTBUILD_DUMMY_OUTPUT_EXTENSION);
  263. std::vector<std::string> const& outputs = ccg.GetOutputs();
  264. std::vector<std::string> const& byproducts = ccg.GetByproducts();
  265. exec.OutputsAlias.Name = exec.Name + FASTBUILD_OUTPUTS_ALIAS_POSTFIX;
  266. // If CC doesn't have any output - we should always run it.
  267. // Tested in "RunCMake.CMakePresetsBuild" test.
  268. bool hasAnyNonSymbolicOutput = false;
  269. bool const trackByproducts =
  270. this->Makefile->IsDefinitionSet(FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT);
  271. auto const isSymbolic = [this](std::string const& file) {
  272. cmSourceFile* sf = this->Makefile->GetSource(file);
  273. if (sf && sf->GetPropertyAsBool("SYMBOLIC")) {
  274. LogMessage("Skipping symbolic file: " + file);
  275. return true;
  276. }
  277. return false;
  278. };
  279. for (std::string const& output : outputs) {
  280. // Tested in "RunCMake.BuildDepends".
  281. if (isSymbolic(output)) {
  282. continue;
  283. }
  284. hasAnyNonSymbolicOutput = true;
  285. std::string const outputPath = this->ConvertToFastbuildPath(output);
  286. LogMessage("CC's output: " + outputPath);
  287. exec.OutputsAlias.PreBuildDependencies.emplace(outputPath);
  288. // Ensure output path exists. For some reason, "CMake -E touch" fails with
  289. // "cmake -E touch: failed to update "...
  290. cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(outputPath));
  291. this->GetGlobalGenerator()->AddFileToClean(outputPath);
  292. }
  293. exec.ByproductsAlias.Name = exec.Name + FASTBUILD_BYPRODUCTS_ALIAS_POSTFIX;
  294. for (std::string const& byproduct : byproducts) {
  295. if (trackByproducts) {
  296. hasAnyNonSymbolicOutput = true;
  297. }
  298. std::string const byproductPath = this->ConvertToFastbuildPath(byproduct);
  299. exec.ByproductsAlias.PreBuildDependencies.emplace(byproductPath);
  300. this->GetGlobalGenerator()->AddFileToClean(byproductPath);
  301. }
  302. auto const addDummyOutput = [&] {
  303. // So that the dummy file is always created.
  304. exec.ExecUseStdOutAsOutput = true;
  305. exec.ExecOutput = this->ConvertToFastbuildPath(dummyOutput);
  306. for (auto const& output : exec.OutputsAlias.PreBuildDependencies) {
  307. OutputsToReplace[output.Name] = exec.ExecOutput;
  308. LogMessage("Adding replace from " + output.Name + " to " +
  309. exec.ExecOutput);
  310. }
  311. };
  312. // We don't have any output that is expected to appear on disk -> run always.
  313. // Tested in "RunCMake.ExternalProject":BUILD_ALWAYS
  314. if (!hasAnyNonSymbolicOutput) {
  315. exec.ExecAlways = true;
  316. addDummyOutput();
  317. return;
  318. }
  319. if (!exec.OutputsAlias.PreBuildDependencies.empty()) {
  320. exec.ExecOutput = this->ConvertToFastbuildPath(
  321. exec.OutputsAlias.PreBuildDependencies.begin()->Name);
  322. } else {
  323. exec.ExecOutput = this->ConvertToFastbuildPath(
  324. exec.ByproductsAlias.PreBuildDependencies.begin()->Name);
  325. }
  326. // Optionally add the "deps-check" Exec if we have more than 1 OUTPUT, but
  327. // allow user to opt out.
  328. if (exec.OutputsAlias.PreBuildDependencies.size() > 1 &&
  329. !this->Makefile->IsDefinitionSet(
  330. FASTBUILD_DISABLE_OUTPUT_PRECHECK_EXEC)) {
  331. exec.NeedsDepsCheckExec = true;
  332. }
  333. }
  334. void cmFastbuildTargetGenerator::AddExecArguments(
  335. FastbuildExecNode& exec, std::string const& scriptFilename) const
  336. {
  337. exec.ExecArguments = FASTBUILD_SCRIPT_FILE_ARG;
  338. exec.ExecArguments +=
  339. cmGlobalFastbuildGenerator::QuoteIfHasSpaces(scriptFilename);
  340. exec.ScriptFile = scriptFilename;
  341. exec.ExecExecutable =
  342. cmGlobalFastbuildGenerator::GetExternalShellExecutable();
  343. }
  344. std::vector<std::string> cmFastbuildTargetGenerator::GetDepends(
  345. cmCustomCommandGenerator const& ccg) const
  346. {
  347. std::vector<std::string> res;
  348. for (auto dep : ccg.GetDepends()) {
  349. LogMessage("Dep: " + dep);
  350. auto orig = dep;
  351. if (this->LocalCommonGenerator->GetRealDependency(dep, Config, dep)) {
  352. LogMessage("Real dep: " + dep);
  353. }
  354. dep = this->ConvertToFastbuildPath(dep);
  355. LogMessage("Real dep converted: " + dep);
  356. auto const targetInfo = this->LocalGenerator->GetSourcesWithOutput(dep);
  357. if (targetInfo.Target) {
  358. LogMessage("dep: " + dep + ", target: " + targetInfo.Target->GetName());
  359. auto const& target = targetInfo.Target;
  360. auto const processCCs = [this, &res,
  361. dep](std::vector<cmCustomCommand> const& ccs,
  362. FastbuildBuildStep step) {
  363. for (auto const& cc : ccs) {
  364. for (auto const& output : cc.GetOutputs()) {
  365. LogMessage("dep: " + dep + ", post output: " +
  366. this->ConvertToFastbuildPath(output));
  367. if (this->ConvertToFastbuildPath(output) == dep) {
  368. auto ccName = this->GetCustomCommandTargetName(cc, step);
  369. LogMessage("Additional CC dep from target: " + ccName);
  370. res.emplace_back(std::move(ccName));
  371. }
  372. }
  373. for (auto const& byproduct : cc.GetByproducts()) {
  374. LogMessage("dep: " + dep + ", post byproduct: " +
  375. this->ConvertToFastbuildPath(byproduct));
  376. if (this->ConvertToFastbuildPath(byproduct) == dep) {
  377. auto ccName = this->GetCustomCommandTargetName(cc, step);
  378. LogMessage("Additional CC dep from target: " + ccName);
  379. res.emplace_back(std::move(ccName));
  380. }
  381. }
  382. }
  383. };
  384. processCCs(target->GetPreBuildCommands(), FastbuildBuildStep::PRE_BUILD);
  385. processCCs(target->GetPreLinkCommands(), FastbuildBuildStep::PRE_LINK);
  386. processCCs(target->GetPostBuildCommands(),
  387. FastbuildBuildStep::POST_BUILD);
  388. continue;
  389. }
  390. if (!targetInfo.Source) {
  391. LogMessage("dep: " + dep + ", no source, byproduct: " +
  392. std::to_string(targetInfo.SourceIsByproduct));
  393. // Tested in "OutDir" test.
  394. res.emplace_back(std::move(orig));
  395. continue;
  396. }
  397. if (!targetInfo.Source->GetCustomCommand()) {
  398. LogMessage("dep: " + dep + ", no GetCustomCommand");
  399. continue;
  400. }
  401. if (targetInfo.Source && targetInfo.Source->GetCustomCommand()) {
  402. auto ccName = this->GetCustomCommandTargetName(
  403. *targetInfo.Source->GetCustomCommand(), FastbuildBuildStep::REST);
  404. LogMessage("Additional CC dep: " + ccName);
  405. res.emplace_back(std::move(ccName));
  406. }
  407. }
  408. return res;
  409. }
  410. void cmFastbuildTargetGenerator::ReplaceProblematicMakeVars(
  411. std::string& command) const
  412. {
  413. // TODO: fix problematic global targets. For now, search and replace the
  414. // makefile vars.
  415. cmSystemTools::ReplaceString(
  416. command, "$(CMAKE_SOURCE_DIR)",
  417. this->LocalGenerator->ConvertToOutputFormat(
  418. this->LocalGenerator->GetSourceDirectory(), cmOutputConverter::SHELL));
  419. cmSystemTools::ReplaceString(
  420. command, "$(CMAKE_BINARY_DIR)",
  421. this->LocalGenerator->ConvertToOutputFormat(
  422. this->LocalGenerator->GetBinaryDirectory(), cmOutputConverter::SHELL));
  423. cmSystemTools::ReplaceString(command, "$(ARGS)", "");
  424. }
  425. FastbuildExecNode cmFastbuildTargetGenerator::GetAppleTextStubCommand() const
  426. {
  427. FastbuildExecNode res;
  428. if (!this->GeneratorTarget->IsApple() ||
  429. !this->GeneratorTarget->HasImportLibrary(Config)) {
  430. return res;
  431. }
  432. auto const names = DetectOutput();
  433. std::string const outpathImp =
  434. this->ConvertToFastbuildPath(this->GeneratorTarget->GetDirectory(
  435. Config, cmStateEnums::ImportLibraryArtifact));
  436. std::string const binPath =
  437. this->ConvertToFastbuildPath(this->GeneratorTarget->GetDirectory(
  438. Config, cmStateEnums::RuntimeBinaryArtifact));
  439. cmSystemTools::MakeDirectory(outpathImp);
  440. std::string rule = this->LocalGenerator->GetMakefile()->GetSafeDefinition(
  441. "CMAKE_CREATE_TEXT_STUBS");
  442. LogMessage("CMAKE_CREATE_TEXT_STUBS:" + rule);
  443. auto rulePlaceholderExpander =
  444. this->GetLocalGenerator()->CreateRulePlaceholderExpander();
  445. cmRulePlaceholderExpander::RuleVariables vars;
  446. res.ExecOutput = cmStrCat(outpathImp, '/', names.ImportReal);
  447. res.ExecInput = { cmStrCat(binPath, '/', names.SharedObject) };
  448. vars.Target = res.ExecInput[0].c_str();
  449. rulePlaceholderExpander->SetTargetImpLib(res.ExecOutput);
  450. rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), rule,
  451. vars);
  452. LogMessage("CMAKE_CREATE_TEXT_STUBS expanded:" + rule);
  453. std::string executable;
  454. std::string args;
  455. if (!cmSystemTools::SplitProgramFromArgs(rule, executable, args)) {
  456. cmSystemTools::Error("Failed to split program from args: " + rule);
  457. return res;
  458. }
  459. res.Name = "create_" + names.ImportOutput + "_text_stub";
  460. res.ExecExecutable = std::move(executable);
  461. res.ExecArguments = std::move(args);
  462. res.ExecWorkingDir = this->LocalCommonGenerator->GetCurrentBinaryDirectory();
  463. // Wait for the build.
  464. res.PreBuildDependencies.emplace(this->GetTargetName());
  465. return res;
  466. }
  467. FastbuildExecNode cmFastbuildTargetGenerator::GetDepsCheckExec(
  468. FastbuildExecNode const& depender)
  469. {
  470. FastbuildExecNode exec;
  471. exec.Name = depender.Name + "-check-depends";
  472. exec.ExecAlways = true;
  473. exec.ExecUseStdOutAsOutput = true;
  474. exec.ExecOutput = depender.ExecOutput + ".deps-checker";
  475. exec.ExecExecutable = cmSystemTools::GetCMakeCommand();
  476. exec.ExecArguments += "-E cmake_fastbuild_check_depends ";
  477. exec.ExecArguments += depender.ExecOutput + " ";
  478. char const* sep = "";
  479. for (auto const& dep : depender.OutputsAlias.PreBuildDependencies) {
  480. exec.ExecArguments += sep;
  481. exec.ExecArguments += dep.Name;
  482. sep = " ";
  483. }
  484. for (auto const& dep : depender.ByproductsAlias.PreBuildDependencies) {
  485. exec.ExecArguments += sep;
  486. exec.ExecArguments += dep.Name;
  487. sep = " ";
  488. }
  489. return exec;
  490. }
  491. FastbuildExecNodes cmFastbuildTargetGenerator::GenerateCommands(
  492. FastbuildBuildStep buildStep)
  493. {
  494. FastbuildExecNodes execs;
  495. execs.Alias.Name = GetUtilityAliasFromBuildStep(buildStep);
  496. std::vector<cmCustomCommand> commands;
  497. if (buildStep == FastbuildBuildStep::PRE_BUILD) {
  498. commands = GeneratorTarget->GetPreBuildCommands();
  499. LogMessage("STEP: PRE_BUILD");
  500. } else if (buildStep == FastbuildBuildStep::PRE_LINK) {
  501. commands = GeneratorTarget->GetPreLinkCommands();
  502. LogMessage("STEP: PRE_LINK");
  503. } else if (buildStep == FastbuildBuildStep::POST_BUILD) {
  504. commands = GeneratorTarget->GetPostBuildCommands();
  505. LogMessage("STEP: POST_BUILD");
  506. } else {
  507. LogMessage("STEP: ALL CUSTOM COMMANDS");
  508. std::vector<cmSourceFile const*> customCommands;
  509. GeneratorTarget->GetCustomCommands(customCommands, Config);
  510. for (cmSourceFile const* source : customCommands) {
  511. cmCustomCommand const* cmd = source->GetCustomCommand();
  512. if (!cmd->GetCommandLines().empty()) {
  513. commands.emplace_back(*cmd);
  514. }
  515. }
  516. }
  517. LogMessage(cmStrCat("Number of custom commands: ", commands.size()));
  518. for (cmCustomCommand const& customCommand : commands) {
  519. cmCustomCommandGenerator ccg(customCommand, Config, LocalCommonGenerator);
  520. std::string launcher = this->MakeCustomLauncher(ccg);
  521. std::string const execName =
  522. GetCustomCommandTargetName(customCommand, buildStep);
  523. std::vector<std::string> cmdLines;
  524. if (ccg.GetNumberOfCommands() > 0) {
  525. cmdLines.push_back(GetCdCommand(ccg));
  526. }
  527. // Since we are not using FASTBuild Exec nodes natively, we need to
  528. // have shell specific escape.
  529. this->LocalGenerator->GetState()->SetFastbuildMake(false);
  530. // To avoid replacing $ with $$ in the command line.
  531. this->LocalGenerator->SetLinkScriptShell(true);
  532. for (unsigned j = 0; j != ccg.GetNumberOfCommands(); ++j) {
  533. std::string const command = ccg.GetCommand(j);
  534. // Tested in "CustomCommand" ("empty_command") test.
  535. if (!command.empty()) {
  536. cmdLines.emplace_back(launcher +
  537. this->LocalGenerator->ConvertToOutputFormat(
  538. command, cmOutputConverter::SHELL));
  539. std::string& cmd = cmdLines.back();
  540. ccg.AppendArguments(j, cmd);
  541. ReplaceProblematicMakeVars(cmd);
  542. LogMessage("cmCustomCommandLine: " + cmd);
  543. }
  544. }
  545. if (cmdLines.empty()) {
  546. return {};
  547. }
  548. this->LocalGenerator->GetState()->SetFastbuildMake(true);
  549. FastbuildExecNode execNode;
  550. execNode.Name = execName;
  551. std::vector<std::string> const inputFiles = GetInputFiles(ccg, buildStep);
  552. for (std::string const& file : inputFiles) {
  553. LogMessage("Input file: " + file);
  554. }
  555. for (auto const& util : ccg.GetUtilities()) {
  556. auto const& utilTargetName = util.Value.first;
  557. LogMessage("Util: " + utilTargetName +
  558. ", cross: " + std::to_string(util.Value.second));
  559. auto* const target = this->Makefile->FindTargetToUse(utilTargetName);
  560. if (target && target->IsImported()) {
  561. std::string importedLoc =
  562. this->ConvertToFastbuildPath(target->ImportedGetFullPath(
  563. Config, cmStateEnums::ArtifactType::RuntimeBinaryArtifact));
  564. if (importedLoc.empty()) {
  565. importedLoc =
  566. this->ConvertToFastbuildPath(target->ImportedGetFullPath(
  567. Config, cmStateEnums::ArtifactType::ImportLibraryArtifact));
  568. }
  569. LogMessage("adding file level dep on imporated target: " +
  570. importedLoc);
  571. execNode.PreBuildDependencies.emplace(std::move(importedLoc));
  572. continue;
  573. }
  574. // This CC uses some executable produced by another target. Add explicit
  575. // dep. Tested in "CustomCommand" test.
  576. if (util.Value.second) {
  577. if (utilTargetName != customCommand.GetTarget()) {
  578. LogMessage("Adding util dep: " + utilTargetName);
  579. execNode.PreBuildDependencies.emplace(utilTargetName);
  580. }
  581. }
  582. }
  583. execNode.ExecInput = inputFiles;
  584. execs.Alias.PreBuildDependencies.emplace(execNode.Name);
  585. LogMessage(cmStrCat("cmdLines size ", cmdLines.size()));
  586. if (!cmdLines.empty()) {
  587. std::string const scriptFileName = GetScriptFilename(execName);
  588. cmsys::ofstream scriptFile(scriptFileName.c_str());
  589. AddOutput(ccg, execNode);
  590. AddExecArguments(execNode, scriptFileName);
  591. AddCommentPrinting(cmdLines, ccg);
  592. WriteScriptProlog(scriptFile);
  593. WriteCmdsToFile(scriptFile, cmdLines);
  594. WriteScriptEpilog(scriptFile);
  595. execNode.ExecWorkingDir = GetScriptWorkingDir(ccg);
  596. }
  597. // Tested in "ObjectLibrary / complexOneConfig" tests.
  598. for (std::string& additionalDep : GetDepends(ccg)) {
  599. if (additionalDep != execName) {
  600. LogMessage("Adding additional dep: " + additionalDep);
  601. execNode.PreBuildDependencies.emplace(std::move(additionalDep));
  602. }
  603. }
  604. if (buildStep == FastbuildBuildStep::POST_BUILD) {
  605. execNode.PreBuildDependencies.emplace(GetTargetName() +
  606. FASTBUILD_BUILD_ALIAS_POSTFIX);
  607. // Execute POST_BUILD in order in which they are declared.
  608. // Tested in "complex" test.
  609. for (auto& exec : execs.Nodes) {
  610. execNode.PreBuildDependencies.emplace(exec.Name);
  611. }
  612. }
  613. for (auto const& out : execNode.OutputsAlias.PreBuildDependencies) {
  614. LogMessage("Adding replace from " + out.Name + " to " + execName);
  615. OutputToExecName[out.Name] = execName;
  616. }
  617. execs.Nodes.emplace_back(std::move(execNode));
  618. }
  619. for (auto& exec : execs.Nodes) {
  620. for (auto& inputFile : exec.ExecInput) {
  621. auto const iter = OutputsToReplace.find(inputFile);
  622. if (iter != OutputsToReplace.end()) {
  623. LogMessage("Replacing input: " + inputFile + " with " + iter->second);
  624. inputFile = iter->second;
  625. }
  626. auto const depIter = std::find_if(
  627. exec.PreBuildDependencies.begin(), exec.PreBuildDependencies.end(),
  628. [this](FastbuildTargetDep const& dep) {
  629. return !OutputToExecName[dep.Name].empty();
  630. });
  631. if (depIter != exec.PreBuildDependencies.end()) {
  632. LogMessage("Replacing dep " + depIter->Name + " with " +
  633. OutputToExecName[depIter->Name]);
  634. exec.PreBuildDependencies.emplace(OutputToExecName[depIter->Name]);
  635. exec.PreBuildDependencies.erase(depIter);
  636. }
  637. }
  638. if (exec.NeedsDepsCheckExec) {
  639. auto depsCheckExec = GetDepsCheckExec(exec);
  640. LogMessage("Adding deps check Exec: " + depsCheckExec.Name);
  641. exec.PreBuildDependencies.emplace(depsCheckExec.Name);
  642. this->GetGlobalGenerator()->AddTarget(std::move(depsCheckExec));
  643. }
  644. }
  645. return execs;
  646. }
  647. std::string cmFastbuildTargetGenerator::MakeCustomLauncher(
  648. cmCustomCommandGenerator const& ccg)
  649. {
  650. // Copied from cmLocalNinjaGenerator::MakeCustomLauncher.
  651. cmValue property_value = this->Makefile->GetProperty("RULE_LAUNCH_CUSTOM");
  652. if (!cmNonempty(property_value)) {
  653. return std::string();
  654. }
  655. // Expand rule variables referenced in the given launcher command.
  656. cmRulePlaceholderExpander::RuleVariables vars;
  657. std::string output;
  658. std::vector<std::string> const& outputs = ccg.GetOutputs();
  659. for (size_t i = 0; i < outputs.size(); ++i) {
  660. output =
  661. cmStrCat(output,
  662. this->LocalGenerator->ConvertToOutputFormat(
  663. ccg.GetWorkingDirectory().empty()
  664. ? this->LocalGenerator->MaybeRelativeToCurBinDir(outputs[i])
  665. : outputs[i],
  666. cmOutputConverter::SHELL));
  667. if (i != outputs.size() - 1) {
  668. output = cmStrCat(output, ',');
  669. }
  670. }
  671. vars.Output = output.c_str();
  672. vars.Role = ccg.GetCC().GetRole().c_str();
  673. auto rulePlaceholderExpander =
  674. this->LocalGenerator->CreateRulePlaceholderExpander();
  675. std::string launcher = *property_value;
  676. rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, launcher,
  677. vars);
  678. if (!launcher.empty()) {
  679. launcher += " ";
  680. }
  681. LogMessage("CC Launcher: " + launcher);
  682. return launcher;
  683. }
  684. std::string cmFastbuildTargetGenerator::GetTargetName() const
  685. {
  686. if (this->GeneratorTarget->GetType() == cmStateEnums::GLOBAL_TARGET) {
  687. return this->GetGlobalGenerator()->GetTargetName(GeneratorTarget);
  688. }
  689. return this->GeneratorTarget->GetName();
  690. }
  691. cmGeneratorTarget::Names cmFastbuildTargetGenerator::DetectOutput() const
  692. {
  693. if (GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) {
  694. return GeneratorTarget->GetExecutableNames(Config);
  695. }
  696. return GeneratorTarget->GetLibraryNames(Config);
  697. }
  698. void cmFastbuildTargetGenerator::AddObjectDependencies(
  699. FastbuildTarget& fastbuildTarget,
  700. std::vector<std::string>& allObjectDepends) const
  701. {
  702. auto const FindObjListWhichOutputs = [&fastbuildTarget](
  703. std::string const& output) {
  704. for (FastbuildObjectListNode const& objList :
  705. fastbuildTarget.ObjectListNodes) {
  706. if (objList.ObjectOutputs.find(output) != objList.ObjectOutputs.end()) {
  707. return objList.Name;
  708. }
  709. }
  710. return std::string{};
  711. };
  712. for (FastbuildObjectListNode& objList : fastbuildTarget.ObjectListNodes) {
  713. objList.PreBuildDependencies.emplace(
  714. fastbuildTarget.Name + FASTBUILD_DEPS_ARTIFACTS_ALIAS_POSTFIX);
  715. for (auto const& objDep : objList.ObjectDepends) {
  716. // Check if there is another object list which outputs (OBJECT_OUTPUTS)
  717. // something that this object list needs (OBJECT_DEPENDS).
  718. auto anotherObjList = FindObjListWhichOutputs(objDep);
  719. if (!anotherObjList.empty()) {
  720. LogMessage("Adding explicit <OBJECT_DEPENDS> dep: " + anotherObjList);
  721. allObjectDepends.emplace_back(anotherObjList);
  722. objList.PreBuildDependencies.emplace(std::move(anotherObjList));
  723. } else {
  724. LogMessage("Adding <OBJECT_DEPENDS> dep: " + objDep);
  725. allObjectDepends.emplace_back(objDep);
  726. objList.PreBuildDependencies.emplace(objDep);
  727. }
  728. }
  729. }
  730. cmGlobalFastbuildGenerator::TopologicalSort(fastbuildTarget.ObjectListNodes);
  731. }
  732. void cmFastbuildTargetGenerator::AddLinkerNodeDependnecies(
  733. FastbuildTarget& fastbuildTarget)
  734. {
  735. for (auto& linkerNode : fastbuildTarget.LinkerNode) {
  736. linkerNode.PreBuildDependencies.emplace(
  737. fastbuildTarget.Name + FASTBUILD_DEPS_ARTIFACTS_ALIAS_POSTFIX);
  738. if (!fastbuildTarget.PreLinkExecNodes.Nodes.empty()) {
  739. linkerNode.PreBuildDependencies.emplace(
  740. fastbuildTarget.Name + FASTBUILD_PRE_LINK_ALIAS_POSTFIX);
  741. }
  742. }
  743. }
  744. std::string cmFastbuildTargetGenerator::GetClangTidyReplacementsFilePath(
  745. std::string const& directory, cmSourceFile const& source,
  746. std::string const& /*config*/) const
  747. {
  748. std::string objectDir =
  749. this->ConvertToFastbuildPath(this->GeneratorTarget->GetSupportDirectory());
  750. std::string const& objectName =
  751. this->GeneratorTarget->GetObjectName(&source);
  752. std::string path =
  753. cmStrCat(directory, '/', objectDir, '/', objectName, ".yaml");
  754. LogMessage("ClangTidy replacements file: " + path);
  755. return path;
  756. }
  757. void cmFastbuildTargetGenerator::AddIncludeFlags(std::string& languageFlags,
  758. std::string const& language,
  759. std::string const&)
  760. {
  761. std::vector<std::string> includes;
  762. this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
  763. language, Config);
  764. // Add include directory flags.
  765. std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
  766. includes, this->GeneratorTarget, language, Config, false);
  767. this->LocalGenerator->AppendFlags(languageFlags, includeFlags);
  768. }
  769. std::string cmFastbuildTargetGenerator::GetName()
  770. {
  771. return GeneratorTarget->GetName();
  772. }
  773. std::string cmFastbuildTargetGenerator::ConvertToFastbuildPath(
  774. std::string const& path) const
  775. {
  776. return GetGlobalGenerator()->ConvertToFastbuildPath(path);
  777. }
  778. cmGlobalFastbuildGenerator* cmFastbuildTargetGenerator::GetGlobalGenerator()
  779. const
  780. {
  781. return this->LocalGenerator->GetGlobalFastbuildGenerator();
  782. }
  783. void cmFastbuildTargetGenerator::AdditionalCleanFiles()
  784. {
  785. if (cmValue prop_value =
  786. this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
  787. auto* lg = this->LocalGenerator;
  788. cmList cleanFiles(cmGeneratorExpression::Evaluate(*prop_value, lg, Config,
  789. this->GeneratorTarget));
  790. std::string const& binaryDir = lg->GetCurrentBinaryDirectory();
  791. auto* gg = lg->GetGlobalFastbuildGenerator();
  792. for (auto const& cleanFile : cleanFiles) {
  793. // Support relative paths
  794. gg->AddFileToClean(gg->ConvertToFastbuildPath(
  795. cmSystemTools::CollapseFullPath(cleanFile, binaryDir)));
  796. }
  797. }
  798. }