Coverage Report

Created: 2026-02-09 06:05

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.RustSources = "$RUST_SOURCES";
483
0
      vars.RustObjectDeps = "$RUST_OBJECT_DEPS";
484
0
    }
485
486
0
    std::string responseFlag;
487
488
0
    std::string cmakeVarLang =
489
0
      cmStrCat("CMAKE_", this->TargetLinkLanguage(config));
490
491
0
    if (this->GeneratorTarget->HasLinkDependencyFile(config)) {
492
0
      auto DepFileFormat = this->GetMakefile()->GetDefinition(
493
0
        cmStrCat(cmakeVarLang, "_LINKER_DEPFILE_FORMAT"));
494
0
      rule.DepType = DepFileFormat;
495
0
      rule.DepFile = "$DEP_FILE";
496
0
    }
497
498
    // build response file name
499
0
    std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
500
0
    cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
501
502
0
    if (flag) {
503
0
      responseFlag = *flag;
504
0
    } else {
505
0
      responseFlag = "@";
506
0
    }
507
508
0
    if (!useResponseFile || responseFlag.empty()) {
509
0
      vars.Objects = "$in";
510
0
      vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
511
0
    } else {
512
0
      rule.RspFile = "$RSP_FILE";
513
0
      responseFlag += rule.RspFile;
514
515
      // build response file content
516
0
      if (this->GetGlobalGenerator()->IsGCCOnWindows()) {
517
0
        rule.RspContent = "$in";
518
0
      } else {
519
0
        rule.RspContent = "$in_newline";
520
0
      }
521
522
      // If libraries in rsp is enable
523
0
      if (this->CheckUseResponseFileForLibraries(lang)) {
524
0
        rule.RspContent += " $LINK_PATH $LINK_LIBRARIES";
525
0
        vars.LinkLibraries = "";
526
0
      } else {
527
0
        vars.LinkLibraries = "$LINK_PATH $LINK_LIBRARIES";
528
0
      }
529
530
0
      if (!this->GetLocalGenerator()->IsSplitSwiftBuild() &&
531
0
          this->TargetLinkLanguage(config) == "Swift") {
532
0
        vars.SwiftSources = responseFlag.c_str();
533
0
      } else {
534
0
        vars.Objects = responseFlag.c_str();
535
0
      }
536
0
    }
537
538
0
    vars.ObjectDir = "$OBJECT_DIR";
539
0
    vars.TargetSupportDir = "$TARGET_SUPPORT_DIR";
540
541
0
    vars.Target = "$TARGET_FILE";
542
543
0
    vars.SONameFlag = "$SONAME_FLAG";
544
0
    vars.TargetSOName = "$SONAME";
545
0
    vars.TargetInstallNameDir = "$INSTALLNAME_DIR";
546
0
    vars.TargetPDB = "$TARGET_PDB";
547
548
    // Setup the target version.
549
0
    std::string targetVersionMajor;
550
0
    std::string targetVersionMinor;
551
0
    {
552
0
      std::ostringstream majorStream;
553
0
      std::ostringstream minorStream;
554
0
      int major;
555
0
      int minor;
556
0
      this->GetGeneratorTarget()->GetTargetVersion(major, minor);
557
0
      majorStream << major;
558
0
      minorStream << minor;
559
0
      targetVersionMajor = majorStream.str();
560
0
      targetVersionMinor = minorStream.str();
561
0
    }
562
0
    vars.TargetVersionMajor = targetVersionMajor.c_str();
563
0
    vars.TargetVersionMinor = targetVersionMinor.c_str();
564
565
0
    vars.Flags = "$FLAGS";
566
0
    vars.LinkFlags = "$LINK_FLAGS";
567
0
    vars.Manifests = "$MANIFESTS";
568
0
    vars.Config = "$CONFIG";
569
570
0
    std::string langFlags;
571
0
    if (targetType != cmStateEnums::EXECUTABLE) {
572
0
      langFlags += "$LANGUAGE_COMPILE_FLAGS $ARCH_FLAGS";
573
0
      vars.LanguageCompileFlags = langFlags.c_str();
574
0
    }
575
576
0
    std::string linkerLauncher = this->GetLinkerLauncher(config);
577
0
    if (cmNonempty(linkerLauncher)) {
578
0
      vars.Launcher = linkerLauncher.c_str();
579
0
    }
580
581
0
    std::string launcher;
582
0
    std::string val = this->GetLocalGenerator()->GetRuleLauncher(
583
0
      this->GetGeneratorTarget(), "RULE_LAUNCH_LINK", config);
584
0
    if (cmNonempty(val)) {
585
0
      launcher = cmStrCat(val, ' ');
586
0
    }
587
588
0
    auto rulePlaceholderExpander =
589
0
      this->GetLocalGenerator()->CreateRulePlaceholderExpander(
590
0
        cmBuildStep::Link);
591
592
    // Rule for linking library/executable.
593
0
    std::vector<std::string> linkCmds = this->ComputeLinkCmd(config);
594
0
    for (std::string& linkCmd : linkCmds) {
595
0
      linkCmd = cmStrCat(launcher, linkCmd);
596
0
      rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
597
0
                                                   linkCmd, vars);
598
0
    }
599
600
    // If there is no ranlib the command will be ":".  Skip it.
601
0
    cm::erase_if(linkCmds, cmNinjaRemoveNoOpCommands());
602
603
0
    linkCmds.insert(linkCmds.begin(), "$PRE_LINK");
604
0
    linkCmds.emplace_back("$POST_BUILD");
605
0
    rule.Command =
606
0
      this->GetLocalGenerator()->BuildCommandLine(linkCmds, config, config);
607
608
    // Write the linker rule with response file if needed.
609
0
    rule.Comment =
610
0
      cmStrCat("Rule for linking ", this->TargetLinkLanguage(config), ' ',
611
0
               this->GetVisibleTypeName(), '.');
612
0
    char const* presep = "";
613
0
    char const* postsep = "";
614
0
    auto prelink = cmJoin(preLinkComments, "; ");
615
0
    NinjaSafeComment(prelink);
616
0
    if (!prelink.empty()) {
617
0
      presep = "; ";
618
0
    }
619
0
    auto postbuild = cmJoin(postBuildComments, "; ");
620
0
    NinjaSafeComment(postbuild);
621
0
    if (!postbuild.empty()) {
622
0
      postsep = "; ";
623
0
    }
624
0
    rule.Description = cmStrCat(
625
0
      prelink, presep, "Linking ", this->TargetLinkLanguage(config), ' ',
626
0
      this->GetVisibleTypeName(), " $TARGET_FILE", postsep, postbuild);
627
0
    rule.Restat = "$RESTAT";
628
0
    this->GetGlobalGenerator()->AddRule(rule);
629
0
  }
630
631
0
  auto const tgtNames = this->TargetNames(config);
