Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmMakefileExecutableTargetGenerator.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 "cmMakefileExecutableTargetGenerator.h"
4
5
#include <set>
6
#include <string>
7
#include <utility>
8
#include <vector>
9
10
#include <cm/memory>
11
#include <cmext/algorithm>
12
13
#include "cmGeneratedFileStream.h"
14
#include "cmGeneratorOptions.h"
15
#include "cmGeneratorTarget.h"
16
#include "cmGlobalUnixMakefileGenerator3.h"
17
#include "cmLinkLineComputer.h"
18
#include "cmLinkLineDeviceComputer.h"
19
#include "cmList.h"
20
#include "cmLocalGenerator.h"
21
#include "cmLocalUnixMakefileGenerator3.h"
22
#include "cmMakefile.h"
23
#include "cmOSXBundleGenerator.h"
24
#include "cmOutputConverter.h"
25
#include "cmRulePlaceholderExpander.h"
26
#include "cmState.h"
27
#include "cmStateDirectory.h"
28
#include "cmStateSnapshot.h"
29
#include "cmStateTypes.h"
30
#include "cmStringAlgorithms.h"
31
#include "cmSystemTools.h"
32
#include "cmValue.h"
33
34
cmMakefileExecutableTargetGenerator::cmMakefileExecutableTargetGenerator(
35
  cmGeneratorTarget* target)
36
0
  : cmMakefileTargetGenerator(target)
37
0
{
38
0
  this->CustomCommandDriver = OnDepends;
39
0
  this->TargetNames =
40
0
    this->GeneratorTarget->GetExecutableNames(this->GetConfigName());
41
42
0
  this->OSXBundleGenerator = cm::make_unique<cmOSXBundleGenerator>(target);
43
0
  this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
44
0
}
45
46
0
cmMakefileExecutableTargetGenerator::~cmMakefileExecutableTargetGenerator() =
47
  default;
48
49
void cmMakefileExecutableTargetGenerator::WriteRuleFiles()
50
0
{
51
  // create the build.make file and directory, put in the common blocks
52
0
  this->CreateRuleFile();
53
54
  // write rules used to help build object files
55
0
  this->WriteCommonCodeRules();
56
57
  // write the per-target per-language flags
58
0
  this->WriteTargetLanguageFlags();
59
60
  // write in rules for object files and custom commands
61
0
  this->WriteTargetBuildRules();
62
63
  // write the device link rules
64
0
  this->WriteDeviceExecutableRule(false);
65
66
  // write the link rules
67
0
  this->WriteExecutableRule(false);
68
0
  if (this->GeneratorTarget->NeedRelinkBeforeInstall(this->GetConfigName())) {
69
    // Write rules to link an installable version of the target.
70
0
    this->WriteExecutableRule(true);
71
0
  }
72
73
0
  this->WriteTargetLinkDependRules();
74
75
  // Write clean target
76
0
  this->WriteTargetCleanRules();
77
78
  // Write the dependency generation rule.  This must be done last so
79
  // that multiple output pair information is available.
80
0
  this->WriteTargetDependRules();
81
82
  // close the streams
83
0
  this->CloseFileStreams();
84
0
}
85
86
void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule(
87
  bool relink)
