Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmFastbuildNormalTargetGenerator.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 "cmFastbuildNormalTargetGenerator.h"
5
6
#include <algorithm>
7
#include <array>
8
#include <cstddef>
9
#include <functional>
10
#include <iterator>
11
#include <map>
12
#include <sstream>
13
#include <string>
14
#include <unordered_map>
15
#include <unordered_set>
16
#include <utility>
17
18
#include <cm/memory>
19
#include <cm/optional>
20
#include <cm/string_view>
21
#include <cmext/string_view>
22
23
#include "cmsys/FStream.hxx"
24
25
#include "cmCommonTargetGenerator.h"
26
#include "cmCryptoHash.h"
27
#include "cmFastbuildTargetGenerator.h"
28
#include "cmGeneratedFileStream.h"
29
#include "cmGeneratorExpression.h"
30
#include "cmGeneratorTarget.h"
31
#include "cmGlobalCommonGenerator.h"
32
#include "cmGlobalFastbuildGenerator.h"
33
#include "cmLinkLineComputer.h"
34
#include "cmLinkLineDeviceComputer.h"
35
#include "cmList.h"
36
#include "cmListFileCache.h"
37
#include "cmLocalCommonGenerator.h"
38
#include "cmLocalFastbuildGenerator.h"
39
#include "cmLocalGenerator.h"
40
#include "cmMakefile.h"
41
#include "cmOSXBundleGenerator.h"
42
#include "cmObjectLocation.h"
43
#include "cmOutputConverter.h"
44
#include "cmSourceFile.h"
45
#include "cmState.h"
46
#include "cmStateDirectory.h"
47
#include "cmStateSnapshot.h"
48
#include "cmStateTypes.h"
49
#include "cmStringAlgorithms.h"
50
#include "cmSystemTools.h"
51
#include "cmTarget.h"
52
#include "cmTargetDepend.h"
53
#include "cmValue.h"
54
#include "cmake.h"
55
56
namespace {
57
58
std::string const COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
59
std::string const COMPILE_OPTIONS("COMPILE_OPTIONS");
60
std::string const COMPILE_FLAGS("COMPILE_FLAGS");
61
std::string const CMAKE_LANGUAGE("CMAKE");
62
std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
63
64
std::string const CMAKE_UNITY_BUILD("CMAKE_UNITY_BUILD");
65
std::string const CMAKE_UNITY_BUILD_BATCH_SIZE("CMAKE_UNITY_BUILD_BATCH_SIZE");
66
std::string const UNITY_BUILD("UNITY_BUILD");
67
std::string const UNITY_BUILD_BATCH_SIZE("UNITY_BUILD_BATCH_SIZE");
68
std::string const SKIP_UNITY_BUILD_INCLUSION("SKIP_UNITY_BUILD_INCLUSION");
69
std::string const UNITY_GROUP("UNITY_GROUP");
70
71
#ifdef _WIN32
72
char const kPATH_SLASH = '\\';
73
#else
74
char const kPATH_SLASH = '/';
75
#endif
76
77
} // anonymous namespace
78
79
cmFastbuildNormalTargetGenerator::cmFastbuildNormalTargetGenerator(
80
  cmGeneratorTarget* gt, std::string configParam)
81
0
  : cmFastbuildTargetGenerator(gt, std::move(configParam))
82
  , RulePlaceholderExpander(
83
0
      this->LocalCommonGenerator->CreateRulePlaceholderExpander())
84
0
  , ObjectOutDir(this->GetGlobalGenerator()->ConvertToFastbuildPath(
85
0
      this->GeneratorTarget->GetObjectDirectory(Config)))
86
0
  , Languages(GetLanguages())
87
0
  , CompileObjectCmakeRules(GetCompileObjectCommand())
88
0
  , CudaCompileMode(this->GetCudaCompileMode())
89
0
{
90
91
0
  LogMessage(cmStrCat("objectOutDir: ", ObjectOutDir));
92
0
  this->OSXBundleGenerator = cm::make_unique<cmOSXBundleGenerator>(gt);
93
0
  this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
94
95
  // Quotes to account for potential spaces.
96
0
  RulePlaceholderExpander->SetTargetImpLib(
97
0
    "\"" FASTBUILD_DOLLAR_TAG "TargetOutputImplib" FASTBUILD_DOLLAR_TAG "\"");
98
0
  for (auto const& lang : Languages) {
99
0
    TargetIncludesByLanguage[lang] = this->GetIncludes(lang, Config);
100
0
    LogMessage(cmStrCat("targetIncludes for lang ", lang, " = ",
101
0
                        TargetIncludesByLanguage[lang]));
102
103
0
    for (auto const& arch : this->GetArches()) {
104
0
      auto& flags = CompileFlagsByLangAndArch[std::make_pair(lang, arch)];
105
0
      this->LocalCommonGenerator->GetTargetCompileFlags(
106
0
        this->GeneratorTarget, Config, lang, flags, arch);
107
0
      LogMessage(
108
0
        cmStrCat("Lang: ", lang, ", arch: ", arch, ", flags: ", flags));
109
0
    }
110
0
  }
111
0
}
112
113
std::string cmFastbuildNormalTargetGenerator::DetectCompilerFlags(
114
  cmSourceFile const& srcFile, std::string const& arch)
115
0
{
116
0
  std::string const language = srcFile.GetLanguage();
117
0
  cmGeneratorExpressionInterpreter genexInterpreter(
118
0
    this->GetLocalGenerator(), Config, this->GeneratorTarget, language);
119
120
0
  std::vector<std::string> sourceIncludesVec;
121
0
  if (cmValue cincludes = srcFile.GetProperty(INCLUDE_DIRECTORIES)) {
122
0
    this->LocalGenerator->AppendIncludeDirectories(
123
0
      sourceIncludesVec,
124
0
      genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES), srcFile);
125
0
  }
126
0
  std::string sourceIncludesStr = this->LocalGenerator->GetIncludeFlags(
127
0
    sourceIncludesVec, this->GeneratorTarget, language, Config, false);
128
0
  LogMessage(cmStrCat("sourceIncludes = ", sourceIncludesStr));
129
130
0
  std::string compileFlags =
131
0
    CompileFlagsByLangAndArch[std::make_pair(language, arch)];
132
0
  this->GeneratorTarget->AddExplicitLanguageFlags(compileFlags, srcFile);
133
134
0
  if (cmValue const cflags = srcFile.GetProperty(COMPILE_FLAGS)) {
135
0
    this->LocalGenerator->AppendFlags(
136
0
      compileFlags, genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS));
137
0
  }
138
139
0
  if (cmValue const coptions = srcFile.GetProperty(COMPILE_OPTIONS)) {
140
0
    this->LocalGenerator->AppendCompileOptions(
141
0
      compileFlags, genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS));
142
0
  }
143
  // Source includes take precedence over target includes.
144
0
  this->LocalGenerator->AppendFlags(compileFlags, sourceIncludesStr);
145
0
  this->LocalGenerator->AppendFlags(compileFlags,
146
0
                                    TargetIncludesByLanguage[language]);
147
148
0
  if (language == "Fortran") {
149
0
    this->AppendFortranFormatFlags(compileFlags, srcFile);
150
0
    this->AppendFortranPreprocessFlags(compileFlags, srcFile);
151
0
  }
152
153
0
  LogMessage(cmStrCat("compileFlags = ", compileFlags));
154
0
  return compileFlags;
155
0
}
156
157
void cmFastbuildNormalTargetGenerator::SplitLinkerFromArgs(
158
  std::string const& command, std::string& outLinkerExecutable,
159
  std::string& outLinkerArgs) const
160
0
{
161
#ifdef _WIN32
162
  std::vector<std::string> args;
163
  std::string tmp;
164
  cmSystemTools::SplitProgramFromArgs(command, tmp, outLinkerArgs);
165
  // cmLocalGenerator::GetStaticLibraryFlags seems to add empty quotes when
166
  // appending "STATIC_LIBRARY_FLAGS_DEBUG"...
167
  cmSystemTools::ReplaceString(outLinkerArgs, "\"\"", "");
168
  cmSystemTools::ParseWindowsCommandLine(command.c_str(), args);
169
  outLinkerExecutable = std::move(args[0]);
170
#else
171
0
  cmSystemTools::SplitProgramFromArgs(command, outLinkerExecutable,
172
0
                                      outLinkerArgs);
173
0
#endif
174
0
}
175
176
void cmFastbuildNormalTargetGenerator::GetLinkerExecutableAndArgs(
177
  std::string const& command, std::string& outLinkerExecutable,
178
  std::string& outLinkerArgs)
179
0
{
180
0
  if (command.empty()) {
181
0
    return;
182
0
  }
183
184
0
  LogMessage("Link Command: " + command);
185
186
0
  auto const& compilers = this->GetGlobalGenerator()->Compilers;
187
0
  auto const linkerLauncherVarName = FASTBUILD_LINKER_LAUNCHER_PREFIX +
188
0
    this->GeneratorTarget->GetLinkerLanguage(Config);
189
0
  auto const iter = compilers.find(linkerLauncherVarName);
190
  // Tested in "RunCMake.LinkerLauncher" test.
191
0
  if (iter != compilers.end()) {
192
0
    LogMessage("Linker launcher: " + iter->first);
193
0
    outLinkerExecutable = iter->second.Executable;
194
0
    outLinkerArgs = cmStrCat(iter->second.Args, ' ', command);
195
0
  } else {
196
0
    SplitLinkerFromArgs(command, outLinkerExecutable, outLinkerArgs);
197
0
  }
198
0
  LogMessage("Linker Exe: " + outLinkerExecutable);
199
0
  LogMessage("Linker args: " + outLinkerArgs);
200
0
}
201
202
bool cmFastbuildNormalTargetGenerator::DetectBaseLinkerCommand(
203
  std::string& command, std::string const& arch,
204
  cmGeneratorTarget::Names const& targetNames)
205
0
{
206
0
  std::string const linkLanguage =
207
0
    this->GeneratorTarget->GetLinkerLanguage(Config);
208
0
  if (linkLanguage.empty()) {
209
0
    cmSystemTools::Error("CMake can not determine linker language for "
210
0
                         "target: " +
211
0
                         this->GeneratorTarget->GetName());
212
0
    return false;
213
0
  }
214
0
  LogMessage("linkLanguage: " + linkLanguage);
215
216
0
  std::string linkLibs;
217
0
  std::string targetFlags;
218
0
  std::string linkFlags;
219
0
  std::string frameworkPath;
220
  // Tested in "RunCMake.StandardLinkDirectories" test.
221
0
  std::string linkPath;
222
223
0
  std::unique_ptr<cmLinkLineComputer> const linkLineComputer =
224
0
    this->GetGlobalGenerator()->CreateLinkLineComputer(
225
0
      this->LocalGenerator,
226
0
      this->GetLocalGenerator()->GetStateSnapshot().GetDirectory());
227
228
0
  this->LocalCommonGenerator->GetTargetFlags(
229
0
    linkLineComputer.get(), Config, linkLibs, targetFlags, linkFlags,
230
0
    frameworkPath, linkPath, this->GeneratorTarget);
231
232
  // cmLocalGenerator::GetStaticLibraryFlags seems to add empty quotes when
233
  // appending "STATIC_LIBRARY_FLAGS_DEBUG"...
234
0
  cmSystemTools::ReplaceString(linkFlags, "\"\"", "");
235
0
  LogMessage("linkLibs: " + linkLibs);
236
0
  LogMessage("targetFlags: " + targetFlags);
237
0
  LogMessage("linkFlags: " + linkFlags);
238
0
  LogMessage("frameworkPath: " + frameworkPath);
239
0
  LogMessage("linkPath: " + linkPath);
240
241
0
  LogMessage("MANIFESTS: " + this->GetManifests(Config));
242
243
0
  cmComputeLinkInformation* linkInfo =
244
0
    this->GeneratorTarget->GetLinkInformation(Config);
245
0
  if (!linkInfo) {
246
0
    return false;
247
0
  }
248
249
  // Tested in "RunCMake.RuntimePath" test.
250
0
  std::string const rpath = linkLineComputer->ComputeRPath(*linkInfo);
251
0
  LogMessage("RPath: " + rpath);
252
253
0
  if (!linkFlags.empty()) {
254
0
    linkFlags += " ";
255
0
  }
256
0
  linkFlags += cmJoin({ rpath, frameworkPath, linkPath }, " ");
257
258
0
  cmStateEnums::TargetType const targetType = this->GeneratorTarget->GetType();
259
  // Add OS X version flags, if any.
260
0
  if (targetType == cmStateEnums::SHARED_LIBRARY ||
261
0
      targetType == cmStateEnums::MODULE_LIBRARY) {
262
0
    this->AppendOSXVerFlag(linkFlags, linkLanguage, "COMPATIBILITY", true);
263
0
    this->AppendOSXVerFlag(linkFlags, linkLanguage, "CURRENT", false);
264
0
  }
265
  // Add Arch flags to link flags for binaries
266
0
  if (targetType == cmStateEnums::SHARED_LIBRARY ||
267
0
      targetType == cmStateEnums::MODULE_LIBRARY ||
268
0
      targetType == cmStateEnums::EXECUTABLE) {
269
0
    this->LocalCommonGenerator->AddArchitectureFlags(
270
0
      linkFlags, this->GeneratorTarget, linkLanguage, Config, arch);
271
0
    this->UseLWYU = this->GetLocalGenerator()->AppendLWYUFlags(
272
0
      linkFlags, this->GetGeneratorTarget(), linkLanguage);
273
0
  }
274
275
0
  cmRulePlaceholderExpander::RuleVariables vars;
276
0
  vars.CMTargetName = this->GeneratorTarget->GetName().c_str();
277
0
  vars.CMTargetType = cmState::GetTargetTypeName(targetType).c_str();
278
0
  vars.Config = Config.c_str();
279
0
  vars.Language = linkLanguage.c_str();
280
0
  std::string const manifests =
281
0
    cmJoin(this->GetManifestsAsFastbuildPath(), " ");
282
0
  vars.Manifests = manifests.c_str();
283
284
0
  std::string const stdLibString = this->Makefile->GetSafeDefinition(
285
0
    cmStrCat("CMAKE_", linkLanguage, "_STANDARD_LIBRARIES"));
286
287
0
  LogMessage(cmStrCat("Target type: ", this->GeneratorTarget->GetType()));
288
0
  if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE ||
289
0
      this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
290
0
      this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
291
0
    vars.Objects = FASTBUILD_1_0_INPUT_PLACEHOLDER;
292
0
    vars.LinkLibraries = stdLibString.c_str();
293
0
  } else {
294
0
    vars.Objects = FASTBUILD_1_INPUT_PLACEHOLDER;
295
0
  }
296
297
0
  vars.ObjectDir = FASTBUILD_DOLLAR_TAG "TargetOutDir" FASTBUILD_DOLLAR_TAG;
298
0
  vars.Target = FASTBUILD_2_INPUT_PLACEHOLDER;
299
300
0
  std::string install_dir;
301
0
  std::string target_so_name;
