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