632
0
  if (tgtNames.Output != tgtNames.Real &&
633
0
      !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
634
0
    std::string cmakeCommand =
635
0
      this->GetLocalGenerator()->ConvertToOutputFormat(
636
0
        cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
637
0
    if (targetType == cmStateEnums::EXECUTABLE) {
638
0
      cmNinjaRule rule("CMAKE_SYMLINK_EXECUTABLE");
639
0
      {
640
0
        std::vector<std::string> cmd;
641
0
        cmd.push_back(cmakeCommand + " -E cmake_symlink_executable $in $out");
642
0
        cmd.emplace_back("$POST_BUILD");
643
0
        rule.Command =
644
0
          this->GetLocalGenerator()->BuildCommandLine(cmd, config, config);
645
0
      }
646
0
      rule.Description = "Creating executable symlink $out";
647
0
      rule.Comment = "Rule for creating executable symlink.";
648
0
      this->GetGlobalGenerator()->AddRule(rule);
649
0
    } else {
650
0
      cmNinjaRule rule("CMAKE_SYMLINK_LIBRARY");
651
0
      {
652
0
        std::vector<std::string> cmd;
653
0
        cmd.push_back(cmakeCommand +
654
0
                      " -E cmake_symlink_library $in $SONAME $out");
655
0
        cmd.emplace_back("$POST_BUILD");
656
0
        rule.Command =
657
0
          this->GetLocalGenerator()->BuildCommandLine(cmd, config, config);
658
0
      }
659
0
      rule.Description = "Creating library symlink $out";
660
0
      rule.Comment = "Rule for creating library symlink.";
661
0
      this->GetGlobalGenerator()->AddRule(rule);
662
0
    }
663
0
  }
664
665
0
  if (this->GetGeneratorTarget()->IsApple() &&
666
0
      this->GetGeneratorTarget()->HasImportLibrary(config)) {
667
0
    cmNinjaRule rule(this->TextStubsGeneratorRule(config));
668
0
    rule.Comment = cmStrCat("Rule for generating text-based stubs for ",
669
0
                            this->GetVisibleTypeName(), '.');
670
0
    rule.Description = "Creating text-based stubs $out";
671
672
0
    std::string cmd =
673
0
      this->GetMakefile()->GetDefinition("CMAKE_CREATE_TEXT_STUBS");
674
0
    auto rulePlaceholderExpander =
675
0
      this->GetLocalGenerator()->CreateRulePlaceholderExpander();
676
0
    cmRulePlaceholderExpander::RuleVariables vars;
677
0
    vars.Target = "$in";
678
0
    rulePlaceholderExpander->SetTargetImpLib("$out");
679
0
    rulePlaceholderExpander->ExpandRuleVariables(this->GetLocalGenerator(),
680
0
                                                 cmd, vars);
681
682
0
    rule.Command =
683
0
      this->GetLocalGenerator()->BuildCommandLine({ cmd }, config, config);
684
0
    this->GetGlobalGenerator()->AddRule(rule);
685
686
0
    if (tgtNames.ImportOutput != tgtNames.ImportReal &&
687
0
        !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
688
0
      cmNinjaRule slRule("CMAKE_SYMLINK_IMPORT_LIBRARY");
689
0
      {
690
0
        std::string cmakeCommand =
691
0
          this->GetLocalGenerator()->ConvertToOutputFormat(
692
0
            cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
693
0
        std::string slCmd =
694
0
          cmStrCat(cmakeCommand, " -E cmake_symlink_library $in $SONAME $out");
695
0
        slRule.Command = this->GetLocalGenerator()->BuildCommandLine(
696
0
          { slCmd }, config, config);
697
0
      }
698
0
      slRule.Description = "Creating import library symlink $out";
699
0
      slRule.Comment = "Rule for creating import library symlink.";
700
0
      this->GetGlobalGenerator()->AddRule(slRule);
701
0
    }
702
0
  }
703
0
}
704
705
std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeDeviceLinkCmd()
706
0
{
707
0
  cmList linkCmds;
708
709
  // this target requires separable cuda compilation
710
  // now build the correct command depending on if the target is
711
  // an executable or a dynamic library.
712
0
  switch (this->GetGeneratorTarget()->GetType()) {
713
0
    case cmStateEnums::STATIC_LIBRARY:
714
0
    case cmStateEnums::SHARED_LIBRARY:
715
0
    case cmStateEnums::MODULE_LIBRARY: {
716
0
      linkCmds.assign(
717
0
        this->GetMakefile()->GetDefinition("CMAKE_CUDA_DEVICE_LINK_LIBRARY"));
718
0
    } break;
719
0
    case cmStateEnums::EXECUTABLE: {
720
0
      linkCmds.assign(this->GetMakefile()->GetDefinition(
721
0
        "CMAKE_CUDA_DEVICE_LINK_EXECUTABLE"));
722
0
    } break;
723
0
    default:
724
0
      break;
725
0
  }
726
0
  return std::move(linkCmds.data());
727
0
}
728
729
std::vector<std::string> cmNinjaNormalTargetGenerator::ComputeLinkCmd(
730
  std::string const& config)
731
0
{
732
0
  cmList linkCmds;
733
0
  cmMakefile* mf = this->GetMakefile();
734
0
  {
735
    // If we have a rule variable prefer it. In the case of static libraries
736
    // this occurs when things like IPO is enabled, and we need to use the
737
    // CMAKE_<lang>_CREATE_STATIC_LIBRARY_IPO define instead.
738
0
    std::string linkCmdVar = this->GetGeneratorTarget()->GetCreateRuleVariable(
739
0
      this->TargetLinkLanguage(config), config);
740
0
    cmValue linkCmd = mf->GetDefinition(linkCmdVar);
741
0
    if (linkCmd) {
742
0
      std::string linkCmdStr = *linkCmd;
743
0
      if (this->GetGeneratorTarget()->HasImplibGNUtoMS(config)) {
744
0
        std::string ruleVar =
745
0
          cmStrCat("CMAKE_", this->GeneratorTarget->GetLinkerLanguage(config),
746
0
                   "_GNUtoMS_RULE");
747
0
        if (cmValue rule = this->Makefile->GetDefinition(ruleVar)) {
748
0
          linkCmdStr += *rule;
749
0
        }
750
0
      }
751
0
      linkCmds.assign(linkCmdStr);
752
0
      if (this->UseLWYU) {
753
0
        cmValue lwyuCheck = mf->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK");
754
0
        if (lwyuCheck) {
755
0
          std::string cmakeCommand = cmStrCat(
756
0
            this->GetLocalGenerator()->ConvertToOutputFormat(
757
0
              cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
758
0
            " -E __run_co_compile --lwyu=");
759
0
          cmakeCommand +=
760
0
            this->GetLocalGenerator()->EscapeForShell(*lwyuCheck);
761
762
0
          std::string targetOutputReal =
763
0
            this->ConvertToNinjaPath(this->GetGeneratorTarget()->GetFullPath(
764
0
              config, cmStateEnums::RuntimeBinaryArtifact,
765
0
              /*realname=*/true));
766
0
          cmakeCommand += cmStrCat(" --source=", targetOutputReal);
767
0
          linkCmds.push_back(std::move(cmakeCommand));
768
0
        }
769
0
      }
770
0
      return std::move(linkCmds.data());
771
0
    }
772
0
  }
773
0
  switch (this->GetGeneratorTarget()->GetType()) {
774
0
    case cmStateEnums::STATIC_LIBRARY: {
775
      // We have archive link commands set. First, delete the existing archive.
776
0
      {
777
0
        std::string cmakeCommand =
778
0
          this->GetLocalGenerator()->ConvertToOutputFormat(
779
0
            cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
780
0
        linkCmds.push_back(cmakeCommand + " -E rm -f $TARGET_FILE");
781
0
      }
782
      // TODO: Use ARCHIVE_APPEND for archives over a certain size.
783
0
      {
784
0
        std::string linkCmdVar = cmStrCat(
785
0
          "CMAKE_", this->TargetLinkLanguage(config), "_ARCHIVE_CREATE");
786
787
0
        linkCmdVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
788
0
          linkCmdVar, this->TargetLinkLanguage(config), config);
789
790
0
        std::string const& linkCmd = mf->GetRequiredDefinition(linkCmdVar);
791
0
        linkCmds.append(linkCmd);
792
0
      }
793
0
      {
794
0
        std::string linkCmdVar = cmStrCat(
795
0
          "CMAKE_", this->TargetLinkLanguage(config), "_ARCHIVE_FINISH");
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
#ifdef __APPLE__
804
      // On macOS ranlib truncates the fractional part of the static archive
805
      // file modification time.  If the archive and at least one contained
806
      // object file were created within the same second this will make look
807
      // the archive older than the object file. On subsequent ninja runs this
808
      // leads to re-archiving and updating dependent targets.
809
      // As a work-around we touch the archive after ranlib (see #19222).
810
      {
811
        std::string cmakeCommand =
812
          this->GetLocalGenerator()->ConvertToOutputFormat(
813
            cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
814
        linkCmds.push_back(cmakeCommand + " -E touch $TARGET_FILE");
815
      }
816
#endif
817
0
    } break;
818
0
    case cmStateEnums::SHARED_LIBRARY:
819
0
    case cmStateEnums::MODULE_LIBRARY:
820
0
    case cmStateEnums::EXECUTABLE:
821
0
      break;
822
0
    default:
823
0
      assert(false && "Unexpected target type");
824
0
  }
825
0
  return std::move(linkCmds.data());
826
0
}
827
828
void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement(
829
  std::string const& config, std::string const& fileConfig,
830
  bool firstForConfig)
831
0
{
832
0
  cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
833
0
  if (!globalGen->GetLanguageEnabled("CUDA")) {
834
0
    return;
835
0
  }
836
837
0
  cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
838
839
0
  bool requiresDeviceLinking = requireDeviceLinking(
840
0
    *this->GeneratorTarget, *this->GetLocalGenerator(), config);
841
0
  if (!requiresDeviceLinking) {
842
0
    return;
843
0
  }
844
845
  // First and very important step is to make sure while inside this
846
  // step our link language is set to CUDA
847
0
  std::string const& objExt =
848
0
    this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
849
850
0
  std::string targetOutputDir =
851
0
    this->GetLocalGenerator()->MaybeRelativeToTopBinDir(
852
0
      cmStrCat(genTarget->GetSupportDirectory(),
853
0
               globalGen->ConfigDirectory(config), '/'));
854
0
  targetOutputDir = globalGen->ExpandCFGIntDir(targetOutputDir, config);
855
856
0
  std::string targetOutputReal =
857
0
    this->ConvertToNinjaPath(targetOutputDir + "cmake_device_link" + objExt);
858
859
0
  if (firstForConfig) {
860
0
    globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal);
861
0
  }
862
0
  this->DeviceLinkObject = targetOutputReal;
863
864
  // Write comments.
865
0
  cmGlobalNinjaGenerator::WriteDivider(this->GetCommonFileStream());
866
0
  this->GetCommonFileStream()
867
0
    << "# Device Link build statements for "
868
0
    << cmState::GetTargetTypeName(genTarget->GetType()) << " target "
869
0
    << this->GetTargetName() << "\n\n";
870
871
0
  if (this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID") == "Clang") {
872
0
    std::string architecturesStr =
873
0
      this->GeneratorTarget->GetSafeProperty("CUDA_ARCHITECTURES");
874
875
0
    if (cmIsOff(architecturesStr)) {
876
0
      this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
877
0
                                   "CUDA_SEPARABLE_COMPILATION on Clang "
878
0
                                   "requires CUDA_ARCHITECTURES to be set.");
879
0
      return;
880
0
    }
881
882
0
    this->WriteDeviceLinkRules(config);
883
0
    this->WriteDeviceLinkStatements(config, cmList{ architecturesStr },
884
0
                                    targetOutputReal);
885
0
  } else {
886
0
    this->WriteNvidiaDeviceLinkStatement(config, fileConfig, targetOutputDir,
887
0
                                         targetOutputReal);
888
0
  }
889
0
}
890
891
void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatements(
892
  std::string const& config, std::vector<std::string> const& architectures,
893
  std::string const& output)
894
0
{
895
  // Ensure there are no duplicates.
896
0
  cmNinjaDeps const explicitDeps = [&]() -> std::vector<std::string> {
897
0
    std::unordered_set<std::string> depsSet;
898
0
    cmNinjaDeps const linkDeps =
899
0
      this->ComputeLinkDeps(this->TargetLinkLanguage(config), config, true);
900
0
    cmNinjaDeps const objects = this->GetObjects(config);
901
0
    depsSet.insert(linkDeps.begin(), linkDeps.end());
902
0
    depsSet.insert(objects.begin(), objects.end());
903
904
0
    std::vector<std::string> deps;
905
0
    std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps));
906
0
    return deps;
907
0
  }();
908
909
0
  cmGlobalNinjaGenerator* globalGen{ this->GetGlobalGenerator() };
910
0
  std::string const objectDir =
911
0
    cmStrCat(this->GeneratorTarget->GetSupportDirectory(),
912
0
             globalGen->ConfigDirectory(config));
913
0
  std::string const ninjaOutputDir = this->ConvertToNinjaPath(objectDir);
914
915
0
  cmNinjaBuild fatbinary(this->LanguageLinkerCudaFatbinaryRule(config));
916
917
  // Link device code for each architecture.
918
0
  for (std::string const& architectureKind : architectures) {
919
    // Clang always generates real code, so strip the specifier.
920
0
    std::string const architecture =
921
0
      architectureKind.substr(0, architectureKind.find('-'));
922
0
    std::string const cubin =
923
0
      cmStrCat(ninjaOutputDir, "/sm_", architecture, ".cubin");
924
925
0
    cmNinjaBuild dlink(this->LanguageLinkerCudaDeviceRule(config));
926
0
    dlink.ExplicitDeps = explicitDeps;
927
0
    dlink.Outputs = { cubin };
928
0
    dlink.Variables["ARCH"] = cmStrCat("sm_", architecture);
929
930
    // The generated register file contains macros that when expanded register
931
    // the device routines. Because the routines are the same for all
932
    // architectures the register file will be the same too. Thus generate it
933
    // only on the first invocation to reduce overhead.
934
0
    if (fatbinary.ExplicitDeps.empty()) {
935
0
      dlink.Variables["REGISTER"] = cmStrCat(
936
0
        "--register-link-binaries=", ninjaOutputDir, "/cmake_cuda_register.h");
937
0
    }
938
939
0
    fatbinary.Variables["PROFILES"] +=
940
0
      cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin);
941
0
    fatbinary.ExplicitDeps.emplace_back(cubin);
942
943
0
    globalGen->WriteBuild(this->GetCommonFileStream(), dlink);
944
0
  }