88
0
{
89
0
#ifndef CMAKE_BOOTSTRAP
90
0
  this->DeviceLinkLanguage = deviceLinkLanguage(
91
0
    *this->GeneratorTarget, *this->LocalGenerator, this->GetConfigName());
92
0
  if (this->DeviceLinkLanguage.empty()) {
93
0
    return;
94
0
  }
95
0
  this->GeneratorTarget->GetLinkInformation(this->GetConfigName());
96
0
  cmGeneratorTarget::DeviceLinkSetter deviceLink(*this->GeneratorTarget);
97
98
0
  std::vector<std::string> commands;
99
100
  // Get the name of the device object to generate.
101
0
  std::string const& objExt = this->Makefile->GetSafeDefinition(
102
0
    cmStrCat("CMAKE_", this->DeviceLinkLanguage, "_OUTPUT_EXTENSION"));
103
0
  std::string const targetOutput = cmStrCat(
104
0
    this->GeneratorTarget->ObjectDirectory, "cmake_device_link", objExt);
105
0
  this->DeviceLinkObject = targetOutput;
106
107
0
  this->NumberOfProgressActions++;
108
0
  if (!this->NoRuleMessages) {
109
0
    cmLocalUnixMakefileGenerator3::EchoProgress progress;
110
0
    this->MakeEchoProgress(progress);
111
    // Add the link message.
112
0
    std::string buildEcho = cmStrCat(
113
0
      "Linking ", this->DeviceLinkLanguage, " device code ",
114
0
      this->LocalGenerator->ConvertToOutputFormat(
115
0
        this->LocalGenerator->MaybeRelativeToCurBinDir(this->DeviceLinkObject),
116
0
        cmOutputConverter::SHELL));
117
0
    this->LocalGenerator->AppendEcho(
118
0
      commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress);
119
0
  }
120
121
0
  if (this->DeviceLinkLanguage == "CUDA" &&
122
0
      this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID") == "Clang") {
123
0
    this->WriteDeviceLinkRule(commands, targetOutput);
124
0
  } else {
125
0
    this->WriteDriverDeviceExecutableRule(relink, commands, targetOutput);
126
0
  }
127
128
  // Write the main driver rule to build everything in this target.
129
0
  this->WriteTargetDriverRule(targetOutput, relink);
130
#else
131
  static_cast<void>(relink);
132
#endif
133
0
}
134
135
void cmMakefileExecutableTargetGenerator::WriteDriverDeviceExecutableRule(
136
  bool relink, std::vector<std::string>& commands,
137
  std::string const& targetOutput)