302
0
  if (this->GeneratorTarget->HasSOName(Config)) {
303
0
    vars.SONameFlag = this->Makefile->GetSONameFlag(
304
0
      this->GeneratorTarget->GetLinkerLanguage(Config));
305
0
    target_so_name =
306
0
      cmGlobalFastbuildGenerator::QuoteIfHasSpaces(targetNames.SharedObject);
307
0
    vars.TargetSOName = target_so_name.c_str();
308
    // Tested in "RunCMake.RuntimePath / RunCMake.INSTALL_NAME_DIR"
309
    // tests.
310
0
    install_dir = this->LocalGenerator->ConvertToOutputFormat(
311
0
      this->GeneratorTarget->GetInstallNameDirForBuildTree(Config),
312
0
      cmOutputConverter::SHELL);
313
0
    vars.TargetInstallNameDir = install_dir.c_str();
314
0
  } else {
315
0
    vars.TargetSOName = "";
316
0
  }
317
0
  vars.TargetPDB = FASTBUILD_DOLLAR_TAG "LinkerPDB" FASTBUILD_DOLLAR_TAG;
318
319
  // Setup the target version.
320
0
  std::string targetVersionMajor;
321
0
  std::string targetVersionMinor;
322
0
  {
323
0
    std::ostringstream majorStream;
324
0
    std::ostringstream minorStream;
325
0
    int major;
326
0
    int minor;
327
0
    this->GeneratorTarget->GetTargetVersion(major, minor);
328
0
    majorStream << major;
329
0
    minorStream << minor;
330
0
    targetVersionMajor = majorStream.str();
331
0
    targetVersionMinor = minorStream.str();
332
0
  }
333
0
  vars.TargetVersionMajor = targetVersionMajor.c_str();
334
0
  vars.TargetVersionMinor = targetVersionMinor.c_str();
335
336
0
  vars.Defines =
337
0
    FASTBUILD_DOLLAR_TAG "CompileDefineFlags" FASTBUILD_DOLLAR_TAG;
338
0
  vars.Flags = targetFlags.c_str();
339
0
  vars.LinkFlags = linkFlags.c_str();
340
0
  vars.LanguageCompileFlags = "";
341
0
  std::string const linker = this->GeneratorTarget->GetLinkerTool(Config);
342
0
  vars.Linker = linker.c_str();
343
0
  std::string const targetSupportPath = this->ConvertToFastbuildPath(
344
0
    this->GetGeneratorTarget()->GetCMFSupportDirectory());
345
0
  vars.TargetSupportDir = targetSupportPath.c_str();
346
347
0
  LogMessage("linkFlags: " + linkFlags);
348
0
  LogMessage("linker: " + linker);
349
350
0
  std::string linkRule = GetLinkCommand();
351
0
  ApplyLinkRuleLauncher(linkRule);
352
0
  RulePlaceholderExpander->ExpandRuleVariables(
353
0
    dynamic_cast<cmLocalFastbuildGenerator*>(this->LocalCommonGenerator),
354
0
    linkRule, vars);
355
356
0
  command = std::move(linkRule);
357
0
  LogMessage(cmStrCat("Expanded link command: ", command));
358
0
  return true;
359
0
}
360
361
void cmFastbuildNormalTargetGenerator::ApplyLinkRuleLauncher(
362
  std::string& command)
363
0
{
364
0
  std::string const val = this->GetLocalGenerator()->GetRuleLauncher(
365
0
    this->GetGeneratorTarget(), "RULE_LAUNCH_LINK", Config);
366
0
  if (cmNonempty(val)) {
367
0
    LogMessage("RULE_LAUNCH_LINK: " + val);
368
0
    command = cmStrCat(val, ' ', command);
369
0
  }
370
0
}
371
372
void cmFastbuildNormalTargetGenerator::ApplyLWYUToLinkerCommand(
373
  FastbuildLinkerNode& linkerNode)
374
0
{
375
0
  cmValue const lwyuCheck =
376
0
    this->Makefile->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK");
377
0
  if (this->UseLWYU && lwyuCheck) {
378
0
    LogMessage("UseLWYU=true");
379
0
    std::string args = " -E __run_co_compile --lwyu=";
380
0
    args += this->GetLocalGenerator()->EscapeForShell(*lwyuCheck);
381
382
0
    args += cmStrCat(
383
0
      " --source=",
384
0
      this->ConvertToFastbuildPath(this->GetGeneratorTarget()->GetFullPath(
385
0
        Config, cmStateEnums::RuntimeBinaryArtifact,
386
0
        /*realname=*/true)));
387
388
0
    LogMessage("LWUY args: " + args);
389
0
    linkerNode.LinkerStampExe = cmSystemTools::GetCMakeCommand();
390
0
    linkerNode.LinkerStampExeArgs = std::move(args);
391
0
  }
392
0
}
393
394
std::string cmFastbuildNormalTargetGenerator::ComputeDefines(
395
  cmSourceFile const& srcFile)
396
0
{
397
0
  std::string const language = srcFile.GetLanguage();
398
0
  std::set<std::string> defines;
399
0
  cmGeneratorExpressionInterpreter genexInterpreter(
400
0
    this->GetLocalGenerator(), Config, this->GeneratorTarget, language);
401
402
0
  if (auto compile_defs = srcFile.GetProperty(COMPILE_DEFINITIONS)) {
403
0
    this->GetLocalGenerator()->AppendDefines(
404
0
      defines, genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS));
405
0
  }
406
407
0
  std::string defPropName = "COMPILE_DEFINITIONS_";
408
0
  defPropName += cmSystemTools::UpperCase(Config);
409
0
  if (auto config_compile_defs = srcFile.GetProperty(defPropName)) {
410
0
    this->GetLocalGenerator()->AppendDefines(
411
0
      defines,
412
0
      genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS));
413
0
  }
414
415
0
  std::string definesString = this->GetDefines(language, Config);
416
0
  LogMessage(cmStrCat("TARGET DEFINES = ", definesString));
417
0
  this->GetLocalGenerator()->JoinDefines(defines, definesString, language);
418
419
0
  LogMessage(cmStrCat("DEFINES = ", definesString));
420
0
  return definesString;
421
0
}
422
423
void cmFastbuildNormalTargetGenerator::ComputePCH(
424
  cmSourceFile const& srcFile, FastbuildObjectListNode& node,
425
  std::set<std::string>& createdPCH)
426
0
{
427
0
  if (srcFile.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
428
0
    return;
429
0
  }
430
  // We have already computed PCH for this node.
431
0
  if (!node.PCHOptions.empty() || !node.PCHInputFile.empty() ||
432
0
      !node.PCHOutputFile.empty()) {
433
0
    return;
434
0
  }
435
0
  std::string const language = srcFile.GetLanguage();
436
0
  cmGeneratorExpressionInterpreter genexInterpreter(
437
0
    this->GetLocalGenerator(), Config, this->GeneratorTarget, language);
438
439
  //.cxx
440
0
  std::string const pchSource =
441
0
    this->GeneratorTarget->GetPchSource(Config, language);
442
  //.hxx
443
0
  std::string const pchHeader =
444
0
    this->GeneratorTarget->GetPchHeader(Config, language);
445
  //.pch
446
0
  std::string const pchFile =
447
0
    this->GeneratorTarget->GetPchFile(Config, language);
448
449
0
  if (pchHeader.empty() || pchFile.empty()) {
450
0
    return;
451
0
  }
452
  // In "RunCMake.GenEx-TARGET_PROPERTY" test we call set
453
  // CMAKE_PCH_EXTENSION="", so pchHeader becomes same as pchFile...
454
0
  if (pchHeader == pchFile) {
455
0
    LogMessage("pchHeader == pchFile > skipping");
456
0
    LogMessage("pchHeader: " + pchHeader);
457
0
    LogMessage("pchFile: " + pchFile);
458
0
    return;
459
0
  }
460
461
0
  node.PCHOutputFile =
462
0
    this->GetGlobalGenerator()->ConvertToFastbuildPath(pchFile);
463
  // Tell the ObjectList how to use PCH.
464
0
  std::string const pchUseOption =
465
0
    this->GeneratorTarget->GetPchUseCompileOptions(Config, language);
466
0
  LogMessage(cmStrCat("pchUseOption: ", pchUseOption));
467
468
0
  std::string origCompileOptions = node.CompilerOptions;
469
0
  for (auto const& opt :
470
0
       cmList{ genexInterpreter.Evaluate(pchUseOption, COMPILE_OPTIONS) }) {
471
0
    node.CompilerOptions += " ";
472
0
    node.CompilerOptions += opt;
473
0
  }
474
475
0
  if (!createdPCH.emplace(node.PCHOutputFile).second) {
476
0
    LogMessage(node.PCHOutputFile + " is already created by this target");
477
0
    return;
478
0
  }
479
480
  // Short circuit if the PCH has already been created by another target.
481
0
  if (!this->GeneratorTarget->GetSafeProperty("PRECOMPILE_HEADERS_REUSE_FROM")
482
0
         .empty()) {
483
0
    LogMessage(cmStrCat("PCH: ", node.PCHOutputFile,
484
0
                        " already created by another target"));
485
0
    return;
486
0
  }
487
488
0
  node.PCHInputFile =
489
0
    this->GetGlobalGenerator()->ConvertToFastbuildPath(pchSource);
490
491
0
  std::string const pchCreateOptions =
492
0
    this->GeneratorTarget->GetPchCreateCompileOptions(Config, language);
493
0
  LogMessage(cmStrCat("pchCreateOptions: ", pchCreateOptions));
494
0
  char const* sep = "";
495
0
  for (auto const& opt : cmList{
496
0
         genexInterpreter.Evaluate(pchCreateOptions, COMPILE_OPTIONS) }) {
497
0
    node.PCHOptions += sep;
498
0
    node.PCHOptions += opt;
499
0
    sep = " ";
500
0
  }
501
502
  // Reuse compiler options for PCH options.
503
0
  node.PCHOptions += origCompileOptions;
504
0
  if (this->Makefile->GetSafeDefinition("CMAKE_" + language +
505
0
                                        "_COMPILER_ID") == "MSVC") {
506
0
    cmSystemTools::ReplaceString(node.PCHOptions,
507
0
                                 FASTBUILD_2_INPUT_PLACEHOLDER,
508
0
                                 FASTBUILD_3_INPUT_PLACEHOLDER);
509
0
  }
510
511
0
  LogMessage("PCH Source: " + pchSource);
512
0
  LogMessage("node.PCHInputFile: " + node.PCHInputFile);
513
0
  LogMessage("node.PCHOutputFile: " + node.PCHOutputFile);
514
0
  LogMessage("node.PCHOptions: " + node.PCHOptions);
515
0
  LogMessage("node.CompilerOptions: " + node.CompilerOptions);
516
0
}
517
518
void cmFastbuildNormalTargetGenerator::EnsureDirectoryExists(
519
  std::string const& path) const
520
0
{
521
0
  if (cmSystemTools::FileIsFullPath(path.c_str())) {
522
0
    cmSystemTools::MakeDirectory(path.c_str());
523
0
  } else {
524
0
    auto* gg = this->GetGlobalGenerator();
525
0
    std::string fullPath = gg->GetCMakeInstance()->GetHomeOutputDirectory();
526
    // Also ensures there is a trailing slash.
527
0
    fullPath += path;
528
0
    cmSystemTools::MakeDirectory(fullPath);
529
0
  }
530
0
}
531
532
void cmFastbuildNormalTargetGenerator::EnsureParentDirectoryExists(
533
  std::string const& path) const
534
0
{
535
0
  this->EnsureDirectoryExists(cmSystemTools::GetParentDirectory(path));
536
0
}
537
538
std::vector<std::string>
539
cmFastbuildNormalTargetGenerator::GetManifestsAsFastbuildPath() const
540
0
{
541
0
  std::vector<cmSourceFile const*> manifest_srcs;
542
0
  this->GeneratorTarget->GetManifests(manifest_srcs, Config);
543
0
  std::vector<std::string> manifests;
544
0
  manifests.reserve(manifest_srcs.size());
545
0
  for (auto& manifest_src : manifest_srcs) {
546
0
    std::string str = this->ConvertToFastbuildPath(
547
0
      cmSystemTools::ConvertToOutputPath(manifest_src->GetFullPath()));
548
0
    LogMessage("Manifest: " + str);
549
0
    manifests.emplace_back(std::move(str));
550
0
  }
551
552
0
  return manifests;
553
0
}
554
555
void cmFastbuildNormalTargetGenerator::GenerateModuleDefinitionInfo(
556
  FastbuildTarget& target) const
557
0
{
558
0
  cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
559
0
    GeneratorTarget->GetModuleDefinitionInfo(Config);
560
0
  if (mdi && mdi->DefFileGenerated) {
561
0
    FastbuildExecNode execNode;
562
0
    execNode.Name = target.Name + "-def-files";
563
0
    execNode.ExecExecutable = cmSystemTools::GetCMakeCommand();
564
0
    execNode.ExecArguments =
565
0
      cmStrCat("-E __create_def ", FASTBUILD_2_INPUT_PLACEHOLDER, ' ',
566
0
               FASTBUILD_1_INPUT_PLACEHOLDER);
567
0
    std::string const obj_list_file = mdi->DefFile + ".objs";
568
569
0
    auto const nm_executable = GetMakefile()->GetDefinition("CMAKE_NM");
570
0
    if (!nm_executable.IsEmpty()) {
571
0
      execNode.ExecArguments += " --nm=";
572
0
      execNode.ExecArguments += ConvertToFastbuildPath(*nm_executable);
573
0
    }
574
0
    execNode.ExecOutput = ConvertToFastbuildPath(mdi->DefFile);
575
0
    execNode.ExecInput.push_back(ConvertToFastbuildPath(obj_list_file));
576
577
    // RunCMake.AutoExportDll
578
0
    for (auto const& objList : target.ObjectListNodes) {
579
0
      execNode.PreBuildDependencies.emplace(objList.Name);
580
0
    }
581
    // Tested in "RunCMake.AutoExportDll" / "ModuleDefinition" tests.
582
0
    for (auto& linkerNode : target.LinkerNode) {
583
0
      linkerNode.Libraries2.emplace_back(execNode.Name);
584
0
    }
585
586
0
    target.PreLinkExecNodes.Nodes.emplace_back(std::move(execNode));
587
588
    // create a list of obj files for the -E __create_def to read
589
0
    cmGeneratedFileStream fout(obj_list_file);
590
    // Since we generate this file once during configuration, we should not
591
    // remove it when "clean" is built.
592
    // Tested in "RunCMake.AutoExportDll" / "ModuleDefinition" tests.
593
0
    this->GetGlobalGenerator()->AllFilesToKeep.insert(obj_list_file);
594
595
0
    if (mdi->WindowsExportAllSymbols) {
596
0
      std::vector<cmSourceFile const*> objectSources;
597
0
      GeneratorTarget->GetObjectSources(objectSources, Config);
598
0
      std::map<cmSourceFile const*, cmObjectLocations> mapping;
599
0
      for (cmSourceFile const* it : objectSources) {
600
0
        mapping[it];
601
0
      }
602
0
      GeneratorTarget->LocalGenerator->ComputeObjectFilenames(mapping, Config,
603
0
                                                              GeneratorTarget);
604
605
0
      std::vector<std::string> objs;
606
0
      for (cmSourceFile const* it : objectSources) {
607
0
        auto const& v = mapping[it];
608
0
        LogMessage("Obj source : " + v.LongLoc.GetPath());
609
0
        std::string objFile = this->ConvertToFastbuildPath(
610
0
          GeneratorTarget->GetObjectDirectory(Config) + v.LongLoc.GetPath());
611
0
        objFile = cmSystemTools::ConvertToOutputPath(objFile);
612
0
        LogMessage("objFile path: " + objFile);
613
0
        objs.push_back(objFile);
614
0
      }
615
616
0
      std::vector<cmSourceFile const*> externalObjectSources;
617
0
      GeneratorTarget->GetExternalObjects(externalObjectSources, Config);
618
0
      for (cmSourceFile const* it : externalObjectSources) {
619
0
        objs.push_back(cmSystemTools::ConvertToOutputPath(
620
0
          this->ConvertToFastbuildPath(it->GetFullPath())));
621
0
      }
622
623
0
      for (std::string const& objFile : objs) {
624
0
        if (cmHasLiteralSuffix(objFile, ".obj")) {
625
0
          fout << objFile << "\n";
626
0
        }
627
0
      }
628
0
    }
629
0
    for (cmSourceFile const* src : mdi->Sources) {
630
0
      fout << src->GetFullPath() << "\n";
631
0
    }
632
0
  }
633
0
}
634
635
void cmFastbuildNormalTargetGenerator::AddPrebuildDeps(
636
  FastbuildTarget& target) const