945
946
  // Combine all architectures into a single fatbinary.
947
0
  fatbinary.Outputs = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") };
948
0
  globalGen->WriteBuild(this->GetCommonFileStream(), fatbinary);
949
950
  // Compile the stub that registers the kernels and contains the fatbinaries.
951
0
  cmLocalNinjaGenerator* localGen{ this->GetLocalGenerator() };
952
0
  cmNinjaBuild dcompile(this->LanguageLinkerCudaDeviceCompileRule(config));
953
0
  dcompile.Outputs = { output };
954
0
  dcompile.ExplicitDeps = { cmStrCat(ninjaOutputDir, "/cmake_cuda_fatbin.h") };
955
0
  dcompile.Variables["FATBIN"] = localGen->ConvertToOutputFormat(
956
0
    cmStrCat(objectDir, "/cmake_cuda_fatbin.h"), cmOutputConverter::SHELL);
957
0
  dcompile.Variables["REGISTER"] = localGen->ConvertToOutputFormat(
958
0
    cmStrCat(objectDir, "/cmake_cuda_register.h"), cmOutputConverter::SHELL);
959
960
0
  cmNinjaLinkLineDeviceComputer linkLineComputer(
961
0
    localGen, localGen->GetStateSnapshot().GetDirectory(), globalGen);
962
0
  linkLineComputer.SetUseNinjaMulti(globalGen->IsMultiConfig());
963
964
  // Link libraries and paths are only used during the final executable/library
965
  // link.
966
0
  std::string frameworkPath;
967
0
  std::string linkPath;
968
0
  std::string linkLibs;
969
0
  localGen->GetDeviceLinkFlags(linkLineComputer, config, linkLibs,
970
0
                               dcompile.Variables["LINK_FLAGS"], frameworkPath,
971
0
                               linkPath, this->GetGeneratorTarget());
972
973
0
  globalGen->WriteBuild(this->GetCommonFileStream(), dcompile);
974
0
}
975
976
void cmNinjaNormalTargetGenerator::WriteNvidiaDeviceLinkStatement(
977
  std::string const& config, std::string const& fileConfig,
978
  std::string const& outputDir, std::string const& output)
