Coverage Report

Created: 2026-07-14 07:09

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