138
0
{
139
0
  std::string const& linkLanguage = this->DeviceLinkLanguage;
140
141
  // Build list of dependencies.
142
0
  std::vector<std::string> depends;
143
0
  this->AppendLinkDepends(depends, linkLanguage);
144
145
  // Add language feature flags.
146
0
  std::string langFlags;
147
0
  this->LocalGenerator->AddLanguageFlagsForLinking(
148
0
    langFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
149
150
  // Construct a list of files associated with this executable that
151
  // may need to be cleaned.
152
0
  std::vector<std::string> exeCleanFiles;
153
0
  exeCleanFiles.push_back(
154
0
    this->LocalGenerator->MaybeRelativeToCurBinDir(targetOutput));
155
156
  // Determine whether a link script will be used.
157
0
  bool useLinkScript = this->GlobalGenerator->GetUseLinkScript();
158
159
  // Construct the main link rule.
160
0
  std::string const linkRuleVar =
161
0
    cmStrCat("CMAKE_", linkLanguage, "_DEVICE_LINK_EXECUTABLE");
162
0
  std::string const linkRule = this->GetLinkRule(linkRuleVar);
163
0
  std::vector<std::string> commands1;
164
0
  cmList real_link_commands(linkRule);
165
166
0
  bool useResponseFileForObjects =
167
0
    this->CheckUseResponseFileForObjects(linkLanguage);
168
0
  bool const useResponseFileForLibs =
169
0
    this->CheckUseResponseFileForLibraries(linkLanguage);
170
171
  // Expand the rule variables.
172
0
  {
173
    // Set path conversion for link script shells.
174
0
    this->LocalGenerator->SetLinkScriptShell(useLinkScript);
175
176
0
    std::unique_ptr<cmLinkLineDeviceComputer> linkLineComputer(
177
0
      new cmLinkLineDeviceComputer(
178
0
        this->LocalGenerator,
179
0
        this->LocalGenerator->GetStateSnapshot().GetDirectory(),
180
0
        linkLanguage));
181
0
    linkLineComputer->SetForResponse(useResponseFileForLibs);
182
0
    linkLineComputer->SetRelink(relink);
183
184
    // Create set of linking flags.
185
0
    std::string linkFlags;
186
0
    std::string ignored_;
187
0
    this->LocalGenerator->GetDeviceLinkFlags(
188
0
      *linkLineComputer, this->GetConfigName(), ignored_, linkFlags, ignored_,
189
0
      ignored_, this->GeneratorTarget);
190
191
    // Collect up flags to link in needed libraries.
192
0
    std::string linkLibs;
193
0
    this->CreateLinkLibs(
194
0
      linkLineComputer.get(), linkLibs, useResponseFileForLibs, depends,
195
0
      linkLanguage, cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink);
196
197
    // Construct object file lists that may be needed to expand the
198
    // rule.
199
0
    std::string buildObjs;
200
0
    this->CreateObjectLists(
201
0
      useLinkScript, false, useResponseFileForObjects, buildObjs, depends,
202
0
      false, linkLanguage,
203
0
      cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink);
204
205
0
    cmRulePlaceholderExpander::RuleVariables vars;
206
0
    std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
207
208
0
    objectDir = this->LocalGenerator->ConvertToOutputFormat(
209
0
      this->LocalGenerator->MaybeRelativeToCurBinDir(objectDir),
210
0
      cmOutputConverter::SHELL);
211
0
    std::string targetSupportDir =
212
0
      this->GeneratorTarget->GetCMFSupportDirectory();
213
214
0
    targetSupportDir = this->LocalGenerator->ConvertToOutputFormat(
215
0
      this->LocalGenerator->MaybeRelativeToTopBinDir(targetSupportDir),
216
0
      cmOutputConverter::SHELL);
217
218
0
    std::string target = this->LocalGenerator->ConvertToOutputFormat(
219
0
      this->LocalGenerator->MaybeRelativeToCurBinDir(targetOutput),
220
0
      cmOutputConverter::SHELL);
221
222
0
    std::string targetFullPathCompilePDB =
223
0
      this->ComputeTargetCompilePDB(this->GetConfigName());
224
0
    std::string targetOutPathCompilePDB =
225
0
      this->LocalGenerator->ConvertToOutputFormat(targetFullPathCompilePDB,
226
0
                                                  cmOutputConverter::SHELL);
227
228
0
    vars.Language = linkLanguage.c_str();
229
0
    vars.Objects = buildObjs.c_str();
230
0
    vars.ObjectDir = objectDir.c_str();
231
0
    vars.Target = target.c_str();
232
0
    vars.TargetSupportDir = targetSupportDir.c_str();
233
0
    vars.LinkLibraries = linkLibs.c_str();
234
0
    vars.LanguageCompileFlags = langFlags.c_str();
235
0
    vars.LinkFlags = linkFlags.c_str();
236
0
    vars.TargetCompilePDB = targetOutPathCompilePDB.c_str();
237
238
0
    std::string launcher;
239
240
0
    std::string val = this->LocalGenerator->GetRuleLauncher(
241
0
      this->GeneratorTarget, "RULE_LAUNCH_LINK",
242
0
      this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
243
0
    if (cmNonempty(val)) {
244
0
      launcher = cmStrCat(val, ' ');
245
0
    }
246
247
0
    auto rulePlaceholderExpander =
248
0
      this->LocalGenerator->CreateRulePlaceholderExpander(cmBuildStep::Link);
249
250
    // Expand placeholders in the commands.
251
0
    rulePlaceholderExpander->SetTargetImpLib(targetOutput);
252
0
    for (auto& real_link_command : real_link_commands) {
253
0
      real_link_command = cmStrCat(launcher, std::move(real_link_command));
254
0
      rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
255
0
                                                   real_link_command, vars);
256
0
    }
257
258
    // Restore path conversion to normal shells.
259
0
    this->LocalGenerator->SetLinkScriptShell(false);
260
0
  }
261
262
  // Optionally convert the build rule to use a script to avoid long
263
  // command lines in the make shell.
264
0
  if (useLinkScript) {
265
    // Use a link script.
266
0
    char const* name = (relink ? "drelink.txt" : "dlink.txt");
267
0
    this->CreateLinkScript(name, real_link_commands, commands1, depends);
268
0
  } else {
269
    // No link script.  Just use the link rule directly.
270
0
    commands1 = real_link_commands;
271
0
  }
272
0
  this->LocalGenerator->CreateCDCommand(
273
0
    commands1, this->Makefile->GetCurrentBinaryDirectory(),
274
0
    this->LocalGenerator->GetBinaryDirectory());
275
0
  cm::append(commands, commands1);
276
0
  commands1.clear();
277
278
  // Write the build rule.
279
0
  this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
280
0
                                      targetOutput, depends, commands, false);
281
282
  // Clean all the possible executable names and symlinks.
283
0
  this->CleanFiles.insert(exeCleanFiles.begin(), exeCleanFiles.end());
284
0
}
285
286
void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
287
0
{
288
0
  std::vector<std::string> commands;
289
290
  // Get the name of the executable to generate.
291
0
  cmGeneratorTarget::Names targetNames =
292
0
    this->GeneratorTarget->GetExecutableNames(this->GetConfigName());
293
294
  // Construct the full path version of the names.
295
0
  std::string outpath =
296
0
    this->GeneratorTarget->GetDirectory(this->GetConfigName());
297
0
  if (this->GeneratorTarget->IsAppBundleOnApple()) {
298
0
    this->OSXBundleGenerator->CreateAppBundle(targetNames.Output, outpath,
299
0
                                              this->GetConfigName());
300
0
  }
301
0
  outpath += '/';
302
0
  std::string outpathImp;
303
0
  if (relink) {
304
0
    outpath = cmStrCat(this->Makefile->GetCurrentBinaryDirectory(),
305
0
                       "/CMakeFiles/CMakeRelink.dir");
306
0
    cmSystemTools::MakeDirectory(outpath);
307
0
    outpath += '/';
308
0
    if (!targetNames.ImportLibrary.empty()) {
309
0
      outpathImp = outpath;
310
0
    }
311
0
  } else {
312
0
    cmSystemTools::MakeDirectory(outpath);
313
0
    if (!targetNames.ImportLibrary.empty()) {
314
0
      outpathImp = this->GeneratorTarget->GetDirectory(
315
0
        this->GetConfigName(), cmStateEnums::ImportLibraryArtifact);
316
0
      cmSystemTools::MakeDirectory(outpathImp);
317
0
      outpathImp += '/';
318
0
    }
319
0
  }
320
321
0
  std::string compilePdbOutputPath =
322
0
    this->GeneratorTarget->GetCompilePDBDirectory(this->GetConfigName());
323
0
  cmSystemTools::MakeDirectory(compilePdbOutputPath);
324
325
0
  std::string pdbOutputPath =
326
0
    this->GeneratorTarget->GetPDBDirectory(this->GetConfigName());
327
0
  cmSystemTools::MakeDirectory(pdbOutputPath);
328
0
  pdbOutputPath += '/';
329
330
0
  std::string targetFullPath = outpath + targetNames.Output;
331
0
  std::string targetFullPathReal = outpath + targetNames.Real;
332
0
  std::string targetFullPathPDB = pdbOutputPath + targetNames.PDB;
333
0
  std::string targetFullPathImport = outpathImp + targetNames.ImportLibrary;
334
0
  std::string targetOutPathPDB = this->LocalGenerator->ConvertToOutputFormat(
335
0
    targetFullPathPDB, cmOutputConverter::SHELL);
336
  // Convert to the output path to use in constructing commands.
337
0
  std::string targetOutPath = this->LocalGenerator->ConvertToOutputFormat(
338
0
    this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPath),
339
0
    cmOutputConverter::SHELL);