637
0
{
638
  // All ObjectLists should wait for PRE_BUILD.
639
0
  for (FastbuildObjectListNode& node : target.ObjectListNodes) {
640
0
    if (!target.PreBuildExecNodes.Name.empty()) {
641
0
      node.PreBuildDependencies.emplace(target.PreBuildExecNodes.Name);
642
0
    }
643
0
    if (!target.ExecNodes.Name.empty()) {
644
0
      node.PreBuildDependencies.emplace(target.ExecNodes.Name);
645
0
    }
646
0
  }
647
0
  for (auto& linkerNode : target.LinkerNode) {
648
    // Wait for 'PRE_BUILD' custom commands.
649
0
    if (!target.PreBuildExecNodes.Name.empty()) {
650
0
      linkerNode.PreBuildDependencies.emplace(target.PreBuildExecNodes.Name);
651
0
    }
652
653
    // Wait for regular custom commands.
654
0
    if (!target.ExecNodes.Name.empty()) {
655
0
      linkerNode.PreBuildDependencies.emplace(target.ExecNodes.Name);
656
0
    }
657
    // All targets that we depend on must be prebuilt.
658
0
    if (!target.DependenciesAlias.PreBuildDependencies.empty()) {
659
0
      linkerNode.PreBuildDependencies.emplace(target.DependenciesAlias.Name);
660
0
    }
661
0
  }
662
0
}
663
664
std::set<std::string> cmFastbuildNormalTargetGenerator::GetLanguages()
665
0
{
666
0
  std::set<std::string> result;
667
0
  this->GetGeneratorTarget()->GetLanguages(result, Config);
668
0
  for (std::string const& lang : result) {
669
0
    this->GetGlobalGenerator()->AddCompiler(lang, this->GetMakefile());
670
0
  }
671
0
  LogMessage("Languages: " + cmJoin(result, ", "));
672
0
  return result;
673
0
}
674
675
std::unordered_map<std::string, std::string>
676
cmFastbuildNormalTargetGenerator::GetCompileObjectCommand() const
677
0
{
678
0
  std::unordered_map<std::string, std::string> result;
679
0
  result.reserve(Languages.size());
680
0
  for (std::string const& lang : Languages) {
681
0
    std::vector<std::string> commands;
682
0
    std::string cmakeVar;
683
0
    cmakeVar = "CMAKE_";
684
0
    cmakeVar += lang;
685
0
    cmakeVar += "_COMPILE_OBJECT";
686
687
0
    std::string cmakeValue =
688
0
      LocalCommonGenerator->GetMakefile()->GetSafeDefinition(cmakeVar);
689
690
0
    LogMessage(cmakeVar.append(" = ").append(cmakeValue));
691
692
0
    result[lang] = std::move(cmakeValue);
693
0
  }
694
0
  return result;
695
0
}
696
std::string cmFastbuildNormalTargetGenerator::GetCudaCompileMode() const
697
0
{
698
0
  if (Languages.find("CUDA") == Languages.end()) {
699
0
    return {};
700
0
  }
701
  // TODO: unify it with makefile / ninja generators.
702
0
  std::string cudaCompileMode;
703
0
  if (this->GeneratorTarget->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION")) {
704
0
    std::string const& rdcFlag =
705
0
      this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG");
706
0
    cudaCompileMode = cmStrCat(cudaCompileMode, rdcFlag, ' ');
707
0
  }
708
0
  static std::array<cm::string_view, 4> const compileModes{
709
0
    { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s }
710
0
  };
711
0
  bool useNormalCompileMode = true;
712
0
  for (cm::string_view mode : compileModes) {
713
0
    auto propName = cmStrCat("CUDA_", mode, "_COMPILATION");
714
0
    auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG");
715
0
    if (this->GeneratorTarget->GetPropertyAsBool(propName)) {
716
0
      std::string const& flag = this->Makefile->GetRequiredDefinition(defName);
717
0
      cudaCompileMode = cmStrCat(cudaCompileMode, flag);
718
0
      useNormalCompileMode = false;
719
0
      break;
720
0
    }
721
0
  }
722
0
  if (useNormalCompileMode) {
723
0
    std::string const& wholeFlag =
724
0
      this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG");
725
0
    cudaCompileMode = cmStrCat(cudaCompileMode, wholeFlag);
726
0
  }
727
0
  return cudaCompileMode;
728
0
}
729
730
std::string cmFastbuildNormalTargetGenerator::GetLinkCommand() const
731
0
{
732
0
  std::string const& linkLanguage = GeneratorTarget->GetLinkerLanguage(Config);
733
0
  std::string linkCmdVar =
734
0
    GeneratorTarget->GetCreateRuleVariable(linkLanguage, Config);
735
0
  std::string res = this->Makefile->GetSafeDefinition(linkCmdVar);
736
0
  if (res.empty() &&
737
0
      this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
738
0
    linkCmdVar = linkCmdVar =
739
0
      cmStrCat("CMAKE_", linkLanguage, "_ARCHIVE_CREATE");
740
0
    res = this->Makefile->GetSafeDefinition(linkCmdVar);
741
0
  }
742
0
  LogMessage("Link rule: " + cmStrCat(linkCmdVar, " = ", res));
743
0
  return res;
744
0
}
745
746
void cmFastbuildNormalTargetGenerator::AddCompilerLaunchersForLanguages()
747
0
{
748
  // General rule for all languages.
749
0
  std::string const launchCompile = this->GetLocalGenerator()->GetRuleLauncher(
750
0
    this->GetGeneratorTarget(), "RULE_LAUNCH_COMPILE", Config);
751
  // See if we need to use a compiler launcher like ccache or distcc
752
0
  for (std::string const& language : Languages) {
753
0
    std::string const compilerLauncher =
754
0
      cmCommonTargetGenerator::GetCompilerLauncher(language, Config);
755
0
    LogMessage("compilerLauncher: " + compilerLauncher);
756
0
    std::vector<std::string> expanded;
757
0
    cmExpandList(compilerLauncher, expanded);
758
759
0
    if (!expanded.empty()) {
760
0
      std::string const exe = expanded[0];
761
0
      expanded.erase(expanded.begin());
762
0
      this->GetGlobalGenerator()->AddLauncher(FASTBUILD_LAUNCHER_PREFIX, exe,
763
0
                                              language, cmJoin(expanded, " "));
764
0
    } else if (!launchCompile.empty()) {
765
0
      std::string exe;
766
0
      std::string args;
767
0
      cmSystemTools::SplitProgramFromArgs(launchCompile, exe, args);
768
0
      this->GetGlobalGenerator()->AddLauncher(FASTBUILD_LAUNCHER_PREFIX, exe,
769
0
                                              language, args);
770
0
    }
771
0
  }
772
0
}
773
void cmFastbuildNormalTargetGenerator::AddLinkerLauncher()
774
0
{
775
0
  std::string const linkerLauncher =
776
0
    cmCommonTargetGenerator::GetLinkerLauncher(Config);
777
0
  std::vector<std::string> args;
778
#ifdef _WIN32
779
  cmSystemTools::ParseWindowsCommandLine(linkerLauncher.c_str(), args);
780
#else
781
0
  cmSystemTools::ParseUnixCommandLine(linkerLauncher.c_str(), args);
782
0
#endif
783
0
  if (!args.empty()) {
784
0
    std::string const exe = std::move(args[0]);
785
0
    args.erase(args.begin());
786
0
    this->GetGlobalGenerator()->AddLauncher(
787
0
      FASTBUILD_LINKER_LAUNCHER_PREFIX, exe,
788
0
      this->GeneratorTarget->GetLinkerLanguage(Config), cmJoin(args, " "));
789
0
  }
790
0
}
791
void cmFastbuildNormalTargetGenerator::AddCMakeLauncher()
792
0
{
793
  // Add CMake launcher (might be used for static analysis).
794
0
  this->GetGlobalGenerator()->AddLauncher(FASTBUILD_LAUNCHER_PREFIX,
795
0
                                          cmSystemTools::GetCMakeCommand(),
796
0
                                          CMAKE_LANGUAGE, "");
797
0
}
798
799
void cmFastbuildNormalTargetGenerator::ComputePaths(
800
  FastbuildTarget& target) const
801
0
{
802
0
  std::string const objPath = GetGeneratorTarget()->GetSupportDirectory();
803
0
  EnsureDirectoryExists(objPath);
804
0
  target.Variables["TargetOutDir"] =
805
0
    cmSystemTools::ConvertToOutputPath(this->ConvertToFastbuildPath(objPath));
806
807
0
  if (GeneratorTarget->GetType() <= cmStateEnums::MODULE_LIBRARY) {
808
0
    std::string const pdbDir = GeneratorTarget->GetPDBDirectory(Config);
809
0
    LogMessage("GetPDBDirectory: " + pdbDir);
810
0
    EnsureDirectoryExists(pdbDir);
811
0
    std::string const linkerPDB =
812
0
      cmStrCat(pdbDir, '/', this->GeneratorTarget->GetPDBName(Config));
813
814
0
    if (!linkerPDB.empty()) {
815
0
      target.Variables["LinkerPDB"] = cmSystemTools::ConvertToOutputPath(
816
0
        this->ConvertToFastbuildPath(linkerPDB));
817
0
    }
818
0
  }
819
0
  std::string const compilerPDB = this->ComputeTargetCompilePDB(this->Config);
820
0
  if (!compilerPDB.empty()) {
821
0
    LogMessage("ComputeTargetCompilePDB: " + compilerPDB);
822
0
    std::string compilerPDBArg = cmSystemTools::ConvertToOutputPath(
823
0
      this->ConvertToFastbuildPath(compilerPDB));
824
0
    if (cmHasSuffix(compilerPDB, '/')) {
825
      // The compiler will choose the .pdb file name.
826
0
      this->EnsureDirectoryExists(compilerPDB);
827
      // ConvertToFastbuildPath dropped the trailing slash.  Add it back.
828
      // We do this after ConvertToOutputPath so that we can use a forward
829
      // slash in the case that the argument is quoted.
830
0
      if (cmHasSuffix(compilerPDBArg, '"')) {
831
        // A quoted trailing backslash requires escaping, e.g., `/Fd"dir\\"`,
832
        // but fbuild does not parse such arguments correctly as of 1.15.
833
        // Always use a forward slash.
834
0
        compilerPDBArg.insert(compilerPDBArg.size() - 1, 1, '/');
835
0
      } else {
836
        // An unquoted trailing slash or backslash is fine.
837
0
        compilerPDBArg.push_back(kPATH_SLASH);
838
0
      }
839
0
    } else {
840
      // We have an explicit .pdb path with file name.
841
0
      this->EnsureParentDirectoryExists(compilerPDB);
842
0
    }
843
0
    target.Variables["CompilerPDB"] = std::move(compilerPDBArg);
844
0
  }
845
0
  std::string const impLibFullPath =
846
0
    GeneratorTarget->GetFullPath(Config, cmStateEnums::ImportLibraryArtifact);
847
0
  std::string impLibFile = ConvertToFastbuildPath(impLibFullPath);
848
0
  cmSystemTools::MakeDirectory(cmSystemTools::GetFilenamePath(impLibFullPath));
849
0
  if (!impLibFile.empty()) {
850
0
    cmSystemTools::ConvertToOutputSlashes(impLibFile);
851
0
    target.Variables["TargetOutputImplib"] = std::move(impLibFile);
852
0
  }
853
0
}
854
855
void cmFastbuildNormalTargetGenerator::Generate()
856
0
{
857
0
  this->GeneratorTarget->CheckCxxModuleStatus(Config);
858
859
0
  FastbuildTarget fastbuildTarget;
860
0
  auto const addUtilDepToTarget = [&fastbuildTarget](std::string depName) {
861
0
    FastbuildTargetDep dep{ depName };
862
0
    dep.Type = FastbuildTargetDepType::UTIL;
863
0
    fastbuildTarget.PreBuildDependencies.emplace(std::move(dep));
864
0
  };
865
866
0
  fastbuildTarget.Name = GetTargetName();
867
0
  fastbuildTarget.BaseName = this->GeneratorTarget->GetName();
868
869
0
  LogMessage("<-------------->");
870
0
  LogMessage("Generate target: " + fastbuildTarget.Name);
871
0
  LogMessage("Config: " + Config);
872
873
0
  LogMessage("Deps: ");
874
0
  for (cmTargetDepend const& dep : TargetDirectDependencies) {
875
0
    auto const tname = dep->GetName();
876
0
    LogMessage(tname);
877
0
    FastbuildTargetDep targetDep{ tname };
878
0
    if (dep->GetType() == cmStateEnums::OBJECT_LIBRARY) {
879
0
      targetDep.Type = FastbuildTargetDepType::ORDER_ONLY;
880
0
    }
881
0
    fastbuildTarget.PreBuildDependencies.emplace(std::move(targetDep));
882
0
  }
883
884
0
  ComputePaths(fastbuildTarget);
885
0
  AddCompilerLaunchersForLanguages();
886
0
  AddLinkerLauncher();
887
0
  AddCMakeLauncher();
888
889
0
  for (auto& cc : GenerateCommands(FastbuildBuildStep::PRE_BUILD).Nodes) {
890
0
    fastbuildTarget.PreBuildExecNodes.PreBuildDependencies.emplace(cc.Name);
891
0
    addUtilDepToTarget(cc.Name);
892
0
    this->GetGlobalGenerator()->AddTarget(std::move(cc));
893
0
  }
894
0
  for (auto& cc : GenerateCommands(FastbuildBuildStep::PRE_LINK).Nodes) {
895
0
    cc.PreBuildDependencies.emplace(fastbuildTarget.Name +
896
0
                                    FASTBUILD_DEPS_ARTIFACTS_ALIAS_POSTFIX);
897
0
    fastbuildTarget.PreLinkExecNodes.Nodes.emplace_back(std::move(cc));
898
0
  }
899
0
  for (auto& cc : GenerateCommands(FastbuildBuildStep::REST).Nodes) {
900
0
    fastbuildTarget.ExecNodes.PreBuildDependencies.emplace(cc.Name);
901
0
    this->GetGlobalGenerator()->AddTarget(std::move(cc));
902
0
  }
903
0
  for (auto& cc : GenerateCommands(FastbuildBuildStep::POST_BUILD).Nodes) {
904
0
    fastbuildTarget.PostBuildExecNodes.Alias.PreBuildDependencies.emplace(
905
0
      cc.Name);
906
0
    fastbuildTarget.PostBuildExecNodes.Nodes.emplace_back(std::move(cc));
907
0
  }
908
909
0
  GenerateObjects(fastbuildTarget);
910
911
0
  std::vector<std::string> objectDepends;
912
0
  AddObjectDependencies(fastbuildTarget, objectDepends);
913
914
0
  GenerateCudaDeviceLink(fastbuildTarget);
915
916
0
  GenerateLink(fastbuildTarget, objectDepends);
917
918
0
  if (fastbuildTarget.LinkerNode.size() > 1) {
919
0
    if (!this->GeneratorTarget->IsApple()) {
920
0
      cmSystemTools::Error(
921
0
        "Can't handle more than 1 arch on non-Apple target");
922
0
      return;
923
0
    }
924
0
    AddLipoCommand(fastbuildTarget);
925
0
  }
926
0
  fastbuildTarget.CopyNodes = std::move(this->CopyNodes);
927
928
  // Generate symlink commands if real output name differs from "expected".
929
0
  for (auto& symlink : GetSymlinkExecs()) {
930
0
    fastbuildTarget.PostBuildExecNodes.Alias.PreBuildDependencies.emplace(
931
0
      symlink.Name);
932
0
    fastbuildTarget.PostBuildExecNodes.Nodes.emplace_back(std::move(symlink));
933
0
  }
934
0
  {
935
0
    auto appleTextStubCommand = GetAppleTextStubCommand();
936
0
    if (!appleTextStubCommand.Name.empty()) {
937
0
      fastbuildTarget.PostBuildExecNodes.Alias.PreBuildDependencies.emplace(
938
0
        appleTextStubCommand.Name);
939
0
      fastbuildTarget.PostBuildExecNodes.Nodes.emplace_back(
940
0
        std::move(appleTextStubCommand));
941
0
    }
942
0
  }
943
944
0
  AddPrebuildDeps(fastbuildTarget);
945
946
0
  fastbuildTarget.IsGlobal =
947
0
    GeneratorTarget->GetType() == cmStateEnums::GLOBAL_TARGET;
948
0
  fastbuildTarget.ExcludeFromAll =
949
0
    this->GetGlobalGenerator()->IsExcluded(GeneratorTarget);
950
0
  if (GeneratorTarget->GetPropertyAsBool("DONT_DISTRIBUTE")) {
951
0
    fastbuildTarget.AllowDistribution = false;
952
0
  }
953
954
0
  GenerateModuleDefinitionInfo(fastbuildTarget);
955
  // Needs to be called after we've added all PRE-LINK steps (like creation of
956
  // .def files on Windows).
957
0
  AddLinkerNodeDependencies(fastbuildTarget);
958
959
  // Must be called after "GenerateObjects", since it also adds Prebuild deps
960
  // to it.
961
  // Also after "GenerateModuleDefinitionInfo", since uses PreLinkExecNodes.
962
963
0
  fastbuildTarget.GenerateAliases();
964
0
  if (!fastbuildTarget.ExecNodes.PreBuildDependencies.empty()) {
965
0
    fastbuildTarget.DependenciesAlias.PreBuildDependencies.emplace(
966
0
      fastbuildTarget.ExecNodes.Name);
967
0
  }
968
969
0
  fastbuildTarget.Hidden = false;
970
971
0
  fastbuildTarget.BasePath = this->GetMakefile()->GetCurrentSourceDirectory();
972
973
0
  this->GetGlobalGenerator()->AddIDEProject(fastbuildTarget, Config);
974
975
0
  AddStampExeIfApplicable(fastbuildTarget);
976
977
  // size 1 means that it's not a multi-arch lib (which can only be the case on
978
  // Darwin).
979
0
  if (fastbuildTarget.LinkerNode.size() == 1 &&
980
0
      fastbuildTarget.LinkerNode[0].Type ==
981
0
        FastbuildLinkerNode::STATIC_LIBRARY &&
982
0
      !fastbuildTarget.PostBuildExecNodes.Nodes.empty()) {
983
0
    ProcessPostBuildForStaticLib(fastbuildTarget);
984
0
  }
985
986
0
  AdditionalCleanFiles();
987
988
0
  if (!fastbuildTarget.DependenciesAlias.PreBuildDependencies.empty()) {
989
0
    for (FastbuildObjectListNode& objListNode :
990
0
         fastbuildTarget.ObjectListNodes) {
991
0
      objListNode.PreBuildDependencies.emplace(
992
0
        fastbuildTarget.DependenciesAlias.Name);
993
0
    }
994
0
    for (auto& linkerNode : fastbuildTarget.LinkerNode) {
995
0
      linkerNode.PreBuildDependencies.emplace(
996
0
        fastbuildTarget.DependenciesAlias.Name);
997
0
    }
998
0
  }
999
1000
0
  this->GetGlobalGenerator()->AddTarget(std::move(fastbuildTarget));
1001
0
}
1002
1003
void cmFastbuildNormalTargetGenerator::ProcessManifests(
1004
  FastbuildLinkerNode& linkerNode) const
