Coverage Report

Created: 2026-04-29 07:01

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