Coverage Report

Created: 2026-06-15 07:03

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