1005
0
{
1006
0
  if (this->GetGlobalGenerator()->GetCMakeInstance()->GetIsInTryCompile()) {
1007
0
    return;
1008
0
  }
1009
0
  auto manifests = this->GetManifestsAsFastbuildPath();
1010
0
  if (manifests.empty()) {
1011
0
    return;
1012
0
  }
1013
  // Manifests should always be in .Libraries2, so we re-link when needed.
1014
  // Tested in RunCMake.BuildDepends
1015
0
  for (auto const& manifest : manifests) {
1016
0
    linkerNode.Libraries2.emplace_back(manifest);
1017
0
  }
1018
1019
0
  if (this->Makefile->GetSafeDefinition("CMAKE_C_COMPILER_ID") != "MSVC") {
1020
0
    return;
1021
0
  }
1022
1023
0
  for (auto const& manifest : manifests) {
1024
0
    linkerNode.LinkerOptions =
1025
0
      cmStrCat("/MANIFESTINPUT:", manifest, ' ', linkerNode.LinkerOptions);
1026
0
  }
1027
  // /MANIFESTINPUT only works with /MANIFEST:EMBED
1028
0
  linkerNode.LinkerOptions =
1029
0
    cmStrCat("/MANIFEST:EMBED ", linkerNode.LinkerOptions);
1030
0
}
1031
1032
void cmFastbuildNormalTargetGenerator::AddStampExeIfApplicable(
1033
  FastbuildTarget& fastbuildTarget) const
1034
0
{
1035
0
  LogMessage("AddStampExeIfApplicable(...)");
1036
0
  if (fastbuildTarget.LinkerNode.empty() ||
1037
0
      (fastbuildTarget.LinkerNode[0].Type != FastbuildLinkerNode::EXECUTABLE &&
1038
0
       fastbuildTarget.LinkerNode[0].Type !=
1039
0
         FastbuildLinkerNode::SHARED_LIBRARY)) {
1040
0
    return;
1041
0
  }
1042
  // File which executes all POST_BUILD steps.
1043
  // We use it in .LinkerStampExeArgs in order to run POST_BUILD steps after
1044
  // the compilation.
1045
0
  if (!fastbuildTarget.PostBuildExecNodes.Nodes.empty()) {
1046
0
    std::string const AllPostBuildExecsScriptFile =
1047
0
      cmStrCat(this->Makefile->GetHomeOutputDirectory(), "/CMakeFiles/",
1048
0
               fastbuildTarget.Name,
1049
0
               "-all-postbuild-commands" FASTBUILD_SCRIPT_FILE_EXTENSION);
1050
1051
0
    CollapseAllExecsIntoOneScriptfile(
1052
0
      AllPostBuildExecsScriptFile, fastbuildTarget.PostBuildExecNodes.Nodes);
1053
0
    auto& linkerNode = fastbuildTarget.LinkerNode.back();
1054
    // On macOS, a target may have multiple linker nodes (e.g., for different
1055
    // architectures). In that case, add the POST_BUILD step to only one node
1056
    // to avoid running lipo multiple times.
1057
0
    linkerNode.LinkerStampExe =
1058
0
      cmGlobalFastbuildGenerator::GetExternalShellExecutable();
1059
0
    linkerNode.LinkerStampExeArgs = FASTBUILD_SCRIPT_FILE_ARG;
1060
0
    linkerNode.LinkerStampExeArgs +=
1061
0
      cmGlobalFastbuildGenerator::QuoteIfHasSpaces(
1062
0
        AllPostBuildExecsScriptFile);
1063
1064
0
  } else {
1065
0
    LogMessage("No POST_BUILD steps for target: " + fastbuildTarget.Name);
1066
0
  }
1067
0
}
1068
1069
void cmFastbuildNormalTargetGenerator::ProcessPostBuildForStaticLib(
1070
  FastbuildTarget& fastbuildTarget) const
1071
0
{
1072
  // "Library" nodes do not have "LinkerStampExe" property, so we need to be
1073
  // clever here: create an alias that will refer to the binary as well as to
1074
  // all post-build steps. Also, make sure that post-build steps depend on the
1075
  // binary itself.
1076
0
  LogMessage("ProcessPostBuildForStaticLib(...)");
1077
0
  FastbuildAliasNode alias;
1078
0
  alias.Name = std::move(fastbuildTarget.LinkerNode[0].Name);
1079
0
  for (FastbuildExecNode& postBuildExec :
1080
0
       fastbuildTarget.PostBuildExecNodes.Nodes) {
1081
0
    postBuildExec.PreBuildDependencies.emplace(
1082
0
      fastbuildTarget.LinkerNode[0].LinkerOutput);
1083
0
    alias.PreBuildDependencies.emplace(postBuildExec.Name);
1084
0
  }
1085
0
  fastbuildTarget.AliasNodes.emplace_back(std::move(alias));
1086
0
}
1087
1088
void cmFastbuildNormalTargetGenerator::CollapseAllExecsIntoOneScriptfile(
1089
  std::string const& scriptFileName,
1090
  std::vector<FastbuildExecNode> const& execs) const
1091
0
{
1092
0
  cmsys::ofstream scriptFile(scriptFileName.c_str());
1093
0
  if (!scriptFile.is_open()) {
1094
0
    cmSystemTools::Error("Failed to open: " + scriptFileName);
1095
0
    return;
1096
0
  }
1097
0
  LogMessage("Writing collapsed Execs to " + scriptFileName);
1098
0
  auto const shell = cmGlobalFastbuildGenerator::GetExternalShellExecutable();
1099
0
  for (auto const& exec : execs) {
1100
0
    if (exec.ScriptFile.empty()) {
1101
0
      scriptFile << cmSystemTools::ConvertToOutputPath(exec.ExecExecutable)
1102
0
                 << " " << exec.ExecArguments << '\n';
1103
0
    } else {
1104
#if defined(_WIN32)
1105
      scriptFile << "call "
1106
                 << cmSystemTools::ConvertToWindowsOutputPath(exec.ScriptFile)
1107
                 << '\n';
1108
#else
1109
0
      scriptFile << cmSystemTools::ConvertToOutputPath(shell) << " "
1110
0
                 << cmSystemTools::ConvertToOutputPath(exec.ScriptFile)
1111
0
                 << '\n';
1112
0
#endif
1113
0
    }
1114
0
  }
1115
0
}
1116
1117
std::string cmFastbuildNormalTargetGenerator::ComputeCodeCheckOptions(
1118
  cmSourceFile const& srcFile)
1119
0
{
1120
0
  cmValue const srcSkipCodeCheckVal = srcFile.GetProperty("SKIP_LINTING");
1121
0
  bool const skipCodeCheck = srcSkipCodeCheckVal.IsSet()
1122
0
    ? srcSkipCodeCheckVal.IsOn()
1123
0
    : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING");
1124
1125
0
  if (skipCodeCheck) {
1126
0
    return {};
1127
0
  }
1128
0
  std::string compilerLauncher;
1129
0
  std::string staticCheckRule = this->GenerateCodeCheckRules(
1130
0
    srcFile, compilerLauncher, "", Config, nullptr);
1131
0
  LogMessage(cmStrCat("CodeCheck: ", staticCheckRule));
1132
0
  return staticCheckRule;
1133
0
}
1134
1135
void cmFastbuildNormalTargetGenerator::ComputeCompilerAndOptions(
1136
  std::string const& compilerOptions, std::string const& staticCheckOptions,
1137
  std::string const& language, FastbuildObjectListNode& outObjectList)
1138
0
{
1139
0
  auto& compilers = this->GetGlobalGenerator()->Compilers;
1140
0
  auto const compilerIter =
1141
0
    compilers.find(FASTBUILD_COMPILER_PREFIX + language);
1142
0
  auto const launcherIter =
1143
0
    compilers.find(FASTBUILD_LAUNCHER_PREFIX + language);
1144
0
  if (!staticCheckOptions.empty()) {
1145
    // If we want to run static checks - use CMake as a launcher.
1146
    // Tested in "RunCMake.ClangTidy", "RunCMake.IncludeWhatYouUse",
1147
    // "RunCMake.Cpplint", "RunCMake.Cppcheck", "RunCMake.MultiLint" tests.
1148
0
    outObjectList.Compiler = "." FASTBUILD_LAUNCHER_PREFIX + CMAKE_LANGUAGE;
1149
0
    outObjectList.CompilerOptions = staticCheckOptions;
1150
    // Add compile command which will be passed to the static analyzer via
1151
    // dash-dash.
1152
0
    if (compilerIter != compilers.end()) {
1153
      // Wrap in quotes to account for potential spaces in the path.
1154
0
      outObjectList.CompilerOptions +=
1155
0
        cmGlobalFastbuildGenerator::QuoteIfHasSpaces(
1156
0
          compilerIter->second.Executable);
1157
0
      outObjectList.CompilerOptions += compilerOptions;
1158
0
    }
1159
0
  } else if (launcherIter != compilers.end()) {
1160
    // Tested in "RunCMake.CompilerLauncher" test.
1161
0
    outObjectList.Compiler = "." + launcherIter->first;
1162
0
    outObjectList.CompilerOptions = launcherIter->second.Args;
1163
1164
0
    auto vars = cmFastbuildNormalTargetGenerator::ComputeRuleVariables();
1165
0
    vars.Language = language.c_str();
1166
0
    std::string const targetSupportPath = this->ConvertToFastbuildPath(
1167
0
      this->GetGeneratorTarget()->GetCMFSupportDirectory());
1168
0
    vars.TargetSupportDir = targetSupportPath.c_str();
1169
0
    RulePlaceholderExpander->ExpandRuleVariables(
1170
0
      LocalCommonGenerator, outObjectList.CompilerOptions, vars);
1171
1172
    // Add compiler executable explicitly to the compile options.
1173
0
    if (compilerIter != compilers.end()) {
1174
0
      outObjectList.CompilerOptions += " ";
1175
      // Wrap in quotes to account for potential spaces in the path.
1176
0
      outObjectList.CompilerOptions +=
1177
0
        cmGlobalFastbuildGenerator::QuoteIfHasSpaces(
1178
0
          compilerIter->second.Executable);
1179
0
      outObjectList.CompilerOptions += compilerOptions;
1180
0
    }
1181
0
  } else if (compilerIter != compilers.end()) {
1182
0
    outObjectList.Compiler = "." + compilerIter->first;
1183
0
    outObjectList.CompilerOptions = compilerOptions;
1184
0
  }
1185
0
  LogMessage(cmStrCat(".Compiler = ", outObjectList.Compiler));
1186
0
  LogMessage(cmStrCat(".CompilerOptions = ", outObjectList.CompilerOptions));
1187
0
}
1188
1189
cmRulePlaceholderExpander::RuleVariables
1190
cmFastbuildNormalTargetGenerator::ComputeRuleVariables() const
1191
0
{
1192
0
  cmRulePlaceholderExpander::RuleVariables compileObjectVars;
1193
0
  compileObjectVars.CMTargetName = GeneratorTarget->GetName().c_str();
1194
0
  compileObjectVars.CMTargetType =
1195
0
    cmState::GetTargetTypeName(GeneratorTarget->GetType()).c_str();
1196
0
  compileObjectVars.Source = FASTBUILD_1_INPUT_PLACEHOLDER;
1197
0
  compileObjectVars.Object = FASTBUILD_2_INPUT_PLACEHOLDER;
1198
0
  compileObjectVars.ObjectDir =
1199
0
    FASTBUILD_DOLLAR_TAG "TargetOutDir" FASTBUILD_DOLLAR_TAG;
1200
0
  compileObjectVars.ObjectFileDir = "";
1201
0
  compileObjectVars.Flags = "";
1202
0
  compileObjectVars.Includes = "";
1203
0
  compileObjectVars.Defines = "";
1204
0
  compileObjectVars.Includes = "";
1205
0
  compileObjectVars.TargetCompilePDB =
1206
0
    FASTBUILD_DOLLAR_TAG "CompilerPDB" FASTBUILD_DOLLAR_TAG;
1207
0
  compileObjectVars.Config = Config.c_str();
1208
0
  return compileObjectVars;
1209
0
}
1210
1211
std::vector<std::string> cmFastbuildNormalTargetGenerator::GetSourceProperty(
1212
  cmSourceFile const& srcFile, std::string const& prop) const
