Coverage Report

Created: 2026-07-14 07:09

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