Coverage Report

Created: 2026-07-14 07:09

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