1213
0
{
1214
0
  std::vector<std::string> res;
1215
0
  if (cmValue val = srcFile.GetProperty(prop)) {
1216
0
    cmExpandList(*val, res);
1217
0
    return GetGlobalGenerator()->ConvertToFastbuildPath(res);
1218
0
  }
1219
0
  return res;
1220
0
}
1221
1222
void cmFastbuildNormalTargetGenerator::AppendExtraResources(
1223
  std::set<std::string>& deps) const
1224
0
{
1225
  // Generate Fastbuild's "Copy" commands to copy resources.
1226
0
  auto const generateCopyCommands =
1227
0
    [this](std::vector<cmSourceFile const*>& frameworkDeps) {
1228
0
      this->OSXBundleGenerator->GenerateMacOSXContentStatements(
1229
0
        frameworkDeps, this->MacOSXContentGenerator.get(), Config);
1230
0
    };
1231
1232
0
  std::vector<cmSourceFile const*> headerSources;
1233
0
  this->GeneratorTarget->GetHeaderSources(headerSources, Config);
1234
0
  generateCopyCommands(headerSources);
1235
1236
0
  std::vector<cmSourceFile const*> extraSources;
1237
0
  this->GeneratorTarget->GetExtraSources(extraSources, Config);
1238
0
  generateCopyCommands(extraSources);
1239
1240
0
  std::vector<cmSourceFile const*> externalObjects;
1241
0
  this->GeneratorTarget->GetExternalObjects(externalObjects, Config);
1242
0
  generateCopyCommands(externalObjects);
1243
1244
0
  for (FastbuildCopyNode const& node : this->CopyNodes) {
1245
0
    LogMessage("Adding resource: " + node.Name);
1246
0
    deps.emplace(node.Name);
1247
0
  }
1248
0
}
1249
1250
std::string cmFastbuildNormalTargetGenerator::GetCompileOptions(
1251
  cmSourceFile const& srcFile, std::string const& arch)
1252
0
{
1253
0
  std::string const language = srcFile.GetLanguage();
1254
0
  cmRulePlaceholderExpander::RuleVariables compileObjectVars =
1255
0
    ComputeRuleVariables();
1256
0
  std::string const compilerFlags = DetectCompilerFlags(srcFile, arch);
1257
0
  std::string const compilerDefines = ComputeDefines(srcFile);
1258
0
  compileObjectVars.Flags = compilerFlags.c_str();
1259
0
  compileObjectVars.Defines = compilerDefines.c_str();
1260
0
  compileObjectVars.Language = language.c_str();
1261
0
  if (language == "CUDA") {
1262
0
    compileObjectVars.CudaCompileMode = this->CudaCompileMode.c_str();
1263
0
  }
1264
1265
0
  std::string rule = CompileObjectCmakeRules.at(language);
1266
0
  RulePlaceholderExpander->ExpandRuleVariables(LocalCommonGenerator, rule,
1267
0
                                               compileObjectVars);
1268
1269
0
  std::string compilerExecutable;
1270
  // Remove the compiler from .CompilerOptions, since it would be set as
1271
  // .Compiler in Fastbuild.
1272
  // See https://www.fastbuild.org/docs/functions/objectlist.html for a
1273
  // reference.
1274
0
  std::string options;
1275
0
  if (!cmSystemTools::SplitProgramFromArgs(rule, compilerExecutable,
1276
0
                                           options)) {
1277
0
    cmSystemTools::Error(cmStrCat("Failed to split compiler options: ", rule));
1278
0
  }
1279
0
  LogMessage("Expanded compile options = " + options);
1280
0
  LogMessage("Compiler executable = " + compilerExecutable);
1281
0
  return options;
1282
0
}
1283
1284
std::vector<std::string> cmFastbuildNormalTargetGenerator::GetArches() const
1285
0
{
1286
0
  auto arches = this->GetGeneratorTarget()->GetAppleArchs(Config, {});
1287
  // Don't add any arch-specific logic if arch is only one.
1288
0
  if (arches.empty() || arches.size() == 1) {
1289
0
    arches.clear();
1290
0
    arches.emplace_back();
1291
0
  }
1292
0
  return arches;
1293
0
}
1294
1295
void cmFastbuildNormalTargetGenerator::GetCudaDeviceLinkLinkerAndArgs(
1296
  std::string& linker, std::string& args) const
1297
0
{
1298
0
  std::string linkCmd =
1299
0
    this->GetMakefile()->GetDefinition("CMAKE_CUDA_DEVICE_LINK_"
1300
0
                                       "LIBRARY");
1301
0
  auto vars = ComputeRuleVariables();
1302
0
  vars.Language = "CUDA";
1303
0
  vars.Objects = FASTBUILD_1_INPUT_PLACEHOLDER;
1304
0
  vars.Target = FASTBUILD_2_INPUT_PLACEHOLDER;
1305
0
  std::unique_ptr<cmLinkLineDeviceComputer> linkLineComputer(
1306
0
    new cmLinkLineDeviceComputer(
1307
0
      this->LocalGenerator,
1308
0
      this->LocalGenerator->GetStateSnapshot().GetDirectory()));
1309
0
  std::string linkLibs;
1310
0
  std::string targetFlags;
1311
0
  std::string linkFlags;
1312
0
  std::string frameworkPath;
1313
0
  std::string linkPath;
1314
  // So that the call to "GetTargetFlags" does not pollute "LinkLibs" and
1315
  // "LinkFlags" with unneeded values.
1316
0
  std::string dummyLinkLibs;
1317
0
  std::string dummyLinkFlags;
1318
0
  this->LocalCommonGenerator->GetDeviceLinkFlags(
1319
0
    *linkLineComputer, Config, linkLibs, linkFlags, frameworkPath, linkPath,
1320
0
    this->GeneratorTarget);
1321
0
  this->LocalCommonGenerator->GetTargetFlags(
1322
0
    linkLineComputer.get(), Config, dummyLinkLibs, targetFlags, dummyLinkFlags,
1323
0
    frameworkPath, linkPath, this->GeneratorTarget);
1324
0
  vars.LanguageCompileFlags = "";
1325
0
  vars.LinkFlags = linkFlags.c_str();
1326
0
  vars.LinkLibraries = linkLibs.c_str();
1327
0
  vars.LanguageCompileFlags = targetFlags.c_str();
1328
0
  this->RulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
1329
0
                                                     linkCmd, vars);
1330
0
  SplitLinkerFromArgs(linkCmd, linker, args);
1331
0
}
1332
1333
void cmFastbuildNormalTargetGenerator::GenerateCudaDeviceLink(
1334
  FastbuildTarget& target) const
1335
0
{
1336
0
  auto const arches = this->GetArches();
1337
0
  if (!requireDeviceLinking(*this->GeneratorTarget, *this->GetLocalGenerator(),
1338
0
                            Config)) {
1339
0
    return;
1340
0
  }
1341
0
  LogMessage("GenerateCudaDeviceLink(...)");
1342
0
  for (auto const& arch : arches) {
1343
0
    std::string linker;
1344
0
    std::string args;
1345
0
    GetCudaDeviceLinkLinkerAndArgs(linker, args);
1346
1347
0
    FastbuildLinkerNode deviceLinkNode;
1348
0
    deviceLinkNode.Name = cmStrCat(target.Name, "_cuda_device_link");
1349
0
    deviceLinkNode.Type = FastbuildLinkerNode::SHARED_LIBRARY;
1350
0
    deviceLinkNode.Linker = std::move(linker);
1351
0
    deviceLinkNode.LinkerOptions = std::move(args);
1352
    // Output
1353
0
    deviceLinkNode.LinkerOutput = this->ConvertToFastbuildPath(cmStrCat(
1354
0
      FASTBUILD_DOLLAR_TAG "TargetOutDi"
1355
0
                           "r" FASTBUILD_DOLLAR_TAG "/cmake_device_link",
1356
0
      (args.empty() ? "" : "_" + arch),
1357
0
      this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_"
1358
0
                                        "EXTENSION")));
1359
1360
    // Input
1361
0
    for (auto const& objList : target.ObjectListNodes) {
1362
0
      deviceLinkNode.LibrarianAdditionalInputs.push_back(objList.Name);
1363
0
    }
1364
0
    target.CudaDeviceLinkNode.emplace_back(std::move(deviceLinkNode));
1365
0
  }
1366
0
  LogMessage("GenerateCudaDeviceLink end");
1367
0
}
1368
1369
void cmFastbuildNormalTargetGenerator::GenerateObjects(FastbuildTarget& target)
1370
0
{
1371
0
  this->GetGlobalGenerator()->AllFoldersToClean.insert(ObjectOutDir);
1372
1373
0
  std::map<std::string, FastbuildObjectListNode> nodesPermutations;
1374
1375
0
  cmCryptoHash hash(cmCryptoHash::AlgoSHA256);
1376
1377
0
  std::vector<cmSourceFile const*> objectSources;
1378
0
  GeneratorTarget->GetObjectSources(objectSources, Config);
1379
1380
0
  std::set<std::string> createdPCH;
1381
1382
  // Directory level.
1383
0
  bool useUnity =
1384
0
    GeneratorTarget->GetLocalGenerator()->GetMakefile()->IsDefinitionSet(
1385
0
      CMAKE_UNITY_BUILD);
1386
  // Check if explicitly disabled for this target.
1387
0
  auto const targetProp = GeneratorTarget->GetProperty(UNITY_BUILD);
1388
0
  if (targetProp.IsSet() && targetProp.IsOff()) {
1389
0
    useUnity = false;
1390
0
  }
1391
1392
  // List of sources isolated from the unity build if enabled.
1393
0
  std::set<std::string> isolatedFromUnity;
1394
1395
  // Mapping from unity group (if any) to sources belonging to that group.
1396
0
  std::map<std::string, std::vector<std::string>> sourcesWithGroups;
1397
1398
0
  for (cmSourceFile const* source : objectSources) {
1399
1400
0
    cmSourceFile const& srcFile = *source;
1401
0
    std::string const pathToFile = srcFile.GetFullPath();
1402
0
    if (useUnity) {
1403
      // Check if the source should be added to "UnityInputIsolatedFiles".
1404
0
      if (srcFile.GetPropertyAsBool(SKIP_UNITY_BUILD_INCLUSION)) {
1405
0
        isolatedFromUnity.emplace(pathToFile);
1406
0
      }
1407
0
      std::string const perFileUnityGroup =
1408
0
        srcFile.GetSafeProperty(UNITY_GROUP);
1409
0
      if (!perFileUnityGroup.empty()) {
1410
0
        sourcesWithGroups[perFileUnityGroup].emplace_back(pathToFile);
1411
0
      }
1412
0
    }
1413
1414
0
    this->GetGlobalGenerator()->AddFileToClean(cmStrCat(
1415
0
      ObjectOutDir, '/', this->GeneratorTarget->GetObjectName(source)));
1416
1417
    // Do not generate separate node for PCH source file.
1418
0
    if (this->GeneratorTarget->GetPchSource(Config, srcFile.GetLanguage()) ==
1419
0
        pathToFile) {
1420
0
      continue;
1421
0
    }
1422
1423
0
    std::string const language = srcFile.GetLanguage();
1424
0
    LogMessage(
1425
0
      cmStrCat("Source file: ", this->ConvertToFastbuildPath(pathToFile)));
1426
0
    LogMessage("Language: " + language);
1427
1428
0
    std::string const staticCheckOptions = ComputeCodeCheckOptions(srcFile);
1429
1430
0
    auto const isDisabled = [this](char const* prop) {
1431
0
      auto const propValue = this->GeneratorTarget->GetProperty(prop);
1432
0
      return propValue && propValue.IsOff();
1433
0
    };
1434
0
    bool const disableCaching = isDisabled("FASTBUILD_CACHING");
1435
0
    bool const disableDistribution = isDisabled("FASTBUILD_DISTRIBUTION");
1436
1437
0
    for (auto const& arch : this->GetArches()) {
1438
0
      std::string const compileOptions = GetCompileOptions(srcFile, arch);
1439
1440
0
      std::string objOutDirWithPossibleSubdir = ObjectOutDir;
1441
1442
      // If object should be placed in some subdir in the output
1443
      // path. Tested in "SourceGroups" test.
1444
0
      auto const subdir = cmSystemTools::GetFilenamePath(
1445
0
        this->GeneratorTarget->GetObjectName(source));
1446
0
      if (!subdir.empty()) {
1447
0
        objOutDirWithPossibleSubdir += "/";
1448
0
        objOutDirWithPossibleSubdir += subdir;
1449
0
      }
1450
1451
0
      std::string const objectListHash = hash.HashString(cmStrCat(
1452
0
        compileOptions, staticCheckOptions, objOutDirWithPossibleSubdir,
1453
        // If file does not need PCH - it must be in another ObjectList.
1454
0
        srcFile.GetProperty("SKIP_PRECOMPILE_HEADERS"),
1455
0
        srcFile.GetLanguage()));
1456
1457
0
      LogMessage("ObjectList Hash: " + objectListHash);
1458
1459
0
      FastbuildObjectListNode& objectListNode =
1460
0
        nodesPermutations[objectListHash];
1461
1462
      // Absolute path needed in "RunCMake.SymlinkTrees" test.
1463
0
      objectListNode.CompilerInputFiles.push_back(pathToFile);
1464
1465
0
      std::vector<std::string> const outputs =
1466
0
        GetSourceProperty(srcFile, "OBJECT_OUTPUTS");
1467
0
      objectListNode.ObjectOutputs.insert(outputs.begin(), outputs.end());
1468
1469
0
      std::vector<std::string> const depends =
1470
0
        GetSourceProperty(srcFile, "OBJECT_DEPENDS");
1471
0
      objectListNode.ObjectDepends.insert(depends.begin(), depends.end());
1472
1473
      // We have already computed properties that are computed below.
1474
      // (.CompilerOptions, .PCH*, etc.). Short circuit this iteration.
1475
0
      if (!objectListNode.CompilerOptions.empty()) {
1476
0
        continue;
1477
0
      }
1478
0
      if (disableCaching) {
1479
0
        objectListNode.AllowCaching = false;
1480
0
      }
1481
0
      if (disableDistribution) {
1482
0
        objectListNode.AllowDistribution = false;
1483
0
      }
1484
1485
0
      objectListNode.CompilerOutputPath = objOutDirWithPossibleSubdir;
1486
0
      LogMessage(cmStrCat("Output path: ", objectListNode.CompilerOutputPath));
1487
1488
0
      ComputeCompilerAndOptions(compileOptions, staticCheckOptions, language,
1489
0
                                objectListNode);
1490
0
      ComputePCH(*source, objectListNode, createdPCH);
1491
1492
0
      objectListNode.Name = cmStrCat(this->GetName(), '_', language, "_Objs");
1493
      // TODO: Ask cmake the output objects and group by extension instead
1494
      // of doing this
1495
0
      if (language == "RC") {
1496
0
        objectListNode.CompilerOutputExtension = ".res";
1497
0
      } else {
1498
0
        if (!arch.empty()) {
1499
0
          objectListNode.CompilerOutputExtension = cmStrCat('.', arch);
1500
0
          objectListNode.arch = arch;
1501
0
        }
1502
0
        char const* customExt =
1503
0
          this->GeneratorTarget->GetCustomObjectExtension();
1504
1505
0
        objectListNode.CompilerOutputExtension +=
1506
0
          this->GetMakefile()->GetSafeDefinition(
1507
0
            cmStrCat("CMAKE_", language, "_OUTPUT_EXTENSION"));
1508
        // Tested in "CudaOnly.ExportPTX" test.
1509
0
        if (customExt) {
1510
0
          objectListNode.CompilerOutputExtension += customExt;
1511
0
        }
1512
0
      }
1513
0
    }
1514
0
  }
1515
1516
0
  int groupNameCount = 0;
1517
1518
0
  for (auto& val : nodesPermutations) {
1519
0
    auto& objectListNode = val.second;
1520
0
    objectListNode.Name = cmStrCat(objectListNode.Name, '_', ++groupNameCount);
1521
0
    LogMessage(cmStrCat("ObjectList name: ", objectListNode.Name));
1522
0
  }
1523
0
  std::vector<FastbuildObjectListNode>& objects = target.ObjectListNodes;
1524
0
  objects.reserve(nodesPermutations.size());
1525
0
  for (auto& val : nodesPermutations) {
1526
0
    auto& node = val.second;
1527
0
    if (!node.PCHInputFile.empty()) {
1528
      // Node that produces PCH should be the first one, since other nodes
1529
      // might reuse this PCH.
1530
      // Note: we might have several such nodes for different languages.
1531
0
      objects.insert(objects.begin(), std::move(node));
1532
0
    } else {
1533
0
      objects.emplace_back(std::move(node));
1534
0
    }
1535
0
  }
1536
0
  if (useUnity) {
1537
0
    target.UnityNodes =
1538
0
      GenerateUnity(objects, isolatedFromUnity, sourcesWithGroups);
1539
0
  }
1540
0
}
1541
1542
FastbuildUnityNode cmFastbuildNormalTargetGenerator::GetOneUnity(
1543
  std::set<std::string> const& isolatedFiles, std::vector<std::string>& files,
1544
  int unitySize) const
