Coverage Report

Created: 2026-04-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmNinjaNormalTargetGenerator.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
#include "cmNinjaNormalTargetGenerator.h"
4
5
#include <algorithm>
6
#include <cassert>
7
#include <iterator>
8
#include <set>
9
#include <sstream>
10
#include <unordered_set>
11
#include <utility>
12
13
#include <cm/filesystem>
14
#include <cm/memory>
15
#include <cm/optional>
16
#include <cm/vector>
17
18
#include "cmComputeLinkInformation.h"
19
#include "cmCustomCommand.h" // IWYU pragma: keep
20
#include "cmCustomCommandGenerator.h"
21
#include "cmGeneratedFileStream.h"
22
#include "cmGeneratorExpression.h"
23
#include "cmGeneratorOptions.h"
24
#include "cmGeneratorTarget.h"
25
#include "cmGlobalNinjaGenerator.h"
26
#include "cmLinkLineComputer.h"
27
#include "cmLinkLineDeviceComputer.h"
28
#include "cmList.h"
29
#include "cmLocalCommonGenerator.h"
30
#include "cmLocalGenerator.h"
31
#include "cmLocalNinjaGenerator.h"
32
#include "cmMakefile.h"
33
#include "cmMessageType.h"
34
#include "cmNinjaLinkLineDeviceComputer.h"
35
#include "cmNinjaTypes.h"
36
#include "cmOSXBundleGenerator.h"
37
#include "cmOutputConverter.h"
38
#include "cmRulePlaceholderExpander.h"
39
#include "cmSourceFile.h"
40
#include "cmState.h"
41
#include "cmStateDirectory.h"
42
#include "cmStateSnapshot.h"
43
#include "cmStateTypes.h"
44
#include "cmStringAlgorithms.h"
45
#include "cmSystemTools.h"
46
#include "cmValue.h"
47
48
cmNinjaNormalTargetGenerator::cmNinjaNormalTargetGenerator(
49
  cmGeneratorTarget* target)
50
0
  : cmNinjaTargetGenerator(target)
51
0
{
52
0
  if (target->GetType() != cmStateEnums::OBJECT_LIBRARY) {
53
    // on Windows the output dir is already needed at compile time
54
    // ensure the directory exists (OutDir test)
55
0
    for (auto const& config : this->GetConfigNames()) {
56
0
      this->EnsureDirectoryExists(target->GetDirectory(config));
57
0
    }
58
0
  }
59
60
0
  this->OSXBundleGenerator = cm::make_unique<cmOSXBundleGenerator>(target);
61
0
  this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
62
0
}
63
64
0
cmNinjaNormalTargetGenerator::~cmNinjaNormalTargetGenerator() = default;
65
66
void cmNinjaNormalTargetGenerator::Generate(std::string const& config)
67
0
{
68
0
  if (this->GetGeneratorTarget()->GetType() !=
69
0
      cmStateEnums::INTERFACE_LIBRARY) {
70
0
    std::string lang = this->GeneratorTarget->GetLinkerLanguage(config);
71
0
    if (this->TargetLinkLanguage(config).empty()) {
72
0
      cmSystemTools::Error(
73
0
        cmStrCat("CMake can not determine linker language for target: ",
74
0
                 this->GetGeneratorTarget()->GetName()));
75
0
      return;
76
0
    }
77
0
  }
78
79
  // Write the rules for each language.
80
0
  this->WriteLanguagesRules(config);
81
82
  // Write the build statements
83
0
  bool firstForConfig = true;
84
0
  for (auto const& fileConfig : this->GetConfigNames()) {
85
0
    if (!this->GetGlobalGenerator()
86
0
           ->GetCrossConfigs(fileConfig)
87
0
           .count(config)) {
88
0
      continue;
89
0
    }
90
0
    this->WriteObjectBuildStatements(config, fileConfig, firstForConfig);
91
0
    firstForConfig = false;
92
0
  }
93
94
0
  if (this->GetGeneratorTarget()->GetType() == cmStateEnums::OBJECT_LIBRARY) {
95
0
    this->WriteObjectLibStatement(config);
96
0
  } else if (this->GetGeneratorTarget()->GetType() ==
97
0
             cmStateEnums::INTERFACE_LIBRARY) {
98
0
    bool haveCxxModuleSources = false;
99
0
    if (this->GetGeneratorTarget()->HaveCxx20ModuleSources()) {
100
0
      haveCxxModuleSources = true;
101
0
    }
102
103
0
    if (!haveCxxModuleSources) {
104
0
      cmSystemTools::Error(cmStrCat(
105
0
        "Ninja does not support INTERFACE libraries without C++ module "
106
0
        "sources as a normal target: ",
107
0
        this->GetGeneratorTarget()->GetName()));
108
0
      return;
109
0
    }
110
111
0
    firstForConfig = true;
112
0
    for (auto const& fileConfig : this->GetConfigNames()) {
113
0
      if (!this->GetGlobalGenerator()
114
0
             ->GetCrossConfigs(fileConfig)
115
0
             .count(config)) {
116
0
        continue;
117
0
      }
118
0
      if (haveCxxModuleSources) {
119
0
        this->WriteCxxModuleLibraryStatement(config, fileConfig,
120
0
                                             firstForConfig);
121
0
      }
122
0
      firstForConfig = false;
123
0
    }
124
0
  } else {
125
0
    firstForConfig = true;
126
0
    for (auto const& fileConfig : this->GetConfigNames()) {
127
0
      if (!this->GetGlobalGenerator()
128
0
             ->GetCrossConfigs(fileConfig)
129
0
             .count(config)) {
130
0
        continue;
131
0
      }
132
      // If this target has cuda language link inputs, and we need to do
133
      // device linking
134
0
      this->WriteDeviceLinkStatement(config, fileConfig, firstForConfig);
135
0
      this->WriteLinkStatement(config, fileConfig, firstForConfig);
136
0
      firstForConfig = false;
137
0
    }
138
0
  }
139
0
  if (this->GetGlobalGenerator()->EnableCrossConfigBuild()) {
140
0
    this->GetGlobalGenerator()->AddTargetAlias(
141
0
      this->GetTargetName(), this->GetGeneratorTarget(), "all");
142
0
  }
143
144
  // Find ADDITIONAL_CLEAN_FILES
145
0
  this->AdditionalCleanFiles(config);
146
0
}
147
148
void cmNinjaNormalTargetGenerator::WriteLanguagesRules(
149
  std::string const& config)
150
0
{
151
#ifdef NINJA_GEN_VERBOSE_FILES
152
  cmGlobalNinjaGenerator::WriteDivider(this->GetRulesFileStream());
153
  this->GetRulesFileStream()
154
    << "# Rules for each language for "
155
    << cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType())
156
    << " target " << this->GetTargetName() << "\n\n";
157
#endif
158
159
  // Write rules for languages compiled in this target.
160
0
  {
161
0
    std::set<std::string> languages;
162
0
    std::vector<cmSourceFile const*> sourceFiles;
163
0
    this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config);
164
0
    if (this->HaveRequiredLanguages(sourceFiles, languages)) {
165
0
      for (std::string const& language : languages) {
166
0
        this->WriteLanguageRules(language, config);
167
0
      }
168
0
    }
169
0
  }
170
171
  // Write rules for languages in BMI-only rules.
172
0
  {
173
0
    std::set<std::string> languages;
174
0
    std::vector<cmSourceFile const*> sourceFiles;
175
0
    this->GetGeneratorTarget()->GetCxxModuleSources(sourceFiles, config);
176
0
    if (this->HaveRequiredLanguages(sourceFiles, languages)) {
177
0
      for (std::string const& language : languages) {
178
0
        this->WriteLanguageRules(language, config);
179
0
      }
180
0
    }
181
0
  }
182
0
}
183
184
char const* cmNinjaNormalTargetGenerator::GetVisibleTypeName() const
185
0
{
186
0
  switch (this->GetGeneratorTarget()->GetType()) {
187
0
    case cmStateEnums::STATIC_LIBRARY:
188
0
      return "static library";
189
0
    case cmStateEnums::SHARED_LIBRARY:
190
0
      return "shared library";
191
0
    case cmStateEnums::MODULE_LIBRARY:
192
0
      if (this->GetGeneratorTarget()->IsCFBundleOnApple()) {
193
0
        return "CFBundle shared module";
194
0
      } else {
195
0
        return "shared module";
196
0
      }
197
0
    case cmStateEnums::EXECUTABLE:
198
0
      return "executable";
199
0
    default:
200
0
      return nullptr;
201
0
  }
202
0
}
203
204
std::string cmNinjaNormalTargetGenerator::LanguageLinkerRule(
205
  std::string const& config) const
206
0
{
207
0
  return cmStrCat(
208
0
    this->TargetLinkLanguage(config), '_',
209
0
    cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()),
210
0
    "_LINKER__",
211
0
    cmGlobalNinjaGenerator::EncodeRuleName(
212
0
      this->GetGeneratorTarget()->GetName()),
213
0
    '_', config);
214
0
}
215
216
std::string cmNinjaNormalTargetGenerator::LanguageLinkerDeviceRule(
217
  std::string const& config) const
218
0
{
219
0
  return cmStrCat(
220
0
    this->TargetLinkLanguage(config), '_',
221
0
    cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()),
222
0
    "_DEVICE_LINKER__",
223
0
    cmGlobalNinjaGenerator::EncodeRuleName(
224
0
      this->GetGeneratorTarget()->GetName()),
225
0
    '_', config);
226
0
}
227
228
std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaDeviceRule(
229
  std::string const& config) const
230
0
{
231
0
  return cmStrCat(
232
0
    this->TargetLinkLanguage(config), "_DEVICE_LINK__",
233
0
    cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
234
0
    '_', config);
235
0
}
236
237
std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaDeviceCompileRule(
238
  std::string const& config) const
239
0
{
240
0
  return cmStrCat(
241
0
    this->TargetLinkLanguage(config), "_DEVICE_LINK_COMPILE__",
242
0
    cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
243
0
    '_', config);
244
0
}
245
246
std::string cmNinjaNormalTargetGenerator::LanguageLinkerCudaFatbinaryRule(
247
  std::string const& config) const
248
0
{
249
0
  return cmStrCat(
250
0
    this->TargetLinkLanguage(config), "_FATBINARY__",
251
0
    cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
252
0
    '_', config);
253
0
}
254
255
std::string cmNinjaNormalTargetGenerator::TextStubsGeneratorRule(
256
  std::string const& config) const
257
0
{
258
0
  return cmStrCat(
259
0
    "TEXT_STUBS_GENERATOR__",
260
0
    cmGlobalNinjaGenerator::EncodeRuleName(this->GeneratorTarget->GetName()),
261
0
    '_', config);
262
0
}
263
264
bool cmNinjaNormalTargetGenerator::CheckUseResponseFileForLibraries(
265
  std::string const& l) const
