Coverage Report

Created: 2026-09-14 06:43

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