979
0
{
980
0
  cmGeneratorTarget* genTarget = this->GetGeneratorTarget();
981
0
  cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
982
983
0
  std::string targetOutputImplib = this->ConvertToNinjaPath(
984
0
    genTarget->GetFullPath(config, cmStateEnums::ImportLibraryArtifact));
985
986
0
  if (config != fileConfig) {
987
0
    std::string targetOutputFileConfigDir =
988
0
      this->GetLocalGenerator()->MaybeRelativeToTopBinDir(
989
0
        cmStrCat(genTarget->GetSupportDirectory(),
990
0
                 globalGen->ConfigDirectory(config), '/'));
991
0
    targetOutputFileConfigDir =
992
0
      globalGen->ExpandCFGIntDir(outputDir, fileConfig);
993
0
    if (outputDir == targetOutputFileConfigDir) {
994
0
      return;
995
0
    }
996
997
0
    if (!genTarget->GetFullName(config, cmStateEnums::ImportLibraryArtifact)
998
0
           .empty() &&
999
0
        !genTarget
1000
0
           ->GetFullName(fileConfig, cmStateEnums::ImportLibraryArtifact)
1001
0
           .empty() &&
1002
0
        targetOutputImplib ==
1003
0
          this->ConvertToNinjaPath(genTarget->GetFullPath(
1004
0
            fileConfig, cmStateEnums::ImportLibraryArtifact))) {
1005
0
      return;
1006
0
    }
1007
0
  }
1008
1009
  // Compute the comment.
1010
0
  cmNinjaBuild build(this->LanguageLinkerDeviceRule(config));
1011
0
  build.Comment =
1012
0
    cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', output);
1013
1014
0
  cmNinjaVars& vars = build.Variables;
1015
1016
  // Compute outputs.
1017
0
  build.Outputs.push_back(output);
1018
  // Compute specific libraries to link with.
1019
0
  build.ExplicitDeps = this->GetObjects(config);
1020
0
  build.ImplicitDeps =
1021
0
    this->ComputeLinkDeps(this->TargetLinkLanguage(config), config);
1022
1023
0
  std::string frameworkPath;
1024
0
  std::string linkPath;
1025
1026
0
  std::string createRule =
1027
0
    genTarget->GetCreateRuleVariable(this->TargetLinkLanguage(config), config);
1028
0
  cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
1029
1030
0
  vars["TARGET_FILE"] =
1031
0
    localGen.ConvertToOutputFormat(output, cmOutputConverter::SHELL);
1032
1033
0
  cmNinjaLinkLineDeviceComputer linkLineComputer(
1034
0
    this->GetLocalGenerator(),
1035
0
    this->GetLocalGenerator()->GetStateSnapshot().GetDirectory(), globalGen);
1036
0
  linkLineComputer.SetUseNinjaMulti(globalGen->IsMultiConfig());
1037
1038
0
  localGen.GetDeviceLinkFlags(linkLineComputer, config, vars["LINK_LIBRARIES"],
1039
0
                              vars["LINK_FLAGS"], frameworkPath, linkPath,
1040
0
                              genTarget);
1041
1042
0
  this->addPoolNinjaVariable("JOB_POOL_LINK", genTarget, nullptr, vars);
1043
1044
0
  vars["MANIFESTS"] = this->GetManifests(config);
1045
1046
0
  vars["LINK_PATH"] = frameworkPath + linkPath;
1047
1048
  // Compute language specific link flags.
1049
0
  std::string langFlags;
1050
0
  localGen.AddLanguageFlagsForLinking(langFlags, genTarget, "CUDA", config);
1051
0
  vars["LANGUAGE_COMPILE_FLAGS"] = langFlags;
1052
1053
0
  auto const tgtNames = this->TargetNames(config);
1054
0
  if (genTarget->HasSOName(config) ||
1055
0
      genTarget->IsArchivedAIXSharedLibrary()) {
1056
0
    vars["SONAME_FLAG"] =
1057
0
      this->GetMakefile()->GetSONameFlag(this->TargetLinkLanguage(config));
1058
0
    vars["SONAME"] = localGen.ConvertToOutputFormat(tgtNames.SharedObject,
1059
0
                                                    cmOutputConverter::SHELL);
1060
0
    if (genTarget->GetType() == cmStateEnums::SHARED_LIBRARY) {
1061
0
      std::string install_dir =
1062
0
        this->GetGeneratorTarget()->GetInstallNameDirForBuildTree(config);
1063
0
      if (!install_dir.empty()) {
1064
0
        vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat(
1065
0
          install_dir, cmOutputConverter::SHELL);
1066
0
      }
1067
0
    }
1068
0
  }
1069
1070
0
  if (!tgtNames.ImportLibrary.empty()) {
1071
0
    std::string const impLibPath = localGen.ConvertToOutputFormat(
1072
0
      targetOutputImplib, cmOutputConverter::SHELL);
1073
0
    vars["TARGET_IMPLIB"] = impLibPath;
1074
0
    this->EnsureParentDirectoryExists(targetOutputImplib);
1075
0
  }
1076
1077
0
  std::string const objPath =
1078
0
    cmStrCat(this->GetGeneratorTarget()->GetSupportDirectory(),
1079
0
             globalGen->ConfigDirectory(config));
1080
1081
0
  vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1082
0
    this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL);
1083
0
  this->EnsureDirectoryExists(objPath);
1084
1085
0
  std::string const targetSupportPath =
1086
0
    this->GetGeneratorTarget()->GetCMFSupportDirectory();
1087
1088
0
  vars["TARGET_SUPPORT_DIR"] =
1089
0
    this->GetLocalGenerator()->ConvertToOutputFormat(
1090
0
      this->ConvertToNinjaPath(targetSupportPath), cmOutputConverter::SHELL);
1091
0
  this->EnsureDirectoryExists(targetSupportPath);
1092
1093
0
  this->SetMsvcTargetPdbVariable(vars, config);
1094
1095
0
  std::string& linkLibraries = vars["LINK_LIBRARIES"];
1096
0
  std::string& link_path = vars["LINK_PATH"];
1097
0
  if (globalGen->IsGCCOnWindows()) {
1098
    // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
1099
0
    std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
1100
0
    std::replace(link_path.begin(), link_path.end(), '\\', '/');
1101
0
  }
1102
1103
  // Device linking currently doesn't support response files so
1104
  // do not check if the user has explicitly forced a response file.
1105
0
  int const commandLineLengthLimit =
1106
0
    static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
1107
0
    globalGen->GetRuleCmdLength(build.Rule);
1108
1109
0
  build.RspFile = this->ConvertToNinjaPath(
1110
0
    cmStrCat("CMakeFiles/", genTarget->GetName(),
1111
0
             globalGen->IsMultiConfig() ? cmStrCat('.', config) : "", ".rsp"));
1112
1113
  // Gather order-only dependencies.
1114
0
  this->GetLocalGenerator()->AppendTargetDepends(
1115
0
    this->GetGeneratorTarget(), build.OrderOnlyDeps, config, config,
1116
0
    DependOnTargetArtifact);
1117
1118
  // Write the build statement for this target.
1119
0
  bool usedResponseFile = false;
1120
0
  globalGen->WriteBuild(this->GetCommonFileStream(), build,
1121
0
                        commandLineLengthLimit, &usedResponseFile);
1122
0
  this->WriteNvidiaDeviceLinkRule(usedResponseFile, config);
1123
0
}
1124
1125
void cmNinjaNormalTargetGenerator::WriteLinkStatement(
1126
  std::string const& config, std::string const& fileConfig,
1127
  bool firstForConfig)
1128
0
{
1129
0
  cmMakefile* mf = this->GetMakefile();
1130
0
  cmGlobalNinjaGenerator* globalGen = this->GetGlobalGenerator();
1131
0
  cmGeneratorTarget* gt = this->GetGeneratorTarget();
1132
1133
0
  std::string targetOutput = this->ConvertToNinjaPath(gt->GetFullPath(config));
1134
0
  std::string targetOutputReal = this->ConvertToNinjaPath(
1135
0
    gt->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact,
1136
0
                    /*realname=*/true));
1137
0
  std::string targetOutputImplib = this->ConvertToNinjaPath(
1138
0
    gt->GetFullPath(config, cmStateEnums::ImportLibraryArtifact));
1139
1140
0
  if (config != fileConfig) {
1141
0
    if (targetOutput ==
1142
0
        this->ConvertToNinjaPath(gt->GetFullPath(fileConfig))) {
1143
0
      return;
1144
0
    }
1145
0
    if (targetOutputReal ==
1146
0
        this->ConvertToNinjaPath(
1147
0
          gt->GetFullPath(fileConfig, cmStateEnums::RuntimeBinaryArtifact,
1148
0
                          /*realname=*/true))) {
1149
0
      return;
1150
0
    }
1151
0
    if (!gt->GetFullName(config, cmStateEnums::ImportLibraryArtifact)
1152
0
           .empty() &&
1153
0
        !gt->GetFullName(fileConfig, cmStateEnums::ImportLibraryArtifact)
1154
0
           .empty() &&
1155
0
        targetOutputImplib ==
1156
0
          this->ConvertToNinjaPath(gt->GetFullPath(
1157
0
            fileConfig, cmStateEnums::ImportLibraryArtifact))) {
1158
0
      return;
1159
0
    }
1160
0
  }
1161
1162
0
  auto const tgtNames = this->TargetNames(config);
1163
0
  if (gt->IsAppBundleOnApple()) {
1164
    // Create the app bundle
1165
0
    std::string outpath = gt->GetDirectory(config);
1166
0
    this->OSXBundleGenerator->CreateAppBundle(tgtNames.Output, outpath,
1167
0
                                              config);
1168
1169
    // Calculate the output path
1170
0
    targetOutput = cmStrCat(outpath, '/', tgtNames.Output);
1171
0
    targetOutput = this->ConvertToNinjaPath(targetOutput);
1172
0
    targetOutputReal = cmStrCat(outpath, '/', tgtNames.Real);
1173
0
    targetOutputReal = this->ConvertToNinjaPath(targetOutputReal);
1174
0
  } else if (gt->IsFrameworkOnApple()) {
1175
    // Create the library framework.
1176
1177
0
    cmOSXBundleGenerator::SkipParts bundleSkipParts;
1178
0
    if (globalGen->GetName() == "Ninja Multi-Config") {
1179
0
      auto const postFix = this->GeneratorTarget->GetFilePostfix(config);
1180
      // Skip creating Info.plist when there are multiple configurations, and
1181
      // the current configuration has a postfix. The non-postfix configuration
1182
      // Info.plist can be used by all the other configurations.
1183
0
      if (!postFix.empty()) {
1184
0
        bundleSkipParts.InfoPlist = true;
1185
0
      }
1186
0
    }
1187
0
    if (gt->HasImportLibrary(config)) {
1188
0
      bundleSkipParts.TextStubs = false;
1189
0
    }
1190
1191
0
    this->OSXBundleGenerator->CreateFramework(
1192
0
      tgtNames.Output, gt->GetDirectory(config), config, bundleSkipParts);
1193
0
  } else if (gt->IsCFBundleOnApple()) {
1194
    // Create the core foundation bundle.
1195
0
    this->OSXBundleGenerator->CreateCFBundle(tgtNames.Output,
1196
0
                                             gt->GetDirectory(config), config);
1197
0
  }