266
0
{
267
  // Check for an explicit setting one way or the other.
268
0
  std::string const responseVar =
269
0
    "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_LIBRARIES";
270
271
  // If the option is defined, read it's value
272
0
  if (cmValue val = this->Makefile->GetDefinition(responseVar)) {
273
0
    return val.IsOn();
274
0
  }
275
276
  // Default to true
277
0
  return true;
278
0
}
279
280
struct cmNinjaRemoveNoOpCommands
281
{
282
  bool operator()(std::string const& cmd)
283
0
  {
284
0
    return cmd.empty() || cmd[0] == ':';
285
0
  }
286
};
287
288
void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkRule(
289
  bool useResponseFile, std::string const& config)
290
0
{
291
0
  cmNinjaRule rule(this->LanguageLinkerDeviceRule(config));
292
0
  if (!this->GetGlobalGenerator()->HasRule(rule.Name)) {
293
0
    cmRulePlaceholderExpander::RuleVariables vars;
294
0
    vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
295
0
    vars.CMTargetType =
296
0
      cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType())
297
0
        .c_str();
298
0
    vars.Language = "CUDA";
299
0
    std::string linker =
300
0
      this->GetGeneratorTarget()->GetLinkerTool("CUDA", config);
301
0
    vars.Linker = linker.c_str();
302
303
    // build response file name
304
0
    std::string responseFlag = this->GetMakefile()->GetSafeDefinition(
305
0
      "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG");
306
307
0
    if (!useResponseFile || responseFlag.empty()) {
308
0
      vars.Objects = "$in";
309
0
      vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
310
0
    } else {
311
0
      rule.RspFile = "$RSP_FILE";
312
0
      responseFlag += rule.RspFile;
313
314
      // build response file content
315
0
      if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
316
0
        rule.RspContent = "$in";
317
0
      } else {
318
0
        rule.RspContent = "$in_newline";
319
0
      }
320
321
      // add the link command in the file if necessary
322
0
      if (this->CheckUseResponseFileForLibraries("CUDA")) {
323
0
        rule.RspContent += " $LINK_LIBRARIES";
324
0
        vars.LinkLibraries = "";
325
0
      } else {
326
0
        vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
327
0
      }
328
329
0
      vars.Objects = responseFlag.c_str();
330
0
    }
331
332
0
    vars.ObjectDir = "$OBJECT_DIR";
333
0
    vars.TargetSupportDir = "$TARGET_SUPPORT_DIR";
334
335
0
    vars.Target = "$TARGET_FILE";
336
337
0
    vars.SONameFlag = "$SONAME_FLAG";
338
0
    vars.TargetSOName = "$SONAME";
339
0
    vars.TargetPDB = "$TARGET_PDB";
340
0
    vars.TargetCompilePDB = "$TARGET_COMPILE_PDB";
341
342
0
    vars.Flags = "$FLAGS";
343
0
    vars.LinkFlags = "$LINK_FLAGS";
344
0
    vars.Manifests = "$MANIFESTS";
345
0
    vars.Config = "$CONFIG";
346
347
0
    vars.LanguageCompileFlags = "$LANGUAGE_COMPILE_FLAGS";
348
349
0
    std::string launcher;
350
0
    std::string val = this->GetLocalGenerator()->GetRuleLauncher(
351
0
      this->GetGeneratorTarget(), "RULE_LAUNCH_LINK", config);
352
0
    if (cmNonempty(val)) {
353
0
      launcher = cmStrCat(val, ' ');
354
0
    }
355
356
0
    auto rulePlaceholderExpander =
357
0
      this->GetLocalGenerator()->CreateRulePlaceholderExpander(
358
0
        cmBuildStep::Link);
359
360
    // Rule for linking library/executable.
361
0
    std::vector<std::string> linkCmds = this->ComputeDeviceLinkCmd();
362
0
    for (std::string& linkCmd : linkCmds) {
363
0
      linkCmd = cmStrCat(launcher, linkCmd);
364
0
      rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
365
0
                                                   linkCmd, vars);
366
0
    }
367
368
    // If there is no ranlib the command will be ":".  Skip it.
369
0
    cm::erase_if(linkCmds, cmNinjaRemoveNoOpCommands());
370
371
0
    rule.Command =
372
0
      this->GetLocalGenerator()->BuildCommandLine(linkCmds, config, config);
373
374
    // Write the linker rule with response file if needed.
375
0
    rule.Comment =
376
0
      cmStrCat("Rule for linking ", this->TargetLinkLanguage(config), ' ',
377
0
               this->GetVisibleTypeName(), '.');
378
0
    rule.Description =
379
0
      cmStrCat("Linking ", this->TargetLinkLanguage(config), ' ',
380
0
               this->GetVisibleTypeName(), " $TARGET_FILE");
381
0
    rule.Restat = "$RESTAT";
382
383
0
    this->GetGlobalGenerator()->AddRule(rule);
384
0
  }
385
0
}
386
387
void cmNinjaNormalTargetGenerator::WriteDeviceLinkRules(
388
  std::string const& config)
389
0
{
390
0
  cmMakefile const* mf = this->GetMakefile();
391
392
0
  cmNinjaRule rule(this->LanguageLinkerCudaDeviceRule(config));
393
0
  rule.Command = this->GetLocalGenerator()->BuildCommandLine(
394
0
    { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"),
395
0
               " -arch=$ARCH $REGISTER -o=$out $in") },
396
0
    config, config);
397
0
  rule.Comment = "Rule for CUDA device linking.";
398
0
  rule.Description = "Linking CUDA $out";
399
0
  this->GetGlobalGenerator()->AddRule(rule);
400
401
0
  cmRulePlaceholderExpander::RuleVariables vars;
402
0
  vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
403
0
  vars.CMTargetType =
404
0
    cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str();
405
0
  vars.Language = "CUDA";
406
0
  vars.Object = "$out";
407
0
  vars.Fatbinary = "$FATBIN";
408
0
  vars.RegisterFile = "$REGISTER";
409
0
  vars.LinkFlags = "$LINK_FLAGS";
410
0
  std::string linker =
411
0
    this->GetGeneratorTarget()->GetLinkerTool("CUDA", config);
412
0
  vars.Linker = linker.c_str();
413
414
0
  std::string flags = this->GetFlags("CUDA", config);
415
0
  vars.Flags = flags.c_str();
416
0
  vars.Config = "$CONFIG";
417
418
0
  std::string compileCmd = this->GetMakefile()->GetRequiredDefinition(
419
0
    "CMAKE_CUDA_DEVICE_LINK_COMPILE");
420
0
  auto rulePlaceholderExpander =
421
0
    this->GetLocalGenerator()->CreateRulePlaceholderExpander(
422
0
      cmBuildStep::Link);
423
0
  rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
424
0
                                               compileCmd, vars);
425
426
0
  rule.Name = this->LanguageLinkerCudaDeviceCompileRule(config);
427
0
  rule.Command = this->GetLocalGenerator()->BuildCommandLine({ compileCmd },
428
0
                                                             config, config);
429
0
  rule.Comment = "Rule for compiling CUDA device stubs.";
430
0
  rule.Description = "Compiling CUDA device stub $out";
431
0
  this->GetGlobalGenerator()->AddRule(rule);
432
433
0
  rule.Name = this->LanguageLinkerCudaFatbinaryRule(config);
434
0
  rule.Command = this->GetLocalGenerator()->BuildCommandLine(
435
0
    { cmStrCat(mf->GetRequiredDefinition("CMAKE_CUDA_FATBINARY"),
436
0
               " -64 -cmdline=--compile-only -compress-all -link "
437
0
               "--embedded-fatbin=$out $PROFILES") },
438
0
    config, config);
439
0
  rule.Comment = "Rule for CUDA fatbinaries.";
440
0
  rule.Description = "Creating fatbinary $out";
441
0
  this->GetGlobalGenerator()->AddRule(rule);
442
0
}
443
444
static void NinjaSafeComment(std::string& comment)
445
0
{
446
  // Replace control characters in comments.
447
0
  cmSystemTools::ReplaceString(comment, "\n", " / ");
448
0
  cmSystemTools::ReplaceString(comment, "$", "$$");
449
0
}
450
451
void cmNinjaNormalTargetGenerator::WriteLinkRule(
452
  bool useResponseFile, std::string const& config,
453
  std::vector<std::string> const& preLinkComments,
454
  std::vector<std::string> const& postBuildComments)