340
0
  std::string targetOutPathReal = this->LocalGenerator->ConvertToOutputFormat(
341
0
    this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal),
342
0
    cmOutputConverter::SHELL);
343
0
  std::string targetOutPathImport =
344
0
    this->LocalGenerator->ConvertToOutputFormat(
345
0
      this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathImport),
346
0
      cmOutputConverter::SHELL);
347
348
  // Get the language to use for linking this executable.
349
0
  std::string linkLanguage =
350
0
    this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName());
351
352
  // Make sure we have a link language.
353
0
  if (linkLanguage.empty()) {
354
0
    cmSystemTools::Error(
355
0
      cmStrCat("Cannot determine link language for target \"",
356
0
               this->GeneratorTarget->GetName(), "\"."));
357
0
    return;
358
0
  }
359
360
0
  auto linker = this->GeneratorTarget->GetLinkerTool(this->GetConfigName());
361
362
  // Build list of dependencies.
363
0
  std::vector<std::string> depends;
364
0
  this->AppendLinkDepends(depends, linkLanguage);
365
0
  if (!this->DeviceLinkObject.empty()) {
366
0
    depends.push_back(this->DeviceLinkObject);
367
0
  }
368
369
0
  this->NumberOfProgressActions++;
370
0
  if (!this->NoRuleMessages) {
371
0
    cmLocalUnixMakefileGenerator3::EchoProgress progress;
372
0
    this->MakeEchoProgress(progress);
373
    // Add the link message.
374
0
    std::string buildEcho =
375
0
      cmStrCat("Linking ", linkLanguage, " executable ", targetOutPath);
376
0
    this->LocalGenerator->AppendEcho(
377
0
      commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress);
378
0
  }