1198
1199
  // Write comments.
1200
0
  cmGlobalNinjaGenerator::WriteDivider(this->GetImplFileStream(fileConfig));
1201
0
  cmStateEnums::TargetType const targetType = gt->GetType();
1202
0
  this->GetImplFileStream(fileConfig)
1203
0
    << "# Link build statements for " << cmState::GetTargetTypeName(targetType)
1204
0
    << " target " << this->GetTargetName() << "\n\n";
1205
1206
0
  cmNinjaBuild linkBuild(this->LanguageLinkerRule(config));
1207
0
  cmNinjaVars& vars = linkBuild.Variables;
1208
1209
0
  if (this->GeneratorTarget->HasLinkDependencyFile(config)) {
1210
0
    this->AddDepfileBinding(vars,
1211
0
                            this->ConvertToNinjaPath(
1212
0
                              this->GetLocalGenerator()->GetLinkDependencyFile(
1213
0
                                this->GeneratorTarget, config)));
1214
0
  }
1215
1216
  // Compute the comment.
1217
0
  linkBuild.Comment =
1218
0
    cmStrCat("Link the ", this->GetVisibleTypeName(), ' ', targetOutputReal);
1219
1220
  // Compute outputs.
1221
0
  linkBuild.Outputs.push_back(targetOutputReal);
1222
0
  if (firstForConfig) {
1223
0
    globalGen->GetByproductsForCleanTarget(config).push_back(targetOutputReal);
1224
0
  }
1225
1226
  // If we can't split the Swift build model (CMP0157 is OLD or unset), fall
1227
  // back on the old one-step "build/link" logic.
1228
0
  if (!this->GetLocalGenerator()->IsSplitSwiftBuild() &&
1229
0
      this->TargetLinkLanguage(config) == "Swift") {
1230
0
    vars["SWIFT_LIBRARY_NAME"] = [this, config]() -> std::string {
1231
0
      cmGeneratorTarget::Names targetNames =
1232
0
        this->GetGeneratorTarget()->GetLibraryNames(config);
1233
0
      return targetNames.Base;
1234
0
    }();
1235
1236
0
    vars["SWIFT_MODULE_NAME"] = gt->GetSwiftModuleName();
1237
0
    vars["SWIFT_MODULE"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1238
0
      this->ConvertToNinjaPath(gt->GetSwiftModulePath(config)),
1239
0
      cmOutputConverter::SHELL);
1240
1241
0
    vars["SWIFT_SOURCES"] = [this, config]() -> std::string {
1242
0
      std::vector<cmSourceFile const*> sourceFiles;
1243
0
      std::stringstream oss;
1244
1245
0
      this->GetGeneratorTarget()->GetObjectSources(sourceFiles, config);
1246
0
      cmLocalGenerator const* LocalGen = this->GetLocalGenerator();
1247
0
      for (auto const& source : sourceFiles) {
1248
0
        std::string const sourcePath = source->GetLanguage() == "Swift"
1249
0
          ? this->GetCompiledSourceNinjaPath(source)
1250
0
          : this->GetObjectFilePath(source, config);
1251
0
        oss << " "
1252
0
            << LocalGen->ConvertToOutputFormat(sourcePath,
1253
0
                                               cmOutputConverter::SHELL);
1254
0
      }
1255
0
      return oss.str();
1256
0
    }();
1257
1258
    // Since we do not perform object builds, compute the
1259
    // defines/flags/includes here so that they can be passed along
1260
    // appropriately.
1261
0
    vars["DEFINES"] = this->GetDefines("Swift", config);
1262
0
    vars["FLAGS"] = this->GetFlags("Swift", config);
1263
0
    vars["INCLUDES"] = this->GetIncludes("Swift", config);
1264
0
    this->GenerateSwiftOutputFileMap(config, vars["FLAGS"]);
1265
1266
    // Compute specific libraries to link with.
1267
0
    std::vector<cmSourceFile const*> sources;
1268
0
    gt->GetObjectSources(sources, config);
1269
0
    for (auto const& source : sources) {
1270
0
      if (source->GetLanguage() == "Swift") {
1271
0
        linkBuild.Outputs.push_back(
1272
0
          this->ConvertToNinjaPath(this->GetObjectFilePath(source, config)));
1273
0
        linkBuild.ExplicitDeps.emplace_back(
1274
0
          this->GetCompiledSourceNinjaPath(source));
1275
0
      } else {
1276
0
        linkBuild.ExplicitDeps.emplace_back(
1277
0
          this->GetObjectFilePath(source, config));
1278
0
      }
1279
0
    }
1280
0
    if (targetType != cmStateEnums::EXECUTABLE ||
1281
0
        gt->IsExecutableWithExports()) {
1282
0
      linkBuild.Outputs.push_back(vars["SWIFT_MODULE"]);
1283
0
    }
1284
0
  } else if (this->TargetLinkLanguage(config) == "Rust") {
1285
    // Use one-step build/link for Rust.
1286
    // Compute specific libraries to link with.
1287
0
    std::vector<cmSourceFile const*> sources;
1288
0
    gt->GetObjectSources(sources, config);
1289
0
    cmLocalGenerator const* lg = this->GetLocalGenerator();
1290
0
    std::string entry_obj;
1291
1292
0
    for (auto const& source : sources) {
1293
0
      if (source->GetLanguage() == "Rust") {
1294
0
        if (vars.count("RUST_SOURCES") == 0) {
1295
0
          std::string const sourcePath =
1296
0
            this->GetCompiledSourceNinjaPath(source);
1297
0
          vars["RUST_SOURCES"] =
1298
0
            lg->ConvertToOutputFormat(sourcePath, cmOutputConverter::SHELL);
1299
0
          entry_obj = this->GetObjectFilePath(source, config);
1300
0
        } else {
1301
0
          assert(false && "Rust crate can only have 1 entry");
1302
0
        }
1303
0
      }
1304
0
    }
1305
1306
0
    linkBuild.ExplicitDeps = this->GetObjects(config);
1307
0
    std::stringstream obj_deps;
1308
1309
    // Do not try linking to object file created from the crate entry.
1310
0
    for (auto const& obj : linkBuild.ExplicitDeps) {
1311
0
      if (obj != entry_obj) {
1312
0
        obj_deps << " "
1313
0
                 << lg->ConvertToOutputFormat(obj, cmOutputConverter::SHELL);
1314
0
      }
1315
0
    }
1316
1317
0
    vars["RUST_OBJECT_DEPS"] = obj_deps.str();
1318
0
  } else {
1319
0
    linkBuild.ExplicitDeps = this->GetObjects(config);
1320
0
  }
1321
1322
0
  std::vector<std::string> extraISPCObjects =
1323
0
    this->GetGeneratorTarget()->GetGeneratedISPCObjects(config);
1324
0
  std::transform(extraISPCObjects.begin(), extraISPCObjects.end(),
1325
0
                 std::back_inserter(linkBuild.ExplicitDeps),
1326
0
                 this->MapToNinjaPath());
1327
1328
0
  linkBuild.ImplicitDeps =
1329
0
    this->ComputeLinkDeps(this->TargetLinkLanguage(config), config);
1330
1331
0
  if (!this->DeviceLinkObject.empty()) {
1332
0
    linkBuild.ExplicitDeps.push_back(this->DeviceLinkObject);
1333
0
  }
1334
1335
0
  std::string frameworkPath;
1336
0
  std::string linkPath;
1337
1338
0
  std::string createRule =
1339
0
    gt->GetCreateRuleVariable(this->TargetLinkLanguage(config), config);
1340
0
  bool useWatcomQuote = mf->IsOn(createRule + "_USE_WATCOM_QUOTE");
1341
0
  cmLocalNinjaGenerator& localGen = *this->GetLocalGenerator();
1342
1343
0
  vars["TARGET_FILE"] =
1344
0
    localGen.ConvertToOutputFormat(targetOutputReal, cmOutputConverter::SHELL);
