Coverage Report

Created: 2026-03-12 06:35

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 "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
}