379
380
  // Build a list of compiler flags and linker flags.
381
0
  std::string flags;
382
0
  std::string linkFlags;
383
384
  // Add flags to create an executable.
385
0
  this->LocalGenerator->AppendTargetCreationLinkFlags(
386
0
    linkFlags, this->GeneratorTarget, linkLanguage);
387
0
  this->LocalGenerator->AddTargetTypeLinkerFlags(
388
0
    linkFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
389
0
  this->LocalGenerator->AddPerLanguageLinkFlags(
390
0
    linkFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
391
392
0
  {
393
0
    auto exeType =
394
0
      cmStrCat("CMAKE_", linkLanguage, "_CREATE_",
395
0
               (this->GeneratorTarget->IsWin32Executable(
396
0
                  this->Makefile->GetDefinition("CMAKE_BUILD_TYPE"))
397
0
                  ? "WIN32"
398
0
                  : "CONSOLE"),
399
0
               "_EXE");
400
0
    this->LocalGenerator->AppendFlags(
401
0
      linkFlags, this->Makefile->GetDefinition(exeType), exeType,
402
0
      this->GeneratorTarget, cmBuildStep::Link, linkLanguage);
403
0
  }
404
405
  // Add symbol export flags if necessary.
406
0
  if (this->GeneratorTarget->IsExecutableWithExports()) {
407
0
    std::string export_flag_var =
408
0
      cmStrCat("CMAKE_EXE_EXPORTS_", linkLanguage, "_FLAG");
409
0
    this->LocalGenerator->AppendFlags(
410
0
      linkFlags, this->Makefile->GetSafeDefinition(export_flag_var));
411
0
  }
412
413
0
  this->LocalGenerator->AppendFlags(linkFlags,
414
0
                                    this->LocalGenerator->GetExeExportFlags(
415
0
                                      linkLanguage, *this->GeneratorTarget));
416
417
0
  this->UseLWYU = this->LocalGenerator->AppendLWYUFlags(
418
0
    linkFlags, this->GeneratorTarget, linkLanguage);
419
420
  // Add language feature flags.
421
0
  this->LocalGenerator->AddLanguageFlagsForLinking(
422
0
    flags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
423
424
0
  this->LocalGenerator->AddArchitectureFlags(
425
0
    flags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
426
427
  // Add target-specific linker flags.
428
0
  this->GetTargetLinkFlags(linkFlags, linkLanguage);
429
430
0
  {
431
0
    std::unique_ptr<cmLinkLineComputer> linkLineComputer =
432
0
      this->CreateLinkLineComputer(
433
0
        this->LocalGenerator,
434
0
        this->LocalGenerator->GetStateSnapshot().GetDirectory());
435
436
0
    this->LocalGenerator->AppendModuleDefinitionFlag(
437
0
      linkFlags, this->GeneratorTarget, linkLineComputer.get(),
438
0
      this->GetConfigName(), linkLanguage);
439
0
  }
440
441
0
  this->LocalGenerator->AppendIPOLinkerFlags(
442
0
    linkFlags, this->GeneratorTarget, this->GetConfigName(), linkLanguage);
443
444
  // Construct a list of files associated with this executable that
445
  // may need to be cleaned.
446
0
  std::vector<std::string> exeCleanFiles;
447
0
  exeCleanFiles.push_back(
448
0
    this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPath));
449
#ifdef _WIN32
450
  // There may be a manifest file for this target.  Add it to the
451
  // clean set just in case.
452
  exeCleanFiles.push_back(this->LocalGenerator->MaybeRelativeToCurBinDir(
453
    targetFullPath + ".manifest"));
454
#endif
455
0
  if (this->TargetNames.Real != this->TargetNames.Output) {
456
0
    exeCleanFiles.push_back(
457
0
      this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal));
458
0
  }
459
0
  if (!this->TargetNames.ImportLibrary.empty()) {
460
0
    exeCleanFiles.push_back(
461
0
      this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathImport));
462
0
    std::string implib;
463
0
    if (this->GeneratorTarget->GetImplibGNUtoMS(
464
0
          this->GetConfigName(), targetFullPathImport, implib)) {
465
0
      exeCleanFiles.push_back(
466
0
        this->LocalGenerator->MaybeRelativeToCurBinDir(implib));
467
0
    }
468
0
  }
469
470
  // List the PDB for cleaning only when the whole target is
471
  // cleaned.  We do not want to delete the .pdb file just before
472
  // linking the target.
473
0
  this->CleanFiles.insert(
474
0
    this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathPDB));