1345
1346
0
  std::unique_ptr<cmLinkLineComputer> linkLineComputer =
1347
0
    globalGen->CreateLinkLineComputer(
1348
0
      this->GetLocalGenerator(),
1349
0
      this->GetLocalGenerator()->GetStateSnapshot().GetDirectory());
1350
0
  linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
1351
0
  linkLineComputer->SetUseNinjaMulti(globalGen->IsMultiConfig());
1352
1353
0
  localGen.GetTargetFlags(linkLineComputer.get(), config,
1354
0
                          vars["LINK_LIBRARIES"], vars["FLAGS"],
1355
0
                          vars["LINK_FLAGS"], frameworkPath, linkPath, gt);
1356
1357
0
  localGen.AppendDependencyInfoLinkerFlags(vars["LINK_FLAGS"], gt, config,
1358
0
                                           this->TargetLinkLanguage(config));
1359
1360
  // Add OS X version flags, if any.
1361
0
  if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
1362
0
      this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
1363
0
    this->AppendOSXVerFlag(vars["LINK_FLAGS"],
1364
0
                           this->TargetLinkLanguage(config), "COMPATIBILITY",
1365
0
                           true);
1366
0
    this->AppendOSXVerFlag(vars["LINK_FLAGS"],
1367
0
                           this->TargetLinkLanguage(config), "CURRENT", false);
1368
0
  }
1369
1370
0
  this->addPoolNinjaVariable("JOB_POOL_LINK", gt, nullptr, vars);
1371
1372
0
  this->UseLWYU = this->GetLocalGenerator()->AppendLWYUFlags(
1373
0
    vars["LINK_FLAGS"], this->GetGeneratorTarget(),
1374
0
    this->TargetLinkLanguage(config));
1375
1376
0
  vars["MANIFESTS"] = this->GetManifests(config);
1377
0
  vars["AIX_EXPORTS"] = this->GetAIXExports(config);
1378
1379
0
  vars["LINK_PATH"] = frameworkPath + linkPath;
1380
0
  vars["CONFIG"] = config;
1381
1382
  // Compute architecture specific link flags.  Yes, these go into a different
1383
  // variable for executables, probably due to a mistake made when duplicating
1384
  // code between the Makefile executable and library generators.
1385
0
  if (targetType == cmStateEnums::EXECUTABLE) {
1386
0
    std::string t = vars["FLAGS"];
1387
0
    localGen.AddArchitectureFlags(t, gt, this->TargetLinkLanguage(config),
1388
0
                                  config);
1389
0
    vars["FLAGS"] = t;
1390
0
  } else {
1391
0
    std::string t = vars["ARCH_FLAGS"];
1392
0
    localGen.AddArchitectureFlags(t, gt, this->TargetLinkLanguage(config),
1393
0
                                  config);
1394
0
    vars["ARCH_FLAGS"] = t;
1395
0
    t.clear();
1396
0
    localGen.AddLanguageFlagsForLinking(
1397
0
      t, gt, this->TargetLinkLanguage(config), config);
1398
0
    vars["LANGUAGE_COMPILE_FLAGS"] = t;
1399
0
  }
1400
0
  if (gt->HasSOName(config) || gt->IsArchivedAIXSharedLibrary()) {
1401
0
    vars["SONAME_FLAG"] = mf->GetSONameFlag(this->TargetLinkLanguage(config));
1402
0
    vars["SONAME"] = localGen.ConvertToOutputFormat(tgtNames.SharedObject,
1403
0
                                                    cmOutputConverter::SHELL);
1404
0
    if (targetType == cmStateEnums::SHARED_LIBRARY) {
1405
0
      std::string install_dir = gt->GetInstallNameDirForBuildTree(config);
1406
0
      if (!install_dir.empty()) {
1407
0
        vars["INSTALLNAME_DIR"] = localGen.ConvertToOutputFormat(
1408
0
          install_dir, cmOutputConverter::SHELL);
1409
0
      }
1410
0
    }
1411
0
  }
1412
1413
0
  cmGlobalNinjaGenerator::CCOutputs byproducts(this->GetGlobalGenerator());
1414
1415
0
  if (!gt->IsApple() && !tgtNames.ImportLibrary.empty()) {
1416
0
    std::string const impLibPath = localGen.ConvertToOutputFormat(
1417
0
      targetOutputImplib, cmOutputConverter::SHELL);
1418
0
    vars["TARGET_IMPLIB"] = impLibPath;
1419
0
    this->EnsureParentDirectoryExists(targetOutputImplib);
1420
0
    if (gt->HasImportLibrary(config)) {
1421
      // Some linkers may update a binary without touching its import lib.
1422
0
      byproducts.ExplicitOuts.emplace_back(targetOutputImplib);
1423
0
      if (firstForConfig) {
1424
0
        globalGen->GetByproductsForCleanTarget(config).push_back(
1425
0
          targetOutputImplib);
1426
0
      }
1427
0
    }
1428
0
  }
1429
1430
0
  if (!this->SetMsvcTargetPdbVariable(vars, config)) {
1431
    // It is common to place debug symbols at a specific place,
1432
    // so we need a plain target name in the rule available.
1433
0
    cmGeneratorTarget::NameComponents const& components =
1434
0
      gt->GetFullNameComponents(config);
1435
0
    std::string dbg_suffix = ".dbg";
1436
    // TODO: Where to document?
1437
0
    if (cmValue d = mf->GetDefinition("CMAKE_DEBUG_SYMBOL_SUFFIX")) {
1438
0
      dbg_suffix = *d;
1439
0
    }
1440
0
    vars["TARGET_PDB"] = components.base + components.suffix + dbg_suffix;
1441
0
  }
1442
1443
0
  std::string const objPath =
1444
0
    cmStrCat(gt->GetSupportDirectory(), globalGen->ConfigDirectory(config));
1445
0
  vars["OBJECT_DIR"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1446
0
    this->ConvertToNinjaPath(objPath), cmOutputConverter::SHELL);
1447
0
  this->EnsureDirectoryExists(objPath);
1448
1449
0
  std::string const targetSupportPath = gt->GetCMFSupportDirectory();
1450
0
  vars["TARGET_SUPPORT_DIR"] =
1451
0
    this->GetLocalGenerator()->ConvertToOutputFormat(
1452
0
      this->ConvertToNinjaPath(targetSupportPath), cmOutputConverter::SHELL);
1453
0
  this->EnsureDirectoryExists(targetSupportPath);
1454
1455
0
  std::string& linkLibraries = vars["LINK_LIBRARIES"];
1456
0
  std::string& link_path = vars["LINK_PATH"];
1457
0
  if (globalGen->IsGCCOnWindows()) {
1458
    // ar.exe can't handle backslashes in rsp files (implicitly used by gcc)
1459
0
    std::replace(linkLibraries.begin(), linkLibraries.end(), '\\', '/');
1460
0
    std::replace(link_path.begin(), link_path.end(), '\\', '/');
1461
0
  }
1462
1463
0
  std::vector<cmCustomCommand> const* cmdLists[3] = {
1464
0
    &gt->GetPreBuildCommands(), &gt->GetPreLinkCommands(),
1465
0
    &gt->GetPostBuildCommands()
1466
0
  };
1467
1468
0
  std::vector<std::string> preLinkComments;
1469
0
  std::vector<std::string> postBuildComments;
1470
1471
0
  std::vector<std::string> preLinkCmdLines;
1472
0
  std::vector<std::string> postBuildCmdLines;
1473
1474
0
  std::vector<std::string>* cmdComments[3] = { &preLinkComments,
1475
0
                                               &preLinkComments,
1476
0
                                               &postBuildComments };
1477
0
  std::vector<std::string>* cmdLineLists[3] = { &preLinkCmdLines,
1478
0
                                                &preLinkCmdLines,
1479
0
                                                &postBuildCmdLines };
1480
0
  cmGeneratorExpression ge(*this->GetLocalGenerator()->GetCMakeInstance());
1481
1482
0
  for (unsigned i = 0; i != 3; ++i) {
1483
0
    for (cmCustomCommand const& cc : *cmdLists[i]) {
1484
0
      if (config == fileConfig ||
1485
0
          this->GetLocalGenerator()->HasUniqueByproducts(cc.GetByproducts(),
1486
0
                                                         cc.GetBacktrace())) {
1487
0
        cmCustomCommandGenerator ccg(cc, fileConfig, this->GetLocalGenerator(),
1488
0
                                     true, config);
1489
0
        localGen.AppendCustomCommandLines(ccg, *cmdLineLists[i]);
1490
0
        if (cc.GetComment()) {
1491
0
          auto cge = ge.Parse(cc.GetComment());
1492
0
          cmdComments[i]->emplace_back(
1493
0
            cge->Evaluate(this->GetLocalGenerator(), config));
1494
0
        }
1495
0
        std::vector<std::string> const& ccByproducts = ccg.GetByproducts();
1496
0
        byproducts.Add(ccByproducts);
1497
0
        std::transform(
1498
0
          ccByproducts.begin(), ccByproducts.end(),
1499
0
          std::back_inserter(globalGen->GetByproductsForCleanTarget()),
1500
0
          this->MapToNinjaPath());
1501
0
      }
1502
0
    }
1503
0
  }