455
0
{
456
0
  cmStateEnums::TargetType targetType = this->GetGeneratorTarget()->GetType();
457
458
0
  std::string linkRuleName = this->LanguageLinkerRule(config);
459
0
  if (!this->GetGlobalGenerator()->HasRule(linkRuleName)) {
460
0
    cmNinjaRule rule(std::move(linkRuleName));
461
0
    cmRulePlaceholderExpander::RuleVariables vars;
462
0
    vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
463
0
    vars.CMTargetType = cmState::GetTargetTypeName(targetType).c_str();
464
0
    std::string linker = this->GetGeneratorTarget()->GetLinkerTool(config);
465
0
    vars.Linker = linker.c_str();
466
0
    std::string lang = this->TargetLinkLanguage(config);
467
0
    vars.Language = lang.c_str();
468
0
    vars.AIXExports = "$AIX_EXPORTS";
469
470
0
    if (!this->GetLocalGenerator()->IsSplitSwiftBuild() &&
471
0
        this->TargetLinkLanguage(config) == "Swift") {
472
0
      vars.SwiftLibraryName = "$SWIFT_LIBRARY_NAME";
473
0
      vars.SwiftModule = "$SWIFT_MODULE";
474
0
      vars.SwiftModuleName = "$SWIFT_MODULE_NAME";
475
0
      vars.SwiftSources = "$SWIFT_SOURCES";
476
477
0
      vars.Defines = "$DEFINES";
478
0
      vars.Flags = "$FLAGS";
479
0
      vars.Includes = "$INCLUDES";
480
0
    }
481
482
0
    if (this->TargetLinkLanguage(config) == "Rust") {
483
0
      vars.RustMainCrateRoot = "$RUST_MAIN_CRATE_ROOT";
484
0
      vars.RustLinkCrates = "$RUST_LINK_CRATES";
485
0
      vars.RustNativeObjects = "$RUST_NATIVE_OBJECTS";
486
0
    }
487
488
0
    std::string responseFlag;
489
490
0
    std::string cmakeVarLang =
491
0
      cmStrCat("CMAKE_", this->TargetLinkLanguage(config));
492
493
0
    if (this->GeneratorTarget->HasLinkDependencyFile(config)) {
494
0
      auto DepFileFormat = this->GetMakefile()->GetDefinition(
495
0
        cmStrCat(cmakeVarLang, "_LINKER_DEPFILE_FORMAT"));
496
0
      rule.DepType = DepFileFormat;
497
0
      rule.DepFile = "$DEP_FILE";
498
0
    }
499
500
    // build response file name
501
0
    std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
502
0
    cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
503
504
0
    if (flag) {
505
0
      responseFlag = *flag;
506
0
    } else {
507
0
      responseFlag = "@";
508
0
    }
509
510
0
    if (!useResponseFile || responseFlag.empty()) {
511
0
      vars.Objects = "$in";
512
0
      vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
513
0
    } else {
514
0
      rule.RspFile = "$RSP_FILE";
515
0
      responseFlag += rule.RspFile;
516
517
      // build response file content
518
0
      if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
519
0
        rule.RspContent = "$in";
520
0
      } else {
521
0
        rule.RspContent = "$in_newline";
522
0
      }
523
524
      // If libraries in rsp is enable
525
0
      if (this->CheckUseResponseFileForLibraries(lang)) {
526
0
        rule.RspContent += " $LINK_PATH $LINK_LIBRARIES";
527
0
        vars.LinkLibraries = "";
528
0
      } else {
529
0
        vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
530
0
      }
531
532
0
      if (!this->GetLocalGenerator()->IsSplitSwiftBuild() &&
533
0
          this->TargetLinkLanguage(config) == "Swift") {
534
0
        vars.SwiftSources = responseFlag.c_str();
535
0
      } else {
536
0
        vars.Objects = responseFlag.c_str();
537
0
      }
538
0
    }
539
540
0
    vars.ObjectDir = "$OBJECT_DIR";
541
0
    vars.TargetSupportDir = "$TARGET_SUPPORT_DIR";
542
543
0
    vars.Target = "$TARGET_FILE";
544
545
0
    vars.SONameFlag = "$SONAME_FLAG";
546
0
    vars.TargetSOName = "$SONAME";
547
0
    vars.TargetInstallNameDir = "$INSTALLNAME_DIR";
548
0
    vars.TargetPDB = "$TARGET_PDB";
549
550
    // Setup the target version.
551
0
    std::string targetVersionMajor;
552
0
    std::string targetVersionMinor;
553
0
    {
554
0
      std::ostringstream majorStream;
555
0
      std::ostringstream minorStream;
556
0
      int major;
557
0
      int minor;
558
0
      this->GetGeneratorTarget()->GetTargetVersion(major, minor);
559
0
      majorStream << major;
560
0
      minorStream << minor;
561
0
      targetVersionMajor = majorStream.str();
562
0
      targetVersionMinor = minorStream.str();
563
0
    }
564
0
    vars.TargetVersionMajor = targetVersionMajor.c_str();
565
0
    vars.TargetVersionMinor = targetVersionMinor.c_str();
566
567
0
    vars.Flags = "$FLAGS";
568
0
    vars.LinkFlags = "$LINK_FLAGS";
569
0
    vars.Manifests = "$MANIFESTS";
570
0
    vars.Config = "$CONFIG";
571
572
0
    std::string langFlags;
573
0
    if (targetType != cmStateEnums::EXECUTABLE) {
574
0
      langFlags += "$LANGUAGE_COMPILE_FLAGS $ARCH_FLAGS";
575
0
      vars.LanguageCompileFlags = langFlags.c_str();
576
0
    }
577
578
0
    std::string linkerLauncher = this->GetLinkerLauncher(config);
579
0
    if (cmNonempty(linkerLauncher)) {
580
0
      vars.Launcher = linkerLauncher.c_str();
581
0
    }
582
583
0
    std::string launcher;
584
0
    std::string val = this->GetLocalGenerator()->GetRuleLauncher(
585
0
      this->GetGeneratorTarget(), "RULE_LAUNCH_LINK", config);
586
0
    if (cmNonempty(val)) {
587
0
      launcher = cmStrCat(val, ' ');
588
0
    }
589
590
0
    auto rulePlaceholderExpander =
591
0
      this->GetLocalGenerator()->CreateRulePlaceholderExpander(
592
0
        cmBuildStep::Link);
593
594
    // Rule for linking library/executable.
595
0
    std::vector<std::string> linkCmds = this->ComputeLinkCmd(config);
596
0
    for (std::string& linkCmd : linkCmds) {
597
0
      linkCmd = cmStrCat(launcher, linkCmd);
598
0
      rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
599
0
                                                   linkCmd, vars);
600
0
    }
601
602
    // If there is no ranlib the command will be ":".  Skip it.
603
0
    cm::erase_if(linkCmds, cmNinjaRemoveNoOpCommands());
604
605
0
    linkCmds.insert(linkCmds.begin(), "$PRE_LINK");
606
0
    linkCmds.emplace_back("$POST_BUILD");
607
0
    rule.Command =
608
0
      this->GetLocalGenerator()->BuildCommandLine(linkCmds, config, config);
609
610
    // Write the linker rule with response file if needed.
611
0
    rule.Comment =
612
0
      cmStrCat("Rule for linking ", this->TargetLinkLanguage(config), ' ',
613
0
               this->GetVisibleTypeName(), '.');
614
0
    char const* presep = "";
615
0
    char const* postsep = "";
616
0
    auto prelink = cmJoin(preLinkComments, "; ");
617
0
    NinjaSafeComment(prelink);
618
0
    if (!prelink.empty()) {
619
0
      presep = "; ";
620
0
    }
621
0
    auto postbuild = cmJoin(postBuildComments, "; ");
622
0
    NinjaSafeComment(postbuild);
623
0
    if (!postbuild.empty()) {
624
0
      postsep = "; ";
625
0
    }
626
0
    rule.Description = cmStrCat(
627
0
      prelink, presep, "Linking ", this->TargetLinkLanguage(config), ' ',
628
0
      this->GetVisibleTypeName(), " $TARGET_FILE", postsep, postbuild);
629
0
    rule.Restat = "$RESTAT";
630
0
    this->GetGlobalGenerator()->AddRule(rule);
631
0
  }
632
633
0
  auto const tgtNames = this->TargetNames(config);
634
0
  if (tgtNames.Output != tgtNames.Real &&
635
0
      !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
636
0
    std::string cmakeCommand =
637
0
      this->GetLocalGenerator()->ConvertToOutputFormat(
638
0
        cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
639
0
    if (targetType == cmStateEnums::EXECUTABLE) {
640
0
      cmNinjaRule rule("CMAKE_SYMLINK_EXECUTABLE");
641
0
      {
642
0
        std::vector<std::string> cmd;
643
0
        cmd.push_back(cmakeCommand + " -E cmake_symlink_executable $in $out");
644
0
        cmd.emplace_back("$POST_BUILD");
645
0
        rule.Command =
646
0
          this->GetLocalGenerator()->BuildCommandLine(cmd, config, config);
647
0
      }
648
0
      rule.Description = "Creating executable symlink $out";
649
0
      rule.Comment = "Rule for creating executable symlink.";
650
0
      this->GetGlobalGenerator()->AddRule(rule);
651
0
    } else {
652
0
      cmNinjaRule rule("CMAKE_SYMLINK_LIBRARY");
653
0
      {
654
0
        std::vector<std::string> cmd;
655
0
        cmd.push_back(cmakeCommand +
656
0
                      " -E cmake_symlink_library $in $SONAME $out");
657
0
        cmd.emplace_back("$POST_BUILD");
658
0
        rule.Command =
659
0
          this->GetLocalGenerator()->BuildCommandLine(cmd, config, config);
660
0
      }
661
0
      rule.Description = "Creating library symlink $out";
662
0
      rule.Comment = "Rule for creating library symlink.";
663
0
      this->GetGlobalGenerator()->AddRule(rule);
664
0
    }
665
0
  }
666
667
0
  if (this->GetGeneratorTarget()->IsApple() &&
668
0
      this->GetGeneratorTarget()->HasImportLibrary(config)) {
669
0
    cmNinjaRule rule(this->TextStubsGeneratorRule(config));
670
0
    rule.Comment = cmStrCat("Rule for generating text-based stubs for ",
671
0
                            this->GetVisibleTypeName(), '.');
672
0
    rule.Description = "Creating text-based stubs $out";
673
674
0
    std::string cmd =
675
0
      this->GetMakefile()->GetDefinition("CMAKE_CREATE_TEXT_STUBS");
676
0
    auto rulePlaceholderExpander =
677
0
      this->GetLocalGenerator()->CreateRulePlaceholderExpander();
678
0
    cmRulePlaceholderExpander::RuleVariables vars;
679
0
    vars.Target = "$in";
680
0
    rulePlaceholderExpander->SetTargetImpLib("$out");
681
0
    rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
682
0
                                                 cmd, vars);
683
684
0
    rule.Command =
685
0
      this->GetLocalGenerator()->BuildCommandLine({ cmd }, config, config);
686
0
    this->GetGlobalGenerator()->AddRule(rule);
687
688
0
    if (tgtNames.ImportOutput != tgtNames.ImportReal &&
689
0
        !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
690
0
      cmNinjaRule slRule("CMAKE_SYMLINK_IMPORT_LIBRARY");
691
0
      {
692
0
        std::string cmakeCommand =
693
0
          this->GetLocalGenerator()->ConvertToOutputFormat(
694
0
            cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
695
0
        std::string slCmd =
696
0
          cmStrCat(cmakeCommand, " -E cmake_symlink_library $in $SONAME $out");
697
0
        slRule.Command = this->GetLocalGenerator()->BuildCommandLine(
698
0
          { slCmd }, config, config);
699
0
      }
700
0
      slRule.Description = "Creating import library symlink $out";
701
0
      slRule.Comment = "Rule for creating import library symlink.";
702
0
      this->GetGlobalGenerator()->AddRule(slRule);
703
0
    }
704
0
  }
705
0
}
706
707
std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd()
708
0
{
709
0
  cmList linkCmds;
710
711
  // this target requires separable cuda compilation
712
  // now build the correct command depending on if the target is
713
  // an executable or a dynamic library.
714
0
  switch (this->GetGeneratorTarget()->GetType()) {
715
0
    case cmStateEnums::STATIC_LIBRARY:
716
0
    case cmStateEnums::SHARED_LIBRARY:
717
0
    case cmStateEnums::MODULE_LIBRARY: {
718
0
      linkCmds.assign(
719
0
        this->GetMakefile()->GetDefinition("CMAKE_CUDA_DEVICE_LINK_LIBRARY"));
720
0
    } break;
721
0
    case cmStateEnums::EXECUTABLE: {
722
0
      linkCmds.assign(this->GetMakefile()->GetDefinition(
723
0
        "CMAKE_CUDA_DEVICE_LINK_EXECUTABLE"));
724
0
    } break;
725
0
    default:
726
0
      break;
727
0
  }
728
0
  return std::move(linkCmds.data());
729
0
}
730
731
std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd(
732
  std::string const& config)
