cmGlobalFastbuildGenerator.cxx 65 KB

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