1504
1505
  // If we have any PRE_LINK commands, we need to go back to CMAKE_BINARY_DIR
1506
  // for the link commands.
1507
0
  if (!preLinkCmdLines.empty()) {
1508
0
    std::string const homeOutDir = localGen.ConvertToOutputFormat(
1509
0
      localGen.GetBinaryDirectory(), cmOutputConverter::SHELL);
1510
0
    preLinkCmdLines.push_back("cd " + homeOutDir);
1511
0
  }
1512
1513
  // maybe create .def file from list of objects
1514
0
  cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
1515
0
    gt->GetModuleDefinitionInfo(config);
1516
0
  if (mdi && mdi->DefFileGenerated) {
1517
0
    std::string cmakeCommand =
1518
0
      this->GetLocalGenerator()->ConvertToOutputFormat(
1519
0
        cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL);
1520
0
    std::string cmd =
1521
0
      cmStrCat(cmakeCommand, " -E __create_def ",
1522
0
               this->GetLocalGenerator()->ConvertToOutputFormat(
1523
0
                 mdi->DefFile, cmOutputConverter::SHELL),
1524
0
               ' ');
1525
0
    std::string obj_list_file = mdi->DefFile + ".objs";
1526
0
    cmd += this->GetLocalGenerator()->ConvertToOutputFormat(
1527
0
      obj_list_file, cmOutputConverter::SHELL);
1528
1529
0
    cmValue nm_executable = this->GetMakefile()->GetDefinition("CMAKE_NM");
1530
0
    if (cmNonempty(nm_executable)) {
1531
0
      cmd += " --nm=";
1532
0
      cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
1533
0
        *nm_executable, cmOutputConverter::SHELL);
1534
0
    }
1535
0
    preLinkCmdLines.push_back(std::move(cmd));
1536
1537
    // create a list of obj files for the -E __create_def to read
1538
0
    cmGeneratedFileStream fout(obj_list_file);
1539
1540
0
    if (mdi->WindowsExportAllSymbols) {
1541
0
      cmNinjaDeps objs = this->GetObjects(config);
1542
0
      for (std::string const& obj : objs) {
1543
0
        if (cmHasLiteralSuffix(obj, ".obj")) {
1544
0
          fout << obj << "\n";
1545
0
        }
1546
0
      }
1547
0
    }
1548
1549
0
    for (cmSourceFile const* src : mdi->Sources) {
1550
0
      fout << src->GetFullPath() << "\n";
1551
0
    }
1552
0
  }
1553
1554
0
  vars["PRE_LINK"] = localGen.BuildCommandLine(
1555
0
    preLinkCmdLines, config, fileConfig, "pre-link", this->GeneratorTarget);
1556
0
  std::string postBuildCmdLine =
1557
0
    localGen.BuildCommandLine(postBuildCmdLines, config, fileConfig,
1558
0
                              "post-build", this->GeneratorTarget);
1559
1560
0
  cmNinjaVars symlinkVars;
1561
0
  bool const symlinkNeeded =
1562
0
    (targetOutput != targetOutputReal && !gt->IsFrameworkOnApple() &&
1563
0
     !gt->IsArchivedAIXSharedLibrary());
1564
0
  if (!symlinkNeeded) {
1565
0
    vars["POST_BUILD"] = postBuildCmdLine;
1566
0
  } else {
1567
0
    vars["POST_BUILD"] = cmGlobalNinjaGenerator::SHELL_NOOP;
1568
0
    symlinkVars["POST_BUILD"] = postBuildCmdLine;
1569
0
  }
1570
1571
0
  std::string cmakeVarLang =
1572
0
    cmStrCat("CMAKE_", this->TargetLinkLanguage(config));
1573
1574
  // build response file name
1575
0
  std::string cmakeLinkVar = cmakeVarLang + "_RESPONSE_FILE_LINK_FLAG";
1576
1577
0
  cmValue flag = this->GetMakefile()->GetDefinition(cmakeLinkVar);
1578
1579
0
  bool const lang_supports_response =
1580
0
    !(this->TargetLinkLanguage(config) == "RC" ||
1581
0
      (this->TargetLinkLanguage(config) == "CUDA" && !flag));
1582
0
  int commandLineLengthLimit = -1;
1583
0
  if (!lang_supports_response || !this->ForceResponseFile()) {
1584
0
    commandLineLengthLimit =
1585
0
      static_cast<int>(cmSystemTools::CalculateCommandLineLengthLimit()) -
1586
0
      globalGen->GetRuleCmdLength(linkBuild.Rule);
1587
0
  }
1588
1589
0
  linkBuild.RspFile = this->ConvertToNinjaPath(
1590
0
    cmStrCat("CMakeFiles/", gt->GetName(),
1591
0
             globalGen->IsMultiConfig() ? cmStrCat('.', config) : "", ".rsp"));
1592
1593
  // Gather order-only dependencies.
1594
0
  this->GetLocalGenerator()->AppendTargetDepends(
1595
0
    gt, linkBuild.OrderOnlyDeps, config, fileConfig, DependOnTargetArtifact);
1596
1597
  // Add order-only dependencies on versioning symlinks of shared libs we link.
1598
  // If our target is not producing a runtime binary, it doesn't need the
1599
  // symlinks (anything that links to the target might, but that consumer will
1600
  // get its own order-only dependency).
1601
0
  if (!gt->IsDLLPlatform() && gt->IsRuntimeBinary()) {
1602
0
    if (cmComputeLinkInformation* cli = gt->GetLinkInformation(config)) {
1603
0
      for (auto const& item : cli->GetItems()) {
1604
0
        if (item.Target &&
1605
0
            item.Target->GetType() == cmStateEnums::SHARED_LIBRARY &&
1606
0
            !item.Target->IsFrameworkOnApple()) {
1607
0
          std::string const& lib =
1608
0
            this->ConvertToNinjaPath(item.Target->GetFullPath(config));
1609
0
          if (std::find(linkBuild.ImplicitDeps.begin(),
1610
0
                        linkBuild.ImplicitDeps.end(),
1611
0
                        lib) == linkBuild.ImplicitDeps.end()) {
1612
0
            linkBuild.OrderOnlyDeps.emplace_back(lib);
1613
0
          }
1614
0
        }
1615
0
      }
1616
0
    }
1617
0
  }
1618
1619
  // Add dependencies on swiftmodule files when using the swift linker
1620
0
  if (!this->GetLocalGenerator()->IsSplitSwiftBuild() &&
1621
0
      this->TargetLinkLanguage(config) == "Swift") {
1622
0
    if (cmComputeLinkInformation* cli =
1623
0
          this->GeneratorTarget->GetLinkInformation(config)) {
1624
0
      for (auto const& dependency : cli->GetItems()) {
1625
        // Both the current target and the linked target must be swift targets
1626
        // in order for there to be a swiftmodule to depend on
1627
0
        if (dependency.Target &&
1628
0
            dependency.Target->GetLinkerLanguage(config) == "Swift") {
1629
0
          std::string swiftmodule = this->ConvertToNinjaPath(
1630
0
            dependency.Target->GetSwiftModulePath(config));
1631
0
          linkBuild.ImplicitDeps.emplace_back(swiftmodule);
1632
0
        }
1633
0
      }
1634
0
    }
1635
0
  }
1636
1637
  // Ninja should restat after linking if and only if there are byproducts.
1638
0
  vars["RESTAT"] = byproducts.ExplicitOuts.empty() ? "" : "1";
1639
1640
0
  linkBuild.Outputs.reserve(linkBuild.Outputs.size() +
1641
0
                            byproducts.ExplicitOuts.size());
1642
0
  std::move(byproducts.ExplicitOuts.begin(), byproducts.ExplicitOuts.end(),
1643
0
            std::back_inserter(linkBuild.Outputs));
1644
0
  linkBuild.WorkDirOuts = std::move(byproducts.WorkDirOuts);
1645
1646
  // Write the build statement for this target.
1647
0
  bool usedResponseFile = false;
