cmGlobalFastbuildGenerator.cxx 64 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879
  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 "cmGlobalFastbuildGenerator.h"
  4. #include <algorithm>
  5. #include <cstdlib>
  6. #include <initializer_list>
  7. #include <iterator>
  8. #include <queue>
  9. #include <sstream>
  10. #include <cm/memory>
  11. #include "cmsys/FStream.hxx"
  12. #include "cmsys/RegularExpression.hxx"
  13. #include "cmFastbuildLinkLineComputer.h"
  14. #include "cmFastbuildTargetGenerator.h" // IWYU pragma: keep
  15. #include "cmGeneratedFileStream.h"
  16. #include "cmGeneratorTarget.h"
  17. #include "cmGlobCacheEntry.h"
  18. #include "cmGlobalGenerator.h"
  19. #include "cmGlobalGeneratorFactory.h"
  20. #include "cmList.h"
  21. #include "cmLocalFastbuildGenerator.h"
  22. #include "cmLocalGenerator.h"
  23. #include "cmMakefile.h"
  24. #include "cmMessageType.h"
  25. #include "cmState.h"
  26. #include "cmStateDirectory.h"
  27. #include "cmStateSnapshot.h"
  28. #include "cmStringAlgorithms.h"
  29. #include "cmSystemTools.h"
  30. #include "cmValue.h"
  31. #include "cmVersion.h"
  32. #include "cmake.h"
  33. #if defined(_WIN32)
  34. # include <future>
  35. # include <objbase.h>
  36. # include <shellapi.h>
  37. #endif
  38. class cmLinkLineComputer;
  39. #define FASTBUILD_REBUILD_BFF_TARGET_NAME "rebuild-bff"
  40. #define FASTBUILD_GLOB_CHECK_TARGET "glob-check"
  41. #define FASTBUILD_ENV_VAR_NAME "LocalEnv"
  42. // IDE support
  43. #define FASTBUILD_XCODE_BASE_PATH "XCode/Projects"
  44. #define FASTBUILD_VS_BASE_PATH "VisualStudio/Projects"
  45. #define FASTBUILD_IDE_VS_COMMAND_PREFIX "cd ^$(SolutionDir).. && "
  46. #define FASTBUILD_IDE_BUILD_ARGS " -ide -cache -summary -dist "
  47. constexpr auto FASTBUILD_CAPTURE_SYSTEM_ENV =
  48. "CMAKE_FASTBUILD_CAPTURE_SYSTEM_ENV";
  49. constexpr auto FASTBUILD_ENV_OVERRIDES = "CMAKE_FASTBUILD_ENV_OVERRIDES";
  50. // Inherits from "CMAKE_FASTBUILD_VERBOSE_GENERATOR" env variable.
  51. constexpr auto FASTBUILD_VERBOSE_GENERATOR =
  52. "CMAKE_FASTBUILD_VERBOSE_GENERATOR";
  53. constexpr auto FASTBUILD_CACHE_PATH = "CMAKE_FASTBUILD_CACHE_PATH";
  54. // Compiler settings.
  55. constexpr auto FASTBUILD_COMPILER_EXTRA_FILES =
  56. "CMAKE_FASTBUILD_COMPILER_EXTRA_FILES";
  57. constexpr auto FASTBUILD_USE_LIGHTCACHE = "CMAKE_FASTBUILD_USE_LIGHTCACHE";
  58. constexpr auto FASTBUILD_USE_RELATIVE_PATHS =
  59. "CMAKE_FASTBUILD_USE_RELATIVE_PATHS";
  60. constexpr auto FASTBUILD_USE_DETERMINISTIC_PATHS =
  61. "CMAKE_FASTBUILD_USE_DETERMINISTIC_PATHS";
  62. constexpr auto FASTBUILD_SOURCE_MAPPING = "CMAKE_FASTBUILD_SOURCE_MAPPING";
  63. constexpr auto FASTBUILD_CLANG_REWRITE_INCLUDES =
  64. "CMAKE_FASTBUILD_CLANG_REWRITE_INCLUDES";
  65. constexpr auto FASTBUILD_CLANG_GCC_UPDATE_XLANG_ARG =
  66. "CMAKE_FASTBUILD_CLANG_GCC_UPDATE_XLANG_ARG";
  67. constexpr auto FASTBUILD_ALLOW_RESPONSE_FILE =
  68. "CMAKE_FASTBUILD_ALLOW_RESPONSE_FILE";
  69. constexpr auto FASTBUILD_FORCE_RESPONSE_FILE =
  70. "CMAKE_FASTBUILD_FORCE_RESPONSE_FILE";
  71. static std::map<std::string, std::string> const compilerIdToFastbuildFamily = {
  72. { "MSVC", "msvc" }, { "Clang", "clang" }, { "AppleClang", "clang" },
  73. { "GNU", "gcc" }, { "NVIDIA", "cuda-nvcc" }, { "Clang-cl", "clang-cl" },
  74. };
  75. static std::set<std::string> const supportedLanguages = { "C", "CXX", "CUDA",
  76. "OBJC", "OBJCXX" };
  77. static void ReadCompilerOptions(FastbuildCompiler& compiler, cmMakefile* mf)
  78. {
  79. if (compiler.CompilerFamily == "custom") {
  80. return;
  81. }
  82. if (cmIsOn(mf->GetSafeDefinition(FASTBUILD_USE_LIGHTCACHE))) {
  83. compiler.UseLightCache = true;
  84. }
  85. if (cmIsOn(mf->GetSafeDefinition(FASTBUILD_USE_RELATIVE_PATHS))) {
  86. compiler.UseRelativePaths = true;
  87. }
  88. if (cmIsOn(mf->GetSafeDefinition(FASTBUILD_USE_DETERMINISTIC_PATHS))) {
  89. compiler.UseDeterministicPaths = true;
  90. }
  91. std::string sourceMapping = mf->GetSafeDefinition(FASTBUILD_SOURCE_MAPPING);
  92. if (!sourceMapping.empty()) {
  93. compiler.SourceMapping = std::move(sourceMapping);
  94. }
  95. auto const clangRewriteIncludesDef =
  96. mf->GetDefinition(FASTBUILD_CLANG_REWRITE_INCLUDES);
  97. if (clangRewriteIncludesDef.IsSet() && clangRewriteIncludesDef.IsOff()) {
  98. compiler.ClangRewriteIncludes = false;
  99. }
  100. if (cmIsOn(mf->GetSafeDefinition(FASTBUILD_CLANG_GCC_UPDATE_XLANG_ARG))) {
  101. compiler.ClangGCCUpdateXLanguageArg = true;
  102. }
  103. if (cmIsOn(mf->GetSafeDefinition(FASTBUILD_ALLOW_RESPONSE_FILE))) {
  104. compiler.AllowResponseFile = true;
  105. }
  106. if (cmIsOn(mf->GetSafeDefinition(FASTBUILD_FORCE_RESPONSE_FILE))) {
  107. compiler.ForceResponseFile = true;
  108. }
  109. }
  110. template <class T>
  111. FastbuildAliasNode generateAlias(std::string const& name, char const* postfix,
  112. T const& nodes)
  113. {
  114. FastbuildAliasNode alias;
  115. alias.Name = name + postfix;
  116. for (auto const& node : nodes) {
  117. alias.PreBuildDependencies.emplace(node.Name);
  118. }
  119. return alias;
  120. }
  121. void FastbuildTarget::GenerateAliases()
  122. {
  123. // -deps
  124. this->DependenciesAlias.Name =
  125. this->Name + FASTBUILD_DEPS_ARTIFACTS_ALIAS_POSTFIX;
  126. for (auto const& dep : this->PreBuildDependencies) {
  127. if (dep.Type != FastbuildTargetDepType::ORDER_ONLY) {
  128. this->DependenciesAlias.PreBuildDependencies.emplace(dep);
  129. }
  130. }
  131. // PRE/POST/REST
  132. if (!this->PreBuildExecNodes.PreBuildDependencies.empty()) {
  133. this->PreBuildExecNodes.Name =
  134. this->Name + FASTBUILD_PRE_BUILD_ALIAS_POSTFIX;
  135. }
  136. if (!this->PreLinkExecNodes.Nodes.empty()) {
  137. this->PreLinkExecNodes.Alias =
  138. generateAlias(this->Name, FASTBUILD_PRE_LINK_ALIAS_POSTFIX,
  139. this->PreLinkExecNodes.Nodes);
  140. }
  141. if (!this->PostBuildExecNodes.Alias.PreBuildDependencies.empty()) {
  142. this->PostBuildExecNodes.Alias.Name =
  143. this->Name + FASTBUILD_POST_BUILD_ALIAS_POSTFIX;
  144. }
  145. if (!this->ExecNodes.PreBuildDependencies.empty()) {
  146. this->ExecNodes.Name = this->Name + FASTBUILD_CUSTOM_COMMAND_ALIAS_POSTFIX;
  147. }
  148. // If we don't have any node that we can build by name (e.g. no static /
  149. // dynamic lib or executable) -> create an alias so that we can build this
  150. // target by name.
  151. if (LinkerNode.empty()) {
  152. FastbuildAliasNode alias;
  153. alias.Name = this->Name;
  154. if (LinkerNode.empty()) {
  155. for (FastbuildObjectListNode const& objListNode : ObjectListNodes) {
  156. alias.PreBuildDependencies.emplace(objListNode.Name);
  157. }
  158. } else {
  159. for (FastbuildLinkerNode const& linkerNode : LinkerNode) {
  160. alias.PreBuildDependencies.emplace(linkerNode.Name);
  161. }
  162. }
  163. AliasNodes.emplace_back(std::move(alias));
  164. }
  165. // Link artifacts (should not be added to all
  166. // since on Windows it might contain Import Lib and FASTBuild doesn't know
  167. // how to create it, so "-all" will fail).
  168. AliasNodes.emplace_back(generateAlias(
  169. this->Name, FASTBUILD_OBJECTS_ALIAS_POSTFIX, this->ObjectListNodes));
  170. for (auto const& linkerNode : this->LinkerNode) {
  171. if (linkerNode.Type == FastbuildLinkerNode::SHARED_LIBRARY ||
  172. linkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY ||
  173. linkerNode.Type == FastbuildLinkerNode::EXECUTABLE) {
  174. std::string postfix = FASTBUILD_LINK_ARTIFACTS_ALIAS_POSTFIX;
  175. if (!linkerNode.Arch.empty()) {
  176. postfix += cmStrCat('-', linkerNode.Arch);
  177. }
  178. #ifdef _WIN32
  179. // On Windows DLL and Executables must be linked via Import Lib file
  180. // (.lib).
  181. if (linkerNode.Type == FastbuildLinkerNode::SHARED_LIBRARY ||
  182. linkerNode.Type == FastbuildLinkerNode::EXECUTABLE) {
  183. FastbuildAliasNode linkAlias;
  184. linkAlias.Name = this->Name + FASTBUILD_LINK_ARTIFACTS_ALIAS_POSTFIX;
  185. linkAlias.PreBuildDependencies.emplace(
  186. FASTBUILD_DOLLAR_TAG "TargetOutputImplib" FASTBUILD_DOLLAR_TAG);
  187. AliasNodes.emplace_back(std::move(linkAlias));
  188. continue;
  189. }
  190. #endif
  191. FastbuildAliasNode alias;
  192. alias.Name = this->Name + postfix;
  193. alias.PreBuildDependencies.emplace(linkerNode.LinkerOutput);
  194. AliasNodes.emplace_back(std::move(alias));
  195. }
  196. }
  197. }
  198. cmGlobalFastbuildGenerator::cmGlobalFastbuildGenerator(cmake* cm)
  199. : cmGlobalCommonGenerator(cm)
  200. , BuildFileStream(nullptr)
  201. {
  202. #ifdef _WIN32
  203. cm->GetState()->SetWindowsShell(true);
  204. #endif
  205. this->FindMakeProgramFile = "CMakeFastbuildFindMake.cmake";
  206. cm->GetState()->SetFastbuildMake(true);
  207. cm->GetState()->SetIsGeneratorMultiConfig(false);
  208. }
  209. void cmGlobalFastbuildGenerator::ProcessEnvironment()
  210. {
  211. bool const CaptureSystemEnv =
  212. !this->GetGlobalSetting(FASTBUILD_CAPTURE_SYSTEM_ENV).IsSet() ||
  213. this->GetGlobalSetting(FASTBUILD_CAPTURE_SYSTEM_ENV).IsOn();
  214. // On Windows environment is needed for MSVC, but preserve ability to discard
  215. // it from the generated file if requested.
  216. if (CaptureSystemEnv) {
  217. LocalEnvironment = cmSystemTools::GetEnvironmentVariables();
  218. }
  219. // FASTBuild strips off "-isysroot" command line option (see :
  220. // https://github.com/fastbuild/fastbuild/issues/1066).
  221. // If 'SDK_ROOT' is not set via env and '-isysroot' is absent, AppleClang
  222. // seems to use MacOS SDK by default (even though FBuild flattens includes
  223. // before compiling). It breaks cross-compilation for iOS. Tested in
  224. // "RunCMake.Framework" test.
  225. std::string const osxRoot = this->GetSafeGlobalSetting("CMAKE_OSX_SYSROOT");
  226. if (!osxRoot.empty()) {
  227. LocalEnvironment.emplace_back("SDKROOT=" + osxRoot);
  228. }
  229. auto const EnvOverrides =
  230. this->GetSafeGlobalSetting(FASTBUILD_ENV_OVERRIDES);
  231. if (!EnvOverrides.empty()) {
  232. auto const overrideEnvVar = [this](std::string const& prefix,
  233. std::string val) {
  234. auto const iter =
  235. std::find_if(LocalEnvironment.begin(), LocalEnvironment.end(),
  236. [&prefix](std::string const& value) {
  237. return cmSystemTools::StringStartsWith(value.c_str(),
  238. prefix.c_str());
  239. });
  240. if (iter != LocalEnvironment.end()) {
  241. *iter = std::move(val);
  242. } else {
  243. LocalEnvironment.emplace_back(std::move(val));
  244. }
  245. };
  246. for (auto& val : cmList{ EnvOverrides }) {
  247. auto const pos = val.find('=');
  248. if (pos != std::string::npos && ((pos + 1) < val.size())) {
  249. overrideEnvVar(val.substr(0, pos + 1), std::move(val));
  250. }
  251. }
  252. }
  253. // Empty strings are not allowed.
  254. LocalEnvironment.erase(
  255. std::remove_if(LocalEnvironment.begin(), LocalEnvironment.end(),
  256. [](std::string const& s) { return s.empty(); }),
  257. LocalEnvironment.end());
  258. }
  259. std::unique_ptr<cmGlobalGeneratorFactory>
  260. cmGlobalFastbuildGenerator::NewFactory()
  261. {
  262. return std::unique_ptr<cmGlobalGeneratorFactory>(
  263. new cmGlobalGeneratorSimpleFactory<cmGlobalFastbuildGenerator>());
  264. }
  265. void cmGlobalFastbuildGenerator::EnableLanguage(
  266. std::vector<std::string> const& lang, cmMakefile* mf, bool optional)
  267. {
  268. this->cmGlobalGenerator::EnableLanguage(lang, mf, optional);
  269. for (std::string const& l : lang) {
  270. if (l == "NONE") {
  271. continue;
  272. }
  273. this->ResolveLanguageCompiler(l, mf, optional);
  274. }
  275. }
  276. bool cmGlobalFastbuildGenerator::FindMakeProgram(cmMakefile* mf)
  277. {
  278. if (!cmGlobalGenerator::FindMakeProgram(mf)) {
  279. return false;
  280. }
  281. if (auto fastbuildCommand = mf->GetDefinition("CMAKE_MAKE_PROGRAM")) {
  282. this->FastbuildCommand = *fastbuildCommand;
  283. std::vector<std::string> command;
  284. command.push_back(this->FastbuildCommand);
  285. command.emplace_back("-version");
  286. std::string version;
  287. std::string error;
  288. if (!cmSystemTools::RunSingleCommand(command, &version, &error, nullptr,
  289. nullptr,
  290. cmSystemTools::OUTPUT_NONE)) {
  291. mf->IssueMessage(MessageType::FATAL_ERROR,
  292. "Running\n '" + cmJoin(command, "' '") +
  293. "'\n"
  294. "failed with:\n " +
  295. error);
  296. cmSystemTools::SetFatalErrorOccurred();
  297. return false;
  298. }
  299. cmsys::RegularExpression versionRegex(R"(^FASTBuild v([0-9]+\.[0-9]+))");
  300. versionRegex.find(version);
  301. this->FastbuildVersion = versionRegex.match(1);
  302. }
  303. return true;
  304. }
  305. std::unique_ptr<cmLocalGenerator>
  306. cmGlobalFastbuildGenerator::CreateLocalGenerator(cmMakefile* makefile)
  307. {
  308. return std::unique_ptr<cmLocalGenerator>(
  309. cm::make_unique<cmLocalFastbuildGenerator>(this, makefile));
  310. }
  311. std::vector<cmGlobalGenerator::GeneratedMakeCommand>
  312. cmGlobalFastbuildGenerator::GenerateBuildCommand(
  313. std::string const& makeProgram, std::string const& /*projectName*/,
  314. std::string const& projectDir, std::vector<std::string> const& targetNames,
  315. std::string const& /*config*/, int /*jobs*/, bool verbose,
  316. cmBuildOptions /*buildOptions*/, std::vector<std::string> const& makeOptions)
  317. {
  318. GeneratedMakeCommand makeCommand;
  319. this->FastbuildCommand = this->SelectMakeProgram(makeProgram);
  320. makeCommand.Add(this->FastbuildCommand);
  321. // A build command for fastbuild looks like this:
  322. // fbuild.exe [make-options] [-config projectName.bff] <target>
  323. std::string configFile = cmStrCat(projectDir, '/', FASTBUILD_BUILD_FILE);
  324. // Push in the make options
  325. makeCommand.Add(makeOptions.begin(), makeOptions.end());
  326. if (!configFile.empty()) {
  327. makeCommand.Add("-config", configFile);
  328. }
  329. // Tested in "RunCMake.SymlinkTrees" test.
  330. makeCommand.Add("-continueafterdbmove");
  331. // Tested in RunCMake.LinkWhatYouUse on Linux. (We need to see output of
  332. // LinkerStampExe process).
  333. // In general, it might be useful to see output of external processes
  334. // regardless of their outcome.
  335. makeCommand.Add("-showcmdoutput");
  336. // Add the target-config to the command
  337. for (auto const& tname : targetNames) {
  338. if (!tname.empty()) {
  339. makeCommand.Add(tname);
  340. }
  341. }
  342. if (verbose) {
  343. makeCommand.Add("-verbose");
  344. }
  345. // Make "rebuild-bff" target up-to-date before running the build.
  346. std::string output;
  347. ExecuteFastbuildTarget(projectDir, FASTBUILD_REBUILD_BFF_TARGET_NAME, output,
  348. { "-why" });
  349. // If fbuild.bff was re-generated we need to "restat" it.
  350. if (output.find("Need to build") != std::string::npos) {
  351. // Let the user know that re-generation happened (and why it
  352. // happened).
  353. cmSystemTools::Stdout(output);
  354. // FASTBuild will consider the target out-of-date in case some of the
  355. // inputs have changes after re-generation which might happen if, for
  356. // example, configuration depends on some files generated during
  357. // the configuration itself.
  358. AskCMakeToMakeRebuildBFFUpToDate(projectDir);
  359. }
  360. return { std::move(makeCommand) };
  361. }
  362. void cmGlobalFastbuildGenerator::ComputeTargetObjectDirectory(
  363. cmGeneratorTarget* gt) const
  364. {
  365. // Compute full path to object file directory for this target.
  366. std::string dir =
  367. cmStrCat(gt->GetSupportDirectory(), '/', this->GetCMakeCFGIntDir(), '/');
  368. gt->ObjectDirectory = std::move(dir);
  369. }
  370. void cmGlobalFastbuildGenerator::AppendDirectoryForConfig(
  371. std::string const& prefix, std::string const& config,
  372. std::string const& suffix, std::string& dir)
  373. {
  374. if (!config.empty() && this->IsMultiConfig()) {
  375. dir += cmStrCat(prefix, config, suffix);
  376. }
  377. }
  378. cmDocumentationEntry cmGlobalFastbuildGenerator::GetDocumentation()
  379. {
  380. return { cmGlobalFastbuildGenerator::GetActualName(),
  381. "Generates fbuild.bff files." };
  382. }
  383. void cmGlobalFastbuildGenerator::Generate()
  384. {
  385. // Check minimum Fastbuild version.
  386. if (cmSystemTools::VersionCompare(cmSystemTools::OP_LESS,
  387. this->FastbuildVersion,
  388. RequiredFastbuildVersion())) {
  389. std::ostringstream msg;
  390. msg << "The detected version of Fastbuild (" << this->FastbuildVersion;
  391. msg << ") is less than the version of Fastbuild required by CMake (";
  392. msg << this->RequiredFastbuildVersion() << ").";
  393. this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR,
  394. msg.str());
  395. return;
  396. }
  397. this->ProcessEnvironment();
  398. this->OpenBuildFileStream();
  399. this->WriteSettings();
  400. this->WriteEnvironment();
  401. // Execute the standard generate process
  402. cmGlobalGenerator::Generate();
  403. // Write compilers
  404. this->WriteCompilers();
  405. this->WriteTargets();
  406. this->CloseBuildFileStream();
  407. if (cmSystemTools::GetErrorOccurredFlag()) {
  408. return;
  409. }
  410. this->RemoveUnknownClangTidyExportFixesFiles();
  411. if (this->GetCMakeInstance()->GetRegenerateDuringBuild()) {
  412. return;
  413. }
  414. // TODO: figure out how to skip this in TryCompile
  415. // Make "rebuild-bff" target up-to-date after the generation.
  416. // This is actually a noop, it just asks CMake to touch the generated file
  417. // so FASTBuild would consider the target as up-to-date.
  418. AskCMakeToMakeRebuildBFFUpToDate(
  419. this->GetCMakeInstance()->GetHomeOutputDirectory());
  420. }
  421. void cmGlobalFastbuildGenerator::AskCMakeToMakeRebuildBFFUpToDate(
  422. std::string const& workingDir) const
  423. {
  424. // "restat" the generated build file.
  425. // The idea here is to mimic what Ninja's "restat" command does.
  426. // We need to make the "rebuild.bff" target up-to-date, so the regeneration
  427. // will only be triggered when CMake files have actually changed.
  428. // Tested in "RunCMake.Configure" test.
  429. cmsys::ofstream{
  430. cmStrCat(workingDir, '/', FASTBUILD_RESTAT_FILE).c_str(),
  431. std::ios::out | std::ios::binary
  432. } << cmStrCat(workingDir, '/', FASTBUILD_BUILD_FILE);
  433. std::string output;
  434. ExecuteFastbuildTarget(workingDir, FASTBUILD_REBUILD_BFF_TARGET_NAME,
  435. output);
  436. }
  437. void cmGlobalFastbuildGenerator::ExecuteFastbuildTarget(
  438. std::string const& dir, std::string const& target, std::string& output,
  439. std::vector<std::string> const& fbuildOptions) const
  440. {
  441. std::vector<std::string> command;
  442. command.emplace_back(this->FastbuildCommand);
  443. command.emplace_back("-config");
  444. std::string const file = cmStrCat(dir, '/', FASTBUILD_BUILD_FILE);
  445. command.emplace_back(file);
  446. command.emplace_back(target);
  447. if (!fbuildOptions.empty()) {
  448. command.emplace_back(cmJoin(fbuildOptions, " "));
  449. }
  450. int retVal = 0;
  451. if (!cmSystemTools::RunSingleCommand(command, &output, nullptr, &retVal,
  452. dir.c_str(),
  453. cmSystemTools::OUTPUT_NONE) ||
  454. retVal != 0) {
  455. cmSystemTools::Error(cmStrCat("Failed to run FASTBuild command:\n '",
  456. cmJoin(command, "' '"), "'\nOutput:\n",
  457. output));
  458. cmSystemTools::Stdout(output);
  459. std::exit(retVal);
  460. }
  461. }
  462. void cmGlobalFastbuildGenerator::WriteSettings()
  463. {
  464. // Define some placeholder
  465. WriteDivider();
  466. *this->BuildFileStream << "// Helper variables\n\n";
  467. WriteVariable("FB_INPUT_1_PLACEHOLDER", Quote("\"%1\""));
  468. WriteVariable("FB_INPUT_1_0_PLACEHOLDER", Quote("\"%1[0]\""));
  469. WriteVariable("FB_INPUT_1_1_PLACEHOLDER", Quote("\"%1[1]\""));
  470. WriteVariable("FB_INPUT_2_PLACEHOLDER", Quote("\"%2\""));
  471. WriteVariable("FB_INPUT_3_PLACEHOLDER", Quote("\"%3\""));
  472. std::string cacheDir;
  473. // If explicitly set from CMake.
  474. auto val = this->GetSafeGlobalSetting(FASTBUILD_CACHE_PATH);
  475. if (!val.empty()) {
  476. cacheDir = std::move(val);
  477. cmSystemTools::ConvertToOutputSlashes(cacheDir);
  478. }
  479. WriteDivider();
  480. *this->BuildFileStream << "// Settings\n\n";
  481. WriteCommand("Settings");
  482. *this->BuildFileStream << "{\n";
  483. if (!cacheDir.empty()) {
  484. WriteVariable("CachePath", Quote(cacheDir), 1);
  485. }
  486. // Concurrency groups.
  487. WriteStruct(
  488. FASTBUILD_UTIL_CONCURRENCY_GROUP_NAME,
  489. { { "ConcurrencyGroupName", Quote(FASTBUILD_UTIL_CONCURRENCY_GROUP_NAME) },
  490. { "ConcurrencyLimit", "1" } },
  491. 1);
  492. WriteArray("ConcurrencyGroups",
  493. { "." FASTBUILD_UTIL_CONCURRENCY_GROUP_NAME }, 1);
  494. *this->BuildFileStream << "}\n";
  495. }
  496. void cmGlobalFastbuildGenerator::WriteEnvironment()
  497. {
  498. if (!LocalEnvironment.empty()) {
  499. WriteArray(FASTBUILD_ENV_VAR_NAME, Wrap(LocalEnvironment), 0);
  500. }
  501. }
  502. void cmGlobalFastbuildGenerator::WriteDivider()
  503. {
  504. *this->BuildFileStream << "// ======================================"
  505. "=======================================\n";
  506. }
  507. void cmGlobalFastbuildGenerator::Indent(int count)
  508. {
  509. for (int i = 0; i < count; ++i) {
  510. *this->BuildFileStream << " ";
  511. }
  512. }
  513. void cmGlobalFastbuildGenerator::WriteComment(std::string const& comment,
  514. int indent)
  515. {
  516. if (comment.empty()) {
  517. return;
  518. }
  519. std::string::size_type lpos = 0;
  520. std::string::size_type rpos;
  521. *this->BuildFileStream << "\n";
  522. Indent(indent);
  523. *this->BuildFileStream << "/////////////////////////////////////////////\n";
  524. while ((rpos = comment.find('\n', lpos)) != std::string::npos) {
  525. Indent(indent);
  526. *this->BuildFileStream << "// " << comment.substr(lpos, rpos - lpos)
  527. << "\n";
  528. lpos = rpos + 1;
  529. }
  530. Indent(indent);
  531. *this->BuildFileStream << "// " << comment.substr(lpos) << "\n\n";
  532. }
  533. void cmGlobalFastbuildGenerator::WriteVariable(std::string const& key,
  534. std::string const& value,
  535. int indent)
  536. {
  537. WriteVariable(key, value, "=", indent);
  538. }
  539. void cmGlobalFastbuildGenerator::WriteVariable(std::string const& key,
  540. std::string const& value,
  541. std::string const& op,
  542. int indent)
  543. {
  544. Indent(indent);
  545. *this->BuildFileStream << "." << key << " " + op + " " << value << "\n";
  546. }
  547. void cmGlobalFastbuildGenerator::WriteCommand(std::string const& command,
  548. std::string const& value,
  549. int indent)
  550. {
  551. Indent(indent);
  552. *this->BuildFileStream << command;
  553. if (!value.empty()) {
  554. *this->BuildFileStream << "(" << value << ")";
  555. }
  556. *this->BuildFileStream << "\n";
  557. }
  558. void cmGlobalFastbuildGenerator::WriteArray(
  559. std::string const& key, std::vector<std::string> const& values, int indent)
  560. {
  561. WriteArray(key, values, "=", indent);
  562. }
  563. void cmGlobalFastbuildGenerator::WriteArray(
  564. std::string const& key, std::vector<std::string> const& values,
  565. std::string const& op, int indent)
  566. {
  567. WriteVariable(key, "", op, indent);
  568. Indent(indent);
  569. *this->BuildFileStream << "{\n";
  570. char const* sep = "";
  571. for (std::string const& value : values) {
  572. *this->BuildFileStream << sep;
  573. sep = ",\n";
  574. Indent(indent + 1);
  575. *this->BuildFileStream << value;
  576. }
  577. *this->BuildFileStream << "\n";
  578. Indent(indent);
  579. *this->BuildFileStream << "}\n";
  580. }
  581. void cmGlobalFastbuildGenerator::WriteStruct(
  582. std::string const& name,
  583. std::vector<std::pair<std::string, std::string>> const& variables,
  584. int indent)
  585. {
  586. WriteVariable(name, "", "=", indent);
  587. Indent(indent);
  588. *this->BuildFileStream << "[\n";
  589. for (auto const& val : variables) {
  590. auto const& key = val.first;
  591. auto const& value = val.second;
  592. WriteVariable(key, value, "=", indent + 1);
  593. }
  594. Indent(indent);
  595. *this->BuildFileStream << "]\n";
  596. }
  597. std::string cmGlobalFastbuildGenerator::Quote(std::string const& str,
  598. std::string const& quotation)
  599. {
  600. std::string result = str;
  601. cmSystemTools::ReplaceString(result, quotation, "^" + quotation);
  602. cmSystemTools::ReplaceString(result, FASTBUILD_DOLLAR_TAG, "$");
  603. return quotation + result + quotation;
  604. }
  605. std::string cmGlobalFastbuildGenerator::QuoteIfHasSpaces(std::string str)
  606. {
  607. if (str.find(' ') != std::string::npos) {
  608. return '"' + str + '"';
  609. }
  610. return str;
  611. }
  612. struct WrapHelper
  613. {
  614. std::string Prefix;
  615. std::string Suffix;
  616. bool EscapeDollar;
  617. std::string operator()(std::string in)
  618. {
  619. // If we have ^ in env variable - need to escape it.
  620. cmSystemTools::ReplaceString(in, "^", "^^");
  621. // Those all are considered as line ends by FASTBuild.
  622. cmSystemTools::ReplaceString(in, "\n", "\\n");
  623. cmSystemTools::ReplaceString(in, "\r", "\\r");
  624. // Escaping of single quotes tested in "RunCMake.CompilerArgs" test.
  625. cmSystemTools::ReplaceString(in, "'", "^'");
  626. std::string result = Prefix + in + Suffix;
  627. if (EscapeDollar) {
  628. cmSystemTools::ReplaceString(result, "$", "^$");
  629. cmSystemTools::ReplaceString(result, FASTBUILD_DOLLAR_TAG, "$");
  630. }
  631. return result;
  632. }
  633. std::string operator()(FastbuildTargetDep const& in)
  634. {
  635. return (*this)(in.Name);
  636. }
  637. };
  638. template <class T>
  639. std::vector<std::string> cmGlobalFastbuildGenerator::Wrap(
  640. T const& in, std::string const& prefix, std::string const& suffix,
  641. bool const escape_dollar)
  642. {
  643. std::vector<std::string> result;
  644. WrapHelper helper = { prefix, suffix, escape_dollar };
  645. std::transform(in.begin(), in.end(), std::back_inserter(result), helper);
  646. return result;
  647. }
  648. void cmGlobalFastbuildGenerator::TopologicalSort(
  649. std::vector<FastbuildTargetPtrT>& nodes)
  650. {
  651. std::unordered_map<std::string, int> inDegree;
  652. std::unordered_map<std::string, std::set<std::string>> reverseDeps;
  653. std::unordered_map<std::string, std::size_t> originalIndex;
  654. // Track original positions
  655. for (std::size_t i = 0; i < nodes.size(); ++i) {
  656. auto const& node = nodes[i];
  657. inDegree[node->Name] = 0;
  658. originalIndex[node->Name] = i;
  659. }
  660. // Build reverse dependency graph and in-degree map
  661. for (auto const& node : nodes) {
  662. for (auto const& dep : node->PreBuildDependencies) {
  663. if (inDegree.count(dep.Name)) {
  664. reverseDeps[dep.Name].insert(node->Name);
  665. ++inDegree[node->Name];
  666. }
  667. }
  668. }
  669. // Min-heap based on original position
  670. auto const cmp = [&](std::string const& a, std::string const& b) {
  671. return originalIndex[a] > originalIndex[b];
  672. };
  673. std::priority_queue<std::string, std::vector<std::string>, decltype(cmp)>
  674. zeroInDegree(cmp);
  675. for (auto const& val : inDegree) {
  676. auto const& degree = val.second;
  677. auto const& name = val.first;
  678. if (degree == 0) {
  679. zeroInDegree.push(name);
  680. }
  681. }
  682. std::vector<std::string> sorted;
  683. while (!zeroInDegree.empty()) {
  684. std::string node = zeroInDegree.top();
  685. zeroInDegree.pop();
  686. sorted.push_back(node);
  687. for (auto const& dep : reverseDeps[node]) {
  688. if (--inDegree[dep] == 0) {
  689. zeroInDegree.push(dep);
  690. }
  691. }
  692. }
  693. if (sorted.size() != nodes.size()) {
  694. cmSystemTools::Error("Failed to sort (Cyclic dependency)");
  695. cmSystemTools::Error(cmStrCat("Sorted size: ", sorted.size()));
  696. cmSystemTools::Error(cmStrCat("nodes size: ", nodes.size()));
  697. for (auto const& node : nodes) {
  698. cmSystemTools::Error("Node: " + node->Name);
  699. for (auto const& dep : reverseDeps[node->Name]) {
  700. cmSystemTools::Error("\tReverse dep: " + dep);
  701. }
  702. for (auto const& child : node->PreBuildDependencies) {
  703. cmSystemTools::Error("\tChild: " + child.Name);
  704. }
  705. }
  706. for (auto const& node : sorted) {
  707. cmSystemTools::Error("Sorted: " + node);
  708. }
  709. for (auto const& node : nodes) {
  710. cmSystemTools::Error("In node: " + node->Name);
  711. }
  712. }
  713. // Reconstruct sorted nodes
  714. std::vector<FastbuildTargetPtrT> result;
  715. for (auto const& name : sorted) {
  716. auto it = std::find_if(
  717. nodes.begin(), nodes.end(), [&name](FastbuildTargetPtrT const& node) {
  718. return node /* the node might be in moved-from state*/ &&
  719. node->Name == name;
  720. });
  721. if (it != nodes.end()) {
  722. result.emplace_back(std::move(*it));
  723. }
  724. }
  725. std::swap(result, nodes);
  726. }
  727. void cmGlobalFastbuildGenerator::WriteDisclaimer()
  728. {
  729. *this->BuildFileStream << "// CMAKE generated file: DO NOT EDIT!\n"
  730. << "// Generated by \"" << this->GetName() << "\""
  731. << " Generator, CMake Version "
  732. << cmVersion::GetMajorVersion() << "."
  733. << cmVersion::GetMinorVersion() << "\n\n";
  734. }
  735. void cmGlobalFastbuildGenerator::OpenBuildFileStream()
  736. {
  737. // Compute Fastbuild's build file path.
  738. std::string buildFilePath =
  739. this->GetCMakeInstance()->GetHomeOutputDirectory();
  740. buildFilePath += "/";
  741. buildFilePath += FASTBUILD_BUILD_FILE;
  742. // Get a stream where to generate things.
  743. if (!this->BuildFileStream) {
  744. this->BuildFileStream = cm::make_unique<cmGeneratedFileStream>(
  745. buildFilePath, false, this->GetMakefileEncoding());
  746. if (!this->BuildFileStream) {
  747. // An error message is generated by the constructor if it cannot
  748. // open the file.
  749. return;
  750. }
  751. }
  752. // Write the do not edit header.
  753. this->WriteDisclaimer();
  754. // Write a comment about this file.
  755. *this->BuildFileStream
  756. << "// This file contains all the build statements\n\n";
  757. }
  758. void cmGlobalFastbuildGenerator::CloseBuildFileStream()
  759. {
  760. if (this->BuildFileStream) {
  761. this->BuildFileStream.reset();
  762. } else {
  763. cmSystemTools::Error("Build file stream was not open.");
  764. }
  765. }
  766. void cmGlobalFastbuildGenerator::WriteCompilers()
  767. {
  768. WriteDivider();
  769. *this->BuildFileStream << "// Compilers\n\n";
  770. for (auto const& val : Compilers) {
  771. auto const& compilerDef = val.second;
  772. std::string compilerPath = compilerDef.Executable;
  773. // Write out the compiler that has been configured
  774. WriteCommand("Compiler", Quote(compilerDef.Name));
  775. *this->BuildFileStream << "{\n";
  776. for (auto const& extra : compilerDef.ExtraVariables) {
  777. auto const& extraKey = extra.first;
  778. auto const& extraVal = extra.second;
  779. WriteVariable(extraKey, Quote(extraVal), 1);
  780. }
  781. WriteVariable("Executable", Quote(compilerPath), 1);
  782. WriteVariable("CompilerFamily", Quote(compilerDef.CompilerFamily), 1);
  783. if (compilerDef.UseLightCache && compilerDef.CompilerFamily == "msvc") {
  784. WriteVariable("UseLightCache_Experimental", "true", 1);
  785. }
  786. if (compilerDef.UseRelativePaths) {
  787. WriteVariable("UseRelativePaths_Experimental", "true", 1);
  788. }
  789. if (compilerDef.UseDeterministicPaths) {
  790. WriteVariable("UseDeterministicPaths_Experimental", "true", 1);
  791. }
  792. if (!compilerDef.SourceMapping.empty()) {
  793. WriteVariable("SourceMapping_Experimental",
  794. Quote(compilerDef.SourceMapping), 1);
  795. }
  796. auto const isClang = [&compilerDef] {
  797. return compilerDef.CompilerFamily == "clang" ||
  798. compilerDef.CompilerFamily == "clang-cl";
  799. };
  800. if (!compilerDef.ClangRewriteIncludes && isClang()) {
  801. WriteVariable("ClangRewriteIncludes", "false", 1);
  802. }
  803. if (compilerDef.ClangGCCUpdateXLanguageArg &&
  804. (isClang() || compilerDef.CompilerFamily == "gcc")) {
  805. WriteVariable("ClangGCCUpdateXLanguageArg", "true", 1);
  806. }
  807. if (compilerDef.AllowResponseFile) {
  808. WriteVariable("AllowResponseFile", "true", 1);
  809. }
  810. if (compilerDef.ForceResponseFile) {
  811. WriteVariable("ForceResponseFile", "true", 1);
  812. }
  813. if (compilerDef.DontUseEnv) {
  814. LogMessage("Not using system environment");
  815. } else {
  816. if (!LocalEnvironment.empty()) {
  817. WriteVariable("Environment", "." FASTBUILD_ENV_VAR_NAME, 1);
  818. }
  819. }
  820. if (!compilerDef.ExtraFiles.empty()) {
  821. // Do not escape '$' sign, CMAKE_${LANG}_FASTBUILD_EXTRA_FILES might
  822. // contain FB variables to be expanded (we do use some internally).
  823. // Besides a path cannot contain a '$'
  824. WriteArray("ExtraFiles", Wrap(compilerDef.ExtraFiles, "'", "'", false),
  825. 1);
  826. }
  827. *this->BuildFileStream << "}\n";
  828. auto const compilerId = compilerDef.Name;
  829. WriteVariable(compilerId, Quote(compilerDef.Name));
  830. *this->BuildFileStream << "\n";
  831. }
  832. // We need this because the Library command needs a compiler
  833. // even if don't compile anything
  834. if (!this->Compilers.empty()) {
  835. WriteVariable("Compiler_dummy",
  836. Quote(this->Compilers.begin()->second.Name));
  837. }
  838. }
  839. void cmGlobalFastbuildGenerator::AddCompiler(std::string const& language,
  840. cmMakefile* mf)
  841. {
  842. if (this->Compilers.find(FASTBUILD_COMPILER_PREFIX + language) !=
  843. this->Compilers.end()) {
  844. return;
  845. }
  846. // Calculate the root location of the compiler
  847. std::string const variableString = "CMAKE_" + language + "_COMPILER";
  848. std::string const compilerLocation = mf->GetSafeDefinition(variableString);
  849. if (compilerLocation.empty()) {
  850. return;
  851. }
  852. // Calculate the i18n number.
  853. std::string i18nNum = "1033";
  854. // Add the language to the compiler's name
  855. FastbuildCompiler compilerDef;
  856. compilerDef.ExtraVariables["Root"] =
  857. cmSystemTools::GetFilenamePath(compilerLocation);
  858. compilerDef.Name = FASTBUILD_COMPILER_PREFIX + language;
  859. compilerDef.Executable = compilerLocation;
  860. compilerDef.CmakeCompilerID =
  861. mf->GetSafeDefinition("CMAKE_" + language + "_COMPILER_ID");
  862. if (compilerDef.CmakeCompilerID == "Clang" &&
  863. mf->GetSafeDefinition("CMAKE_" + language +
  864. "_COMPILER_FRONTEND_VARIANT") == "MSVC") {
  865. compilerDef.CmakeCompilerID = "Clang-cl";
  866. }
  867. compilerDef.CmakeCompilerVersion =
  868. mf->GetSafeDefinition("CMAKE_" + language + "_COMPILER_VERSION");
  869. compilerDef.Language = language;
  870. cmExpandList(mf->GetSafeDefinition(FASTBUILD_COMPILER_EXTRA_FILES),
  871. compilerDef.ExtraFiles);
  872. if (supportedLanguages.find(language) != supportedLanguages.end()) {
  873. auto const iter =
  874. compilerIdToFastbuildFamily.find(compilerDef.CmakeCompilerID);
  875. if (iter != compilerIdToFastbuildFamily.end()) {
  876. compilerDef.CompilerFamily = iter->second;
  877. }
  878. }
  879. // Has to be called after we determined 'CompilerFamily'.
  880. ReadCompilerOptions(compilerDef, mf);
  881. // If FASTBUILD_COMPILER_EXTRA_FILES is not set - automatically add extra
  882. // files based on compiler (see
  883. // https://fastbuild.org/docs/functions/compiler.html)
  884. if (compilerDef.ExtraFiles.empty() &&
  885. (language == "C" || language == "CXX") &&
  886. compilerDef.CmakeCompilerID == "MSVC") {
  887. // https://cmake.org/cmake/help/latest/variable/MSVC_VERSION.html
  888. // Visual Studio 17 (19.30 to 19.39)
  889. // TODO
  890. // Visual Studio 16 (19.20 to 19.29)
  891. if (cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
  892. compilerDef.CmakeCompilerVersion,
  893. "19.20")) {
  894. compilerDef.ExtraFiles.push_back("$Root$/c1.dll");
  895. compilerDef.ExtraFiles.push_back("$Root$/c1xx.dll");
  896. compilerDef.ExtraFiles.push_back("$Root$/c2.dll");
  897. compilerDef.ExtraFiles.push_back(
  898. "$Root$/atlprov.dll"); // Only needed if using ATL
  899. compilerDef.ExtraFiles.push_back("$Root$/msobj140.dll");
  900. compilerDef.ExtraFiles.push_back("$Root$/mspdb140.dll");
  901. compilerDef.ExtraFiles.push_back("$Root$/mspdbcore.dll");
  902. compilerDef.ExtraFiles.push_back("$Root$/mspdbsrv.exe");
  903. compilerDef.ExtraFiles.push_back("$Root$/mspft140.dll");
  904. compilerDef.ExtraFiles.push_back("$Root$/msvcp140.dll");
  905. compilerDef.ExtraFiles.push_back(
  906. "$Root$/msvcp140_atomic_wait.dll"); // Required circa 16.8.3
  907. // (14.28.29333)
  908. compilerDef.ExtraFiles.push_back(
  909. "$Root$/tbbmalloc.dll"); // Required as of 16.2 (14.22.27905)
  910. compilerDef.ExtraFiles.push_back("$Root$/vcruntime140.dll");
  911. compilerDef.ExtraFiles.push_back(
  912. "$Root$/vcruntime140_1.dll"); // Required as of 16.5.1 (14.25.28610)
  913. compilerDef.ExtraFiles.push_back("$Root$/" + i18nNum + "/clui.dll");
  914. compilerDef.ExtraFiles.push_back(
  915. "$Root$/" + i18nNum + "/mspft140ui.dll"); // Localized messages for
  916. // static analysis
  917. }
  918. // Visual Studio 15 (19.10 to 19.19)
  919. else if (cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
  920. compilerDef.CmakeCompilerVersion,
  921. "19.10")) {
  922. compilerDef.ExtraFiles.push_back("$Root$/c1.dll");
  923. compilerDef.ExtraFiles.push_back("$Root$/c1xx.dll");
  924. compilerDef.ExtraFiles.push_back("$Root$/c2.dll");
  925. compilerDef.ExtraFiles.push_back(
  926. "$Root$/atlprov.dll"); // Only needed if using ATL
  927. compilerDef.ExtraFiles.push_back("$Root$/msobj140.dll");
  928. compilerDef.ExtraFiles.push_back("$Root$/mspdb140.dll");
  929. compilerDef.ExtraFiles.push_back("$Root$/mspdbcore.dll");
  930. compilerDef.ExtraFiles.push_back("$Root$/mspdbsrv.exe");
  931. compilerDef.ExtraFiles.push_back("$Root$/mspft140.dll");
  932. compilerDef.ExtraFiles.push_back("$Root$/msvcp140.dll");
  933. compilerDef.ExtraFiles.push_back("$Root$/vcruntime140.dll");
  934. compilerDef.ExtraFiles.push_back("$Root$/" + i18nNum + "/clui.dll");
  935. }
  936. }
  937. // TODO: Handle Intel compiler
  938. this->Compilers[compilerDef.Name] = std::move(compilerDef);
  939. }
  940. void cmGlobalFastbuildGenerator::AddLauncher(std::string const& prefix,
  941. std::string const& launcher,
  942. std::string const& language,
  943. std::string const& args)
  944. {
  945. if (this->Compilers.find(prefix + language) != this->Compilers.end()) {
  946. return;
  947. }
  948. LogMessage("Launcher: " + launcher);
  949. LogMessage("Launcher args: " + args);
  950. FastbuildCompiler compilerDef;
  951. compilerDef.Name = prefix + language;
  952. compilerDef.Args = args;
  953. if (cmSystemTools::FileIsFullPath(launcher)) {
  954. compilerDef.Executable = launcher;
  955. } else {
  956. // FASTBuild needs an absolute path to the executable.
  957. compilerDef.Executable = cmSystemTools::FindProgram(launcher);
  958. if (compilerDef.Executable.empty()) {
  959. cmSystemTools::Error("Failed to find path to " + launcher);
  960. return;
  961. }
  962. }
  963. // When CTest is used as a launcher, there is an interesting env variable
  964. // "CTEST_LAUNCH_LOGS" which is set by parent CTest process and is expected
  965. // to be read from global (sic!) env by the launched CTest process. So we
  966. // will need to make this global env available for CTest executable used as a
  967. // "launcher". Tested in RunCMake.ctest_labels_for_subprojects test..
  968. compilerDef.DontUseEnv = true;
  969. this->Compilers[compilerDef.Name] = std::move(compilerDef);
  970. }
  971. std::string cmGlobalFastbuildGenerator::ConvertToFastbuildPath(
  972. std::string const& path) const
  973. {
  974. cmLocalGenerator const* root = LocalGenerators[0].get();
  975. return root->MaybeRelativeToWorkDir(cmSystemTools::FileIsFullPath(path)
  976. ? cmSystemTools::CollapseFullPath(path)
  977. : path);
  978. }
  979. std::unique_ptr<cmLinkLineComputer>
  980. cmGlobalFastbuildGenerator::CreateLinkLineComputer(
  981. cmOutputConverter* outputConverter,
  982. cmStateDirectory const& /* stateDir */) const
  983. {
  984. return cm::make_unique<cmFastbuildLinkLineComputer>(
  985. outputConverter,
  986. this->LocalGenerators[0]->GetStateSnapshot().GetDirectory(), this);
  987. }
  988. void cmGlobalFastbuildGenerator::WriteExec(FastbuildExecNode const& Exec,
  989. int indent)
  990. {
  991. auto const identPlus1 = indent + 1;
  992. WriteCommand("Exec", Exec.Name.empty() ? std::string{} : Quote(Exec.Name),
  993. indent);
  994. Indent(indent);
  995. *BuildFileStream << "{\n";
  996. {
  997. if (!Exec.PreBuildDependencies.empty()) {
  998. WriteArray("PreBuildDependencies", Wrap(Exec.PreBuildDependencies),
  999. identPlus1);
  1000. }
  1001. WriteVariable("ExecExecutable", Quote(Exec.ExecExecutable), identPlus1);
  1002. if (!Exec.ExecArguments.empty()) {
  1003. WriteVariable("ExecArguments", Quote(Exec.ExecArguments), identPlus1);
  1004. }
  1005. if (!Exec.ExecWorkingDir.empty()) {
  1006. WriteVariable("ExecWorkingDir", Quote(Exec.ExecWorkingDir), identPlus1);
  1007. }
  1008. if (!Exec.ExecInput.empty()) {
  1009. WriteArray("ExecInput", Wrap(Exec.ExecInput), identPlus1);
  1010. }
  1011. if (Exec.ExecUseStdOutAsOutput) {
  1012. WriteVariable("ExecUseStdOutAsOutput", "true", identPlus1);
  1013. }
  1014. if (!Exec.ExecInputPath.empty()) {
  1015. WriteArray("ExecInputPath", Wrap(Exec.ExecInputPath), identPlus1);
  1016. }
  1017. if (!Exec.ExecInputPattern.empty()) {
  1018. WriteArray("ExecInputPattern", Wrap(Exec.ExecInputPattern), identPlus1);
  1019. }
  1020. WriteVariable("ExecAlwaysShowOutput", "true", identPlus1);
  1021. WriteVariable("ExecOutput", Quote(Exec.ExecOutput), identPlus1);
  1022. WriteVariable("ExecAlways", Exec.ExecAlways ? "true" : "false",
  1023. identPlus1);
  1024. if (!Exec.ConcurrencyGroupName.empty()) {
  1025. WriteVariable("ConcurrencyGroupName", Quote(Exec.ConcurrencyGroupName),
  1026. identPlus1);
  1027. }
  1028. }
  1029. Indent(indent);
  1030. *BuildFileStream << "}\n";
  1031. static bool const verbose = GlobalSettingIsOn(FASTBUILD_VERBOSE_GENERATOR) ||
  1032. cmSystemTools::HasEnv(FASTBUILD_VERBOSE_GENERATOR);
  1033. // Those aliases are only used for troubleshooting the generated file.
  1034. if (verbose) {
  1035. WriteAlias(Exec.OutputsAlias);
  1036. WriteAlias(Exec.ByproductsAlias);
  1037. }
  1038. }
  1039. void cmGlobalFastbuildGenerator::WriteObjectList(
  1040. FastbuildObjectListNode const& ObjectList, bool allowDistribution)
  1041. {
  1042. WriteCommand("ObjectList", Quote(ObjectList.Name), 1);
  1043. Indent(1);
  1044. *BuildFileStream << "{\n";
  1045. {
  1046. if (!allowDistribution) {
  1047. WriteVariable("AllowDistribution", "false", 2);
  1048. }
  1049. if (!ObjectList.PreBuildDependencies.empty()) {
  1050. WriteArray("PreBuildDependencies", Wrap(ObjectList.PreBuildDependencies),
  1051. 2);
  1052. }
  1053. WriteVariable("Compiler", ObjectList.Compiler, 2);
  1054. // If only PCH output is present - this node reuses existing PCH.
  1055. if (!ObjectList.PCHOutputFile.empty()) {
  1056. WriteVariable("PCHOutputFile", Quote(ObjectList.PCHOutputFile), 2);
  1057. }
  1058. // If PCHInputFile and PCHOptions are present - this node creates PCH.
  1059. if (!ObjectList.PCHInputFile.empty() && !ObjectList.PCHOptions.empty()) {
  1060. WriteVariable("PCHInputFile", Quote(ObjectList.PCHInputFile), 2);
  1061. WriteVariable("PCHOptions", Quote(ObjectList.PCHOptions), 2);
  1062. }
  1063. WriteVariable("CompilerOptions", Quote(ObjectList.CompilerOptions), 2);
  1064. WriteVariable("CompilerOutputPath", Quote(ObjectList.CompilerOutputPath),
  1065. 2);
  1066. WriteVariable("CompilerOutputExtension",
  1067. Quote(ObjectList.CompilerOutputExtension), 2);
  1068. WriteVariable("CompilerOutputKeepBaseExtension", "true", 2);
  1069. WriteArray("CompilerInputFiles", Wrap(ObjectList.CompilerInputFiles), 2);
  1070. if (!ObjectList.AllowCaching) {
  1071. WriteVariable("AllowCaching", "false", 2);
  1072. }
  1073. if (!ObjectList.AllowDistribution) {
  1074. WriteVariable("AllowDistribution", "false", 2);
  1075. }
  1076. if (ObjectList.Hidden) {
  1077. WriteVariable("Hidden", "true", 2);
  1078. }
  1079. }
  1080. Indent(1);
  1081. *BuildFileStream << "}\n";
  1082. }
  1083. void cmGlobalFastbuildGenerator::WriteLinker(
  1084. FastbuildLinkerNode const& LinkerNode, bool allowDistribution)
  1085. {
  1086. WriteCommand(
  1087. LinkerNode.Type == FastbuildLinkerNode::EXECUTABLE ? "Executable"
  1088. : LinkerNode.Type == FastbuildLinkerNode::SHARED_LIBRARY ? "DLL"
  1089. : "Library",
  1090. (!LinkerNode.Name.empty() && LinkerNode.Name != LinkerNode.LinkerOutput)
  1091. ? Quote(LinkerNode.Name)
  1092. : "",
  1093. 1);
  1094. Indent(1);
  1095. *BuildFileStream << "{\n";
  1096. {
  1097. if (!LinkerNode.PreBuildDependencies.empty()) {
  1098. WriteArray("PreBuildDependencies", Wrap(LinkerNode.PreBuildDependencies),
  1099. 2);
  1100. }
  1101. if (!allowDistribution) {
  1102. WriteVariable("AllowDistribution", "false", 2);
  1103. }
  1104. if (!LinkerNode.Compiler.empty()) {
  1105. WriteVariable("Compiler", LinkerNode.Compiler, 2);
  1106. WriteVariable("CompilerOptions", Quote(LinkerNode.CompilerOptions), 2);
  1107. WriteVariable("CompilerOutputPath", Quote("."), 2);
  1108. }
  1109. if (!LocalEnvironment.empty()) {
  1110. WriteVariable("Environment", "." FASTBUILD_ENV_VAR_NAME, 2);
  1111. }
  1112. WriteVariable(LinkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY
  1113. ? "Librarian"
  1114. : "Linker",
  1115. Quote(LinkerNode.Linker), 2);
  1116. WriteVariable(LinkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY
  1117. ? "LibrarianOptions"
  1118. : "LinkerOptions",
  1119. Quote(LinkerNode.LinkerOptions), 2);
  1120. WriteVariable(LinkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY
  1121. ? "LibrarianOutput"
  1122. : "LinkerOutput",
  1123. Quote(LinkerNode.LinkerOutput), 2);
  1124. if (!LinkerNode.LibrarianAdditionalInputs.empty()) {
  1125. WriteArray(LinkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY
  1126. ? "LibrarianAdditionalInputs"
  1127. : "Libraries",
  1128. Wrap(LinkerNode.LibrarianAdditionalInputs), 2);
  1129. }
  1130. if (!LinkerNode.Libraries2.empty()) {
  1131. WriteArray("Libraries2", Wrap(LinkerNode.Libraries2), 2);
  1132. }
  1133. if (!LinkerNode.LibrarianAdditionalInputs.empty()) {
  1134. if (!LinkerNode.LinkerType.empty()) {
  1135. WriteVariable("LinkerType", Quote(LinkerNode.LinkerType), 2);
  1136. }
  1137. }
  1138. if (LinkerNode.Type == FastbuildLinkerNode::EXECUTABLE ||
  1139. LinkerNode.Type == FastbuildLinkerNode::SHARED_LIBRARY) {
  1140. WriteVariable("LinkerLinkObjects",
  1141. LinkerNode.LinkerLinkObjects ? "true" : "false", 2);
  1142. if (!LinkerNode.LinkerStampExe.empty()) {
  1143. WriteVariable("LinkerStampExe", Quote(LinkerNode.LinkerStampExe), 2);
  1144. if (!LinkerNode.LinkerStampExeArgs.empty()) {
  1145. WriteVariable("LinkerStampExeArgs",
  1146. Quote(LinkerNode.LinkerStampExeArgs), 2);
  1147. }
  1148. }
  1149. }
  1150. Indent(1);
  1151. *BuildFileStream << "}\n";
  1152. }
  1153. }
  1154. void cmGlobalFastbuildGenerator::WriteAlias(FastbuildAliasNode const& Alias,
  1155. int indent)
  1156. {
  1157. if (Alias.PreBuildDependencies.empty()) {
  1158. return;
  1159. }
  1160. auto const identPlus1 = indent + 1;
  1161. WriteCommand("Alias", Quote(Alias.Name), indent);
  1162. Indent(indent);
  1163. *BuildFileStream << "{\n";
  1164. WriteArray("Targets", Wrap(Alias.PreBuildDependencies), identPlus1);
  1165. if (Alias.Hidden) {
  1166. WriteVariable("Hidden", "true", identPlus1);
  1167. }
  1168. Indent(indent);
  1169. *BuildFileStream << "}\n";
  1170. }
  1171. void cmGlobalFastbuildGenerator::WriteCopy(FastbuildCopyNode const& Copy)
  1172. {
  1173. cmGlobalFastbuildGenerator::WriteCommand(
  1174. Copy.CopyDir ? "CopyDir" : "Copy",
  1175. cmGlobalFastbuildGenerator::Quote(Copy.Name), 1);
  1176. cmGlobalFastbuildGenerator::Indent(1);
  1177. *BuildFileStream << "{\n";
  1178. WriteVariable("PreBuildDependencies",
  1179. cmGlobalFastbuildGenerator::Quote(Copy.PreBuildDependencies),
  1180. 2);
  1181. WriteVariable(Copy.CopyDir ? "SourcePaths" : "Source",
  1182. cmGlobalFastbuildGenerator::Quote(Copy.Source), 2);
  1183. WriteVariable("Dest", cmGlobalFastbuildGenerator::Quote(Copy.Dest), 2);
  1184. cmGlobalFastbuildGenerator::Indent(1);
  1185. *BuildFileStream << "}\n";
  1186. }
  1187. void cmGlobalFastbuildGenerator::WriteTarget(FastbuildTarget const& target)
  1188. {
  1189. for (auto const& val : target.Variables) {
  1190. auto const& key = val.first;
  1191. auto const& value = val.second;
  1192. WriteVariable(key, cmGlobalFastbuildGenerator::Quote(value), 1);
  1193. }
  1194. // add_custom_commands(...)
  1195. for (auto const& alias : { target.ExecNodes }) {
  1196. this->WriteAlias(alias);
  1197. }
  1198. // -deps Alias.
  1199. this->WriteAlias(target.DependenciesAlias);
  1200. // PRE_BUILD.
  1201. for (auto const& alias : { target.PreBuildExecNodes }) {
  1202. this->WriteAlias(alias);
  1203. }
  1204. // Copy commands.
  1205. for (FastbuildCopyNode const& node : target.CopyNodes) {
  1206. this->WriteCopy(node);
  1207. }
  1208. // Objects.
  1209. for (FastbuildObjectListNode const& objectList : target.ObjectListNodes) {
  1210. this->WriteObjectList(objectList, target.AllowDistribution);
  1211. }
  1212. if (!target.PreLinkExecNodes.Nodes.empty()) {
  1213. for (auto const& exec : target.PreLinkExecNodes.Nodes) {
  1214. this->WriteExec(exec);
  1215. }
  1216. this->WriteAlias(target.PreLinkExecNodes.Alias);
  1217. }
  1218. // Libraries / executables.
  1219. if (!target.LinkerNode.empty()) {
  1220. for (auto const& linkerNode : target.LinkerNode) {
  1221. this->WriteLinker(linkerNode, target.AllowDistribution);
  1222. }
  1223. }
  1224. if (!target.PostBuildExecNodes.Nodes.empty()) {
  1225. for (auto const& exec : target.PostBuildExecNodes.Nodes) {
  1226. this->WriteExec(exec);
  1227. }
  1228. this->WriteAlias(target.PostBuildExecNodes.Alias);
  1229. }
  1230. // Aliases (if any).
  1231. for (FastbuildAliasNode const& alias : target.AliasNodes) {
  1232. this->WriteAlias(alias);
  1233. }
  1234. }
  1235. void cmGlobalFastbuildGenerator::WriteIDEProjects()
  1236. {
  1237. for (auto const& proj : IDEProjects) {
  1238. (void)proj;
  1239. // VS
  1240. #if defined(_WIN32)
  1241. auto const& VSProj = proj.second.first;
  1242. WriteCommand("VCXProject", Quote(VSProj.Alias));
  1243. *this->BuildFileStream << "{\n";
  1244. WriteVariable("ProjectOutput", Quote(VSProj.ProjectOutput), 1);
  1245. WriteIDEProjectConfig(VSProj.ProjectConfigs);
  1246. WriteVSBuildCommands();
  1247. WriteIDEProjectCommon(VSProj);
  1248. *this->BuildFileStream << "}\n\n";
  1249. // XCode
  1250. #elif defined(__APPLE__)
  1251. auto const& XCodeProj = proj.second.second;
  1252. WriteCommand("XCodeProject", Quote(XCodeProj.Alias), 0);
  1253. *this->BuildFileStream << "{\n";
  1254. WriteVariable("ProjectOutput", Quote(XCodeProj.ProjectOutput), 1);
  1255. WriteIDEProjectConfig(XCodeProj.ProjectConfigs);
  1256. WriteXCodeBuildCommands();
  1257. WriteIDEProjectCommon(XCodeProj);
  1258. *this->BuildFileStream << "}\n\n";
  1259. #endif
  1260. }
  1261. #if defined(_WIN32)
  1262. this->WriteSolution();
  1263. #elif defined(__APPLE__)
  1264. this->WriteXCodeTopLevelProject();
  1265. #endif
  1266. }
  1267. void cmGlobalFastbuildGenerator::WriteVSBuildCommands()
  1268. {
  1269. WriteVariable("ProjectBuildCommand",
  1270. Quote(FASTBUILD_IDE_VS_COMMAND_PREFIX +
  1271. this->FastbuildCommand +
  1272. FASTBUILD_IDE_BUILD_ARGS " ^$(ProjectName)"),
  1273. 1);
  1274. WriteVariable("ProjectRebuildCommand",
  1275. Quote(FASTBUILD_IDE_VS_COMMAND_PREFIX +
  1276. this->FastbuildCommand +
  1277. FASTBUILD_IDE_BUILD_ARGS "-clean ^$(ProjectName)"),
  1278. 1);
  1279. WriteVariable("ProjectCleanCommand",
  1280. Quote(FASTBUILD_IDE_VS_COMMAND_PREFIX +
  1281. this->FastbuildCommand + " -ide clean"),
  1282. 1);
  1283. }
  1284. void cmGlobalFastbuildGenerator::WriteXCodeBuildCommands()
  1285. {
  1286. WriteVariable("XCodeBuildToolPath", Quote(this->FastbuildCommand), 1);
  1287. WriteVariable("XCodeBuildToolArgs",
  1288. Quote(FASTBUILD_IDE_BUILD_ARGS "^$(FASTBUILD_TARGET)"), 1);
  1289. WriteVariable("XCodeBuildWorkingDir",
  1290. Quote(this->CMakeInstance->GetHomeOutputDirectory()), 1);
  1291. }
  1292. void cmGlobalFastbuildGenerator::WriteIDEProjectCommon(
  1293. IDEProjectCommon const& project)
  1294. {
  1295. WriteVariable("ProjectBasePath", Quote(project.ProjectBasePath), 1);
  1296. // So Fastbuild will pick up files relative to CMakeLists.txt
  1297. WriteVariable("ProjectInputPaths", Quote(project.ProjectBasePath), 1);
  1298. }
  1299. void cmGlobalFastbuildGenerator::WriteIDEProjectConfig(
  1300. std::vector<IDEProjectConfig> const& configs, std::string const& keyName)
  1301. {
  1302. std::vector<std::string> allConfigVariables;
  1303. for (auto const& config : configs) {
  1304. std::string configName = "Config" + config.Config;
  1305. WriteVariable(configName, "", 1);
  1306. Indent(1);
  1307. *this->BuildFileStream << "[\n";
  1308. WriteVariable("Config", Quote(config.Config), 2);
  1309. if (!config.Target.empty()) {
  1310. WriteVariable("Target", Quote(config.Target), 2);
  1311. }
  1312. if (!config.Platform.empty()) {
  1313. WriteVariable("Platform", Quote(config.Platform), 2);
  1314. }
  1315. Indent(1);
  1316. *this->BuildFileStream << "]\n";
  1317. allConfigVariables.emplace_back(std::move(configName));
  1318. }
  1319. WriteArray(keyName, Wrap(allConfigVariables, ".", ""), 1);
  1320. }
  1321. void cmGlobalFastbuildGenerator::AddTargetAll()
  1322. {
  1323. FastbuildAliasNode allAliasNode;
  1324. allAliasNode.Name = FASTBUILD_ALL_TARGET_NAME;
  1325. for (auto const& targetBase : FastbuildTargets) {
  1326. if (targetBase->Type == FastbuildTargetType::LINK) {
  1327. auto const& target = static_cast<FastbuildTarget const&>(*targetBase);
  1328. // Add non-global and non-excluded targets to "all"
  1329. if (!target.IsGlobal && !target.ExcludeFromAll) {
  1330. allAliasNode.PreBuildDependencies.emplace(target.Name);
  1331. }
  1332. } else if (targetBase->Type == FastbuildTargetType::ALIAS) {
  1333. auto const& target = static_cast<FastbuildAliasNode const&>(*targetBase);
  1334. if (!target.ExcludeFromAll) {
  1335. allAliasNode.PreBuildDependencies.emplace(target.Name);
  1336. }
  1337. }
  1338. }
  1339. if (allAliasNode.PreBuildDependencies.empty()) {
  1340. allAliasNode.PreBuildDependencies.emplace(FASTBUILD_NOOP_FILE_NAME);
  1341. }
  1342. this->AddTarget(std::move(allAliasNode));
  1343. }
  1344. void cmGlobalFastbuildGenerator::AddGlobCheckExec()
  1345. {
  1346. // Tested in "RunCMake.file" test.
  1347. std::string const globScript =
  1348. this->GetCMakeInstance()->GetGlobVerifyScript();
  1349. if (!globScript.empty()) {
  1350. FastbuildExecNode globCheck;
  1351. globCheck.Name = FASTBUILD_GLOB_CHECK_TARGET;
  1352. globCheck.ExecExecutable = cmSystemTools::GetCMakeCommand();
  1353. globCheck.ExecArguments = "-P " FASTBUILD_1_INPUT_PLACEHOLDER;
  1354. globCheck.ExecInput = { this->ConvertToFastbuildPath(globScript) };
  1355. globCheck.ExecAlways = false;
  1356. globCheck.ExecUseStdOutAsOutput = false;
  1357. auto const cache = this->GetCMakeInstance()->GetGlobCacheEntries();
  1358. for (auto const& entry : cache) {
  1359. auto path = cmSystemTools::GetFilenamePath(entry.Expression);
  1360. auto expression = cmSystemTools::GetFilenameName(entry.Expression);
  1361. if (std::find(globCheck.ExecInputPath.begin(),
  1362. globCheck.ExecInputPath.end(),
  1363. path) == globCheck.ExecInputPath.end()) {
  1364. globCheck.ExecInputPath.emplace_back(std::move(path));
  1365. }
  1366. if (std::find(globCheck.ExecInputPattern.begin(),
  1367. globCheck.ExecInputPattern.end(),
  1368. expression) == globCheck.ExecInputPattern.end()) {
  1369. globCheck.ExecInputPattern.emplace_back(std::move(expression));
  1370. }
  1371. }
  1372. globCheck.ExecOutput = this->ConvertToFastbuildPath(
  1373. this->GetCMakeInstance()->GetGlobVerifyStamp());
  1374. this->AddTarget(std::move(globCheck));
  1375. }
  1376. }
  1377. void cmGlobalFastbuildGenerator::WriteSolution()
  1378. {
  1379. std::string const solutionName = LocalGenerators[0]->GetProjectName();
  1380. std::map<std::string /*folder*/, std::vector<std::string>> VSProjects;
  1381. std::vector<std::string> VSProjectsWithoutFolder;
  1382. for (auto const& IDEProj : IDEProjects) {
  1383. auto const VSProj = IDEProj.second.first;
  1384. VSProjects[VSProj.folder].emplace_back(VSProj.Alias);
  1385. }
  1386. WriteCommand("VSSolution", Quote("solution"));
  1387. *this->BuildFileStream << "{\n";
  1388. WriteVariable("SolutionOutput",
  1389. Quote(cmJoin({ "VisualStudio", solutionName + ".sln" }, "/")),
  1390. 1);
  1391. auto const& configs = IDEProjects.begin()->second.first.ProjectConfigs;
  1392. WriteIDEProjectConfig(configs, "SolutionConfigs");
  1393. int folderNumber = 0;
  1394. std::vector<std::string> folders;
  1395. for (auto& item : VSProjects) {
  1396. auto const& pathToFolder = item.first;
  1397. auto& projectsInFolder = item.second;
  1398. if (pathToFolder.empty()) {
  1399. std::move(projectsInFolder.begin(), projectsInFolder.end(),
  1400. std::back_inserter(VSProjectsWithoutFolder));
  1401. } else {
  1402. std::string folderName =
  1403. cmStrCat("Folder_", std::to_string(++folderNumber));
  1404. WriteStruct(
  1405. folderName,
  1406. { { "Path", Quote(pathToFolder) },
  1407. { "Projects",
  1408. cmStrCat("{", cmJoin(Wrap(projectsInFolder), ","), "}") } },
  1409. 1);
  1410. folders.emplace_back(std::move(folderName));
  1411. }
  1412. }
  1413. if (!folders.empty()) {
  1414. WriteArray("SolutionFolders ", Wrap(folders, ".", ""), 1);
  1415. }
  1416. if (!VSProjectsWithoutFolder.empty()) {
  1417. WriteArray("SolutionProjects", Wrap(VSProjectsWithoutFolder), 1);
  1418. }
  1419. *this->BuildFileStream << "}\n";
  1420. }
  1421. void cmGlobalFastbuildGenerator::WriteXCodeTopLevelProject()
  1422. {
  1423. std::string const projectName = LocalGenerators[0]->GetProjectName();
  1424. std::vector<std::string> XCodeProjects;
  1425. for (auto const& IDEProj : IDEProjects) {
  1426. auto const XCodeProj = IDEProj.second.second;
  1427. XCodeProjects.emplace_back(XCodeProj.Alias);
  1428. }
  1429. WriteCommand("XCodeProject", Quote("xcode"));
  1430. *this->BuildFileStream << "{\n";
  1431. WriteVariable(
  1432. "ProjectOutput",
  1433. Quote(
  1434. cmJoin({ "XCode", projectName + ".xcodeproj", "project.pbxproj" }, "/")),
  1435. 1);
  1436. WriteVariable("ProjectBasePath", Quote(FASTBUILD_XCODE_BASE_PATH), 1);
  1437. auto const& configs = IDEProjects.begin()->second.second.ProjectConfigs;
  1438. WriteIDEProjectConfig(configs);
  1439. WriteArray("ProjectFiles", Wrap(XCodeProjects), 1);
  1440. *this->BuildFileStream << "}\n";
  1441. }
  1442. void cmGlobalFastbuildGenerator::LogMessage(std::string const& m) const
  1443. {
  1444. static bool const verbose = GlobalSettingIsOn(FASTBUILD_VERBOSE_GENERATOR) ||
  1445. cmSystemTools::HasEnv(FASTBUILD_VERBOSE_GENERATOR);
  1446. if (verbose) {
  1447. cmSystemTools::Message(m);
  1448. }
  1449. }
  1450. void cmGlobalFastbuildGenerator::AddFileToClean(std::string const& file)
  1451. {
  1452. AllFilesToClean.insert(file);
  1453. }
  1454. std::string cmGlobalFastbuildGenerator::GetExternalShellExecutable()
  1455. {
  1456. // FindProgram is expensive - touches filesystem and makes syscalls, so cache
  1457. // it.
  1458. static std::string const cached =
  1459. #ifdef _WIN32
  1460. cmSystemTools::FindProgram(
  1461. "cmd.exe", std::vector<std::string>{ "C:\\Windows\\System32" });
  1462. #else
  1463. cmSystemTools::FindProgram("sh", std::vector<std::string>{ "/bin" });
  1464. #endif
  1465. return cached;
  1466. }
  1467. void cmGlobalFastbuildGenerator::WriteTargetRebuildBFF()
  1468. {
  1469. std::vector<std::string> implicitDeps;
  1470. for (auto& lg : LocalGenerators) {
  1471. std::vector<std::string> const& lf = lg->GetMakefile()->GetListFiles();
  1472. for (auto const& dep : lf) {
  1473. implicitDeps.push_back(this->ConvertToFastbuildPath(dep));
  1474. }
  1475. }
  1476. auto const* cmake = this->GetCMakeInstance();
  1477. std::string outDir = cmake->GetHomeOutputDirectory() + '/';
  1478. implicitDeps.push_back(outDir + "CMakeCache.txt");
  1479. FastbuildExecNode rebuildBFF;
  1480. rebuildBFF.Name = FASTBUILD_REBUILD_BFF_TARGET_NAME;
  1481. if (!this->GetCMakeInstance()->GetGlobVerifyScript().empty()) {
  1482. implicitDeps.emplace_back(this->GetCMakeInstance()->GetGlobVerifyStamp());
  1483. }
  1484. std::sort(implicitDeps.begin(), implicitDeps.end());
  1485. implicitDeps.erase(std::unique(implicitDeps.begin(), implicitDeps.end()),
  1486. implicitDeps.end());
  1487. std::string args =
  1488. cmStrCat("--regenerate-during-build",
  1489. (this->GetCMakeInstance()->GetIgnoreCompileWarningAsError()
  1490. ? " --compile-no-warning-as-error"
  1491. : ""),
  1492. (this->GetCMakeInstance()->GetIgnoreLinkWarningAsError()
  1493. ? " --link-no-warning-as-error"
  1494. : ""),
  1495. " -S", QuoteIfHasSpaces(cmake->GetHomeDirectory()), " -B",
  1496. QuoteIfHasSpaces(cmake->GetHomeOutputDirectory()));
  1497. rebuildBFF.ExecArguments = std::move(args);
  1498. rebuildBFF.ExecInput = implicitDeps;
  1499. rebuildBFF.ExecExecutable = cmSystemTools::GetCMakeCommand();
  1500. rebuildBFF.ExecWorkingDir = outDir;
  1501. rebuildBFF.ExecOutput = outDir + FASTBUILD_BUILD_FILE;
  1502. this->WriteExec(rebuildBFF, 0);
  1503. }
  1504. void cmGlobalFastbuildGenerator::WriteCleanScript()
  1505. {
  1506. std::string const path =
  1507. cmStrCat(this->GetCMakeInstance()->GetHomeOutputDirectory(), '/',
  1508. FASTBUILD_CLEAN_SCRIPT_NAME);
  1509. cmsys::ofstream scriptFile(path.c_str(), std::ios::out | std::ios::binary);
  1510. if (!scriptFile.is_open()) {
  1511. cmSystemTools::Error("Failed to open: " FASTBUILD_CLEAN_SCRIPT_NAME);
  1512. return;
  1513. }
  1514. for (std::string const& file : AllFilesToClean) {
  1515. #if defined(_WIN32)
  1516. scriptFile << "del /f /q "
  1517. << cmSystemTools::ConvertToWindowsOutputPath(file) << "\n";
  1518. #else
  1519. scriptFile << "rm -f " << file << '\n';
  1520. #endif
  1521. }
  1522. }
  1523. void cmGlobalFastbuildGenerator::WriteTargetClean()
  1524. {
  1525. if (AllFilesToClean.empty()) {
  1526. FastbuildAliasNode clean;
  1527. clean.Name = FASTBUILD_CLEAN_TARGET_NAME;
  1528. clean.PreBuildDependencies.emplace(FASTBUILD_CLEAN_FILE_NAME);
  1529. WriteAlias(clean, 0);
  1530. return;
  1531. }
  1532. WriteCleanScript();
  1533. FastbuildExecNode clean;
  1534. clean.Name = FASTBUILD_CLEAN_TARGET_NAME;
  1535. clean.ExecExecutable = GetExternalShellExecutable();
  1536. clean.ExecArguments =
  1537. FASTBUILD_SCRIPT_FILE_ARG FASTBUILD_1_INPUT_PLACEHOLDER;
  1538. clean.ExecInput = { FASTBUILD_CLEAN_SCRIPT_NAME };
  1539. clean.ExecAlways = true;
  1540. clean.ExecUseStdOutAsOutput = true;
  1541. clean.ExecOutput = FASTBUILD_CLEAN_FILE_NAME;
  1542. clean.ExecWorkingDir = this->GetCMakeInstance()->GetHomeOutputDirectory();
  1543. WriteExec(clean, 0);
  1544. }
  1545. void cmGlobalFastbuildGenerator::WriteTargets()
  1546. {
  1547. std::string const outputDir = this->CMakeInstance->GetHomeOutputDirectory();
  1548. LogMessage("GetHomeOutputDirectory: " + outputDir);
  1549. // Noop file that 'all' can alias to if we don't have any other targets...
  1550. // The exact location of the "noop" file is verified in one of the tests in
  1551. // "RunCMake.CMakePresetsPackage" test suite.
  1552. cmSystemTools::Touch(cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
  1553. '/', FASTBUILD_NOOP_FILE_NAME),
  1554. true);
  1555. cmSystemTools::Touch(cmStrCat(this->CMakeInstance->GetHomeOutputDirectory(),
  1556. '/', FASTBUILD_CLEAN_FILE_NAME),
  1557. true);
  1558. // Add "all" utility target before sorting, so we can correctly sort
  1559. // targets that depend on it
  1560. AddTargetAll();
  1561. TopologicalSort(FastbuildTargets);
  1562. AddGlobCheckExec();
  1563. for (auto const& targetBase : FastbuildTargets) {
  1564. this->WriteComment("Target definition: " + targetBase->Name);
  1565. // Target start.
  1566. *BuildFileStream << "{\n";
  1567. if (targetBase->Type == FastbuildTargetType::EXEC) {
  1568. this->WriteExec(static_cast<FastbuildExecNode const&>(*targetBase));
  1569. } else if (targetBase->Type == FastbuildTargetType::ALIAS) {
  1570. this->WriteAlias(static_cast<FastbuildAliasNode const&>(*targetBase));
  1571. } else if (targetBase->Type == FastbuildTargetType::LINK) {
  1572. auto const& target = static_cast<FastbuildTarget const&>(*targetBase);
  1573. this->WriteTarget(target);
  1574. }
  1575. // Target end.
  1576. *BuildFileStream << "}\n";
  1577. }
  1578. if (!this->GetCMakeInstance()->GetIsInTryCompile()) {
  1579. if (!IDEProjects.empty()) {
  1580. this->WriteIDEProjects();
  1581. }
  1582. }
  1583. this->WriteTargetClean();
  1584. this->WriteTargetRebuildBFF();
  1585. }
  1586. std::string cmGlobalFastbuildGenerator::GetTargetName(
  1587. cmGeneratorTarget const* GeneratorTarget) const
  1588. {
  1589. std::string targetName =
  1590. GeneratorTarget->GetLocalGenerator()->GetCurrentBinaryDirectory();
  1591. targetName += "/";
  1592. targetName += GeneratorTarget->GetName();
  1593. targetName = this->ConvertToFastbuildPath(targetName);
  1594. return targetName;
  1595. }
  1596. cm::optional<FastbuildTarget>
  1597. cmGlobalFastbuildGenerator::GetTargetByOutputName(
  1598. std::string const& output) const
  1599. {
  1600. for (auto const& targetBase : FastbuildTargets) {
  1601. if (targetBase->Type == FastbuildTargetType::LINK) {
  1602. auto const& target = static_cast<FastbuildTarget const&>(*targetBase);
  1603. if (std::any_of(target.LinkerNode.begin(), target.LinkerNode.end(),
  1604. [&output](FastbuildLinkerNode const& target_) {
  1605. return target_.LinkerOutput == output;
  1606. })) {
  1607. return target;
  1608. }
  1609. }
  1610. }
  1611. return cm::nullopt;
  1612. }
  1613. void cmGlobalFastbuildGenerator::AddIDEProject(FastbuildTarget const& target,
  1614. std::string const& config)
  1615. {
  1616. auto const& configs = GetConfigNames();
  1617. if (std::find(configs.begin(), configs.end(), config) == configs.end()) {
  1618. LogMessage("Config " + config + " doesn't exist, IDE projest for " +
  1619. target.Name + " won't be generated");
  1620. return;
  1621. }
  1622. auto& IDEProject = IDEProjects[target.BaseName];
  1623. auto const relativeSubdir = cmSystemTools::RelativePath(
  1624. this->GetCMakeInstance()->GetHomeDirectory(), target.BasePath);
  1625. // VS
  1626. auto& VSProject = IDEProject.first;
  1627. VSProject.Alias = target.BaseName + "-vcxproj";
  1628. VSProject.ProjectOutput = cmStrCat("VisualStudio/Projects/", relativeSubdir,
  1629. '/', target.BaseName + ".vcxproj");
  1630. VSProject.ProjectBasePath = target.BasePath;
  1631. VSProject.folder = relativeSubdir;
  1632. // XCode
  1633. auto& XCodeProject = IDEProject.second;
  1634. XCodeProject.Alias = target.BaseName + "-xcodeproj";
  1635. XCodeProject.ProjectOutput =
  1636. cmStrCat("XCode/Projects/", relativeSubdir, '/',
  1637. target.BaseName + ".xcodeproj/project.pbxproj");
  1638. XCodeProject.ProjectBasePath = target.BasePath;
  1639. IDEProjectConfig VSConfig;
  1640. VSConfig.Platform = "X64";
  1641. IDEProjectConfig XCodeConfig;
  1642. VSConfig.Target = XCodeConfig.Target = target.Name;
  1643. VSConfig.Config = XCodeConfig.Config = config.empty() ? "DEFAULT" : config;
  1644. VSProject.ProjectConfigs.emplace_back(std::move(VSConfig));
  1645. XCodeProject.ProjectConfigs.emplace_back(std::move(XCodeConfig));
  1646. }
  1647. bool cmGlobalFastbuildGenerator::IsExcluded(cmGeneratorTarget* target)
  1648. {
  1649. return cmGlobalGenerator::IsExcluded(LocalGenerators[0].get(), target);
  1650. }
  1651. std::vector<std::string> const& cmGlobalFastbuildGenerator::GetConfigNames()
  1652. const
  1653. {
  1654. return static_cast<cmLocalFastbuildGenerator const*>(
  1655. this->LocalGenerators.front().get())
  1656. ->GetConfigNames();
  1657. }
  1658. bool cmGlobalFastbuildGenerator::Open(std::string const& bindir,
  1659. std::string const& projectName,
  1660. bool dryRun)
  1661. {
  1662. #ifdef _WIN32
  1663. std::string sln = bindir + "/VisualStudio/" + projectName + ".sln";
  1664. if (dryRun) {
  1665. return cmSystemTools::FileExists(sln, true);
  1666. }
  1667. sln = cmSystemTools::ConvertToOutputPath(sln);
  1668. auto OpenSolution = [](std::string pathToSolution) {
  1669. HRESULT comInitialized =
  1670. CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
  1671. if (FAILED(comInitialized)) {
  1672. return false;
  1673. }
  1674. HINSTANCE hi = ShellExecuteA(NULL, "open", pathToSolution.c_str(), NULL,
  1675. NULL, SW_SHOWNORMAL);
  1676. CoUninitialize();
  1677. return reinterpret_cast<intptr_t>(hi) > 32;
  1678. };
  1679. return std::async(std::launch::async, OpenSolution, sln).get();
  1680. #else
  1681. return cmGlobalCommonGenerator::Open(bindir, projectName, dryRun);
  1682. #endif
  1683. }