733
0
{
734
0
  cmList linkCmds;
735
0
  cmMakefile* mf = this->GetMakefile();
736
0
  {
737
    // If we have a rule variable prefer it. In the case of static libraries
738
    // this occurs when things like IPO is enabled, and we need to use the
739
    // CMAKE_<lang>_CREATE_STATIC_LIBRARY_IPO define instead.
740
0
    std::string linkCmdVar = this->GetGeneratorTarget()->GetCreateRuleVariable(
741
0
      this->TargetLinkLanguage(config), config);
742
0
    cmValue linkCmd = mf->GetDefinition(linkCmdVar);
743
0
    if (linkCmd) {
744
0
      std::string linkCmdStr = *linkCmd;
745
0
      if (this->GetGeneratorTarget()->HasImplibGNUtoMS(config)) {
746
0
        std::string ruleVar =
747
0
          cmStrCat("CMAKE_", this->GeneratorTarget->GetLinkerLanguage(config),
748
0
                   "_GNUtoMS_RULE");
749
0
        if (cmValue rule = this->Makefile->GetDefinition(ruleVar)) {
750
0
          linkCmdStr += *rule;
751
0
        }
752
0
      }
753
0
      linkCmds.assign(linkCmdStr);
754
0
      if (this->UseLWYU) {
755
0
        cmValue lwyuCheck = mf->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK");
756
0
        if (lwyuCheck) {
757
0
          std::string cmakeCommand = cmStrCat(
758
0
            this->GetLocalGenerator()->ConvertToOutputFormat(
759
0
              cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
760
0
            " -E __run_co_compile --lwyu=");
761
0
          cmakeCommand +=
762
0
            this->GetLocalGenerator()->EscapeForShell(*lwyuCheck);
763
764
0
          std::string targetOutputReal =
765
0
            this->ConvertToNinjaPath(this->GetGeneratorTarget()->GetFullPath(
766
0
              config, cmStateEnums::RuntimeBinaryArtifact,
767
0
              /*realname=*/true));
768
0
          cmakeCommand += cmStrCat(" --source=", targetOutputReal);
769
0
          linkCmds.push_back(std::move(cmakeCommand));
770
0
        }
771
0
      }
772
0
      return std::move(linkCmds.data());
773
0
    }
774
0
  }
775
0
  switch (this->GetGeneratorTarget()->GetType()) {
776
0
    case cmStateEnums::STATIC_LIBRARY: {
777
      // We have archive link commands set. First, delete the existing archive.
778
0
      {
779
0
        std::string cmakeCommand =
780
0
          this->GetLocalGenerator()->ConvertToOutputFormat(
781
0
            cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
782
0
        linkCmds.push_back(cmakeCommand + " -E rm -f $TARGET_FILE");
783
0
      }
784
      // TODO: Use ARCHIVE_APPEND for archives over a certain size.
785
0
      {
786
0
        std::string linkCmdVar = cmStrCat(
787
0
          "CMAKE_", this->TargetLinkLanguage(config), "_ARCHIVE_CREATE");
788
789
0
        linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
790
0
          linkCmdVar, this->TargetLinkLanguage(config), config);
791
792
0
        std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
793
0
        linkCmds.append(linkCmd);
794
0
      }
795
0
      {
796
0
        std::string linkCmdVar = cmStrCat(
797
0
          "CMAKE_", this->TargetLinkLanguage(config), "_ARCHIVE_FINISH");
798
799
0
        linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
800
0
          linkCmdVar, this->TargetLinkLanguage(config), config);
801
802
0
        std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
803
0
        linkCmds.append(linkCmd);
804
0
      }
805
#ifdef __APPLE__
806
      // On macOS ranlib truncates the fractional part of the static archive
807
      // file modification time.  If the archive and at least one contained
808
      // object file were created within the same second this will make look
809
      // the archive older than the object file. On subsequent ninja runs this
810
      // leads to re-archiving and updating dependent targets.
811
      // As a work-around we touch the archive after ranlib (see #19222).
812
      {
813
        std::string cmakeCommand =
814
          this->GetLocalGenerator()->ConvertToOutputFormat(
815
            cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
816
        linkCmds.push_back(cmakeCommand + " -E touch $TARGET_FILE");
817
      }
818
#endif
819
0
    } break;
820
0
    case cmStateEnums::SHARED_LIBRARY:
821
0
    case cmStateEnums::MODULE_LIBRARY:
822
0
    case cmStateEnums::EXECUTABLE:
823
0
      break;
824
0
    default:
825
0
      assert(false && "Unexpected target type");
826
0
  }
827
0
  return std::move(linkCmds.data());
828
0
}
829
830
void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
831
  std::string const& config, std::string const& fileConfig,
832
  bool firstForConfig)
833
0
{
834
0
  cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
835
0
  if (!globalGen->GetLanguageEnabled("CUDA")) {
836
0
    return;
837
0
  }
838
839
0
  cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
840
841
0
  bool requiresDeviceLinking = requireDeviceLinking(
842
0
    *this->GeneratorTarget, *this->GetLocalGenerator(), config);
843
0
  if (!requiresDeviceLinking) {
844
0
    return;
845
0
  }
846
847
  // First and very important step is to make sure while inside this
848
  // step our link language is set to CUDA
849
0
  std::string const& objExt =
850
0
    this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
851
852
0
  std::string targetOutputDir =
853
0
    this->GetLocalGenerator()->MaybeRelativeToTopBinDir(
854
0
      cmStrCat(genTarget->GetSupportDirectory(),
855
0
               globalGen->ConfigDirectory(config), '/'));
856
0
  targetOutputDir = globalGen->ExpandCFGIntDir(targetOutputDir, config);
857
858
0
  std::string targetOutputReal =
859
0
    this->ConvertToNinjaPath(targetOutputDir + "cmake_device_link" + objExt);
860
861
0
  if (firstForConfig) {
862
0
    globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal);
863
0
  }
864
0
  this->DeviceLinkObject = targetOutputReal;
865
866
  // Write comments.
867
0
  cmGlobalNinjaGenerator::WriteDivider(this->GetCommonFileStream());
868
0
  this->GetCommonFileStream()
869
0
    << "# Device Link build statements for "
870
0
    << cmState::GetTargetTypeName(genTarget->GetType()) << " target "
871
0
    << this->GetTargetName() << "\n\n";
872
873
0
  if (this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID") == "Clang") {
874
0
    std::string architecturesStr =
875
0
      this->GeneratorTarget->GetSafeProperty("CUDA_ARCHITECTURES");
876
877
0
    if (cmIsOff(architecturesStr)) {
878
0
      this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
879
0
                                   "CUDA_SEPARABLE_COMPILATION on Clang "
880
0
                                   "requires CUDA_ARCHITECTURES to be set.");
881
0
      return;
882
0
    }
883
884
0
    this->WriteDeviceLinkRules(config);
885
0
    this->WriteDeviceLinkStatements(config, cmList{ architecturesStr },
886
0
                                    targetOutputReal);
887
0
  } else {
888
0
    this->WriteNvidiaDeviceLinkStatement(config, fileConfig, targetOutputDir,
889
0
                                         targetOutputReal);
890
0
  }
891
0
}
892
893
void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatements(
894
  std::string const& config, std::vector<std::string> const& architectures,
895
  std::string const& output)
896
0
{
897
  // Ensure there are no duplicates.
898
0
  cmNinjaDeps const explicitDeps = [&]() -> std::vector<std::string> {
899
0
    std::unordered_set<std::string> depsSet;
900
0
    cmNinjaDeps const linkDeps =
901
0
      this->ComputeLinkDeps(this->TargetLinkLanguage(config), config, true);
902
0
    cmNinjaDeps const objects = this->GetObjects(config);
903
0
    depsSet.insert(linkDeps.begin(), linkDeps.end());
904
0
    depsSet.insert(objects.begin(), objects.end());
905
906
0
    std::vector<std::string> deps;
907
0
    std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps));
908
0
    return deps;
909
0
  }();
910
911
0
  cmGlobalNinjaGenerator* globalGen{ this->GetGlobalGenerator() };
912
0
  std::string const objectDir =
913
0
    cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
914
0
             globalGen->ConfigDirectory(config));
915
0
  std::string const ninjaOutputDir = this->ConvertToNinjaPath(objectDir);
916
917
0
  cmNinjaBuild fatbinary(this->LanguageLinkerCudaFatbinaryRule(config));
918
919
  // Link device code for each architecture.
920
0
  for (std::string const& architectureKind : architectures) {
921
    // Clang always generates real code, so strip the specifier.
922
0
    std::string const architecture =
923
0
      architectureKind.substr(0, architectureKind.find('-'));
924
0
    std::string const cubin =
925
0
      cmStrCat(ninjaOutputDir, "/sm_", architecture, ".cubin");
926
927
0
    cmNinjaBuild dlink(this->LanguageLinkerCudaDeviceRule(config));
928
0
    dlink.ExplicitDeps = explicitDeps;
929
0
    dlink.Outputs = { cubin };
930
0
    dlink.Variables["ARCH"] = cmStrCat("sm_", architecture);
931
932
    // The generated register file contains macros that when expanded register
933
    // the device routines. Because the routines are the same for all
934
    // architectures the register file will be the same too. Thus generate it
935
    // only on the first invocation to reduce overhead.
936
0
    if (fatbinary.ExplicitDeps.empty()) {
937
0
      dlink.Variables["REGISTER"] = cmStrCat(
938
0
        "--register-link-binaries=", ninjaOutputDir, "/cmake_cuda_register.h");
939
0
    }
940
941
0
    fatbinary.Variables["PROFILES"] +=
942
0
      cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin);
943
0
    fatbinary.ExplicitDeps.emplace_back(cubin);
944
945
0
    globalGen->WriteBuild(this->GetCommonFileStream(), dlink);
946
0
  }
947
948
  // Combine all architectures into a single fatbinary.
949
0
  fatbinary.Outputs = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") };
950
0
  globalGen->WriteBuild(this->GetCommonFileStream(), fatbinary);
951
952
  // Compile the stub that registers the kernels and contains the fatbinaries.
953
0
  cmLocalNinjaGenerator* localGen{ this->GetLocalGenerator() };
954
0
  cmNinjaBuild dcompile(this->LanguageLinkerCudaDeviceCompileRule(config));
955
0
  dcompile.Outputs = { output };
956
0
  dcompile.ExplicitDeps = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") };
957
0
  dcompile.Variables["FATBIN"] = localGen->ConvertToOutputFormat(
958
0
    cmStrCat(objectDir, "/cmake_cuda_fatbin.h"), cmOutputConverter::SHELL);
959
0
  dcompile.Variables["REGISTER"] = localGen->ConvertToOutputFormat(
960
0
    cmStrCat(objectDir, "/cmake_cuda_register.h"), cmOutputConverter::SHELL);
961
962
0
  cmNinjaLinkLineDeviceComputer linkLineComputer(
963
0
    localGen, localGen->GetStateSnapshot().GetDirectory(), globalGen);
964
0
  linkLineComputer.SetUseNinjaMulti(globalGen->IsMultiConfig());
965
966
  // Link libraries and paths are only used during the final executable/library
967
  // link.
968
0
  std::string frameworkPath;
969
0
  std::string linkPath;
970
0
  std::string linkLibs;
971
0
  localGen->GetDeviceLinkFlags(linkLineComputer, config, linkLibs,
972
0
                               dcompile.Variables["LINK_FLAGS"], frameworkPath,
973
0
                               linkPath, this->GetGeneratorTarget());
974
975
0
  globalGen->WriteBuild(this->GetCommonFileStream(), dcompile);
976
0
}
977
978
void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkStatement(
979
  std::string const& config, std::string const& fileConfig,
980
  std::string const& outputDir, std::string const& output)
