Coverage Report

Created: 2026-10-28 06:38

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