cmGlobalFastbuildGenerator.cxx 66 KB

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