1648
0
  globalGen->WriteBuild(this->GetImplFileStream(fileConfig), linkBuild,
1649
0
                        commandLineLengthLimit, &usedResponseFile);
1650
0
  this->WriteLinkRule(usedResponseFile, config, preLinkComments,
1651
0
                      postBuildComments);
1652
1653
0
  if (symlinkNeeded) {
1654
0
    if (targetType == cmStateEnums::EXECUTABLE) {
1655
0
      cmNinjaBuild build("CMAKE_SYMLINK_EXECUTABLE");
1656
0
      build.Comment = "Create executable symlink " + targetOutput;
1657
0
      build.Outputs.push_back(targetOutput);
1658
0
      if (firstForConfig) {
1659
0
        globalGen->GetByproductsForCleanTarget(config).push_back(targetOutput);
1660
0
      }
1661
0
      build.ExplicitDeps.push_back(targetOutputReal);
1662
0
      build.Variables = std::move(symlinkVars);
1663
0
      globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build);
1664
0
    } else {
1665
0
      cmNinjaBuild build("CMAKE_SYMLINK_LIBRARY");
1666
0
      build.Comment = "Create library symlink " + targetOutput;
1667
1668
0
      std::string const soName = this->ConvertToNinjaPath(
1669
0
        this->GetTargetFilePath(tgtNames.SharedObject, config));
1670
      // If one link has to be created.
1671
0
      if (targetOutputReal == soName || targetOutput == soName) {
1672
0
        symlinkVars["SONAME"] =
1673
0
          this->GetLocalGenerator()->ConvertToOutputFormat(
1674
0
            soName, cmOutputConverter::SHELL);
1675
0
      } else {
1676
0
        symlinkVars["SONAME"].clear();
1677
0
        build.Outputs.push_back(soName);
1678
0
        if (firstForConfig) {
1679
0
          globalGen->GetByproductsForCleanTarget(config).push_back(soName);
1680
0
        }
1681
0
      }
1682
0
      build.Outputs.push_back(targetOutput);
1683
0
      if (firstForConfig) {
1684
0
        globalGen->GetByproductsForCleanTarget(config).push_back(targetOutput);
1685
0
      }
1686
0
      build.ExplicitDeps.push_back(targetOutputReal);
1687
0
      build.Variables = std::move(symlinkVars);
1688
1689
0
      globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build);
1690
0
    }
1691
0
  }
1692
1693
  // Add aliases for the file name and the target name.
1694
0
  globalGen->AddTargetAlias(tgtNames.Output, gt, config);
1695
0
  globalGen->AddTargetAlias(this->GetTargetName(), gt, config);
1696
1697
0
  if (this->GetGeneratorTarget()->IsApple() &&
1698
0
      this->GetGeneratorTarget()->HasImportLibrary(config)) {
1699
0
    auto dirTBD =
1700
0
      gt->GetDirectory(config, cmStateEnums::ImportLibraryArtifact);
1701
0
    auto targetTBD =
1702
0
      this->ConvertToNinjaPath(cmStrCat(dirTBD, '/', tgtNames.ImportReal));
1703
0
    this->EnsureParentDirectoryExists(targetTBD);
1704
0
    cmNinjaBuild build(this->TextStubsGeneratorRule(config));
1705
0
    build.Comment = cmStrCat("Generate the text-based stubs file ", targetTBD);
1706
0
    build.Outputs.push_back(targetTBD);
1707
0
    build.ExplicitDeps.push_back(targetOutputReal);
1708
0
    globalGen->WriteBuild(this->GetImplFileStream(fileConfig), build);
1709
1710
0
    if (tgtNames.ImportOutput != tgtNames.ImportReal &&
1711
0
        !this->GetGeneratorTarget()->IsFrameworkOnApple()) {
1712
0
      auto outputTBD =
1713
0
        this->ConvertToNinjaPath(cmStrCat(dirTBD, '/', tgtNames.ImportOutput));
1714
0
      std::string const soNameTBD = this->ConvertToNinjaPath(
1715
0
        cmStrCat(dirTBD, '/', tgtNames.ImportLibrary));
1716
1717
0
      cmNinjaBuild slBuild("CMAKE_SYMLINK_IMPORT_LIBRARY");
1718
0
      slBuild.Comment = cmStrCat("Create import library symlink ", outputTBD);
1719
0
      cmNinjaVars slVars;
1720
1721
      // If one link has to be created.
1722
0
      if (targetTBD == soNameTBD || outputTBD == soNameTBD) {
1723
0
        slVars["SONAME"] = this->GetLocalGenerator()->ConvertToOutputFormat(
1724
0
          soNameTBD, cmOutputConverter::SHELL);
1725
0
      } else {
1726
0
        slVars["SONAME"].clear();
1727
0
        slBuild.Outputs.push_back(soNameTBD);
1728
0
        if (firstForConfig) {
1729
0
          globalGen->GetByproductsForCleanTarget(config).push_back(soNameTBD);
1730
0
        }
1731
0
      }
1732
0
      slBuild.Outputs.push_back(outputTBD);
1733
0
      if (firstForConfig) {
1734
0
        globalGen->GetByproductsForCleanTarget(config).push_back(outputTBD);
1735
0
      }
1736
0
      slBuild.ExplicitDeps.push_back(targetTBD);
1737
0
      slBuild.Variables = std::move(slVars);
1738
1739
0
      globalGen->WriteBuild(this->GetImplFileStream(fileConfig), slBuild);
1740
0
    }
1741
1742
    // Add alias for the import file name
1743
0
    globalGen->AddTargetAlias(tgtNames.ImportOutput, gt, config);
1744
0
  }
1745
0
}
1746
1747
void cmNinjaNormalTargetGenerator::WriteObjectLibStatement(
1748
  std::string const& config)
1749
0
{
1750
  // Write a phony output that depends on all object files.
1751
0
  {
1752
0
    cmNinjaBuild build("phony");
1753
0
    build.Comment = "Object library " + this->GetTargetName();
1754
0
    this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(),
1755
0
                                                   build.Outputs, config);
1756
0
    this->GetLocalGenerator()->AppendTargetOutputs(
1757
0
      this->GetGeneratorTarget(),
1758
0
      this->GetGlobalGenerator()->GetByproductsForCleanTarget(config), config);
1759
0
    build.ExplicitDeps = this->GetObjects(config);
1760
0
    this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), build);
1761
0
  }
1762
1763
  // Add aliases for the target name.
1764
0
  this->GetGlobalGenerator()->AddTargetAlias(
1765
0
    this->GetTargetName(), this->GetGeneratorTarget(), config);
1766
0
}
1767
1768
void cmNinjaNormalTargetGenerator::WriteCxxModuleLibraryStatement(
1769
  std::string const& config, std::string const& /*fileConfig*/,
1770
  bool firstForConfig)
1771
0
{
1772
  // TODO: How to use `fileConfig` properly?
1773
1774
  // Write a phony output that depends on the scanning output.
1775
0
  {
1776
0
    cmNinjaBuild build("phony");
1777
0
    build.Comment =
1778
0
      cmStrCat("Imported C++ module library ", this->GetTargetName());
1779
0
    this->GetLocalGenerator()->AppendTargetOutputs(this->GetGeneratorTarget(),
1780
0
                                                   build.Outputs, config);
1781
0
    if (firstForConfig) {
1782
0
      this->GetLocalGenerator()->AppendTargetOutputs(
1783
0
        this->GetGeneratorTarget(),
1784
0
        this->GetGlobalGenerator()->GetByproductsForCleanTarget(config),
1785
0
        config);
1786
0
    }
1787
0
    build.ExplicitDeps.emplace_back(this->GetDyndepFilePath("CXX", config));
1788
0
    this->GetGlobalGenerator()->WriteBuild(this->GetCommonFileStream(), build);
1789
0
  }
1790
1791
  // Add aliases for the target name.
1792
0
  this->GetGlobalGenerator()->AddTargetAlias(
1793
0
    this->GetTargetName(), this->GetGeneratorTarget(), config);
1794
0
}
1795
1796
cmGeneratorTarget::Names cmNinjaNormalTargetGenerator::TargetNames(
1797
  std::string const& config) const
1798
0
{
1799
0
  if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE) {
1800
0
    return this->GeneratorTarget->GetExecutableNames(config);
1801
0
  }
1802
0
  return this->GeneratorTarget->GetLibraryNames(config);
1803
0
}
1804
1805
std::string cmNinjaNormalTargetGenerator::TargetLinkLanguage(
1806
  std::string const& config) const
1807
0
{
1808
0
  return this->GeneratorTarget->GetLinkerLanguage(config);
1809
0
}