981
0
{
982
0
  cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
983
0
  cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
984
985
0
  std::string targetOutputImplib = this->ConvertToNinjaPath(
986
0
    genTarget->GetFullPath(config, cmStateEnums::ImportLibraryArtifact));
987
988
0
  if (config != fileConfig) {
989
0
    std::string targetOutputFileConfigDir =
990
0
      this->GetLocalGenerator()->MaybeRelativeToTopBinDir(
991
0
        cmStrCat(genTarget->GetSupportDirectory(),
992
0
                 globalGen->ConfigDirectory(config), '/'));
993
0
    targetOutputFileConfigDir =
994
0
      globalGen->ExpandCFGIntDir(outputDir, fileConfig);
995
0
    if (outputDir == targetOutputFileConfigDir) {
996
0
      return;
997
0
    }
998
999
0
    if (!genTarget->GetFullName(config, cmStateEnums::ImportLibraryArtifact)
1000
0
           .empty() &&
1001
0
        !genTarget
1002
0
           ->GetFullName(fileConfig, cmStateEnums::ImportLibraryArtifact)
1003
0
           .empty() &&
1004
0
        targetOutputImplib ==
1005
0
          this->ConvertToNinjaPath(genTarget->GetFullPath(
1006
0
            fileConfig, cmStateEnums::ImportLibraryArtifact))) {
1007
0
      return;
1008
0
    }
1009
0
  }
1010
1011
  // Compute the comment.
1012
0
  cmNinjaBuild build(this->LanguageLinkerDeviceRule(config));
1013
0
  build.Comment =
1014
0
    cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', output);
1015
1016
0
  cmNinjaVars& vars = build.Variables;
1017
1018
  // Compute outputs.
1019
0
  build.Outputs.push_back(output);
1020
  // Compute specific libraries to link with.
1021
0
  build.ExplicitDeps = this->GetObjects(config);
1022
0
  build.ImplicitDeps =
1023
0
    this->ComputeLinkDeps(this->TargetLinkLanguage(config), config);
1024
1025
0
  std::string frameworkPath;
1026
0
  std::string linkPath;
1027
1028
0
  std::string createRule =
1029
0
    genTarget->GetCreateRuleVariable(this->TargetLinkLanguage(config), config);
1030
0
  cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
1031
1032
0
  vars["TARGET_FILE"] =
1033
0
    localGen.ConvertToOutputFormat(output, cmOutputConverter::SHELL);
1034
1035
0
  cmNinjaLinkLineDeviceComputer linkLineComputer(
1036
0
    this->GetLocalGenerator(),
1037
0
    this->GetLocalGenerator()->GetStateSnapshot().GetDirectory(), globalGen);
1038
0
  linkLineComputer.SetUseNinjaMulti(globalGen->IsMultiConfig());
1039
1040
0
  localGen.GetDeviceLinkFlags(linkLineComputer, config, vars["LINK_LIBRARIES"],
1041
0
                              vars["LINK_FLAGS"], frameworkPath, linkPath,
1042
0
                              genTarget);
1043
1044
0
  this->addPoolNinjaVariable("JOB_POOL_LINK", genTarget, nullptr, vars);
1045
1046
0
  vars["MANIFESTS"] = this->GetManifests(config);
1047
1048
0
  vars["LINK_PATH"] = frameworkPath + linkPath;
1049
1050
  // Compute language specific link flags.
1051
0
  std::string langFlags;
1052
0
  localGen.AddLanguageFlagsForLinking(langFlags, genTarget, "CUDA", config);
1053
0
  vars["LANGUAGE_COMPILE_FLAGS"] = langFlags;
1054
1055
0
  auto const tgtNames = this->TargetNames(config);
1056
0
  if (genTarget->HasSOName(config) ||
1057
0
      genTarget->IsArchivedAIXSharedLibrary()) {
1058
0
    vars["SONAME_FLAG"] =
1059
0
      this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage(config));
1060
0
    vars["SONAME"] = localGen.ConvertToOutputFormat(tgtNames.SharedObject,
1061
0
                                                    cmOutputConverter::SHELL);
1062
0
    if (genTarget->GetType() == cmStateEnums::SHARED_LIBRARY) {
1063
0
      std::string install_dir =
1064
0
        this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(config);
1065
0
      if (!install_dir.empty()) {
1066
0
        vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat(
1067
0
          install_dir, cmOutputConverter::SHELL);
1068
0
      }
1069
0
    }
1070
0
  }
1071
1072
0
  if (!tgtNames.ImportLibrary.empty()) {
1073
0
    std::string const impLibPath = localGen.ConvertToOutputFormat(
1074
0
      targetOutputImplib, cmOutputConverter::SHELL);
1075
0
    vars["TARGET_IMPLIB"] = impLibPath;
1076
0
    this->EnsureParentDirectoryExists(targetOutputImplib);
1077
0
  }
1078
1079
0
  std::string const objPath =
1080
0
    cmStrCat(this->GetGeneratorTarget()->GetSupportDirectory(),
1081
0
             globalGen->ConfigDirectory(config));
1082
1083
0
  vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1084
0
    this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL);
1085
0
  this->EnsureDirectoryExists(objPath);
1086
1087
0
  std::string const targetSupportPath =
1088
0
    this->GetGeneratorTarget()->GetCMFSupportDirectory();
1089
1090
0
  vars["TARGET_SUPPORT_DIR"] =
1091
0
    this->GetLocalGenerator()->ConvertToOutputFormat(
1092
0
      this->ConvertToNinjaPath(targetSupportPath), cmOutputConverter::SHELL);
1093
0
  this->EnsureDirectoryExists(targetSupportPath);
1094
1095
0
  this->SetMsvcTargetPdbVariable(vars, config);
1096
1097
0
  std::string& linkLibraries = vars["LINK_LIBRARIES"];
1098
0
  std::string& link_path = vars["LINK_PATH"];
1099
0
  if (globalGen->IsGCCOnWindows()) {
1100
    // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
1101
0
    std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
1102
0
    std::replace(link_path.begin(), link_path.end(), '\\', '/');
1103
0
  }
1104
1105
  // Device linking currently doesn't support response files so
1106
  // do not check if the user has explicitly forced a response file.
1107
0
  int const commandLineLengthLimit =
1108
0
    static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
1109
0
    globalGen->GetRuleCmdLength(build.Rule);
1110
1111
0
  build.RspFile = this->ConvertToNinjaPath(
1112
0
    cmStrCat("CMakeFiles/", genTarget->GetName(),
1113
0
             globalGen->IsMultiConfig() ? cmStrCat('.', config) : "", ".rsp"));
1114
1115
  // Gather order-only dependencies.
1116
0
  this->GetLocalGenerator()->AppendTargetDepends(
1117
0
    this->GetGeneratorTarget(), build.OrderOnlyDeps, config, config,
1118
0
    DependOnTargetArtifact);
1119
1120
  // Write the build statement for this target.
1121
0
  bool usedResponseFile = false;
1122
0
  globalGen->WriteBuild(this->GetCommonFileStream(), build,
1123
0
                        commandLineLengthLimit, &usedResponseFile);
1124
0
  this->WriteNvidiaDeviceLinkRule(usedResponseFile, config);
1125
0
}
1126
1127
void cmNinjaNormalTargetGenerator::WriteLinkStatement(
1128
  std::string const& config, std::string const& fileConfig,
1129
  bool firstForConfig)
1130
0
{
1131
0
  cmMakefile* mf = this->GetMakefile();
1132
0
  cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
1133
0
  cmGeneratorTarget* gt = this->GetGeneratorTarget();
1134
1135
0
  std::string targetOutput = this->ConvertToNinjaPath(gt->GetFullPath(config));
1136
0
  std::string targetOutputReal = this->ConvertToNinjaPath(
1137
0
    gt->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact,
1138
0
                    /*realname=*/true));
1139
0
  std::string targetOutputImplib = this->ConvertToNinjaPath(
1140
0
    gt->GetFullPath(config, cmStateEnums::ImportLibraryArtifact));
1141
1142
0
  if (config != fileConfig) {
1143
0
    if (targetOutput ==
1144
0
        this->ConvertToNinjaPath(gt->GetFullPath(fileConfig))) {
1145
0
      return;
1146
0
    }
1147
0
    if (targetOutputReal ==
1148
0
        this->ConvertToNinjaPath(
1149
0
          gt->GetFullPath(fileConfig, cmStateEnums::RuntimeBinaryArtifact,
1150
0
                          /*realname=*/true))) {
1151
0
      return;
1152
0
    }
1153
0
    if (!gt->GetFullName(config, cmStateEnums::ImportLibraryArtifact)
1154
0
           .empty() &&
1155
0
        !gt->GetFullName(fileConfig, cmStateEnums::ImportLibraryArtifact)
1156
0
           .empty() &&
1157
0
        targetOutputImplib ==
1158
0
          this->ConvertToNinjaPath(gt->GetFullPath(
1159
0
            fileConfig, cmStateEnums::ImportLibraryArtifact))) {
1160
0
      return;
1161
0
    }
1162
0
  }
1163
1164
0
  auto const tgtNames = this->TargetNames(config);
1165
0
  if (gt->IsAppBundleOnApple()) {
1166
    // Create the app bundle
1167
0
    std::string outpath = gt->GetDirectory(config);
1168
0
    this->OSXBundleGenerator->CreateAppBundle(tgtNames.Output, outpath,
1169
0
                                              config);
1170
1171
    // Calculate the output path
1172
0
    targetOutput = cmStrCat(outpath, '/', tgtNames.Output);
1173
0
    targetOutput = this->ConvertToNinjaPath(targetOutput);
1174
0
    targetOutputReal = cmStrCat(outpath, '/', tgtNames.Real);
1175
0
    targetOutputReal = this->ConvertToNinjaPath(targetOutputReal);
1176
0
  } else if (gt->IsFrameworkOnApple()) {
1177
    // Create the library framework.
1178
1179
0
    cmOSXBundleGenerator::SkipParts bundleSkipParts;
1180
0
    if (globalGen->GetName() == "Ninja Multi-Config") {
1181
0
      auto const postFix = this->GeneratorTarget->GetFilePostfix(config);
1182
      // Skip creating Info.plist when there are multiple configurations, and
1183
      // the current configuration has a postfix. The non-postfix configuration
1184
      // Info.plist can be used by all the other configurations.
1185
0
      if (!postFix.empty()) {
1186
0
        bundleSkipParts.InfoPlist = true;
1187
0
      }
1188
0
    }
1189
0
    if (gt->HasImportLibrary(config)) {
1190
0
      bundleSkipParts.TextStubs = false;
1191
0
    }
1192
1193
0
    this->OSXBundleGenerator->CreateFramework(
1194
0
      tgtNames.Output, gt->GetDirectory(config), config, bundleSkipParts);
1195
0
  } else if (gt->IsCFBundleOnApple()) {
1196
    // Create the core foundation bundle.
1197
0
    this->OSXBundleGenerator->CreateCFBundle(tgtNames.Output,
1198
0
                                             gt->GetDirectory(config), config);
1199
0
  }
1200
1201
  // Write comments.
