Coverage Report

Created: 2026-06-15 07:03

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