Coverage Report

Created: 2026-02-09 06:05

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