cmGlobalFastbuildGenerator.cxx 63 KB

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