cmGlobalFastbuildGenerator.cxx 66 KB

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