1202
0
  cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig));
1203
0
  cmStateEnums::TargetType const targetType = gt->GetType();
1204
0
  this->GetImplFileStream(fileConfig)
1205
0
    << "# Link build statements for " << cmState::GetTargetTypeName(targetType)
1206
0
    << " target " << this->GetTargetName() << "\n\n";
1207
1208
0
  cmNinjaBuild linkBuild(this->LanguageLinkerRule(config));
1209
0
  cmNinjaVars& vars = linkBuild.Variables;
1210
1211
0
  if (this->GeneratorTarget->HasLinkDependencyFile(config)) {
1212
0
    this->AddDepfileBinding(vars,
1213
0
                            this->ConvertToNinjaPath(
1214
0
                              this->GetLocalGenerator()->GetLinkDependencyFile(
1215
0
                                this->GeneratorTarget, config)));
1216
0
  }
1217
1218
  // Compute the comment.
1219
0
  linkBuild.Comment =
1220
0
    cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal);
1221
1222
  // Compute outputs.
1223
0
  linkBuild.Outputs.push_back(targetOutputReal);
1224
0
  if (firstForConfig) {
1225
0
    globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal);
1226
0
  }
1227
1228
  // If we can't split the Swift build model (CMP0157 is OLD or unset), fall
1229
  // back on the old one-step "build/link" logic.
1230
0
  if (!this->GetLocalGenerator()->IsSplitSwiftBuild() &&
1231
0
      this->TargetLinkLanguage(config) == "Swift") {
1232
0
    vars["SWIFT_LIBRARY_NAME"] = [this, config]() -> std::string {
1233
0
      cmGeneratorTarget::Names targetNames =
1234
0
        this->GetGeneratorTarget()->GetLibraryNames(config);
1235
0
      return targetNames.Base;
1236
0
    }();
1237
1238
0
    vars["SWIFT_MODULE_NAME"] = gt->GetSwiftModuleName();
1239
0
    vars["SWIFT_MODULE"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1240
0
      this->ConvertToNinjaPath(gt->GetSwiftModulePath(config)),
1241
0
      cmOutputConverter::SHELL);
1242
1243
0
    vars["SWIFT_SOURCES"] = [this, config]() -> std::string {
1244
0
      std::vector<cmSourceFile const*> sourceFiles;
1245
0
      std::stringstream oss;
1246
1247
0
      this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config);
1248
0
      cmLocalGenerator const* LocalGen = this->GetLocalGenerator();
1249
0
      for (auto const& source : sourceFiles) {
1250
0
        std::string const sourcePath = source->GetLanguage() == "Swift"
1251
0
          ? this->GetCompiledSourceNinjaPath(source)
1252
0
          : this->GetObjectFilePath(source, config);
1253
0
        oss << " "
1254
0
            << LocalGen->ConvertToOutputFormat(sourcePath,
1255
0
                                               cmOutputConverter::SHELL);
1256
0
      }
1257
0
      return oss.str();
1258
0
    }();
1259
1260
    // Since we do not perform object builds, compute the
1261
    // defines/flags/includes here so that they can be passed along
1262
    // appropriately.
1263
0
    vars["DEFINES"] = this->GetDefines("Swift", config);
1264
0
    vars["FLAGS"] = this->GetFlags("Swift", config);
1265
0
    vars["INCLUDES"] = this->GetIncludes("Swift", config);
1266
0
    this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]);
1267
1268
    // Compute specific libraries to link with.
1269
0
    std::vector<cmSourceFile const*> sources;
1270
0
    gt->GetObjectSources(sources, config);
1271
0
    for (auto const& source : sources) {
1272
0
      if (source->GetLanguage() == "Swift") {
1273
0
        linkBuild.Outputs.push_back(
1274
0
          this->ConvertToNinjaPath(this->GetObjectFilePath(source, config)));
1275
0
        linkBuild.ExplicitDeps.emplace_back(
1276
0
          this->GetCompiledSourceNinjaPath(source));
1277
0
      } else {
1278
0
        linkBuild.ExplicitDeps.emplace_back(
1279
0
          this->GetObjectFilePath(source, config));
1280
0
      }
1281
0
    }
1282
0
    if (targetType != cmStateEnums::EXECUTABLE ||
1283
0
        gt->IsExecutableWithExports()) {
1284
0
      linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]);
1285
0
    }
1286
0
  } else if (this->TargetLinkLanguage(config) == "Rust") {
1287
    // Use one-step build/link for Rust.
1288
    // Compute specific libraries to link with.
1289
0
    cmLocalGenerator const* lg = this->GetLocalGenerator();
1290
1291
0
    linkBuild.ExplicitDeps = this->GetObjects(config);
1292
1293
    // First we handle Rust rlib and normal native objects.
1294
0
    std::stringstream rlibs;
1295
0
    std::stringstream objects;
1296
0
    for (auto const& obj : linkBuild.ExplicitDeps) {
1297
0
      cm::filesystem::path const objPath(obj);
1298
0
      if (objPath.extension() == ".rlib") {
1299
        // Drop the "lib..." prefix and the ".rs" suffix. The prefix is
1300
        // required by Rust on the crate rlib file, but is hidden from the user
1301
        // when using the crate from Rust source code, so we drop it to be
1302
        // consistent with common usage in Rust.
1303
0
        std::string objStem = objPath.stem().string();
1304
0
        objStem = objStem.substr(3, objStem.length() - 6);
1305
0
        rlibs << " --extern=" << objStem << "="
1306
0
              << lg->ConvertToOutputFormat(obj, cmOutputConverter::SHELL);
1307
0
      } else {
1308
0
        objects << " -Clink-arg="
1309
0
                << lg->ConvertToOutputFormat(obj, cmOutputConverter::SHELL);
1310
0
      }
1311
0
    }
1312
0
    vars["RUST_LINK_CRATES"] = rlibs.str();
1313
0
    vars["RUST_NATIVE_OBJECTS"] = objects.str();
1314
1315
    // Then, we handle the main crate root that is build as part of the link
1316
    // step.
1317
0
    std::vector<cmSourceFile const*> mainCrateRoot;
1318
0
    gt->GetRustMainCrateRoot(mainCrateRoot, config);
1319
0
    if (mainCrateRoot.size() != 1) {
1320
0
      this->Makefile->IssueMessage(
1321
0
        MessageType::FATAL_ERROR,
1322
0
        "Target " + gt->GetName() +
1323
0
          " has none or more than one main crate root.");
1324
0
      return;
1325
0
    }
1326
0
    std::string mainCrateRootPath =
1327
0
      this->GetCompiledSourceNinjaPath(mainCrateRoot[0]);
1328
0
    linkBuild.ExplicitDeps.emplace_back(mainCrateRootPath);
1329
0
    mainCrateRootPath =
1330
0
      lg->ConvertToOutputFormat(mainCrateRootPath, cmOutputConverter::SHELL);
1331
0
    vars["RUST_MAIN_CRATE_ROOT"] = mainCrateRootPath;
1332
0
  } else {
1333
0
    linkBuild.ExplicitDeps = this->GetObjects(config);
1334
0
  }
1335
1336
0
  auto extraISPCObjects =
1337
0
    this->GetGeneratorTarget()->GetGeneratedISPCObjects(config);
1338
0
  auto const mapToNinjaPath = this->MapToNinjaPath();
1339
0
  std::transform(
1340
0
    extraISPCObjects.begin(), extraISPCObjects.end(),
1341
0
    std::back_inserter(linkBuild.ExplicitDeps),
1342
0
    [&mapToNinjaPath](std::pair<cmSourceFile const*, std::string> const& obj)
1343
0
      -> std::string { return mapToNinjaPath(obj.second); });
1344
1345
0
  linkBuild.ImplicitDeps =
1346
0
    this->ComputeLinkDeps(this->TargetLinkLanguage(config), config);
1347
1348
0
  if (!this->DeviceLinkObject.empty()) {
1349
0
    linkBuild.ExplicitDeps.push_back(this->DeviceLinkObject);
1350
0
  }
1351
1352
0
  std::string frameworkPath;
1353
0
  std::string linkPath;
1354
1355
0
  std::string createRule =
1356
0
    gt->GetCreateRuleVariable(this->TargetLinkLanguage(config), config);
1357
0
  bool useWatcomQuote = mf->IsOn(createRule + "_USE_WATCOM_QUOTE");
1358
0
  cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
1359
1360
0
  vars["TARGET_FILE"] =
1361
0
    localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL);
1362
1363
0
  std::unique_ptr<cmLinkLineComputer> linkLineComputer =
1364
0
    globalGen->CreateLinkLineComputer(
1365
0
      this->GetLocalGenerator(),
1366
0
      this->GetLocalGenerator()->GetStateSnapshot().GetDirectory());
1367
0
  linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
1368
0
  linkLineComputer->SetUseNinjaMulti(globalGen->IsMultiConfig());
1369
1370
0
  localGen.GetTargetFlags(linkLineComputer.get(), config,
1371
0
                          vars["LINK_LIBRARIES"], vars["FLAGS"],
1372
0
                          vars["LINK_FLAGS"], frameworkPath, linkPath, gt);
1373
1374
0
  localGen.AppendDependencyInfoLinkerFlags(vars["LINK_FLAGS"], gt, config,
1375
0
                                           this->TargetLinkLanguage(config));
1376
1377
  // Add OS X version flags, if any.
1378
0
  if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
1379
0
      this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
1380
0
    this->AppendOSXVerFlag(vars["LINK_FLAGS"],
1381
0
                           this->TargetLinkLanguage(config), "COMPATIBILITY",
1382
0
                           true);
1383
0
    this->AppendOSXVerFlag(vars["LINK_FLAGS"],
1384
0
                           this->TargetLinkLanguage(config), "CURRENT", false);
1385
0
  }
1386
1387
0
  this->addPoolNinjaVariable("JOB_POOL_LINK", gt, nullptr, vars);
1388
1389
0
  this->UseLWYU = this->GetLocalGenerator()->AppendLWYUFlags(
1390
0
    vars["LINK_FLAGS"], this->GetGeneratorTarget(),
1391
0
    this->TargetLinkLanguage(config));
1392
1393
0
  vars["MANIFESTS"] = this->GetManifests(config);
1394
0
  vars["AIX_EXPORTS"] = this->GetAIXExports(config);
1395
1396
0
  vars["LINK_PATH"] = frameworkPath + linkPath;
1397
0
  vars["CONFIG"] = config;
1398
1399
  // Compute architecture specific link flags.  Yes, these go into a different
1400
  // variable for executables, probably due to a mistake made when duplicating
1401
  // code between the Makefile executable and library generators.