475
476
  // Add the pre-build and pre-link rules building but not when relinking.
477
0
  if (!relink) {
478
0
    this->LocalGenerator->AppendCustomCommands(
479
0
      commands, this->GeneratorTarget->GetPreBuildCommands(),
480
0
      this->GeneratorTarget, this->LocalGenerator->GetBinaryDirectory());
481
0
    this->LocalGenerator->AppendCustomCommands(
482
0
      commands, this->GeneratorTarget->GetPreLinkCommands(),
483
0
      this->GeneratorTarget, this->LocalGenerator->GetBinaryDirectory());
484
0
  }
485
486
  // Determine whether a link script will be used.
487
0
  bool useLinkScript = this->GlobalGenerator->GetUseLinkScript();
488
489
  // Construct the main link rule.
490
0
  std::string linkRuleVar = this->GeneratorTarget->GetCreateRuleVariable(
491
0
    linkLanguage, this->GetConfigName());
492
0
  std::string linkRule = this->GetLinkRule(linkRuleVar);
493
0
  std::vector<std::string> commands1;
494
0
  cmList real_link_commands(linkRule);
495
496
0
  if (this->GeneratorTarget->IsExecutableWithExports()) {
497
    // If a separate rule for creating an import library is specified
498
    // add it now.
499
0
    std::string implibRuleVar =
500
0
      cmStrCat("CMAKE_", linkLanguage, "_CREATE_IMPORT_LIBRARY");
501
0
    real_link_commands.append(this->Makefile->GetDefinition(implibRuleVar));
502
0
  }
503
504
0
  bool useResponseFileForObjects =
505
0
    this->CheckUseResponseFileForObjects(linkLanguage);
506
0
  bool const useResponseFileForLibs =
507
0
    this->CheckUseResponseFileForLibraries(linkLanguage);
508
509
  // Expand the rule variables.
