cmFastbuildTargetGenerator.cxx 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  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. 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. }
  596. // Tested in "ObjectLibrary / complexOneConfig" tests.
  597. for (std::string& additionalDep : GetDepends(ccg)) {
  598. if (additionalDep != execName) {
  599. LogMessage("Adding additional dep: " + additionalDep);
  600. execNode.PreBuildDependencies.emplace(std::move(additionalDep));
  601. }
  602. }
  603. if (buildStep == FastbuildBuildStep::POST_BUILD) {
  604. // Execute POST_BUILD in order in which they are declared.
  605. // Tested in "complex" test.
  606. for (auto& exec : execs.Nodes) {
  607. execNode.PreBuildDependencies.emplace(exec.Name);
  608. }
  609. }
  610. for (auto const& out : execNode.OutputsAlias.PreBuildDependencies) {
  611. LogMessage("Adding replace from " + out.Name + " to " + execName);
  612. OutputToExecName[out.Name] = execName;
  613. }
  614. execs.Nodes.emplace_back(std::move(execNode));
  615. }
  616. for (auto& exec : execs.Nodes) {
  617. for (auto& inputFile : exec.ExecInput) {
  618. auto const iter = OutputsToReplace.find(inputFile);
  619. if (iter != OutputsToReplace.end()) {
  620. LogMessage("Replacing input: " + inputFile + " with " + iter->second);
  621. inputFile = iter->second;
  622. }
  623. auto const depIter = std::find_if(
  624. exec.PreBuildDependencies.begin(), exec.PreBuildDependencies.end(),
  625. [this](FastbuildTargetDep const& dep) {
  626. return !OutputToExecName[dep.Name].empty();
  627. });
  628. if (depIter != exec.PreBuildDependencies.end()) {
  629. LogMessage("Replacing dep " + depIter->Name + " with " +
  630. OutputToExecName[depIter->Name]);
  631. exec.PreBuildDependencies.emplace(OutputToExecName[depIter->Name]);
  632. exec.PreBuildDependencies.erase(depIter);
  633. }
  634. }
  635. if (exec.NeedsDepsCheckExec) {
  636. auto depsCheckExec = GetDepsCheckExec(exec);
  637. LogMessage("Adding deps check Exec: " + depsCheckExec.Name);
  638. exec.PreBuildDependencies.emplace(depsCheckExec.Name);
  639. this->GetGlobalGenerator()->AddTarget(std::move(depsCheckExec));
  640. }
  641. }
  642. return execs;
  643. }
  644. std::string cmFastbuildTargetGenerator::MakeCustomLauncher(
  645. cmCustomCommandGenerator const& ccg)
  646. {
  647. // Copied from cmLocalNinjaGenerator::MakeCustomLauncher.
  648. cmValue property_value = this->Makefile->GetProperty("RULE_LAUNCH_CUSTOM");
  649. if (!cmNonempty(property_value)) {
  650. return std::string();
  651. }
  652. // Expand rule variables referenced in the given launcher command.
  653. cmRulePlaceholderExpander::RuleVariables vars;
  654. std::string output;
  655. std::vector<std::string> const& outputs = ccg.GetOutputs();
  656. for (size_t i = 0; i < outputs.size(); ++i) {
  657. output =
  658. cmStrCat(output,
  659. this->LocalGenerator->ConvertToOutputFormat(
  660. ccg.GetWorkingDirectory().empty()
  661. ? this->LocalGenerator->MaybeRelativeToCurBinDir(outputs[i])
  662. : outputs[i],
  663. cmOutputConverter::SHELL));
  664. if (i != outputs.size() - 1) {
  665. output = cmStrCat(output, ',');
  666. }
  667. }
  668. vars.Output = output.c_str();
  669. vars.Role = ccg.GetCC().GetRole().c_str();
  670. auto rulePlaceholderExpander =
  671. this->LocalGenerator->CreateRulePlaceholderExpander();
  672. std::string launcher = *property_value;
  673. rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, launcher,
  674. vars);
  675. if (!launcher.empty()) {
  676. launcher += " ";
  677. }
  678. LogMessage("CC Launcher: " + launcher);
  679. return launcher;
  680. }
  681. std::string cmFastbuildTargetGenerator::GetTargetName() const
  682. {
  683. if (this->GeneratorTarget->GetType() == cmStateEnums::GLOBAL_TARGET) {
  684. return this->GetGlobalGenerator()->GetTargetName(GeneratorTarget);
  685. }
  686. return this->GeneratorTarget->GetName();
  687. }
  688. cmGeneratorTarget::Names cmFastbuildTargetGenerator::DetectOutput() const
  689. {
  690. if (GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) {
  691. return GeneratorTarget->GetExecutableNames(Config);
  692. }
  693. return GeneratorTarget->GetLibraryNames(Config);
  694. }
  695. void cmFastbuildTargetGenerator::AddObjectDependencies(
  696. FastbuildTarget& fastbuildTarget,
  697. std::vector<std::string>& allObjectDepends) const
  698. {
  699. auto const FindObjListWhichOutputs = [&fastbuildTarget](
  700. std::string const& output) {
  701. for (FastbuildObjectListNode const& objList :
  702. fastbuildTarget.ObjectListNodes) {
  703. if (objList.ObjectOutputs.find(output) != objList.ObjectOutputs.end()) {
  704. return objList.Name;
  705. }
  706. }
  707. return std::string{};
  708. };
  709. for (FastbuildObjectListNode& objList : fastbuildTarget.ObjectListNodes) {
  710. objList.PreBuildDependencies.emplace(
  711. fastbuildTarget.Name + FASTBUILD_DEPS_ARTIFACTS_ALIAS_POSTFIX);
  712. for (auto const& objDep : objList.ObjectDepends) {
  713. // Check if there is another object list which outputs (OBJECT_OUTPUTS)
  714. // something that this object list needs (OBJECT_DEPENDS).
  715. auto anotherObjList = FindObjListWhichOutputs(objDep);
  716. if (!anotherObjList.empty()) {
  717. LogMessage("Adding explicit <OBJECT_DEPENDS> dep: " + anotherObjList);
  718. allObjectDepends.emplace_back(anotherObjList);
  719. objList.PreBuildDependencies.emplace(std::move(anotherObjList));
  720. } else {
  721. LogMessage("Adding <OBJECT_DEPENDS> dep: " + objDep);
  722. allObjectDepends.emplace_back(objDep);
  723. objList.PreBuildDependencies.emplace(objDep);
  724. }
  725. }
  726. }
  727. cmGlobalFastbuildGenerator::TopologicalSort(fastbuildTarget.ObjectListNodes);
  728. }
  729. void cmFastbuildTargetGenerator::AddLinkerNodeDependnecies(
  730. FastbuildTarget& fastbuildTarget)
  731. {
  732. for (auto& linkerNode : fastbuildTarget.LinkerNode) {
  733. linkerNode.PreBuildDependencies.emplace(
  734. fastbuildTarget.Name + FASTBUILD_DEPS_ARTIFACTS_ALIAS_POSTFIX);
  735. if (!fastbuildTarget.PreLinkExecNodes.Nodes.empty()) {
  736. linkerNode.PreBuildDependencies.emplace(
  737. fastbuildTarget.Name + FASTBUILD_PRE_LINK_ALIAS_POSTFIX);
  738. }
  739. }
  740. }
  741. std::string cmFastbuildTargetGenerator::GetClangTidyReplacementsFilePath(
  742. std::string const& directory, cmSourceFile const& source,
  743. std::string const& /*config*/) const
  744. {
  745. std::string objectDir =
  746. this->ConvertToFastbuildPath(this->GeneratorTarget->GetSupportDirectory());
  747. std::string const& objectName =
  748. this->GeneratorTarget->GetObjectName(&source);
  749. std::string path =
  750. cmStrCat(directory, '/', objectDir, '/', objectName, ".yaml");
  751. LogMessage("ClangTidy replacements file: " + path);
  752. return path;
  753. }
  754. void cmFastbuildTargetGenerator::AddIncludeFlags(std::string& languageFlags,
  755. std::string const& language,
  756. std::string const&)
  757. {
  758. std::vector<std::string> includes;
  759. this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
  760. language, Config);
  761. // Add include directory flags.
  762. std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
  763. includes, this->GeneratorTarget, language, Config, false);
  764. this->LocalGenerator->AppendFlags(languageFlags, includeFlags);
  765. }
  766. std::string cmFastbuildTargetGenerator::GetName()
  767. {
  768. return GeneratorTarget->GetName();
  769. }
  770. std::string cmFastbuildTargetGenerator::ConvertToFastbuildPath(
  771. std::string const& path) const
  772. {
  773. return GetGlobalGenerator()->ConvertToFastbuildPath(path);
  774. }
  775. cmGlobalFastbuildGenerator* cmFastbuildTargetGenerator::GetGlobalGenerator()
  776. const
  777. {
  778. return this->LocalGenerator->GetGlobalFastbuildGenerator();
  779. }
  780. void cmFastbuildTargetGenerator::AdditionalCleanFiles()
  781. {
  782. if (cmValue prop_value =
  783. this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
  784. auto* lg = this->LocalGenerator;
  785. cmList cleanFiles(cmGeneratorExpression::Evaluate(*prop_value, lg, Config,
  786. this->GeneratorTarget));
  787. std::string const& binaryDir = lg->GetCurrentBinaryDirectory();
  788. auto* gg = lg->GetGlobalFastbuildGenerator();
  789. for (auto const& cleanFile : cleanFiles) {
  790. // Support relative paths
  791. gg->AddFileToClean(gg->ConvertToFastbuildPath(
  792. cmSystemTools::CollapseFullPath(cleanFile, binaryDir)));
  793. }
  794. }
  795. }