1545
0
{
1546
0
  FastbuildUnityNode result;
1547
0
  for (auto iter = files.begin(); iter != files.end();) {
1548
0
    std::string pathToFile = std::move(*iter);
1549
0
    iter = files.erase(iter);
1550
    // This source must be isolated
1551
0
    if (isolatedFiles.find(pathToFile) != isolatedFiles.end()) {
1552
0
      result.UnityInputFiles.emplace_back(pathToFile);
1553
0
      result.UnityInputIsolatedFiles.emplace_back(std::move(pathToFile));
1554
0
    } else {
1555
0
      result.UnityInputFiles.emplace_back(std::move(pathToFile));
1556
0
    }
1557
0
    if (int(result.UnityInputFiles.size() -
1558
0
            result.UnityInputIsolatedFiles.size()) == unitySize) {
1559
0
      break;
1560
0
    }
1561
0
  }
1562
0
  return result;
1563
0
}
1564
int cmFastbuildNormalTargetGenerator::GetUnityBatchSize() const
1565
0
{
1566
0
  int unitySize = 8;
1567
0
  try {
1568
0
    auto const perTargetSize =
1569
0
      GeneratorTarget->GetSafeProperty(UNITY_BUILD_BATCH_SIZE);
1570
0
    if (!perTargetSize.empty()) {
1571
0
      unitySize = std::stoi(perTargetSize);
1572
0
    }
1573
    // Per-directory level.
1574
0
    else {
1575
0
      unitySize = std::stoi(
1576
0
        GeneratorTarget->GetLocalGenerator()->GetMakefile()->GetDefinition(
1577
0
          CMAKE_UNITY_BUILD_BATCH_SIZE));
1578
0
    }
1579
0
  } catch (...) {
1580
0
    return unitySize;
1581
0
  }
1582
0
  return unitySize;
1583
0
}
1584
1585
std::vector<FastbuildUnityNode>
1586
cmFastbuildNormalTargetGenerator::GenerateUnity(
1587
  std::vector<FastbuildObjectListNode>& objects,
1588
  std::set<std::string> const& isolatedSources,
1589
  std::map<std::string, std::vector<std::string>> const& sourcesWithGroups)
1590
0
{
1591
0
  int const unitySize = GetUnityBatchSize();
1592
  // Unity of size less than 2 doesn't make sense.
1593
0
  if (unitySize < 2) {
1594
0
    return {};
1595
0
  }
1596
1597
0
  int unityNumber = 0;
1598
0
  int unityGroupNumber = 0;
1599
0
  std::vector<FastbuildUnityNode> result;
1600
1601
0
  for (FastbuildObjectListNode& obj : objects) {
1602
    // Don't use unity for only 1 file.
1603
0
    if (obj.CompilerInputFiles.size() < 2) {
1604
0
      continue;
1605
0
    }
1606
0
    std::string const ext =
1607
0
      cmSystemTools::GetFilenameExtension(obj.CompilerInputFiles[0]);
1608
    // Process groups.
1609
0
    auto groupedNode = GenerateGroupedUnityNode(
1610
0
      obj.CompilerInputFiles, sourcesWithGroups, unityGroupNumber);
1611
    // We have at least 2 sources in the group.
1612
0
    if (groupedNode.UnityInputFiles.size() > 1) {
1613
0
      groupedNode.UnityOutputPath = obj.CompilerOutputPath;
1614
0
      obj.CompilerInputUnity.emplace_back(groupedNode.Name);
1615
0
      groupedNode.UnityOutputPattern = cmStrCat(groupedNode.Name, ext);
1616
0
      result.emplace_back(std::move(groupedNode));
1617
0
    }
1618
    // General unity batching of the remaining (non-grouped) sources.
1619
0
    while (!obj.CompilerInputFiles.empty()) {
1620
0
      FastbuildUnityNode node =
1621
0
        GetOneUnity(isolatedSources, obj.CompilerInputFiles, unitySize);
1622
0
      node.Name = cmStrCat(this->GetName(), "_Unity_", ++unityNumber);
1623
0
      node.UnityOutputPath = obj.CompilerOutputPath;
1624
0
      node.UnityOutputPattern = cmStrCat(node.Name, ext);
1625
1626
      // Unity group of size 1 doesn't make sense - just isolate the source.
1627
0
      if (groupedNode.UnityInputFiles.size() == 1) {
1628
0
        node.UnityInputIsolatedFiles.emplace_back(
1629
0
          groupedNode.UnityInputFiles[0]);
1630
0
        node.UnityInputFiles.emplace_back(
1631
0
          std::move(groupedNode.UnityInputFiles[0]));
1632
        // Clear so we don't enter here on the next iteration.
1633
0
        groupedNode.UnityInputFiles.clear();
1634
0
      }
1635
1636
      // We've got only 1 file left. No need to create a Unity node for it,
1637
      // just return it back to the ObjectList and exit.
1638
0
      if (node.UnityInputFiles.size() == 1) {
1639
0
        obj.CompilerInputFiles.emplace_back(
1640
0
          std::move(node.UnityInputFiles[0]));
1641
0
        break;
1642
0
      }
1643
1644
0
      obj.CompilerInputUnity.emplace_back(node.Name);
1645
0
      result.emplace_back(std::move(node));
1646
0
    }
1647
0
  }
1648
0
  return result;
1649
0
}
1650
1651
FastbuildUnityNode cmFastbuildNormalTargetGenerator::GenerateGroupedUnityNode(
1652
  std::vector<std::string>& inputFiles,
1653
  std::map<std::string, std::vector<std::string>> const& sourcesWithGroups,
1654
  int& groupId)
1655
0
{
1656
0
  std::vector<FastbuildUnityNode> result;
1657
0
  for (auto const& item : sourcesWithGroups) {
1658
0
    auto const& group = item.first;
1659
0
    auto const& sources = item.second;
1660
0
    FastbuildUnityNode node;
1661
    // Check if any of the sources belong to this group.
1662
0
    for (auto const& source : sources) {
1663
0
      auto const iter =
1664
0
        std::find(inputFiles.begin(), inputFiles.end(), source);
1665
0
      if (iter == inputFiles.end()) {
1666
0
        continue;
1667
0
      }
1668
0
      node.Name =
1669
0
        cmStrCat(this->GetName(), "_Unity_Group_", group, '_', ++groupId);
1670
0
      node.UnityInputFiles.emplace_back(source);
1671
1672
      // Remove from the general batching.
1673
0
      inputFiles.erase(
1674
0
        std::remove(inputFiles.begin(), inputFiles.end(), source),
1675
0
        inputFiles.end());
1676
0
    }
1677
0
    if (!node.UnityInputFiles.empty()) {
1678
      // The unity group belongs to the ObjectLists that we're processing.
1679
      // We've grouped all the sources we could from the current ObjectList.
1680
0
      return node;
1681
0
    }
1682
0
  }
1683
0
  return {};
1684
0
}
1685
1686
std::string cmFastbuildNormalTargetGenerator::ResolveIfAlias(
1687
  std::string const& targetName) const
1688
0
{
1689
0
  LogMessage("targetName: " + targetName);
1690
0
  std::map<std::string, std::string> const aliases =
1691
0
    this->Makefile->GetAliasTargets();
1692
0
  auto const iter = aliases.find(targetName);
1693
0
  if (iter != aliases.end()) {
1694
0
    LogMessage("Non alias name: " + iter->second);
1695
0
    return iter->second;
1696
0
  }
1697
0
  return targetName;
1698
0
}
1699
1700
void cmFastbuildNormalTargetGenerator::AppendExternalObject(
1701
  FastbuildLinkerNode& linkerNode, std::set<std::string>& linkedDeps) const
1702
0
{
1703
  // Different aspects of this logic exercised in "ObjectLibrary" and
1704
  // "ExportImport" test. When making changes here - verify that both of those
1705
  // tests are still passing.
1706
0
  LogMessage("AppendExternalObject(...)");
1707
0
  std::vector<cmSourceFile const*> extObjects;
1708
0
  this->GeneratorTarget->GetExternalObjects(extObjects, Config);
1709
0
  for (cmSourceFile const* src : extObjects) {
1710
1711
0
    std::string const pathToObj =
1712
0
      this->ConvertToFastbuildPath(src->GetFullPath());
1713
0
    LogMessage("EXT OBJ: " + pathToObj);
1714
0
    std::string const objLibName = ResolveIfAlias(src->GetObjectLibrary());
1715
0
    LogMessage("GetObjectLibrary: " + objLibName);
1716
    // Tested in "ExternalOBJ" test.
1717
0
    cmTarget const* target =
1718
0
      this->GlobalCommonGenerator->FindTarget(objLibName);
1719
0
    if (objLibName.empty()) {
1720
0
      linkerNode.LibrarianAdditionalInputs.emplace_back(pathToObj);
1721
0
    }
1722
    // We know how to generate this target and haven't added this dependency
1723
    // yet.
1724
0
    else if (target) {
1725
0
      if (!linkedDeps.emplace(objLibName + FASTBUILD_OBJECTS_ALIAS_POSTFIX)
1726
0
             .second) {
1727
0
        LogMessage("Object Target: " + objLibName +
1728
0
                   FASTBUILD_OBJECTS_ALIAS_POSTFIX " already linked");
1729
0
        continue;
1730
0
      }
1731
0
      linkerNode.LibrarianAdditionalInputs.emplace_back(
1732
0
        objLibName + FASTBUILD_OBJECTS_ALIAS_POSTFIX);
1733
0
    } else if (linkedDeps.emplace(pathToObj).second) {
1734
0
      LogMessage("Adding obj dep : " + pathToObj);
1735
0
      linkerNode.LibrarianAdditionalInputs.emplace_back(pathToObj);
1736
0
    }
1737
0
  }
1738
0
}
1739
1740
void cmFastbuildNormalTargetGenerator::AppendExeToLink(
1741
  FastbuildLinkerNode& linkerNode,
1742
  cmComputeLinkInformation::Item const& item) const
1743
0
{
1744
0
  std::string const decorated =
1745
0
    item.GetFormattedItem(this->ConvertToFastbuildPath(item.Value.Value))
1746
0
      .Value;
1747
0
  LogMessage("Linking to executable : " + decorated);
1748
  // Tested in "InterfaceLinkLibrariesDirect" and "Plugin" test.
1749
0
  linkerNode.LinkerOptions +=
1750
0
    (" " + cmGlobalFastbuildGenerator::QuoteIfHasSpaces(decorated));
1751
0
}
1752
1753
std::string cmFastbuildNormalTargetGenerator::GetImportedLoc(
1754
  cmComputeLinkInformation::Item const& item) const
1755
0
{
1756
  // Link to import library when possible.
1757
  // Tested in "StagingPrefix" test on Windows/MSVC.
1758
0
  cmStateEnums::ArtifactType const artifact =
1759
0
    item.Target->HasImportLibrary(Config)
1760
0
    ? cmStateEnums::ImportLibraryArtifact
1761
0
    : cmStateEnums::RuntimeBinaryArtifact;
1762
1763
0
  std::string importedLoc = this->ConvertToFastbuildPath(
1764
0
    item.Target->GetFullPath(Config, artifact, true));
1765
0
  LogMessage("ImportedGetLocation: " + importedLoc);
1766
0
  return importedLoc;
1767
0
}
1768
1769
void cmFastbuildNormalTargetGenerator::AppendTargetDep(
1770
  FastbuildLinkerNode& linkerNode, std::set<std::string>& linkedObjects,
1771
  cmComputeLinkInformation::Item const& item) const