1402
0
  if (targetType == cmStateEnums::EXECUTABLE) {
1403
0
    std::string t = vars["FLAGS"];
1404
0
    localGen.AddArchitectureFlags(t, gt, this->TargetLinkLanguage(config),
1405
0
                                  config);
1406
0
    vars["FLAGS"] = t;
1407
0
  } else {
1408
0
    std::string t = vars["ARCH_FLAGS"];
1409
0
    localGen.AddArchitectureFlags(t, gt, this->TargetLinkLanguage(config),
1410
0
                                  config);
1411
0
    vars["ARCH_FLAGS"] = t;
1412
0
    t.clear();
1413
0
    localGen.AddLanguageFlagsForLinking(
1414
0
      t, gt, this->TargetLinkLanguage(config), config);
1415
0
    vars["LANGUAGE_COMPILE_FLAGS"] = t;
1416
0
  }
1417
0
  if (gt->HasSOName(config) || gt->IsArchivedAIXSharedLibrary()) {
1418
0
    vars["SONAME_FLAG"] = mf->GetSONameFlag(this->TargetLinkLanguage(config));
1419
0
    vars["SONAME"] = localGen.ConvertToOutputFormat(tgtNames.SharedObject,
1420
0
                                                    cmOutputConverter::SHELL);
1421
0
    if (targetType == cmStateEnums::SHARED_LIBRARY) {
1422
0
      std::string install_dir = gt->GetInstallNameDirForBuildTree(config);
1423
0
      if (!install_dir.empty()) {
1424
0
        vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat(
1425
0
          install_dir, cmOutputConverter::SHELL);
1426
0
      }
1427
0
    }
1428
0
  }
1429
1430
0
  cmGlobalNinjaGenerator::CCOutputs byproducts(this->GetGlobalGenerator());
1431
1432
0
  if (!gt->IsApple() && !tgtNames.ImportLibrary.empty()) {
1433
0
    std::string const impLibPath = localGen.ConvertToOutputFormat(
1434
0
      targetOutputImplib, cmOutputConverter::SHELL);
1435
0
    vars["TARGET_IMPLIB"] = impLibPath;
1436
0
    this->EnsureParentDirectoryExists(targetOutputImplib);
1437
0
    if (gt->HasImportLibrary(config)) {
1438
      // Some linkers may update a binary without touching its import lib.
1439
0
      byproducts.ExplicitOuts.emplace_back(targetOutputImplib);
1440
0
      if (firstForConfig) {
1441
0
        globalGen->GetByproductsForCleanTarget(config).push_back(
1442
0
          targetOutputImplib);
1443
0
      }
1444
0
    }
1445
0
  }
1446
1447
0
  if (!this->SetMsvcTargetPdbVariable(vars, config)) {
1448
    // It is common to place debug symbols at a specific place,
1449
    // so we need a plain target name in the rule available.
1450
0
    cmGeneratorTarget::NameComponents const& components =
1451
0
      gt->GetFullNameComponents(config);
1452
0
    std::string dbg_suffix = ".dbg";
1453
    // TODO: Where to document?
1454
0
    if (cmValue d = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) {
1455
0
      dbg_suffix = *d;
1456
0
    }
1457
0
    vars["TARGET_PDB"] = components.base + components.suffix + dbg_suffix;
1458
0
  }
1459
1460
0
  std::string const objPath =
1461
0
    cmStrCat(gt->GetSupportDirectory(), globalGen->ConfigDirectory(config));
1462
0
  vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1463
0
    this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL);
1464
0
  this->EnsureDirectoryExists(objPath);
1465
1466
0
  std::string const targetSupportPath = gt->GetCMFSupportDirectory();
1467
0
  vars["TARGET_SUPPORT_DIR"] =
1468
0
    this->GetLocalGenerator()->ConvertToOutputFormat(
1469
0
      this->ConvertToNinjaPath(targetSupportPath), cmOutputConverter::SHELL);
1470
0
  this->EnsureDirectoryExists(targetSupportPath);
1471
1472
0
  std::string& linkLibraries = vars["LINK_LIBRARIES"];
1473
0
  std::string& link_path = vars["LINK_PATH"];
1474
0
  if (globalGen->IsGCCOnWindows()) {
1475
    // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
1476
0
    std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
1477
0
    std::replace(link_path.begin(), link_path.end(), '\\', '/');
1478
0
  }
1479
1480
0
  std::vector<cmCustomCommand> const* cmdLists[3] = {
1481
0
    &gt->GetPreBuildCommands(), &gt->GetPreLinkCommands(),
1482
0
    &gt->GetPostBuildCommands()
1483
0
  };
1484
1485
0
  std::vector<std::string> preLinkComments;
1486
0
  std::vector<std::string> postBuildComments;
1487
1488
0
  std::vector<std::string> preLinkCmdLines;
1489
0
  std::vector<std::string> postBuildCmdLines;
1490
1491
0
  std::vector<std::string>* cmdComments[3] = { &preLinkComments,
1492
0
                                               &preLinkComments,
1493
0
                                               &postBuildComments };
1494
0
  std::vector<std::string>* cmdLineLists[3] = { &preLinkCmdLines,
1495
0
                                                &preLinkCmdLines,
1496
0
                                                &postBuildCmdLines };
1497
0
  cmGeneratorExpression ge(*this->GetLocalGenerator()->GetCMakeInstance());
1498
1499
0
  for (unsigned i = 0; i != 3; ++i) {
1500
0
    for (cmCustomCommand const& cc : *cmdLists[i]) {
1501
0
      if (config == fileConfig ||
1502
0
          this->GetLocalGenerator()->HasUniqueByproducts(cc.GetByproducts(),
1503
0
                                                         cc.GetBacktrace())) {
1504
0
        cmCustomCommandGenerator ccg(cc, fileConfig, this->GetLocalGenerator(),
1505
0
                                     true, config);
1506
0
        localGen.AppendCustomCommandLines(ccg, *cmdLineLists[i]);
1507
0
        if (cc.GetComment()) {
1508
0
          auto cge = ge.Parse(cc.GetComment());
1509
0
          cmdComments[i]->emplace_back(
1510
0
            cge->Evaluate(this->GetLocalGenerator(), config));
1511
0
        }
1512
0
        std::vector<std::string> const& ccByproducts = ccg.GetByproducts();
1513
0
        byproducts.Add(ccByproducts);
1514
0
        std::transform(
1515
0
          ccByproducts.begin(), ccByproducts.end(),
1516
0
          std::back_inserter(globalGen->GetByproductsForCleanTarget()),
1517
0
          mapToNinjaPath);
1518
0
      }
1519
0
    }
1520
0
  }
1521
1522
  // If we have any PRE_LINK commands, we need to go back to CMAKE_BINARY_DIR
1523
  // for the link commands.
1524
0
  if (!preLinkCmdLines.empty()) {
1525
0
    std::string const homeOutDir = localGen.ConvertToOutputFormat(
1526
0
      localGen.GetBinaryDirectory(), cmOutputConverter::SHELL);
1527
0
    preLinkCmdLines.push_back("cd " + homeOutDir);
1528
0
  }
1529
1530
  // maybe create .def file from list of objects
1531
0
  cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
1532
0
    gt->GetModuleDefinitionInfo(config);
1533
0
  if (mdi && mdi->DefFileGenerated) {
1534
0
    std::string cmakeCommand =
1535
0
      this->GetLocalGenerator()->ConvertToOutputFormat(
1536
0
        cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
1537
0
    std::string cmd =
1538
0
      cmStrCat(cmakeCommand, " -E __create_def ",
1539
0
               this->GetLocalGenerator()->ConvertToOutputFormat(
1540
0
                 mdi->DefFile, cmOutputConverter::SHELL),
1541
0
               ' ');
1542
0
    std::string obj_list_file = mdi->DefFile + ".objs";
1543
0
    cmd += this->GetLocalGenerator()->ConvertToOutputFormat(
1544
0
      obj_list_file, cmOutputConverter::SHELL);
1545
1546
0
    cmValue nm_executable = this->GetMakefile()->GetDefinition("CMAKE_NM");
1547
0
    if (cmNonempty(nm_executable)) {
1548
0
      cmd += " --nm=";
1549
0
      cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
1550
0
        *nm_executable, cmOutputConverter::SHELL);
1551
0
    }
1552
0
    preLinkCmdLines.push_back(std::move(cmd));
1553
1554
    // create a list of obj files for the -E __create_def to read
1555
0
    cmGeneratedFileStream fout(obj_list_file);
1556
1557
0
    if (mdi->WindowsExportAllSymbols) {
1558
0
      cmNinjaDeps objs = this->GetObjects(config);
1559
0
      for (std::string const& obj : objs) {
1560
0
        if (cmHasLiteralSuffix(obj, ".obj")) {
1561
0
          fout << obj << "\n";
1562
0
        }
1563
0
      }
1564
0
    }
1565
1566
0
    for (cmSourceFile const* src : mdi->Sources) {
1567
0
      fout << src->GetFullPath() << "\n";
1568
0
    }
1569
0
  }
1570
1571
0
  vars["PRE_LINK"] = localGen.BuildCommandLine(
1572
0
    preLinkCmdLines, config, fileConfig, "pre-link", this->GeneratorTarget);
1573
0
  std::string postBuildCmdLine =
1574
0
    localGen.BuildCommandLine(postBuildCmdLines, config, fileConfig,
1575
0
                              "post-build", this->GeneratorTarget);
1576
1577
0
  cmNinjaVars symlinkVars;
1578
0
  bool const symlinkNeeded =
1579
0
    (targetOutput != targetOutputReal && !gt->IsFrameworkOnApple() &&
1580
0
     !gt->IsArchivedAIXSharedLibrary());
1581
0
  if (!symlinkNeeded) {
1582
0
    vars["POST_BUILD"] = postBuildCmdLine;
1583
0
  } else {
1584
0
    vars["POST_BUILD"] = cmGlobalNinjaGenerator::SHELL_NOOP;
1585
0
    symlinkVars["POST_BUILD"] = postBuildCmdLine;
1586
0
  }
1587
1588
0
  std::string cmakeVarLang =
1589
0
    cmStrCat("CMAKE_", this->TargetLinkLanguage(config));
1590
1591
  // build response file name
1592
0
  std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
1593
1594
0
  cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
1595
1596
0
  bool const lang_supports_response =
1597
0
    !(this->TargetLinkLanguage(config) == "RC" ||
1598
0
      (this->TargetLinkLanguage(config) == "CUDA" && !flag));
1599
0
  int commandLineLengthLimit = -1;
1600
0
  if (!lang_supports_response || !this->ForceResponseFile()) {
1601
0
    commandLineLengthLimit =
1602
0
      static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
1603
0
      globalGen->GetRuleCmdLength(linkBuild.Rule);
1604
0
  }
1605
1606
0
  linkBuild.RspFile = this->ConvertToNinjaPath(
1607
0
    cmStrCat("CMakeFiles/", gt->GetName(),
1608
0
             globalGen->IsMultiConfig() ? cmStrCat('.', config) : "", ".rsp"));
1609
1610
  // Gather order-only dependencies.
1611
0
  this->GetLocalGenerator()->AppendTargetDepends(
1612
0
    gt, linkBuild.OrderOnlyDeps, config, fileConfig, DependOnTargetArtifact);
1613
1614
  // Add order-only dependencies on versioning symlinks of shared libs we link.
1615
  // If our target is not producing a runtime binary, it doesn't need the