510
0
  {
511
0
    bool useWatcomQuote =
512
0
      this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE");
513
514
    // Set path conversion for link script shells.
515
0
    this->LocalGenerator->SetLinkScriptShell(useLinkScript);
516
517
0
    std::unique_ptr<cmLinkLineComputer> linkLineComputer =
518
0
      this->CreateLinkLineComputer(
519
0
        this->LocalGenerator,
520
0
        this->LocalGenerator->GetStateSnapshot().GetDirectory());
521
0
    linkLineComputer->SetForResponse(useResponseFileForLibs);
522
0
    linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
523
0
    linkLineComputer->SetRelink(relink);
524
525
    // Collect up flags to link in needed libraries.
526
0
    std::string linkLibs;
527
0
    this->CreateLinkLibs(linkLineComputer.get(), linkLibs,
528
0
                         useResponseFileForLibs, depends, linkLanguage);
529
530
    // Construct object file lists that may be needed to expand the
531
    // rule.
532
0
    std::string buildObjs;
533
0
    this->CreateObjectLists(useLinkScript, false, useResponseFileForObjects,
534
0
                            buildObjs, depends, useWatcomQuote, linkLanguage);
535
0
    if (!this->DeviceLinkObject.empty()) {
536
0
      buildObjs = cmStrCat(std::move(buildObjs), ' ',
537
0
                           this->LocalGenerator->ConvertToOutputFormat(
538
0
                             this->LocalGenerator->MaybeRelativeToCurBinDir(
539
0
                               this->DeviceLinkObject),
540
0
                             cmOutputConverter::SHELL));
541
0
    }
542
543
    // maybe create .def file from list of objects
544
0
    this->GenDefFile(real_link_commands);
545
546
0
    std::string manifests = this->GetManifests(this->GetConfigName());
547
548
0
    std::string const& aixExports = this->GetAIXExports(this->GetConfigName());
549
550
0
    cmRulePlaceholderExpander::RuleVariables vars;
551
0
    vars.CMTargetName = this->GeneratorTarget->GetName().c_str();
552
0
    vars.CMTargetType =
553
0
      cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str();
554
0
    vars.Language = linkLanguage.c_str();
555
0
    vars.Linker = linker.c_str();
556
0
    vars.AIXExports = aixExports.c_str();
557
0
    vars.Objects = buildObjs.c_str();
558
0
    std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
559
560
0
    objectDir = this->LocalGenerator->ConvertToOutputFormat(
561
0
      this->LocalGenerator->MaybeRelativeToCurBinDir(objectDir),
562
0
      cmOutputConverter::SHELL);
563
0
    vars.ObjectDir = objectDir.c_str();
564
0
    std::string targetSupportDir =
565
0
      this->GeneratorTarget->GetCMFSupportDirectory();
566
567
0
    targetSupportDir = this->LocalGenerator->ConvertToOutputFormat(
568
0
      this->LocalGenerator->MaybeRelativeToTopBinDir(targetSupportDir),
569
0
      cmOutputConverter::SHELL);
570
0
    vars.TargetSupportDir = targetSupportDir.c_str();
571
0
    std::string target = this->LocalGenerator->ConvertToOutputFormat(
572
0
      this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal),
573
0
      cmOutputConverter::SHELL, useWatcomQuote);
574
0
    vars.Target = target.c_str();
575
0
    vars.TargetPDB = targetOutPathPDB.c_str();
576
0
    vars.Config = this->GetConfigName().c_str();
577
578
    // Setup the target version.
579
0
    std::string targetVersionMajor;
580
0
    std::string targetVersionMinor;
581
0
    {
582
0
      int major;
583
0
      int minor;
584
0
      this->GeneratorTarget->GetTargetVersion(major, minor);
585
0
      targetVersionMajor = std::to_string(major);
586
0
      targetVersionMinor = std::to_string(minor);
587
0
    }
588
0
    vars.TargetVersionMajor = targetVersionMajor.c_str();
589
0
    vars.TargetVersionMinor = targetVersionMinor.c_str();
590
591
0
    vars.LinkLibraries = linkLibs.c_str();
592
0
    vars.Flags = flags.c_str();
593
0
    vars.LinkFlags = linkFlags.c_str();
594
0
    vars.Manifests = manifests.c_str();
595
596
0
    std::string const linkerLauncher = this->ConvertLauncherToShell(
597
0
      this->GetLinkerLauncher(this->GetConfigName()));
598
0
    if (cmNonempty(linkerLauncher)) {
599
0
      vars.Launcher = linkerLauncher.c_str();
600
0
    }
601
602
0
    std::string rustMainCrateRootPath;
603
0
    std::string rustLinkCrates;
604
0
    std::string rustNativeObjects;
605
0
    if (CreateRustLinkArguments(linkLanguage, rustMainCrateRootPath,
606
0
                                rustLinkCrates, rustNativeObjects)) {
607
0
      vars.RustMainCrateRoot = rustMainCrateRootPath.c_str();
608
0
      vars.RustLinkCrates = rustLinkCrates.c_str();
609
0
      vars.RustNativeObjects = rustNativeObjects.c_str();
610
0
    }