1772
0
{
1773
0
  LogMessage("AppendTargetDep(...)");
1774
0
  cmStateEnums::TargetType const depType = item.Target->GetType();
1775
0
  LogMessage("Link dep type: " + std::to_string(depType));
1776
0
  LogMessage("Target name: " + item.Target->GetName());
1777
0
  auto const resolvedTargetName = ResolveIfAlias(item.Target->GetName());
1778
0
  LogMessage("Resolved: " + resolvedTargetName);
1779
0
  if (depType == cmStateEnums::INTERFACE_LIBRARY) {
1780
0
    return;
1781
0
  }
1782
0
  std::string const feature = item.GetFeatureName();
1783
1784
0
  if (item.Target->IsImported()) {
1785
1786
0
    if (feature == "FRAMEWORK") {
1787
      // Use just framework's name. The exact path where to look for the
1788
      // framework will be provided from "frameworkPath" in
1789
      // "cmFastbuildNormalTargetGenerator::DetectBaseLinkerCommand(...)".
1790
      // Tested in "RunCMake.Framework - ImportedFrameworkConsumption".
1791
0
      std::string const decorated =
1792
0
        item.GetFormattedItem(item.Value.Value).Value;
1793
0
      LogMessage(
1794
0
        cmStrCat("Adding framework dep <", decorated, "> to command line"));
1795
0
      linkerNode.LinkerOptions += (" " + decorated);
1796
0
      return;
1797
0
    }
1798
0
    if (depType == cmStateEnums::UNKNOWN_LIBRARY) {
1799
0
      LogMessage("Unknown library -- adding to LibrarianAdditionalInputs or "
1800
0
                 "Libraries2");
1801
0
      if (UsingCommandLine) {
1802
0
        AppendCommandLineDep(linkerNode, item);
1803
0
      } else {
1804
0
        AppendLinkDep(linkerNode, GetImportedLoc(item));
1805
0
      }
1806
0
      return;
1807
0
    }
1808
    // Tested in "ExportImport" test.
1809
0
    if (depType == cmStateEnums::EXECUTABLE) {
1810
0
      AppendExeToLink(linkerNode, item);
1811
0
      return;
1812
0
    }
1813
    // Skip exported objects.
1814
    // Tested in "ExportImport" test.
1815
0
    if (depType == cmStateEnums::OBJECT_LIBRARY) {
1816
0
      LogMessage("target : " + item.Target->GetName() +
1817
0
                 " already linked... Skipping");
1818
0
      return;
1819
0
    }
1820
    // Tested in "ExportImport" test.
1821
0
    cmList const list{ GetImportedLoc(item) };
1822
0
    for (std::string const& linkDep : list) {
1823
0
      AppendLinkDep(linkerNode, linkDep);
1824
0
    }
1825
0
  } else {
1826
0
    if (depType == cmStateEnums::SHARED_LIBRARY &&
1827
0
        this->GeneratorTarget->GetPropertyAsBool("LINK_DEPENDS_NO_SHARED")) {
1828
      // It moves the dep outside of FASTBuild control, so the binary won't
1829
      // be re-built if the shared lib has changed.
1830
      // Tested in "BuildDepends" test.
1831
0
      LogMessage(
1832
0
        cmStrCat("LINK_DEPENDS_NO_SHARED is set on the target, adding dep",
1833
0
                 item.Value.Value, " as is"));
1834
0
      linkerNode.LinkerOptions +=
1835
0
        (" " + cmGlobalFastbuildGenerator::QuoteIfHasSpaces(item.Value.Value));
1836
0
      return;
1837
0
    }
1838
    // Just add path to binary artifact to command line (except for OBJECT
1839
    // libraries which we will link directly).
1840
0
    if (UsingCommandLine && depType != cmStateEnums::OBJECT_LIBRARY) {
1841
0
      AppendCommandLineDep(linkerNode, item);
1842
0
      return;
1843
0
    }
1844
    // This dep has a special way of linking to it (e.g.
1845
    // "CMAKE_LINK_LIBRARY_USING_<FEATURE>").
1846
0
    bool const isFeature = !feature.empty() && feature != "DEFAULT";
1847
0
    if (isFeature) {
1848
0
      std::string const decorated =
1849
0
        item.GetFormattedItem(this->ConvertToFastbuildPath(item.Value.Value))
1850
0
          .Value;
1851
0
      LogMessage("Prepending with feature: " + decorated);
1852
0
      linkerNode.LinkerOptions += (" " + decorated);
1853
0
    }
1854
1855
0
    std::string dep = resolvedTargetName +
1856
0
      (depType == cmStateEnums::OBJECT_LIBRARY
1857
0
         ? FASTBUILD_OBJECTS_ALIAS_POSTFIX
1858
0
         : FASTBUILD_LINK_ARTIFACTS_ALIAS_POSTFIX);
1859
0
    if (!linkerNode.Arch.empty()) {
1860
0
      dep += cmStrCat('-', linkerNode.Arch);
1861
0
    }
1862
    // If we have a special way of linking the dep, we can't have it in
1863
    // ".Libraries" (since there might be multiple such deps, but
1864
    // FASTBuild expands ".Libraries" as a continuous array, so we can't
1865
    // inject any properties in between). Tested in
1866
    // "RunCMake.target_link_libraries-LINK_LIBRARY" test.
1867
0
    if (isFeature) {
1868
0
      LogMessage(cmStrCat("AppendTargetDep: ", dep, " as prebuild"));
1869
0
      linkerNode.PreBuildDependencies.emplace(dep);
1870
0
      return;
1871
0
    }
1872
1873
0
    if (depType != cmStateEnums::OBJECT_LIBRARY ||
1874
0
        linkedObjects.emplace(dep).second) {
1875
0
      AppendLinkDep(linkerNode, dep);
1876
0
    }
1877
0
    AppendTransitivelyLinkedObjects(*item.Target, linkedObjects);
1878
0
  }
1879
0
}
1880
1881
void cmFastbuildNormalTargetGenerator::AppendPrebuildDeps(
1882
  FastbuildLinkerNode& linkerNode,
1883
  cmComputeLinkInformation::Item const& item) const
1884
0
{
1885
0
  if (!item.Target->IsImported()) {
1886
0
    return;
1887
0
  }
1888
  // In "RunCMake.FileAPI" imported object library "imported_object_lib" is
1889
  // added w/o import location...
1890
0
  if (item.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
1891
0
    return;
1892
0
  }
1893
0
  cmList const list{ GetImportedLoc(item) };
1894
0
  for (std::string const& linkDep : list) {
1895
    // In case we know how to generate this file (needed for proper
1896
    // sorting by deps). Tested in "RunCMake.target_link_libraries-ALIAS"
1897
    // test.
1898
0
    auto fastbuildTarget =
1899
0
      this->GetGlobalGenerator()->GetTargetByOutputName(linkDep);
1900
0
    std::string fastbuildTargetName;
1901
0
    if (fastbuildTarget) {
1902
0
      fastbuildTargetName = std::move(fastbuildTarget->Name);
1903
0
    }
1904
0
    if (!fastbuildTargetName.empty()) {
1905
0
      LogMessage("Adding dep to " + fastbuildTargetName);
1906
0
      linkerNode.PreBuildDependencies.insert(std::move(fastbuildTargetName));
1907
0
    } else {
1908
0
      if (!cmIsNOTFOUND(linkDep)) {
1909
0
        LogMessage(cmStrCat("Adding dep ", linkDep, " for sorting"));
1910
0
        linkerNode.PreBuildDependencies.insert(linkDep);
1911
0
      }
1912
0
    }
1913
0
  }
1914
0
}
1915
1916
void cmFastbuildNormalTargetGenerator::AppendTransitivelyLinkedObjects(
1917
  cmGeneratorTarget const& target, std::set<std::string>& linkedObjects) const
1918
0
{
1919
0
  std::vector<std::string> objs;
1920
  // Consider that all those object are now linked as well.
1921
  // Tested in "ExportImport" test.
1922
0
  target.GetTargetObjectNames(Config, objs);
1923
0
  for (std::string const& obj : objs) {
1924
0
    std::string const pathToObj = this->ConvertToFastbuildPath(
1925
0
      cmStrCat(target.GetObjectDirectory(Config), '/', obj));
1926
0
    linkedObjects.insert(pathToObj);
1927
0
  }
1928
  // Object libs should not be propagated transitively. It's especially
1929
  // important for LinkObjRHSObject2 test where the absence of the propagation
1930
  // is tested.
1931
0
  for (auto const& linkedTarget :
1932
0
       target.Target->GetLinkImplementationEntries()) {
1933
0
    auto objAlias = linkedTarget.Value + FASTBUILD_OBJECTS_ALIAS_POSTFIX;
1934
0
    LogMessage("Object target is linked transitively " + objAlias);
1935
0
    linkedObjects.emplace(std::move(objAlias));
1936
0
  }
1937
0
}
1938
1939
void cmFastbuildNormalTargetGenerator::AppendCommandLineDep(
1940
  FastbuildLinkerNode& linkerNode,
1941
  cmComputeLinkInformation::Item const& item) const
1942
0
{
1943
0
  LogMessage("AppendCommandLineDep(...)");
1944
  // Tested in:
1945
  // "LinkDirectory" (TargetType::EXECUTABLE),
1946
  // "ObjC.simple-build-test" (TargetType::SHARED_LIBRARY),
1947
  // "XCTest" (TargetType::MODULE_LIBRARY) tests.
1948
1949
0
  std::string formatted;
1950
0
  if (item.Target && item.Target->IsImported()) {
1951
0
    formatted = GetImportedLoc(item);
1952
0
  } else {
1953
0
    formatted = item.GetFormattedItem(item.Value.Value).Value;
1954
0
  }
1955
0
  formatted = this->ConvertToFastbuildPath(formatted);
1956
1957
0
  LogMessage(
1958
0
    cmStrCat("Unknown link dep: ", formatted, ", adding to command line"));
1959
1960
  // Only add real artifacts to .Libraries2, otherwise Fastbuild will always
1961
  // consider the target out-of-date (since its input doesn't exist).
1962
0
  if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes &&
1963
0
      item.GetFeatureName() == "DEFAULT") {
1964
0
    linkerNode.LinkerOptions +=
1965
0
      (" " + cmGlobalFastbuildGenerator::QuoteIfHasSpaces(formatted));
1966
0
    AppendToLibraries2IfApplicable(linkerNode, std::move(formatted));
1967
0
  } else {
1968
    // It's some link option, not a path.
1969
0
    linkerNode.LinkerOptions += (" " + formatted);
1970
0
  }
1971
0
}
1972
1973
void cmFastbuildNormalTargetGenerator::AppendToLibraries2IfApplicable(
1974
  FastbuildLinkerNode& linkerNode, std::string dep) const
1975
0
{
1976
  // Strings like "-framework Cocoa" in .Libraries2 node will always make the
1977
  // target out-of-date (since it never exists).
1978
0
  if (this->GeneratorTarget->IsApple() &&
1979
0
      cmSystemTools::StringStartsWith(dep, "-framework")) {
1980
0
    LogMessage(cmStrCat("Not adding framework: ", dep, " to .Libraries2"));
1981
0
    return;
1982
0
  }
1983
1984
0
  auto const target = this->GetGlobalGenerator()->GetTargetByOutputName(dep);
1985
  // Fastbuild doesn't support executables in .Libraries2, though we can use
1986
  // Executables via "-bundle_loader" on Apple.
1987
0
  if (this->GeneratorTarget->IsApple() && target &&
1988
0
      !target->LinkerNode.empty() &&
1989
0
      target->LinkerNode[0].Type == FastbuildLinkerNode::EXECUTABLE) {
1990
0
    LogMessage(cmStrCat("Not adding DLL/Executable(", linkerNode.Name,
1991
0
                        " to .Libraries2"));
1992
0
    return;
1993
0
  }
1994
1995
  // Adding to .Libraries2 for tracking.
1996
0
  LogMessage(cmStrCat("Adding ", dep, " .Libraries2"));
1997
0
  linkerNode.Libraries2.emplace_back(std::move(dep));
1998
0
}
1999
2000
void cmFastbuildNormalTargetGenerator::AppendLINK_DEPENDS(
2001
  FastbuildLinkerNode& linkerNode) const
2002
0
{
2003
  // LINK_DEPENDS and such.
2004
  // Tested in "BuildDepends" test.
2005
0
  for (std::string const& lang : Languages) {
2006
0
    for (BT<std::string> const& dep :
2007
0
         this->GeneratorTarget->GetLinkDepends(Config, lang)) {
2008
      // We can't add "LINK_DEPENDS" to .PreBuildDependencies, since FASTBuild
2009
      // only forces such targets to be built and doesn't force re-linking if
2010
      // they've changed.
2011
0
      linkerNode.Libraries2.emplace_back(
2012
0
        this->ConvertToFastbuildPath(dep.Value));
2013
0
    }
2014
0
  }
2015
0
}
2016
2017
void cmFastbuildNormalTargetGenerator::AppendLinkDep(
2018
  FastbuildLinkerNode& linkerNode, std::string dep) const
2019
0
{
2020
0
  LogMessage(cmStrCat("AppendLinkDep: ", dep,
2021
0
                      " to .LibrarianAdditionalInputs/.Libraries"));
2022
0
  linkerNode.LibrarianAdditionalInputs.emplace_back(std::move(dep));
2023
0
}
2024
2025
void cmFastbuildNormalTargetGenerator::AppendDirectObjectLibs(
2026
  FastbuildLinkerNode& linkerNode, std::set<std::string>& linkedObjects)
2027
0
{
2028
0
  auto const srcs = this->GeneratorTarget->GetSourceFiles(Config);
2029
0
  for (auto const& entry : srcs) {
2030
0
    auto const objLib = entry.Value->GetObjectLibrary();
2031
0
    auto const objPath = entry.Value->GetFullPath();
2032
0
    LogMessage("Source obj entry: " + objPath);
2033
0
    if (!objLib.empty()) {
2034
0
      auto* const objTarget =
2035
0
        this->LocalGenerator->FindGeneratorTargetToUse(objLib);
2036
0
      if (objTarget) {
2037
0
        LogMessage("Imported: " + std::to_string(objTarget->IsImported()));
2038
0
        std::string fastbuildTarget;
2039
        // If target is imported - we don't have it in our build file, so can't
2040
        // refer to it by name. Use file path to the object then.
2041
        // Tested in "ExportImport" test.
2042
0
        if (objTarget->IsImported()) {
2043
0
          fastbuildTarget = entry.Value->GetFullPath();
2044
0
        } else {
2045
          // Mark all target objects as linked.
2046
0
          linkedObjects.emplace(this->ConvertToFastbuildPath(objPath));
2047
0
          fastbuildTarget =
2048
0
            objTarget->GetName() + FASTBUILD_OBJECTS_ALIAS_POSTFIX;
2049
0
        }
2050
0
        if (linkedObjects.emplace(fastbuildTarget).second) {
2051
0
          LogMessage("Adding object target: " + fastbuildTarget);
2052
0
          linkerNode.LibrarianAdditionalInputs.emplace_back(
2053
0
            std::move(fastbuildTarget));
2054
0
        }
2055
0
      }
2056
0
    }
2057
0
  }
2058
0
}
2059
2060
void cmFastbuildNormalTargetGenerator::AppendLinkDeps(
2061
  std::set<FastbuildTargetDep>& preBuildDeps, FastbuildLinkerNode& linkerNode,
2062
  FastbuildLinkerNode& cudaDeviceLinkLinkerNode)