1616
  // symlinks (anything that links to the target might, but that consumer will
1617
  // get its own order-only dependency).
1618
0
  if (!gt->IsDLLPlatform() && gt->IsRuntimeBinary()) {
1619
0
    if (cmComputeLinkInformation* cli = gt->GetLinkInformation(config)) {
1620
0
      for (auto const& item : cli->GetItems()) {
1621
0
        if (item.Target &&
1622
0
            item.Target->GetType() == cmStateEnums::SHARED_LIBRARY &&
1623
0
            !item.Target->IsFrameworkOnApple()) {
1624
0
          std::string const& lib =
1625
0
            this->ConvertToNinjaPath(item.Target->GetFullPath(config));
1626
0
          if (std::find(linkBuild.ImplicitDeps.begin(),
1627
0
                        linkBuild.ImplicitDeps.end(),
1628
0
                        lib) == linkBuild.ImplicitDeps.end()) {
1629
0
            linkBuild.OrderOnlyDeps.emplace_back(lib);
1630
0
          }
1631
0
        }
1632
0
      }
1633
0
    }
1634
0
  }
1635
1636
  // Add dependencies on swiftmodule files when using the swift linker
1637
0
  if (!this->GetLocalGenerator()->IsSplitSwiftBuild() &&
1638
0
      this->TargetLinkLanguage(config) == "Swift") {
1639
0
    if (cmComputeLinkInformation* cli =
1640
0
          this->GeneratorTarget->GetLinkInformation(config)) {
1641
0
      for (auto const& dependency : cli->GetItems()) {
1642
        // Only depend on swiftmodule from targets that actually compile
1643
        // Swift sources. A C/C++ target may have Swift as its linker
1644
        // language (due to language propagation) without producing one.
1645
0
        if (dependency.Target &&
1646
0
            dependency.Target->IsLanguageUsed("Swift", config)) {
1647
0
          std::string swiftmodule = this->ConvertToNinjaPath(
1648
0
            dependency.Target->GetSwiftModulePath(config));
1649
0
          linkBuild.ImplicitDeps.emplace_back(swiftmodule);
1650
0
        }
1651
0
      }
1652
0
    }
1653
0
  }
1654
1655
  // Ninja should restat after linking if and only if there are byproducts.
1656
0
  vars["RESTAT"] = byproducts.ExplicitOuts.empty() ? "" : "1";
1657
1658
0
  linkBuild.Outputs.reserve(linkBuild.Outputs.size() +
1659
0
                            byproducts.ExplicitOuts.size());
1660
0
  std::move(byproducts.ExplicitOuts.begin(), byproducts.ExplicitOuts.end(),
1661
0
            std::back_inserter(linkBuild.Outputs));
1662
0
  linkBuild.WorkDirOuts = std::move(byproducts.WorkDirOuts);
1663
1664
  // Write the build statement for this target.
1665
0
  bool usedResponseFile = false;
1666
0
  globalGen->WriteBuild(this->GetImplFileStream(fileConfig), linkBuild,
1667
0
                        commandLineLengthLimit, &usedResponseFile);
1668
0
  this->WriteLinkRule(usedResponseFile, config, preLinkComments,
1669
0
                      postBuildComments);
1670
1671
0
  if (symlinkNeeded) {
1672
0
    if (targetType == cmStateEnums::EXECUTABLE) {
1673
0
      cmNinjaBuild build("CMAKE_SYMLINK_EXECUTABLE");
1674
0
      build.Comment = "Create executable symlink " + targetOutput;
1675
0
      build.Outputs.push_back(targetOutput);
1676
0
      if (firstForConfig) {
1677
0
        globalGen->GetByproductsForCleanTarget(config).push_back(targetOutput);
1678
0
      }
1679
0
      build.ExplicitDeps.push_back(targetOutputReal);
1680
0
      build.Variables = std::move(symlinkVars);
1681
0
      globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build);
1682
0
    } else {
1683
0
      cmNinjaBuild build("CMAKE_SYMLINK_LIBRARY");
1684
0
      build.Comment = "Create library symlink " + targetOutput;
1685
1686
0
      std::string const soName = this->ConvertToNinjaPath(
1687
0
        this->GetTargetFilePath(tgtNames.SharedObject, config));
1688
      // If one link has to be created.
1689
0
      if (targetOutputReal == soName || targetOutput == soName) {
1690
0
        symlinkVars["SONAME"] =
1691
0
          this->GetLocalGenerator()->ConvertToOutputFormat(
1692
0
            soName, cmOutputConverter::SHELL);
1693
0
      } else {
1694
0
        symlinkVars["SONAME"].clear();
1695
0
        build.Outputs.push_back(soName);
1696
0
        if (firstForConfig) {
1697
0
          globalGen->GetByproductsForCleanTarget(config).push_back(soName);
1698
0
        }
1699
0
      }
1700
0
      build.Outputs.push_back(targetOutput);
1701
0
      if (firstForConfig) {
1702
0
        globalGen->GetByproductsForCleanTarget(config).push_back(targetOutput);
1703
0
      }
1704
0
      build.ExplicitDeps.push_back(targetOutputReal);
1705
0
      build.Variables = std::move(symlinkVars);
1706
1707
0
      globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build);
1708
0
    }
1709
0
  }
1710
1711
  // Add aliases for the file name and the target name.
1712
0
  globalGen->AddTargetAlias(tgtNames.Output, gt, config);
1713
0
  globalGen->AddTargetAlias(this->GetTargetName(), gt, config);
1714
1715
0
  if (this->GetGeneratorTarget()->IsApple() &&
1716
0
      this->GetGeneratorTarget()->HasImportLibrary(config)) {
1717
0
    auto dirTBD =
1718
0
      gt->GetDirectory(config, cmStateEnums::ImportLibraryArtifact);
1719
0
    auto targetTBD =
1720
0
      this->ConvertToNinjaPath(cmStrCat(dirTBD, '/', tgtNames.ImportReal));
1721
0
    this->EnsureParentDirectoryExists(targetTBD);
1722
0
    cmNinjaBuild build(this->TextStubsGeneratorRule(config));
1723
0
    build.Comment = cmStrCat("Generate the text-based stubs file ", targetTBD);
1724
0
    build.Outputs.push_back(targetTBD);
1725
0
    build.ExplicitDeps.push_back(targetOutputReal);
1726
0
    globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build);
1727
1728
0
    if (tgtNames.ImportOutput != tgtNames.ImportReal &&
1729
0
        !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
1730
0
      auto outputTBD =
1731
0
        this->ConvertToNinjaPath(cmStrCat(dirTBD, '/', tgtNames.ImportOutput));
1732
0
      std::string const soNameTBD = this->ConvertToNinjaPath(
1733
0
        cmStrCat(dirTBD, '/', tgtNames.ImportLibrary));
1734
1735
0
      cmNinjaBuild slBuild("CMAKE_SYMLINK_IMPORT_LIBRARY");
1736
0
      slBuild.Comment = cmStrCat("Create import library symlink ", outputTBD);
1737
0
      cmNinjaVars slVars;
1738
1739
      // If one link has to be created.
1740
0
      if (targetTBD == soNameTBD || outputTBD == soNameTBD) {
1741
0
        slVars["SONAME"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1742
0
          soNameTBD, cmOutputConverter::SHELL);
1743
0
      } else {
1744
0
        slVars["SONAME"].clear();
1745
0
        slBuild.Outputs.push_back(soNameTBD);
1746
0
        if (firstForConfig) {
1747
0
          globalGen->GetByproductsForCleanTarget(config).push_back(soNameTBD);
1748
0
        }
1749
0
      }
1750
0
      slBuild.Outputs.push_back(outputTBD);
1751
0
      if (firstForConfig) {
1752
0
        globalGen->GetByproductsForCleanTarget(config).push_back(outputTBD);
1753
0
      }
1754
0
      slBuild.ExplicitDeps.push_back(targetTBD);
1755
0
      slBuild.Variables = std::move(slVars);
1756
1757
0
      globalGen->WriteBuild(this->GetImplFileStream(fileConfig), slBuild);
1758
0
    }
1759
1760
    // Add alias for the import file name
1761
0
    globalGen->AddTargetAlias(tgtNames.ImportOutput, gt, config);
1762
0
  }
1763
0
}
1764
1765
void cmNinjaNormalTargetGenerator::WriteObjectLibStatement(
1766
  std::string const& config)
1767
0
{
1768
  // Write a phony output that depends on all object files.
1769
0
  {
1770
0
    cmNinjaBuild build("phony");
1771
0
    build.Comment = "Object library " + this->GetTargetName();
1772
0
    this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(),
1773
0
                                                   build.Outputs, config);
1774
0
    this->GetLocalGenerator()->AppendTargetOutputs(
1775
0
      this->GetGeneratorTarget(),
1776
0
      this->GetGlobalGenerator()->GetByproductsForCleanTarget(config), config);
1777
0
    build.ExplicitDeps = this->GetObjects(config);
1778
0
    this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), build);
1779
0
  }
1780
1781
  // Add aliases for the target name.
1782
0
  this->GetGlobalGenerator()->AddTargetAlias(
1783
0
    this->GetTargetName(), this->GetGeneratorTarget(), config);
1784
0
}
1785
1786
void cmNinjaNormalTargetGenerator::WriteCxxModuleLibraryStatement(
1787
  std::string const& config, std::string const& /*fileConfig*/,
1788
  bool firstForConfig)
1789
0
{
1790
  // TODO: How to use `fileConfig` properly?
1791
1792
  // Write a phony output that depends on the scanning output.
1793
0
  {
1794
0
    cmNinjaBuild build("phony");
1795
0
    build.Comment =
1796
0
      cmStrCat("Imported C++ module library ", this->GetTargetName());
1797
0
    this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(),
1798
0
                                                   build.Outputs, config);
1799
0
    if (firstForConfig) {
1800
0
      this->GetLocalGenerator()->AppendTargetOutputs(
1801
0
        this->GetGeneratorTarget(),
1802
0
        this->GetGlobalGenerator()->GetByproductsForCleanTarget(config),
1803
0
        config);
1804
0
    }
1805
0
    build.ExplicitDeps.emplace_back(this->GetDyndepFilePath("CXX", config));
1806
0
    this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), build);
1807
0
  }
1808
1809
  // Add aliases for the target name.
1810
0
  this->GetGlobalGenerator()->AddTargetAlias(
1811
0
    this->GetTargetName(), this->GetGeneratorTarget(), config);
1812
0
}
1813
1814
cmGeneratorTarget::Names cmNinjaNormalTargetGenerator::TargetNames(
1815
  std::string const& config) const
1816
0
{
1817
0
  if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) {
1818
0
    return this->GeneratorTarget->GetExecutableNames(config);
1819
0
  }
1820
0
  return this->GeneratorTarget->GetLibraryNames(config);
1821
0
}
1822
1823
std::string cmNinjaNormalTargetGenerator::TargetLinkLanguage(
1824
  std::string const& config) const
1825
0
{
1826
0
  return this->GeneratorTarget->GetLinkerLanguage(config);
1827
0
}