611
612
0
    if (this->UseLWYU) {
613
0
      cmValue lwyuCheck =
614
0
        this->Makefile->GetDefinition("CMAKE_LINK_WHAT_YOU_USE_CHECK");
615
0
      if (lwyuCheck) {
616
0
        std::string cmakeCommand = cmStrCat(
617
0
          this->LocalGenerator->ConvertToOutputFormat(
618
0
            cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
619
0
          " -E __run_co_compile --lwyu=");
620
0
        cmakeCommand += this->LocalGenerator->EscapeForShell(*lwyuCheck);
621
0
        cmakeCommand =
622
0
          cmStrCat(std::move(cmakeCommand), " --source=", targetOutPathReal);
623
0
        real_link_commands.push_back(std::move(cmakeCommand));
624
0
      }
625
0
    }
626
627
0
    std::string launcher;
628
629
0
    std::string val = this->LocalGenerator->GetRuleLauncher(
630
0
      this->GeneratorTarget, "RULE_LAUNCH_LINK",
631
0
      this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
632
0
    if (cmNonempty(val)) {
633
0
      launcher = cmStrCat(val, ' ');
634
0
    }
635
636
0
    auto rulePlaceholderExpander =
637
0
      this->LocalGenerator->CreateRulePlaceholderExpander(cmBuildStep::Link);
638
639
    // Expand placeholders in the commands.
640
0
    rulePlaceholderExpander->SetTargetImpLib(targetOutPathImport);
641
0
    for (auto& real_link_command : real_link_commands) {
642
0
      real_link_command = cmStrCat(launcher, std::move(real_link_command));
643
0
      rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
644
0
                                                   real_link_command, vars);
645
0
    }
646
647
    // Restore path conversion to normal shells.
648
0
    this->LocalGenerator->SetLinkScriptShell(false);
649
0
  }
650
651
  // Optionally convert the build rule to use a script to avoid long
652
  // command lines in the make shell.
653
0
  if (useLinkScript) {
654
    // Use a link script.
655
0
    char const* name = (relink ? "relink.txt" : "link.txt");
656
0
    this->CreateLinkScript(name, real_link_commands, commands1, depends);
657
0
  } else {
658
    // No link script.  Just use the link rule directly.
659
0
    commands1 = real_link_commands;
660
0
  }
661
0
  this->LocalGenerator->CreateCDCommand(
662
0
    commands1, this->Makefile->GetCurrentBinaryDirectory(),
663
0
    this->LocalGenerator->GetBinaryDirectory());
664
0
  cm::append(commands, commands1);
665
0
  commands1.clear();
666
667
  // Add a rule to create necessary symlinks for the library.
668
0
  if (targetOutPath != targetOutPathReal) {
669
0
    std::string symlink =
670
0
      cmStrCat("$(CMAKE_COMMAND) -E cmake_symlink_executable ",
671
0
               targetOutPathReal, ' ', targetOutPath);
672
0
    commands1.push_back(std::move(symlink));
673
0
    this->LocalGenerator->CreateCDCommand(
674
0
      commands1, this->Makefile->GetCurrentBinaryDirectory(),
675
0
      this->LocalGenerator->GetBinaryDirectory());
676
0
    cm::append(commands, commands1);
677
0
    commands1.clear();
678
0
  }
679
680
  // Add the post-build rules when building but not when relinking.
681
0
  if (!relink) {
682
0
    this->LocalGenerator->AppendCustomCommands(
683
0
      commands, this->GeneratorTarget->GetPostBuildCommands(),
684
0
      this->GeneratorTarget, this->LocalGenerator->GetBinaryDirectory());
685
0
  }
686
687
  // Write the build rule.
688
0
  this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
689
0
                                      targetFullPathReal, depends, commands,
690
0
                                      false);
691
692
  // The symlink name for the target should depend on the real target
693
  // so if the target version changes it rebuilds and recreates the
694
  // symlink.
695
0
  if (targetFullPath != targetFullPathReal) {
696
0
    depends.clear();
697
0
    commands.clear();
698
0
    depends.push_back(targetFullPathReal);
699
0
    this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
700
0
                                        targetFullPath, depends, commands,
701
0
                                        false);
702
0
  }
703
704
  // Write the main driver rule to build everything in this target.
705
0
  this->WriteTargetDriverRule(targetFullPath, relink);
706
707
  // Clean all the possible executable names and symlinks.
708
0
  this->CleanFiles.insert(exeCleanFiles.begin(), exeCleanFiles.end());
709
0
}