cmFastbuildTargetGenerator.cxx 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858
  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, 14);
  137. return targetName;
  138. }
  139. void cmFastbuildTargetGenerator::WriteScriptProlog(cmsys::ofstream& file) const
  140. {
  141. #ifdef _WIN32
  142. file << "@echo off\n";
  143. #else
  144. file << "set -e\n\n";
  145. #endif
  146. }
  147. void cmFastbuildTargetGenerator::WriteScriptEpilog(cmsys::ofstream& file) const
  148. {
  149. (void)file;
  150. #ifdef _WIN32
  151. file << "goto :EOF\n\n"
  152. ":ABORT\n"
  153. "set ERROR_CODE=%ERRORLEVEL%\n"
  154. "echo Batch file failed at line %FAIL_LINE% "
  155. "with errorcode %ERRORLEVEL%\n"
  156. "exit /b %ERROR_CODE%";
  157. #endif
  158. }
  159. std::string cmFastbuildTargetGenerator::GetScriptWorkingDir(
  160. cmCustomCommandGenerator const& ccg) const
  161. {
  162. std::string workingDirectory = ccg.GetWorkingDirectory();
  163. if (workingDirectory.empty()) {
  164. return this->LocalCommonGenerator->GetCurrentBinaryDirectory();
  165. }
  166. return workingDirectory;
  167. }
  168. std::string cmFastbuildTargetGenerator::GetScriptFilename(
  169. std::string const& utilityTargetName) const
  170. {
  171. std::string scriptFileName = Makefile->GetCurrentBinaryDirectory();
  172. scriptFileName += "/CMakeFiles/";
  173. scriptFileName += utilityTargetName;
  174. scriptFileName += FASTBUILD_SCRIPT_FILE_EXTENSION;
  175. return scriptFileName;
  176. }
  177. void cmFastbuildTargetGenerator::AddCommentPrinting(
  178. std::vector<std::string>& cmdLines,
  179. cmCustomCommandGenerator const& ccg) const
  180. {
  181. std::string cmakeCommand = this->GetLocalGenerator()->ConvertToOutputFormat(
  182. cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
  183. auto const comment = ccg.GetComment();
  184. if (comment) {
  185. // Comment printing should be first. Tested in
  186. // RunCMake.ExternalProject:EnvVars-build test.
  187. cmdLines.insert(
  188. cmdLines.begin(),
  189. cmakeCommand.append(" -E echo ")
  190. .append(LocalGenerator->EscapeForShell(cmGeneratorExpression::Evaluate(
  191. *comment, this->LocalGenerator, Config))));
  192. }
  193. }
  194. std::string cmFastbuildTargetGenerator::GetCdCommand(
  195. cmCustomCommandGenerator const& ccg) const
  196. {
  197. return cmStrCat(FASTBUILD_SCRIPT_CD,
  198. this->LocalGenerator->ConvertToOutputFormat(
  199. GetScriptWorkingDir(ccg), cmOutputConverter::SHELL));
  200. }
  201. void cmFastbuildTargetGenerator::WriteCmdsToFile(
  202. cmsys::ofstream& file, std::vector<std::string> const& cmds) const
  203. {
  204. #ifdef _WIN32
  205. int line = 1;
  206. for (auto cmd : cmds) {
  207. // On Windows batch, '%' is a special character that needs to be
  208. // doubled to be escaped
  209. cmSystemTools::ReplaceString(cmd, "%", "%%");
  210. file << cmd << " || (set FAIL_LINE=" << ++line << "& goto :ABORT)" << '\n';
  211. #else
  212. for (auto const& cmd : cmds) {
  213. file << cmd << '\n';
  214. #endif
  215. }
  216. }
  217. void cmFastbuildTargetGenerator::AddOutput(cmCustomCommandGenerator const& ccg,
  218. FastbuildExecNode& exec)
  219. {
  220. std::string dummyOutput = cmSystemTools::JoinPath(
  221. { LocalCommonGenerator->GetMakefile()->GetHomeOutputDirectory(),
  222. "/_fbuild_dummy" });
  223. this->GetGlobalGenerator()->AllFoldersToClean.insert(dummyOutput);
  224. dummyOutput.append("/").append(exec.Name).append(
  225. FASTBUILD_DUMMY_OUTPUT_EXTENSION);
  226. std::vector<std::string> const& outputs = ccg.GetOutputs();
  227. std::vector<std::string> const& byproducts = ccg.GetByproducts();
  228. exec.OutputsAlias.Name = exec.Name + FASTBUILD_OUTPUTS_ALIAS_POSTFIX;
  229. // If CC doesn't have any output - we should always run it.
  230. // Tested in "RunCMake.CMakePresetsBuild" test.
  231. bool hasAnyNonSymbolicOutput = false;
  232. bool const trackByproducts =
  233. this->Makefile->IsDefinitionSet(FASTBUILD_TRACK_BYPRODUCTS_AS_OUTPUT);
  234. auto const isSymbolic = [this](std::string const& file) {
  235. cmSourceFile* sf = this->Makefile->GetSource(file);
  236. if (sf && sf->GetPropertyAsBool("SYMBOLIC")) {
  237. LogMessage("Skipping symbolic file: " + file);
  238. return true;
  239. }
  240. return false;
  241. };
  242. for (std::string const& output : outputs) {
  243. // Tested in "RunCMake.BuildDepends".
  244. if (isSymbolic(output)) {
  245. continue;
  246. }
  247. hasAnyNonSymbolicOutput = true;
  248. std::string const outputPath = this->ConvertToFastbuildPath(output);
  249. LogMessage("CC's output: " + outputPath);
  250. exec.OutputsAlias.PreBuildDependencies.emplace(outputPath);
  251. // Ensure output path exists. For some reason, "CMake -E touch" fails with
  252. // "cmake -E touch: failed to update "...
  253. cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(outputPath));
  254. this->GetGlobalGenerator()->AddFileToClean(outputPath);
  255. }
  256. exec.ByproductsAlias.Name = exec.Name + FASTBUILD_BYPRODUCTS_ALIAS_POSTFIX;
  257. for (std::string const& byproduct : byproducts) {
  258. if (trackByproducts) {
  259. hasAnyNonSymbolicOutput = true;
  260. }
  261. std::string const byproductPath = this->ConvertToFastbuildPath(byproduct);
  262. exec.ByproductsAlias.PreBuildDependencies.emplace(byproductPath);
  263. this->GetGlobalGenerator()->AddFileToClean(byproductPath);
  264. }
  265. auto const addDummyOutput = [&] {
  266. // So that the dummy file is always created.
  267. exec.ExecUseStdOutAsOutput = true;
  268. exec.ExecOutput = this->ConvertToFastbuildPath(dummyOutput);
  269. for (auto const& output : exec.OutputsAlias.PreBuildDependencies) {
  270. OutputsToReplace[output.Name] = exec.ExecOutput;
  271. LogMessage("Adding replace from " + output.Name + " to " +
  272. exec.ExecOutput);
  273. }
  274. };
  275. // We don't have any output that is expected to appear on disk -> run always.
  276. // Tested in "RunCMake.ExternalProject":BUILD_ALWAYS
  277. if (!hasAnyNonSymbolicOutput) {
  278. exec.ExecAlways = true;
  279. addDummyOutput();
  280. return;
  281. }
  282. if (!exec.OutputsAlias.PreBuildDependencies.empty()) {
  283. exec.ExecOutput = this->ConvertToFastbuildPath(
  284. exec.OutputsAlias.PreBuildDependencies.begin()->Name);
  285. } else {
  286. exec.ExecOutput = this->ConvertToFastbuildPath(
  287. exec.ByproductsAlias.PreBuildDependencies.begin()->Name);
  288. }
  289. // Optionally add the "deps-check" Exec if we have more than 1 OUTPUT, but
  290. // allow user to opt out.
  291. if (exec.OutputsAlias.PreBuildDependencies.size() > 1 &&
  292. !this->Makefile->IsDefinitionSet(
  293. FASTBUILD_DISABLE_OUTPUT_PRECHECK_EXEC)) {
  294. exec.NeedsDepsCheckExec = true;
  295. }
  296. }
  297. void cmFastbuildTargetGenerator::AddExecArguments(
  298. FastbuildExecNode& exec, std::string const& scriptFilename) const
  299. {
  300. exec.ExecArguments = FASTBUILD_SCRIPT_FILE_ARG;
  301. exec.ExecArguments +=
  302. cmGlobalFastbuildGenerator::QuoteIfHasSpaces(scriptFilename);
  303. exec.ScriptFile = scriptFilename;
  304. exec.ExecExecutable =
  305. cmGlobalFastbuildGenerator::GetExternalShellExecutable();
  306. }
  307. void cmFastbuildTargetGenerator::GetDepends(
  308. cmCustomCommandGenerator const& ccg, std::string const& currentCCName,
  309. std::vector<std::string>& fileLevelDeps,
  310. std::set<FastbuildTargetDep>& targetDep) const
  311. {
  312. for (auto dep : ccg.GetDepends()) {
  313. LogMessage("Dep: " + dep);
  314. auto orig = dep;
  315. if (this->LocalCommonGenerator->GetRealDependency(dep, Config, dep)) {
  316. LogMessage("Real dep: " + dep);
  317. if (!dep.empty()) {
  318. LogMessage("Custom command real dep: " + dep);
  319. for (auto const& item : cmList{ cmGeneratorExpression::Evaluate(
  320. this->ConvertToFastbuildPath(dep), this->LocalGenerator,
  321. Config) }) {
  322. fileLevelDeps.emplace_back(item);
  323. }
  324. }
  325. }
  326. dep = this->ConvertToFastbuildPath(dep);
  327. LogMessage("Real dep converted: " + dep);
  328. auto const targetInfo = this->LocalGenerator->GetSourcesWithOutput(dep);
  329. if (targetInfo.Target) {
  330. LogMessage("dep: " + dep + ", target: " + targetInfo.Target->GetName());
  331. auto const& target = targetInfo.Target;
  332. auto const processCCs = [this, &currentCCName, &targetDep,
  333. dep](std::vector<cmCustomCommand> const& ccs,
  334. FastbuildBuildStep step) {
  335. for (auto const& cc : ccs) {
  336. for (auto const& output : cc.GetOutputs()) {
  337. LogMessage("dep: " + dep + ", post output: " +
  338. this->ConvertToFastbuildPath(output));
  339. if (this->ConvertToFastbuildPath(output) == dep) {
  340. auto ccName = this->GetCustomCommandTargetName(cc, step);
  341. if (ccName != currentCCName) {
  342. LogMessage("Additional CC dep from target: " + ccName);
  343. targetDep.emplace(std::move(ccName));
  344. }
  345. }
  346. }
  347. for (auto const& byproduct : cc.GetByproducts()) {
  348. LogMessage("dep: " + dep + ", post byproduct: " +
  349. this->ConvertToFastbuildPath(byproduct));
  350. if (this->ConvertToFastbuildPath(byproduct) == dep) {
  351. auto ccName = this->GetCustomCommandTargetName(cc, step);
  352. if (ccName != currentCCName) {
  353. LogMessage("Additional CC dep from target: " + ccName);
  354. targetDep.emplace(std::move(ccName));
  355. }
  356. }
  357. }
  358. }
  359. };
  360. processCCs(target->GetPreBuildCommands(), FastbuildBuildStep::PRE_BUILD);
  361. processCCs(target->GetPreLinkCommands(), FastbuildBuildStep::PRE_LINK);
  362. processCCs(target->GetPostBuildCommands(),
  363. FastbuildBuildStep::POST_BUILD);
  364. continue;
  365. }
  366. if (!targetInfo.Source) {
  367. LogMessage("dep: " + dep + ", no source, byproduct: " +
  368. std::to_string(targetInfo.SourceIsByproduct));
  369. // Tested in "OutDir" test.
  370. if (!cmSystemTools::FileIsFullPath(orig)) {
  371. targetDep.emplace(std::move(orig));
  372. }
  373. continue;
  374. }
  375. if (!targetInfo.Source->GetCustomCommand()) {
  376. LogMessage("dep: " + dep + ", no GetCustomCommand");
  377. continue;
  378. }
  379. if (targetInfo.Source && targetInfo.Source->GetCustomCommand()) {
  380. auto ccName = this->GetCustomCommandTargetName(
  381. *targetInfo.Source->GetCustomCommand(), FastbuildBuildStep::REST);
  382. if (ccName != currentCCName) {
  383. LogMessage("Additional CC dep: " + ccName);
  384. targetDep.emplace(std::move(ccName));
  385. }
  386. }
  387. }
  388. }
  389. void cmFastbuildTargetGenerator::ReplaceProblematicMakeVars(
  390. std::string& command) const
  391. {
  392. // TODO: fix problematic global targets. For now, search and replace the
  393. // makefile vars.
  394. cmSystemTools::ReplaceString(
  395. command, "$(CMAKE_SOURCE_DIR)",
  396. this->LocalGenerator->ConvertToOutputFormat(
  397. this->LocalGenerator->GetSourceDirectory(), cmOutputConverter::SHELL));
  398. cmSystemTools::ReplaceString(
  399. command, "$(CMAKE_BINARY_DIR)",
  400. this->LocalGenerator->ConvertToOutputFormat(
  401. this->LocalGenerator->GetBinaryDirectory(), cmOutputConverter::SHELL));
  402. cmSystemTools::ReplaceString(command, "$(ARGS)", "");
  403. }
  404. FastbuildExecNode cmFastbuildTargetGenerator::GetAppleTextStubCommand() const
  405. {
  406. FastbuildExecNode res;
  407. if (!this->GeneratorTarget->IsApple() ||
  408. !this->GeneratorTarget->HasImportLibrary(Config)) {
  409. return res;
  410. }
  411. auto const names = DetectOutput();
  412. std::string const outpathImp =
  413. this->ConvertToFastbuildPath(this->GeneratorTarget->GetDirectory(
  414. Config, cmStateEnums::ImportLibraryArtifact));
  415. std::string const binPath =
  416. this->ConvertToFastbuildPath(this->GeneratorTarget->GetDirectory(
  417. Config, cmStateEnums::RuntimeBinaryArtifact));
  418. cmSystemTools::MakeDirectory(outpathImp);
  419. std::string rule = this->LocalGenerator->GetMakefile()->GetSafeDefinition(
  420. "CMAKE_CREATE_TEXT_STUBS");
  421. LogMessage("CMAKE_CREATE_TEXT_STUBS:" + rule);
  422. auto rulePlaceholderExpander =
  423. this->GetLocalGenerator()->CreateRulePlaceholderExpander();
  424. cmRulePlaceholderExpander::RuleVariables vars;
  425. res.ExecOutput = cmStrCat(outpathImp, '/', names.ImportReal);
  426. res.ExecInput = { cmStrCat(binPath, '/', names.SharedObject) };
  427. vars.Target = res.ExecInput[0].c_str();
  428. rulePlaceholderExpander->SetTargetImpLib(res.ExecOutput);
  429. rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(), rule,
  430. vars);
  431. LogMessage("CMAKE_CREATE_TEXT_STUBS expanded:" + rule);
  432. std::string executable;
  433. std::string args;
  434. if (!cmSystemTools::SplitProgramFromArgs(rule, executable, args)) {
  435. cmSystemTools::Error("Failed to split program from args: " + rule);
  436. return res;
  437. }
  438. res.Name = "create_" + names.ImportOutput + "_text_stub";
  439. res.ExecExecutable = std::move(executable);
  440. res.ExecArguments = std::move(args);
  441. res.ExecWorkingDir = this->LocalCommonGenerator->GetCurrentBinaryDirectory();
  442. // Wait for the build.
  443. res.PreBuildDependencies.emplace(this->GetTargetName());
  444. return res;
  445. }
  446. FastbuildExecNode cmFastbuildTargetGenerator::GetDepsCheckExec(
  447. FastbuildExecNode const& depender)
  448. {
  449. FastbuildExecNode exec;
  450. exec.Name = depender.Name + "-check-depends";
  451. exec.ExecAlways = true;
  452. exec.ExecUseStdOutAsOutput = true;
  453. exec.ExecOutput = depender.ExecOutput + ".deps-checker";
  454. exec.ExecExecutable = cmSystemTools::GetCMakeCommand();
  455. exec.ExecArguments += "-E cmake_fastbuild_check_depends ";
  456. exec.ExecArguments += depender.ExecOutput + " ";
  457. char const* sep = "";
  458. for (auto const& dep : depender.OutputsAlias.PreBuildDependencies) {
  459. exec.ExecArguments += sep;
  460. exec.ExecArguments += dep.Name;
  461. sep = " ";
  462. }
  463. for (auto const& dep : depender.ByproductsAlias.PreBuildDependencies) {
  464. exec.ExecArguments += sep;
  465. exec.ExecArguments += dep.Name;
  466. sep = " ";
  467. }
  468. return exec;
  469. }
  470. FastbuildExecNodes cmFastbuildTargetGenerator::GenerateCommands(
  471. FastbuildBuildStep buildStep)
  472. {
  473. FastbuildExecNodes execs;
  474. execs.Alias.Name = GetUtilityAliasFromBuildStep(buildStep);
  475. std::vector<cmCustomCommand> commands;
  476. if (buildStep == FastbuildBuildStep::PRE_BUILD) {
  477. commands = GeneratorTarget->GetPreBuildCommands();
  478. LogMessage("STEP: PRE_BUILD");
  479. } else if (buildStep == FastbuildBuildStep::PRE_LINK) {
  480. commands = GeneratorTarget->GetPreLinkCommands();
  481. LogMessage("STEP: PRE_LINK");
  482. } else if (buildStep == FastbuildBuildStep::POST_BUILD) {
  483. commands = GeneratorTarget->GetPostBuildCommands();
  484. LogMessage("STEP: POST_BUILD");
  485. } else {
  486. LogMessage("STEP: ALL CUSTOM COMMANDS");
  487. std::vector<cmSourceFile const*> customCommands;
  488. GeneratorTarget->GetCustomCommands(customCommands, Config);
  489. for (cmSourceFile const* source : customCommands) {
  490. cmCustomCommand const* cmd = source->GetCustomCommand();
  491. if (!cmd->GetCommandLines().empty()) {
  492. commands.emplace_back(*cmd);
  493. }
  494. }
  495. }
  496. LogMessage(cmStrCat("Number of custom commands: ", commands.size()));
  497. for (cmCustomCommand const& customCommand : commands) {
  498. cmCustomCommandGenerator ccg(customCommand, Config, LocalCommonGenerator);
  499. std::string launcher = this->MakeCustomLauncher(ccg);
  500. std::string const execName =
  501. GetCustomCommandTargetName(customCommand, buildStep);
  502. std::vector<std::string> cmdLines;
  503. if (ccg.GetNumberOfCommands() > 0) {
  504. cmdLines.push_back(GetCdCommand(ccg));
  505. }
  506. // Since we are not using FASTBuild Exec nodes natively, we need to
  507. // have shell specific escape.
  508. this->LocalGenerator->GetState()->SetFastbuildMake(false);
  509. // To avoid replacing $ with $$ in the command line.
  510. this->LocalGenerator->SetLinkScriptShell(true);
  511. for (unsigned j = 0; j != ccg.GetNumberOfCommands(); ++j) {
  512. std::string const command = ccg.GetCommand(j);
  513. // Tested in "CustomCommand" ("empty_command") test.
  514. if (!command.empty()) {
  515. cmdLines.emplace_back(launcher +
  516. this->LocalGenerator->ConvertToOutputFormat(
  517. command, cmOutputConverter::SHELL));
  518. std::string& cmd = cmdLines.back();
  519. ccg.AppendArguments(j, cmd);
  520. ReplaceProblematicMakeVars(cmd);
  521. LogMessage("cmCustomCommandLine: " + cmd);
  522. }
  523. }
  524. if (cmdLines.empty()) {
  525. return {};
  526. }
  527. this->LocalGenerator->GetState()->SetFastbuildMake(true);
  528. FastbuildExecNode execNode;
  529. execNode.Name = execName;
  530. // Add depncencies to "ExecInput" so that FASTBuild will re-run the Exec
  531. // when needed, but also add to "PreBuildDependencies" for correct sorting.
  532. // Tested in "ObjectLibrary / complexOneConfig" tests.
  533. GetDepends(ccg, execName, execNode.ExecInput,
  534. execNode.PreBuildDependencies);
  535. for (auto const& util : ccg.GetUtilities()) {
  536. auto const& utilTargetName = util.Value.first;
  537. LogMessage("Util: " + utilTargetName +
  538. ", cross: " + std::to_string(util.Value.second));
  539. auto* const target = this->Makefile->FindTargetToUse(utilTargetName);
  540. if (target && target->IsImported()) {
  541. std::string importedLoc =
  542. this->ConvertToFastbuildPath(target->ImportedGetFullPath(
  543. Config, cmStateEnums::ArtifactType::RuntimeBinaryArtifact));
  544. if (importedLoc.empty()) {
  545. importedLoc =
  546. this->ConvertToFastbuildPath(target->ImportedGetFullPath(
  547. Config, cmStateEnums::ArtifactType::ImportLibraryArtifact));
  548. }
  549. LogMessage("adding file level dep on imported target: " + importedLoc);
  550. execNode.ExecInput.emplace_back(std::move(importedLoc));
  551. continue;
  552. }
  553. // This CC uses some executable produced by another target. Add explicit
  554. // dep. Tested in "CustomCommand" test.
  555. if (util.Value.second) {
  556. if (utilTargetName != customCommand.GetTarget()) {
  557. LogMessage("Adding util dep: " + utilTargetName);
  558. execNode.PreBuildDependencies.emplace(utilTargetName);
  559. }
  560. }
  561. }
  562. execs.Alias.PreBuildDependencies.emplace(execNode.Name);
  563. LogMessage(cmStrCat("cmdLines size ", cmdLines.size()));
  564. if (!cmdLines.empty()) {
  565. std::string const scriptFileName = GetScriptFilename(execName);
  566. cmsys::ofstream scriptFile(scriptFileName.c_str());
  567. AddOutput(ccg, execNode);
  568. AddExecArguments(execNode, scriptFileName);
  569. AddCommentPrinting(cmdLines, ccg);
  570. WriteScriptProlog(scriptFile);
  571. WriteCmdsToFile(scriptFile, cmdLines);
  572. WriteScriptEpilog(scriptFile);
  573. }
  574. if (buildStep == FastbuildBuildStep::POST_BUILD) {
  575. // Execute POST_BUILD in order in which they are declared.
  576. // Tested in "complex" test.
  577. for (auto& exec : execs.Nodes) {
  578. execNode.PreBuildDependencies.emplace(exec.Name);
  579. }
  580. }
  581. for (auto const& out : execNode.OutputsAlias.PreBuildDependencies) {
  582. LogMessage("Adding replace from " + out.Name + " to " + execName);
  583. OutputToExecName[out.Name] = execName;
  584. }
  585. execs.Nodes.emplace_back(std::move(execNode));
  586. }
  587. for (auto& exec : execs.Nodes) {
  588. for (auto& inputFile : exec.ExecInput) {
  589. auto const iter = OutputsToReplace.find(inputFile);
  590. if (iter != OutputsToReplace.end()) {
  591. LogMessage("Replacing input: " + inputFile + " with " + iter->second);
  592. inputFile = iter->second;
  593. }
  594. auto const depIter = std::find_if(
  595. exec.PreBuildDependencies.begin(), exec.PreBuildDependencies.end(),
  596. [this](FastbuildTargetDep const& dep) {
  597. return !OutputToExecName[dep.Name].empty();
  598. });
  599. if (depIter != exec.PreBuildDependencies.end()) {
  600. LogMessage("Replacing dep " + depIter->Name + " with " +
  601. OutputToExecName[depIter->Name]);
  602. exec.PreBuildDependencies.emplace(OutputToExecName[depIter->Name]);
  603. exec.PreBuildDependencies.erase(depIter);
  604. }
  605. }
  606. if (exec.NeedsDepsCheckExec) {
  607. auto depsCheckExec = GetDepsCheckExec(exec);
  608. LogMessage("Adding deps check Exec: " + depsCheckExec.Name);
  609. exec.PreBuildDependencies.emplace(depsCheckExec.Name);
  610. this->GetGlobalGenerator()->AddTarget(std::move(depsCheckExec));
  611. }
  612. }
  613. return execs;
  614. }
  615. std::string cmFastbuildTargetGenerator::MakeCustomLauncher(
  616. cmCustomCommandGenerator const& ccg)
  617. {
  618. // Copied from cmLocalNinjaGenerator::MakeCustomLauncher.
  619. cmValue property_value = this->Makefile->GetProperty("RULE_LAUNCH_CUSTOM");
  620. if (!cmNonempty(property_value)) {
  621. return std::string();
  622. }
  623. // Expand rule variables referenced in the given launcher command.
  624. cmRulePlaceholderExpander::RuleVariables vars;
  625. std::string output;
  626. std::vector<std::string> const& outputs = ccg.GetOutputs();
  627. for (size_t i = 0; i < outputs.size(); ++i) {
  628. output =
  629. cmStrCat(output,
  630. this->LocalGenerator->ConvertToOutputFormat(
  631. ccg.GetWorkingDirectory().empty()
  632. ? this->LocalGenerator->MaybeRelativeToCurBinDir(outputs[i])
  633. : outputs[i],
  634. cmOutputConverter::SHELL));
  635. if (i != outputs.size() - 1) {
  636. output = cmStrCat(output, ',');
  637. }
  638. }
  639. vars.Output = output.c_str();
  640. vars.Role = ccg.GetCC().GetRole().c_str();
  641. vars.CMTargetName = ccg.GetCC().GetTarget().c_str();
  642. vars.Config = ccg.GetOutputConfig().c_str();
  643. auto rulePlaceholderExpander =
  644. this->LocalGenerator->CreateRulePlaceholderExpander();
  645. std::string launcher = *property_value;
  646. rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, launcher,
  647. vars);
  648. if (!launcher.empty()) {
  649. launcher += " ";
  650. }
  651. LogMessage("CC Launcher: " + launcher);
  652. return launcher;
  653. }
  654. std::string cmFastbuildTargetGenerator::GetTargetName() const
  655. {
  656. if (this->GeneratorTarget->GetType() == cmStateEnums::GLOBAL_TARGET) {
  657. return this->GetGlobalGenerator()->GetTargetName(GeneratorTarget);
  658. }
  659. return this->GeneratorTarget->GetName();
  660. }
  661. cmGeneratorTarget::Names cmFastbuildTargetGenerator::DetectOutput() const
  662. {
  663. if (GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) {
  664. return GeneratorTarget->GetExecutableNames(Config);
  665. }
  666. return GeneratorTarget->GetLibraryNames(Config);
  667. }
  668. void cmFastbuildTargetGenerator::AddObjectDependencies(
  669. FastbuildTarget& fastbuildTarget,
  670. std::vector<std::string>& allObjectDepends) const
  671. {
  672. auto const FindObjListWhichOutputs = [&fastbuildTarget](
  673. std::string const& output) {
  674. for (FastbuildObjectListNode const& objList :
  675. fastbuildTarget.ObjectListNodes) {
  676. if (objList.ObjectOutputs.find(output) != objList.ObjectOutputs.end()) {
  677. return objList.Name;
  678. }
  679. }
  680. return std::string{};
  681. };
  682. for (FastbuildObjectListNode& objList : fastbuildTarget.ObjectListNodes) {
  683. for (auto const& objDep : objList.ObjectDepends) {
  684. // Check if there is another object list which outputs (OBJECT_OUTPUTS)
  685. // something that this object list needs (OBJECT_DEPENDS).
  686. auto anotherObjList = FindObjListWhichOutputs(objDep);
  687. if (!anotherObjList.empty()) {
  688. LogMessage("Adding explicit <OBJECT_DEPENDS> dep: " + anotherObjList);
  689. allObjectDepends.emplace_back(anotherObjList);
  690. objList.PreBuildDependencies.emplace(std::move(anotherObjList));
  691. } else {
  692. LogMessage("Adding <OBJECT_DEPENDS> dep: " + objDep);
  693. allObjectDepends.emplace_back(objDep);
  694. objList.PreBuildDependencies.emplace(objDep);
  695. }
  696. }
  697. }
  698. cmGlobalFastbuildGenerator::TopologicalSort(fastbuildTarget.ObjectListNodes);
  699. }
  700. void cmFastbuildTargetGenerator::AddLinkerNodeDependnecies(
  701. FastbuildTarget& fastbuildTarget)
  702. {
  703. for (auto& linkerNode : fastbuildTarget.LinkerNode) {
  704. if (!fastbuildTarget.PreLinkExecNodes.Nodes.empty()) {
  705. linkerNode.PreBuildDependencies.emplace(
  706. fastbuildTarget.Name + FASTBUILD_PRE_LINK_ALIAS_POSTFIX);
  707. }
  708. }
  709. }
  710. std::string cmFastbuildTargetGenerator::GetClangTidyReplacementsFilePath(
  711. std::string const& directory, cmSourceFile const& source,
  712. std::string const& /*config*/) const
  713. {
  714. std::string objectDir =
  715. this->ConvertToFastbuildPath(this->GeneratorTarget->GetSupportDirectory());
  716. std::string const& objectName =
  717. this->GeneratorTarget->GetObjectName(&source);
  718. std::string path =
  719. cmStrCat(directory, '/', objectDir, '/', objectName, ".yaml");
  720. LogMessage("ClangTidy replacements file: " + path);
  721. return path;
  722. }
  723. void cmFastbuildTargetGenerator::AddIncludeFlags(std::string& languageFlags,
  724. std::string const& language,
  725. std::string const&)
  726. {
  727. std::vector<std::string> includes;
  728. this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
  729. language, Config);
  730. // Add include directory flags.
  731. std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
  732. includes, this->GeneratorTarget, language, Config, false);
  733. this->LocalGenerator->AppendFlags(languageFlags, includeFlags);
  734. }
  735. std::string cmFastbuildTargetGenerator::GetName()
  736. {
  737. return GeneratorTarget->GetName();
  738. }
  739. std::string cmFastbuildTargetGenerator::ConvertToFastbuildPath(
  740. std::string const& path) const
  741. {
  742. return GetGlobalGenerator()->ConvertToFastbuildPath(path);
  743. }
  744. cmGlobalFastbuildGenerator* cmFastbuildTargetGenerator::GetGlobalGenerator()
  745. const
  746. {
  747. return this->LocalGenerator->GetGlobalFastbuildGenerator();
  748. }
  749. void cmFastbuildTargetGenerator::AdditionalCleanFiles()
  750. {
  751. if (cmValue prop_value =
  752. this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
  753. auto* lg = this->LocalGenerator;
  754. cmList cleanFiles(cmGeneratorExpression::Evaluate(*prop_value, lg, Config,
  755. this->GeneratorTarget));
  756. std::string const& binaryDir = lg->GetCurrentBinaryDirectory();
  757. auto* gg = lg->GetGlobalFastbuildGenerator();
  758. for (auto const& cleanFile : cleanFiles) {
  759. // Support relative paths
  760. gg->AddFileToClean(gg->ConvertToFastbuildPath(
  761. cmSystemTools::CollapseFullPath(cleanFile, binaryDir)));
  762. }
  763. }
  764. }