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