2063
0
{
2064
0
  std::set<std::string> linkedObjects;
2065
0
  cmComputeLinkInformation const* linkInfo =
2066
0
    this->GeneratorTarget->GetLinkInformation(Config);
2067
0
  if (!linkInfo) {
2068
0
    return;
2069
0
  }
2070
2071
0
  UsingCommandLine = false;
2072
0
  AppendLINK_DEPENDS(linkerNode);
2073
  // Object libs that are linked directly to target (e.g.
2074
  // add_executable(test_exe archiveObjs)
2075
0
  AppendDirectObjectLibs(linkerNode, linkedObjects);
2076
0
  std::size_t numberOfDirectlyLinkedObjects =
2077
0
    linkerNode.LibrarianAdditionalInputs.size();
2078
  // target_link_libraries.
2079
0
  cmComputeLinkInformation::ItemVector const items = linkInfo->GetItems();
2080
2081
0
  LogMessage(cmStrCat("Link items size: ", items.size()));
2082
0
  for (cmComputeLinkInformation::Item const& item : items) {
2083
0
    std::string const feature = item.GetFeatureName();
2084
0
    LogMessage("GetFeatureName: " + feature);
2085
0
    if (!feature.empty()) {
2086
0
      LogMessage("GetFormattedItem: " +
2087
0
                 item.GetFormattedItem(item.Value.Value).Value);
2088
0
    }
2089
    // We're linked to `$<TARGET_OBJECTS>`.
2090
    // Static libs transitively propagate such deps, see:
2091
    // https://cmake.org/cmake/help/latest/command/target_link_libraries.html#linking-object-libraries-via-target-objects
2092
0
    if (item.ObjectSource &&
2093
0
        linkerNode.Type != FastbuildLinkerNode::STATIC_LIBRARY) {
2094
      // Tested in "ObjectLibrary" test.
2095
0
      auto libName = item.ObjectSource->GetObjectLibrary();
2096
0
      std::string dep = libName + FASTBUILD_OBJECTS_ALIAS_POSTFIX;
2097
0
      if (linkedObjects.emplace(dep).second) {
2098
0
        FastbuildTargetDep targetDep{ std::move(libName) };
2099
0
        targetDep.Type = FastbuildTargetDepType::ORDER_ONLY;
2100
0
        preBuildDeps.emplace(std::move(targetDep));
2101
0
        linkerNode.LibrarianAdditionalInputs.emplace_back(std::move(dep));
2102
0
      }
2103
0
    } else if (linkerNode.Type == FastbuildLinkerNode::STATIC_LIBRARY) {
2104
0
      LogMessage(cmStrCat("Skipping linking to STATIC_LIBRARY (",
2105
0
                          linkerNode.Name, ')'));
2106
0
      continue;
2107
0
    }
2108
    // We're linked to exact target.
2109
0
    else if (item.Target) {
2110
0
      AppendTargetDep(linkerNode, linkedObjects, item);
2111
0
      AppendPrebuildDeps(linkerNode, item);
2112
0
      if (!item.Target->IsImported() &&
2113
0
          item.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
2114
0
        ++numberOfDirectlyLinkedObjects;
2115
0
        cudaDeviceLinkLinkerNode.LibrarianAdditionalInputs.emplace_back(
2116
0
          cmStrCat(item.Target->GetName(), FASTBUILD_OBJECTS_ALIAS_POSTFIX));
2117
0
      }
2118
2119
0
    } else {
2120
0
      AppendCommandLineDep(linkerNode, item);
2121
0
      UsingCommandLine = true;
2122
0
    }
2123
0
  }
2124
0
  AppendExternalObject(linkerNode, linkedObjects);
2125
2126
0
  if (!cudaDeviceLinkLinkerNode.Name.empty()) {
2127
0
    linkerNode.LibrarianAdditionalInputs.push_back(
2128
0
      cudaDeviceLinkLinkerNode.Name);
2129
    // CUDA device-link stub needs to go AFTER direct object dependencies, but
2130
    // BEFORE all other dependencies. Needed for the correct left-to-right
2131
    // symbols resolution on Linux.
2132
0
    std::swap(
2133
0
      linkerNode.LibrarianAdditionalInputs[numberOfDirectlyLinkedObjects],
2134
0
      linkerNode.LibrarianAdditionalInputs.back());
2135
0
  }
2136
0
}
2137
2138
void cmFastbuildNormalTargetGenerator::AddLipoCommand(FastbuildTarget& target)
2139
0
{
2140
0
  static auto const lipo = cmSystemTools::FindProgram("lipo");
2141
0
  LogMessage("found lipo at " + lipo);
2142
0
  FastbuildExecNode exec;
2143
0
  exec.ExecExecutable = lipo;
2144
0
  exec.ExecOutput = target.RealOutput;
2145
0
  if (exec.ExecOutput != target.Name) {
2146
0
    exec.Name = target.Name;
2147
0
  }
2148
0
  for (auto const& ArchSpecificTarget : target.LinkerNode) {
2149
0
    exec.ExecInput.emplace_back(ArchSpecificTarget.LinkerOutput);
2150
0
  }
2151
0
  exec.ExecArguments += cmStrCat("-create -output ", target.RealOutput, " ",
2152
0
                                 cmJoin(exec.ExecInput, " "));
2153
0
  target.PostBuildExecNodes.Alias.PreBuildDependencies.emplace(
2154
0
    exec.ExecOutput);
2155
0
  target.PostBuildExecNodes.Nodes.emplace_back(std::move(exec));
2156
0
}
2157
2158
void cmFastbuildNormalTargetGenerator::GenerateLink(
2159
  FastbuildTarget& target, std::vector<std::string> const& objectDepends)
2160
0
{
2161
0
  std::string const targetName = this->GetTargetName();
2162
0
  cmGeneratorTarget::Names const targetNames = DetectOutput();
2163
0
  LogMessage("targetNames.Real: " + targetNames.Real);
2164
0
  LogMessage("targetNames.ImportOutput: " + targetNames.ImportOutput);
2165
0
  LogMessage("targetNames.SharedObject: " + targetNames.SharedObject);
2166
0
  LogMessage("targetNames.Base: " + targetNames.Base);
2167
2168
0
  std::vector<std::string> allNodes;
2169
0
  auto const arches = this->GetArches();
2170
0
  for (std::size_t i = 0; i < arches.size(); ++i) {
2171
0
    auto const& arch = arches[i];
2172
0
    FastbuildLinkerNode linkerNode;
2173
0
    ProcessManifests(linkerNode);
2174
    // Objects built by the current target.
2175
0
    for (auto const& objectList : target.ObjectListNodes) {
2176
0
      if (objectList.arch.empty() || objectList.arch == arch) {
2177
0
        linkerNode.LibrarianAdditionalInputs.push_back(objectList.Name);
2178
0
      }
2179
0
    }
2180
2181
    // Detection of the link command as follows:
2182
0
    auto const type = this->GeneratorTarget->GetType();
2183
0
    switch (type) {
2184
0
      case cmStateEnums::EXECUTABLE: {
2185
0
        LogMessage("Generating EXECUTABLE");
2186
0
        linkerNode.Type = FastbuildLinkerNode::EXECUTABLE;
2187
0
        break;
2188
0
      }
2189
0
      case cmStateEnums::MODULE_LIBRARY: {
2190
0
        LogMessage("Generating MODULE_LIBRARY");
2191
0
        linkerNode.Type = FastbuildLinkerNode::SHARED_LIBRARY;
2192
0
        break;
2193
0
      }
2194
0
      case cmStateEnums::SHARED_LIBRARY: {
2195
0
        LogMessage("Generating SHARED_LIBRARY");
2196
0
        linkerNode.Type = FastbuildLinkerNode::SHARED_LIBRARY;
2197
0
        break;
2198
0
      }
2199
0
      case cmStateEnums::STATIC_LIBRARY: {
2200
0
        LogMessage("Generating STATIC_LIBRARY");
2201
0
        linkerNode.Type = FastbuildLinkerNode::STATIC_LIBRARY;
2202
0
        break;
2203
0
      }
2204
0
      case cmStateEnums::OBJECT_LIBRARY: {
2205
0
        LogMessage("Generating OBJECT_LIBRARY");
2206
0
        return;
2207
0
      }
2208
0
      default: {
2209
0
        LogMessage("Skipping GenerateLink");
2210
0
        return;
2211
0
      }
2212
0
    }
2213
2214
0
    std::string const targetOutput =
2215
0
      ConvertToFastbuildPath(GeneratorTarget->GetFullPath(Config));
2216
0
    std::string targetOutputReal = ConvertToFastbuildPath(
2217
0
      GeneratorTarget->GetFullPath(Config, cmStateEnums::RuntimeBinaryArtifact,
2218
0
                                   /*realname=*/true));
2219
0
    LogMessage("targetOutput: " + targetOutput);
2220
0
    LogMessage("targetOutputReal: " + targetOutputReal);
2221
2222
0
    std::string const output =
2223
0
      cmSystemTools::GetFilenameName(targetNames.Output);
2224
0
    std::string const outputReal =
2225
0
      cmSystemTools::GetFilenameName(targetNames.Real);
2226
    // Generate "Copy" nodes for copying Framework / Bundle resources.
2227
0
    AppendExtraResources(linkerNode.PreBuildDependencies);
2228
2229
0
    if (type == cmStateEnums::EXECUTABLE ||
2230
0
        type == cmStateEnums::SHARED_LIBRARY) {
2231
      // Tested in "RunCMake.BuildDepends" test (we need to rebuild when
2232
      // manifest  changes).
2233
0
      std::copy(objectDepends.begin(), objectDepends.end(),
2234
0
                std::back_inserter(linkerNode.Libraries2));
2235
0
    }
2236
2237
0
    if (GeneratorTarget->IsAppBundleOnApple()) {
2238
      // Create the app bundle
2239
0
      std::string outpath = GeneratorTarget->GetDirectory(Config);
2240
0
      this->OSXBundleGenerator->CreateAppBundle(targetNames.Output, outpath,
2241
0
                                                Config);
2242
0
      targetOutputReal = cmStrCat(outpath, '/', outputReal);
2243
0
      targetOutputReal = this->ConvertToFastbuildPath(targetOutputReal);
2244
0
    } else if (GeneratorTarget->IsFrameworkOnApple()) {
2245
      // Create the library framework.
2246
0
      this->OSXBundleGenerator->CreateFramework(
2247
0
        targetNames.Output, GeneratorTarget->GetDirectory(Config), Config);
2248
0
    } else if (GeneratorTarget->IsCFBundleOnApple()) {
2249
      // Create the core foundation bundle.
2250
0
      this->OSXBundleGenerator->CreateCFBundle(
2251
0
        targetNames.Output, GeneratorTarget->GetDirectory(Config), Config);
2252
0
    }
2253
2254
0
    std::string linkCmd;
2255
0
    if (!DetectBaseLinkerCommand(linkCmd, arch, targetNames)) {
2256
0
      LogMessage("No linker command detected");
2257
0
      return;
2258
0
    }
2259
2260
0
    std::string executable;
2261
0
    std::string linkerOptions;
2262
0
    std::string linkerType = "auto";
2263
2264
0
    GetLinkerExecutableAndArgs(linkCmd, executable, linkerOptions);
2265
2266
0
    linkerNode.Compiler = ".Compiler_dummy";
2267
0
    linkerNode.CompilerOptions = " ";
2268
2269
0
    linkerNode.Name = targetName;
2270
0
    linkerNode.LinkerOutput = targetOutputReal;
2271
0
    this->GetGlobalGenerator()->AddFileToClean(linkerNode.LinkerOutput);
2272
0
    target.RealOutput = targetOutputReal;
2273
0
    if (!arch.empty()) {
2274
0
      linkerNode.Name += cmStrCat('-', arch);
2275
0
      linkerNode.LinkerOutput += cmStrCat('.', arch);
2276
0
      linkerNode.Arch = arch;
2277
0
    }
2278
0
    linkerNode.Linker = executable;
2279
0
    linkerNode.LinkerType = linkerType;
2280
0
    linkerNode.LinkerOptions += linkerOptions;
2281
2282
    // Check if we have CUDA device link stub for this target.
2283
0
    FastbuildLinkerNode dummyCudaDeviceLinkNode;
2284
0
    AppendLinkDeps(target.PreBuildDependencies, linkerNode,
2285
0
                   target.CudaDeviceLinkNode.size() > i
2286
0
                     ? target.CudaDeviceLinkNode[i]
2287
0
                     : dummyCudaDeviceLinkNode);
2288
0
    ApplyLWYUToLinkerCommand(linkerNode);
2289
2290
    // On macOS, only the last LinkerNode performs lipo in POST_BUILD.
2291
    // Make it depend on all previous nodes to ensure correct execution order.
2292
0
    if (i == arches.size() - 1) {
2293
0
      for (auto& prevNode : allNodes) {
2294
0
        linkerNode.PreBuildDependencies.emplace(std::move(prevNode));
2295
0
      }
2296
0
    } else {
2297
0
      allNodes.emplace_back(linkerNode.Name);
2298
0
    }
2299
0
    if (!target.ObjectListNodes.empty()) {
2300
      // Just reuse any of compiler options mainly for the correct IDE project
2301
      // generation.
2302
0
      linkerNode.CompilerOptions = target.ObjectListNodes[0].CompilerOptions;
2303
0
    }
2304
0
    target.LinkerNode.emplace_back(std::move(linkerNode));
2305
0
  }
2306
0
}
2307
2308
std::vector<FastbuildExecNode>
2309
cmFastbuildNormalTargetGenerator::GetSymlinkExecs() const
2310
0
{
2311
0
  std::vector<FastbuildExecNode> res;
2312
0
  cmGeneratorTarget::Names const targetNames = DetectOutput();
2313
0
  LogMessage("targetNames.Real: " + targetNames.Real);
2314
0
  LogMessage("targetNames.ImportOutput: " + targetNames.ImportOutput);
2315
0
  LogMessage("targetNames.SharedObject: " + targetNames.SharedObject);
2316
0
  LogMessage("targetNames.Base: " + targetNames.Base);
2317
2318
0
  std::string const targetOutput =
2319
0
    ConvertToFastbuildPath(GeneratorTarget->GetFullPath(Config));
2320
0
  std::string const targetOutputReal = ConvertToFastbuildPath(
2321
0
    GeneratorTarget->GetFullPath(Config, cmStateEnums::RuntimeBinaryArtifact,
2322
0
                                 /*realname=*/true));
2323
0
  LogMessage("targetOutput: " + targetOutput);
2324
2325
0
  LogMessage("targetOutputReal: " + targetOutputReal);
2326
2327
0
  if (targetOutput != targetOutputReal &&
2328
0
      !GeneratorTarget->IsFrameworkOnApple()) {
2329
0
    auto const generateSymlinkCommand = [&](std::string const& from,
2330
0
                                            std::string const& to) {
2331
0
      if (from.empty() || to.empty() || from == to) {
2332
0
        return;
2333
0
      }
2334
0
      LogMessage(cmStrCat("Symlinking ", from, " -> ", to));
2335
0
      FastbuildExecNode postBuildExecNode;
2336
0
      postBuildExecNode.Name = "cmake_symlink_" + to;
2337
0
      postBuildExecNode.ExecOutput =
2338
0
        cmJoin({ GeneratorTarget->GetDirectory(Config), to }, "/");
2339
0
      postBuildExecNode.ExecExecutable = cmSystemTools::GetCMakeCommand();
2340
0
      postBuildExecNode.ExecArguments = cmStrCat(
2341
0
        "-E cmake_symlink_executable ",
2342
0
        cmGlobalFastbuildGenerator::QuoteIfHasSpaces(from), ' ',
2343
0
        cmGlobalFastbuildGenerator::QuoteIfHasSpaces(
2344
0
          this->ConvertToFastbuildPath(postBuildExecNode.ExecOutput)));
2345
0
      res.emplace_back(std::move(postBuildExecNode));
2346
0
    };
2347
0
    generateSymlinkCommand(targetNames.Real, targetNames.Output);
2348
0
    generateSymlinkCommand(targetNames.Real, targetNames.SharedObject);
2349
0
    generateSymlinkCommand(targetNames.ImportReal, targetNames.ImportOutput);
2350
0
  }
2351
0
  return res;
2352
0
}