cmMakefileLibraryTargetGenerator.cxx 37.1 KB
Newer Older
1
2
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
   file Copyright.txt or https://cmake.org/licensing for details.  */
3
4
#include "cmMakefileLibraryTargetGenerator.h"

5
#include <algorithm>
6
#include <memory> // IWYU pragma: keep
7
#include <sstream>
8
#include <stddef.h>
9
#include <utility>
10
11
#include <vector>

12
#include "cmGeneratedFileStream.h"
13
#include "cmGeneratorTarget.h"
14
#include "cmGlobalUnixMakefileGenerator3.h"
15
#include "cmLinkLineComputer.h"
16
#include "cmLinkLineDeviceComputer.h"
17
#include "cmLocalGenerator.h"
18
19
#include "cmLocalUnixMakefileGenerator3.h"
#include "cmMakefile.h"
20
21
#include "cmOSXBundleGenerator.h"
#include "cmOutputConverter.h"
22
#include "cmRulePlaceholderExpander.h"
23
#include "cmState.h"
24
25
#include "cmStateDirectory.h"
#include "cmStateSnapshot.h"
26
#include "cmStateTypes.h"
27
#include "cmSystemTools.h"
28

29
30
31
cmMakefileLibraryTargetGenerator::cmMakefileLibraryTargetGenerator(
  cmGeneratorTarget* target)
  : cmMakefileTargetGenerator(target)
32
{
33
  this->CustomCommandDriver = OnDepends;
34
  if (this->GeneratorTarget->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
35
    this->GeneratorTarget->GetLibraryNames(
36
37
      this->TargetNameOut, this->TargetNameSO, this->TargetNameReal,
      this->TargetNameImport, this->TargetNamePDB, this->ConfigName);
38
  }
39

40
41
  this->OSXBundleGenerator =
    new cmOSXBundleGenerator(target, this->ConfigName);
42
43
44
  this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
}

45
cmMakefileLibraryTargetGenerator::~cmMakefileLibraryTargetGenerator()
46
47
{
  delete this->OSXBundleGenerator;
48
49
}

50
51
52
53
54
void cmMakefileLibraryTargetGenerator::WriteRuleFiles()
{
  // create the build.make file and directory, put in the common blocks
  this->CreateRuleFile();

55
  // write rules used to help build object files
56
  this->WriteCommonCodeRules();
57
58
59
60

  // write the per-target per-language flags
  this->WriteTargetLanguageFlags();

61
62
63
  // write in rules for object files and custom commands
  this->WriteTargetBuildRules();

64
65
  // write the link rules
  // Write the rule for this target type.
66
  switch (this->GeneratorTarget->GetType()) {
67
    case cmStateEnums::STATIC_LIBRARY:
68
69
      this->WriteStaticLibraryRules();
      break;
70
    case cmStateEnums::SHARED_LIBRARY:
71
      this->WriteSharedLibraryRules(false);
72
      if (this->GeneratorTarget->NeedRelinkBeforeInstall(this->ConfigName)) {
73
74
        // Write rules to link an installable version of the target.
        this->WriteSharedLibraryRules(true);
75
      }
76
      break;
77
    case cmStateEnums::MODULE_LIBRARY:
78
      this->WriteModuleLibraryRules(false);
79
      if (this->GeneratorTarget->NeedRelinkBeforeInstall(this->ConfigName)) {
80
81
        // Write rules to link an installable version of the target.
        this->WriteModuleLibraryRules(true);
82
      }
83
      break;
84
    case cmStateEnums::OBJECT_LIBRARY:
85
86
      this->WriteObjectLibraryRules();
      break;
87
88
89
90
    default:
      // If language is not known, this is an error.
      cmSystemTools::Error("Unknown Library Type");
      break;
91
  }
92
93
94
95

  // Write clean target
  this->WriteTargetCleanRules();

96
97
98
99
  // Write the dependency generation rule.  This must be done last so
  // that multiple output pair information is available.
  this->WriteTargetDependRules();

100
101
102
103
  // close the streams
  this->CloseFileStreams();
}

104
105
106
107
108
109
void cmMakefileLibraryTargetGenerator::WriteObjectLibraryRules()
{
  std::vector<std::string> commands;
  std::vector<std::string> depends;

  // Add post-build rules.
110
111
  this->LocalGenerator->AppendCustomCommands(
    commands, this->GeneratorTarget->GetPostBuildCommands(),
112
    this->GeneratorTarget, this->LocalGenerator->GetBinaryDirectory());
113
114
115
116
117

  // Depend on the object files.
  this->AppendObjectDepends(depends);

  // Write the rule.
Daniel Pfeifer's avatar
Daniel Pfeifer committed
118
  this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
119
                                      this->GeneratorTarget->GetName(),
120
121
122
                                      depends, commands, true);

  // Write the main driver rule to build everything in this target.
123
  this->WriteTargetDriverRule(this->GeneratorTarget->GetName(), false);
124
125
}

126
127
void cmMakefileLibraryTargetGenerator::WriteStaticLibraryRules()
{
128
129
130
131
132
133
134
135
136
137
138
139
  const std::string cuda_lang("CUDA");
  cmGeneratorTarget::LinkClosure const* closure =
    this->GeneratorTarget->GetLinkClosure(this->ConfigName);

  const bool hasCUDA =
    (std::find(closure->Languages.begin(), closure->Languages.end(),
               cuda_lang) != closure->Languages.end());

  const bool resolveDeviceSymbols =
    this->GeneratorTarget->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS");
  if (hasCUDA && resolveDeviceSymbols) {
    std::string linkRuleVar = "CMAKE_CUDA_DEVICE_LINK_LIBRARY";
140
    this->WriteDeviceLibraryRules(linkRuleVar, false);
141
142
  }

143
  std::string linkLanguage =
144
    this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
145

146
147
  std::string linkRuleVar = this->GeneratorTarget->GetCreateRuleVariable(
    linkLanguage, this->ConfigName);
148

149
  std::string extraFlags;
150
  this->LocalGenerator->GetStaticLibraryFlags(
151
    extraFlags, cmSystemTools::UpperCase(this->ConfigName), linkLanguage,
152
    this->GeneratorTarget);
Stephen Kelly's avatar
Stephen Kelly committed
153
  this->WriteLibraryRules(linkRuleVar, extraFlags, false);
154
155
}

156
void cmMakefileLibraryTargetGenerator::WriteSharedLibraryRules(bool relink)
157
{
158
  if (this->GeneratorTarget->IsFrameworkOnApple()) {
159
160
    this->WriteFrameworkRules(relink);
    return;
161
  }
162
163
164
165
166
167
168
169
170
171
172

  if (!relink) {
    const std::string cuda_lang("CUDA");
    cmGeneratorTarget::LinkClosure const* closure =
      this->GeneratorTarget->GetLinkClosure(this->ConfigName);

    const bool hasCUDA =
      (std::find(closure->Languages.begin(), closure->Languages.end(),
                 cuda_lang) != closure->Languages.end());
    if (hasCUDA) {
      std::string linkRuleVar = "CMAKE_CUDA_DEVICE_LINK_LIBRARY";
173
      this->WriteDeviceLibraryRules(linkRuleVar, relink);
174
175
176
    }
  }

177
  std::string linkLanguage =
178
    this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
179
  std::string linkRuleVar = "CMAKE_";
180
  linkRuleVar += linkLanguage;
181
182
183
  linkRuleVar += "_CREATE_SHARED_LIBRARY";

  std::string extraFlags;
184
  this->GetTargetLinkFlags(extraFlags, linkLanguage);
185
186
  this->LocalGenerator->AddConfigVariableFlags(
    extraFlags, "CMAKE_SHARED_LINKER_FLAGS", this->ConfigName);
187

188
  std::unique_ptr<cmLinkLineComputer> linkLineComputer(
189
    this->CreateLinkLineComputer(
190
      this->LocalGenerator,
191
192
193
      this->LocalGenerator->GetStateSnapshot().GetDirectory()));

  this->AddModuleDefinitionFlag(linkLineComputer.get(), extraFlags);
194

195
  if (this->GeneratorTarget->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
196
197
    this->LocalGenerator->AppendFlags(extraFlags, " -Wl,--no-as-needed");
  }
Stephen Kelly's avatar
Stephen Kelly committed
198
  this->WriteLibraryRules(linkRuleVar, extraFlags, relink);
199
200
}

201
void cmMakefileLibraryTargetGenerator::WriteModuleLibraryRules(bool relink)
202
{
203
204
205
206
207
208
209
210
211
212
213

  if (!relink) {
    const std::string cuda_lang("CUDA");
    cmGeneratorTarget::LinkClosure const* closure =
      this->GeneratorTarget->GetLinkClosure(this->ConfigName);

    const bool hasCUDA =
      (std::find(closure->Languages.begin(), closure->Languages.end(),
                 cuda_lang) != closure->Languages.end());
    if (hasCUDA) {
      std::string linkRuleVar = "CMAKE_CUDA_DEVICE_LINK_LIBRARY";
214
      this->WriteDeviceLibraryRules(linkRuleVar, relink);
215
216
217
    }
  }

218
  std::string linkLanguage =
219
    this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
220
  std::string linkRuleVar = "CMAKE_";
221
  linkRuleVar += linkLanguage;
222
223
224
  linkRuleVar += "_CREATE_SHARED_MODULE";

  std::string extraFlags;
225
  this->GetTargetLinkFlags(extraFlags, linkLanguage);
226
227
  this->LocalGenerator->AddConfigVariableFlags(
    extraFlags, "CMAKE_MODULE_LINKER_FLAGS", this->ConfigName);
228

229
  std::unique_ptr<cmLinkLineComputer> linkLineComputer(
230
    this->CreateLinkLineComputer(
231
      this->LocalGenerator,
232
233
234
      this->LocalGenerator->GetStateSnapshot().GetDirectory()));

  this->AddModuleDefinitionFlag(linkLineComputer.get(), extraFlags);
Ken Martin's avatar
Ken Martin committed
235

Stephen Kelly's avatar
Stephen Kelly committed
236
  this->WriteLibraryRules(linkRuleVar, extraFlags, relink);
237
238
}

239
240
void cmMakefileLibraryTargetGenerator::WriteFrameworkRules(bool relink)
{
241
  std::string linkLanguage =
242
    this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
243
  std::string linkRuleVar = "CMAKE_";
244
  linkRuleVar += linkLanguage;
245
246
247
  linkRuleVar += "_CREATE_MACOSX_FRAMEWORK";

  std::string extraFlags;
248
  this->GetTargetLinkFlags(extraFlags, linkLanguage);
249
250
  this->LocalGenerator->AddConfigVariableFlags(
    extraFlags, "CMAKE_MACOSX_FRAMEWORK_LINKER_FLAGS", this->ConfigName);
251

Stephen Kelly's avatar
Stephen Kelly committed
252
  this->WriteLibraryRules(linkRuleVar, extraFlags, relink);
253
254
}

255
void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules(
256
  const std::string& linkRuleVar, bool relink)
257
258
259
260
261
262
263
264
{
#ifdef CMAKE_BUILD_WITH_CMAKE
  // TODO: Merge the methods that call this method to avoid
  // code duplication.
  std::vector<std::string> commands;

  // Get the language to use for linking this library.
  std::string linkLanguage = "CUDA";
265
266
  std::string const objExt =
    this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
267

268
269
270
271
  // Build list of dependencies.
  std::vector<std::string> depends;
  this->AppendLinkDepends(depends, linkLanguage);

272
273
  // Create set of linking flags.
  std::string linkFlags;
274
  this->GetTargetLinkFlags(linkFlags, linkLanguage);
275
276
277

  // Get the name of the device object to generate.
  std::string const targetOutputReal =
278
    this->GeneratorTarget->ObjectDirectory + "cmake_device_link" + objExt;
279
280
281
282
283
284
285
  this->DeviceLinkObject = targetOutputReal;

  this->NumberOfProgressActions++;
  if (!this->NoRuleMessages) {
    cmLocalUnixMakefileGenerator3::EchoProgress progress;
    this->MakeEchoProgress(progress);
    // Add the link message.
286
287
288
289
290
291
    std::string buildEcho = "Linking " + linkLanguage + " device code ";
    buildEcho += this->LocalGenerator->ConvertToOutputFormat(
      this->LocalGenerator->MaybeConvertToRelativePath(
        this->LocalGenerator->GetCurrentBinaryDirectory(),
        this->DeviceLinkObject),
      cmOutputConverter::SHELL);
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
    this->LocalGenerator->AppendEcho(
      commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress);
  }
  // Clean files associated with this library.
  std::vector<std::string> libCleanFiles;
  libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
    this->LocalGenerator->GetCurrentBinaryDirectory(), targetOutputReal));

  // Determine whether a link script will be used.
  bool useLinkScript = this->GlobalGenerator->GetUseLinkScript();

  bool useResponseFileForObjects =
    this->CheckUseResponseFileForObjects(linkLanguage);
  bool const useResponseFileForLibs =
    this->CheckUseResponseFileForLibraries(linkLanguage);

  cmRulePlaceholderExpander::RuleVariables vars;
  vars.Language = linkLanguage.c_str();

  // Expand the rule variables.
  std::vector<std::string> real_link_commands;
  {
    bool useWatcomQuote =
      this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE");

    // Set path conversion for link script shells.
    this->LocalGenerator->SetLinkScriptShell(useLinkScript);

    // Collect up flags to link in needed libraries.
    std::string linkLibs;
    if (this->GeneratorTarget->GetType() != cmStateEnums::STATIC_LIBRARY) {

324
      std::unique_ptr<cmLinkLineComputer> linkLineComputer(
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
        new cmLinkLineDeviceComputer(
          this->LocalGenerator,
          this->LocalGenerator->GetStateSnapshot().GetDirectory()));
      linkLineComputer->SetForResponse(useResponseFileForLibs);
      linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
      linkLineComputer->SetRelink(relink);

      this->CreateLinkLibs(linkLineComputer.get(), linkLibs,
                           useResponseFileForLibs, depends);
    }

    // Construct object file lists that may be needed to expand the
    // rule.
    std::string buildObjs;
    this->CreateObjectLists(useLinkScript, false, // useArchiveRules
                            useResponseFileForObjects, buildObjs, depends,
                            useWatcomQuote);

    cmOutputConverter::OutputFormat output = (useWatcomQuote)
      ? cmOutputConverter::WATCOMQUOTE
      : cmOutputConverter::SHELL;

    std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
    objectDir = this->LocalGenerator->ConvertToOutputFormat(
      this->LocalGenerator->MaybeConvertToRelativePath(
        this->LocalGenerator->GetCurrentBinaryDirectory(), objectDir),
      cmOutputConverter::SHELL);

    std::string target = this->LocalGenerator->ConvertToOutputFormat(
      this->LocalGenerator->MaybeConvertToRelativePath(
        this->LocalGenerator->GetCurrentBinaryDirectory(), targetOutputReal),
      output);

358
359
360
361
362
    std::string targetFullPathCompilePDB = this->ComputeTargetCompilePDB();
    std::string targetOutPathCompilePDB =
      this->LocalGenerator->ConvertToOutputFormat(targetFullPathCompilePDB,
                                                  cmOutputConverter::SHELL);

363
364
365
366
367
368
    vars.Objects = buildObjs.c_str();
    vars.ObjectDir = objectDir.c_str();
    vars.Target = target.c_str();
    vars.LinkLibraries = linkLibs.c_str();
    vars.ObjectsQuoted = buildObjs.c_str();
    vars.LinkFlags = linkFlags.c_str();
369
    vars.TargetCompilePDB = targetOutPathCompilePDB.c_str();
370

371
    // Add language-specific flags.
372
    std::string langFlags;
373
374
    this->LocalGenerator->AddLanguageFlagsForLinking(
      langFlags, this->GeneratorTarget, linkLanguage, this->ConfigName);
375
376
377
378
379
380
381
382
383
384
385

    vars.LanguageCompileFlags = langFlags.c_str();

    std::string launcher;
    const char* val = this->LocalGenerator->GetRuleLauncher(
      this->GeneratorTarget, "RULE_LAUNCH_LINK");
    if (val && *val) {
      launcher = val;
      launcher += " ";
    }

386
    std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
387
388
389
390
391
392
393
394
      this->LocalGenerator->CreateRulePlaceholderExpander());

    // Construct the main link rule and expand placeholders.
    rulePlaceholderExpander->SetTargetImpLib(targetOutputReal);
    std::string linkRule = this->GetLinkRule(linkRuleVar);
    cmSystemTools::ExpandListArgument(linkRule, real_link_commands);

    // Expand placeholders.
395
396
397
398
    for (std::string& real_link_command : real_link_commands) {
      real_link_command = launcher + real_link_command;
      rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
                                                   real_link_command, vars);
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
    }
    // Restore path conversion to normal shells.
    this->LocalGenerator->SetLinkScriptShell(false);

    // Clean all the possible library names and symlinks.
    this->CleanFiles.insert(this->CleanFiles.end(), libCleanFiles.begin(),
                            libCleanFiles.end());
  }

  std::vector<std::string> commands1;
  // Optionally convert the build rule to use a script to avoid long
  // command lines in the make shell.
  if (useLinkScript) {
    // Use a link script.
    const char* name = (relink ? "drelink.txt" : "dlink.txt");
    this->CreateLinkScript(name, real_link_commands, commands1, depends);
  } else {
    // No link script.  Just use the link rule directly.
    commands1 = real_link_commands;
  }
  this->LocalGenerator->CreateCDCommand(
    commands1, this->Makefile->GetCurrentBinaryDirectory(),
    this->LocalGenerator->GetBinaryDirectory());
  commands.insert(commands.end(), commands1.begin(), commands1.end());
  commands1.clear();

  // Compute the list of outputs.
  std::vector<std::string> outputs(1, targetOutputReal);

  // Write the build rule.
Daniel Pfeifer's avatar
Daniel Pfeifer committed
429
  this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends,
430
431
432
433
434
435
436
437
438
439
                      commands, false);

  // Write the main driver rule to build everything in this target.
  this->WriteTargetDriverRule(targetOutputReal, relink);
#else
  static_cast<void>(linkRuleVar);
  static_cast<void>(relink);
#endif
}

440
441
void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
  const std::string& linkRuleVar, const std::string& extraFlags, bool relink)
442
443
444
445
446
447
{
  // TODO: Merge the methods that call this method to avoid
  // code duplication.
  std::vector<std::string> commands;

  // Get the language to use for linking this library.
448
  std::string linkLanguage =
449
    this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
450
451

  // Make sure we have a link language.
452
  if (linkLanguage.empty()) {
453
    cmSystemTools::Error("Cannot determine link language for target \"",
454
                         this->GeneratorTarget->GetName().c_str(), "\".");
455
    return;
456
  }
457

458
459
460
461
462
463
464
  // Build list of dependencies.
  std::vector<std::string> depends;
  this->AppendLinkDepends(depends, linkLanguage);
  if (!this->DeviceLinkObject.empty()) {
    depends.push_back(this->DeviceLinkObject);
  }

465
466
  // Create set of linking flags.
  std::string linkFlags;
467
  this->LocalGenerator->AppendFlags(linkFlags, extraFlags);
Ruslan Baratov's avatar
Ruslan Baratov committed
468
469
  this->LocalGenerator->AppendIPOLinkerFlags(linkFlags, this->GeneratorTarget,
                                             this->ConfigName, linkLanguage);
470

471
  // Add OSX version flags, if any.
472
473
  if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
      this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
474
475
    this->AppendOSXVerFlag(linkFlags, linkLanguage, "COMPATIBILITY", true);
    this->AppendOSXVerFlag(linkFlags, linkLanguage, "CURRENT", false);
476
  }
477

478
479
480
481
  // Construct the name of the library.
  std::string targetName;
  std::string targetNameSO;
  std::string targetNameReal;
482
  std::string targetNameImport;
483
  std::string targetNamePDB;
484
485
486
  this->GeneratorTarget->GetLibraryNames(targetName, targetNameSO,
                                         targetNameReal, targetNameImport,
                                         targetNamePDB, this->ConfigName);
487
488

  // Construct the full path version of the names.
489
  std::string outpath;
490
  std::string outpathImp;
491
  if (this->GeneratorTarget->IsFrameworkOnApple()) {
492
    outpath = this->GeneratorTarget->GetDirectory(this->ConfigName);
493
494
    this->OSXBundleGenerator->CreateFramework(targetName, outpath);
    outpath += "/";
495
  } else if (this->GeneratorTarget->IsCFBundleOnApple()) {
496
    outpath = this->GeneratorTarget->GetDirectory(this->ConfigName);
497
    this->OSXBundleGenerator->CreateCFBundle(targetName, outpath);
498
    outpath += "/";
499
  } else if (relink) {
500
    outpath = this->Makefile->GetCurrentBinaryDirectory();
501
    outpath += "/CMakeFiles";
502
    outpath += "/CMakeRelink.dir";
503
    cmSystemTools::MakeDirectory(outpath);
504
    outpath += "/";
505
    if (!targetNameImport.empty()) {
506
      outpathImp = outpath;
507
    }
508
  } else {
509
    outpath = this->GeneratorTarget->GetDirectory(this->ConfigName);
510
    cmSystemTools::MakeDirectory(outpath);
511
    outpath += "/";
512
    if (!targetNameImport.empty()) {
513
514
      outpathImp = this->GeneratorTarget->GetDirectory(
        this->ConfigName, cmStateEnums::ImportLibraryArtifact);
515
      cmSystemTools::MakeDirectory(outpathImp);
516
      outpathImp += "/";
517
    }
518
  }
519

520
  std::string compilePdbOutputPath =
521
    this->GeneratorTarget->GetCompilePDBDirectory(this->ConfigName);
522
  cmSystemTools::MakeDirectory(compilePdbOutputPath);
523

524
  std::string pdbOutputPath =
525
    this->GeneratorTarget->GetPDBDirectory(this->ConfigName);
526
  cmSystemTools::MakeDirectory(pdbOutputPath);
527
528
  pdbOutputPath += "/";

529
  std::string targetFullPath = outpath + targetName;
530
  std::string targetFullPathPDB = pdbOutputPath + targetNamePDB;
531
532
  std::string targetFullPathSO = outpath + targetNameSO;
  std::string targetFullPathReal = outpath + targetNameReal;
533
  std::string targetFullPathImport = outpathImp + targetNameImport;
534
535
536

  // Construct the output path version of the names for use in command
  // arguments.
537
538
  std::string targetOutPathPDB = this->LocalGenerator->ConvertToOutputFormat(
    targetFullPathPDB, cmOutputConverter::SHELL);
539
540

  std::string targetOutPath = this->LocalGenerator->ConvertToOutputFormat(
541
    this->LocalGenerator->MaybeConvertToRelativePath(
542
543
544
      this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPath),
    cmOutputConverter::SHELL);
  std::string targetOutPathSO = this->LocalGenerator->ConvertToOutputFormat(
545
    this->LocalGenerator->MaybeConvertToRelativePath(
546
547
548
      this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathSO),
    cmOutputConverter::SHELL);
  std::string targetOutPathReal = this->LocalGenerator->ConvertToOutputFormat(
549
    this->LocalGenerator->MaybeConvertToRelativePath(
550
551
      this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathReal),
    cmOutputConverter::SHELL);
552
  std::string targetOutPathImport =
553
    this->LocalGenerator->ConvertToOutputFormat(
554
      this->LocalGenerator->MaybeConvertToRelativePath(
555
556
557
        this->LocalGenerator->GetCurrentBinaryDirectory(),
        targetFullPathImport),
      cmOutputConverter::SHELL);
558

559
  this->NumberOfProgressActions++;
560
  if (!this->NoRuleMessages) {
561
562
    cmLocalUnixMakefileGenerator3::EchoProgress progress;
    this->MakeEchoProgress(progress);
563
564
565
    // Add the link message.
    std::string buildEcho = "Linking ";
    buildEcho += linkLanguage;
566
    switch (this->GeneratorTarget->GetType()) {
567
      case cmStateEnums::STATIC_LIBRARY:
568
569
        buildEcho += " static library ";
        break;
570
      case cmStateEnums::SHARED_LIBRARY:
571
572
        buildEcho += " shared library ";
        break;
573
      case cmStateEnums::MODULE_LIBRARY:
574
        if (this->GeneratorTarget->IsCFBundleOnApple()) {
575
          buildEcho += " CFBundle";
576
        }
577
578
579
580
581
        buildEcho += " shared module ";
        break;
      default:
        buildEcho += " library ";
        break;
582
    }
583
584
585
    buildEcho += targetOutPath;
    this->LocalGenerator->AppendEcho(
      commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress);
586
  }
587

588
  // Clean files associated with this library.
589
  std::vector<std::string> libCleanFiles;
590
  libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
591
    this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathReal));
592
593
594
595
596
597
598
599
600
601
602
603
604
605

  std::vector<std::string> commands1;
  // Add a command to remove any existing files for this library.
  // for static libs only
  if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
    this->LocalGenerator->AppendCleanCommand(commands1, libCleanFiles,
                                             this->GeneratorTarget, "target");
    this->LocalGenerator->CreateCDCommand(
      commands1, this->Makefile->GetCurrentBinaryDirectory(),
      this->LocalGenerator->GetBinaryDirectory());
    commands.insert(commands.end(), commands1.begin(), commands1.end());
    commands1.clear();
  }

606
  if (targetName != targetNameReal) {
607
    libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
608
      this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPath));
609
  }
610
  if (targetNameSO != targetNameReal && targetNameSO != targetName) {
611
    libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
612
      this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathSO));
613
614
  }
  if (!targetNameImport.empty()) {
615
    libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
616
617
      this->LocalGenerator->GetCurrentBinaryDirectory(),
      targetFullPathImport));
618
    std::string implib;
619
620
    if (this->GeneratorTarget->GetImplibGNUtoMS(
          this->ConfigName, targetFullPathImport, implib)) {
621
      libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
622
        this->LocalGenerator->GetCurrentBinaryDirectory(), implib));
623
    }
624
  }
625
626
627
628

  // List the PDB for cleaning only when the whole target is
  // cleaned.  We do not want to delete the .pdb file just before
  // linking the target.
629
  this->CleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
630
    this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathPDB));
631
632
633
634

#ifdef _WIN32
  // There may be a manifest file for this target.  Add it to the
  // clean set just in case.
635
  if (this->GeneratorTarget->GetType() != cmStateEnums::STATIC_LIBRARY) {
636
    libCleanFiles.push_back(this->LocalGenerator->MaybeConvertToRelativePath(
637
      this->LocalGenerator->GetCurrentBinaryDirectory(),
638
      targetFullPath + ".manifest"));
639
  }
640
641
#endif

642
  // Add the pre-build and pre-link rules building but not when relinking.
643
644
645
  if (!relink) {
    this->LocalGenerator->AppendCustomCommands(
      commands, this->GeneratorTarget->GetPreBuildCommands(),
646
      this->GeneratorTarget, this->LocalGenerator->GetBinaryDirectory());
647
648
    this->LocalGenerator->AppendCustomCommands(
      commands, this->GeneratorTarget->GetPreLinkCommands(),
649
      this->GeneratorTarget, this->LocalGenerator->GetBinaryDirectory());
650
  }
651

652
653
  // Determine whether a link script will be used.
  bool useLinkScript = this->GlobalGenerator->GetUseLinkScript();
654

655
656
657
658
  bool useResponseFileForObjects =
    this->CheckUseResponseFileForObjects(linkLanguage);
  bool const useResponseFileForLibs =
    this->CheckUseResponseFileForLibraries(linkLanguage);
659

660
  // For static libraries there might be archiving rules.
661
  bool haveStaticLibraryRule = false;
662
663
664
665
  std::vector<std::string> archiveCreateCommands;
  std::vector<std::string> archiveAppendCommands;
  std::vector<std::string> archiveFinishCommands;
  std::string::size_type archiveCommandLimit = std::string::npos;
666
  if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
Daniel Pfeifer's avatar
Daniel Pfeifer committed
667
    haveStaticLibraryRule = this->Makefile->IsDefinitionSet(linkRuleVar);
668
669
670
    std::string arCreateVar = "CMAKE_";
    arCreateVar += linkLanguage;
    arCreateVar += "_ARCHIVE_CREATE";
671
672

    arCreateVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
673
      arCreateVar, linkLanguage, this->ConfigName);
674

675
    if (const char* rule = this->Makefile->GetDefinition(arCreateVar)) {
676
      cmSystemTools::ExpandListArgument(rule, archiveCreateCommands);
677
    }
678
679
680
    std::string arAppendVar = "CMAKE_";
    arAppendVar += linkLanguage;
    arAppendVar += "_ARCHIVE_APPEND";
681
682

    arAppendVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
683
      arAppendVar, linkLanguage, this->ConfigName);
684

685
    if (const char* rule = this->Makefile->GetDefinition(arAppendVar)) {
686
      cmSystemTools::ExpandListArgument(rule, archiveAppendCommands);
687
    }
688
689
690
    std::string arFinishVar = "CMAKE_";
    arFinishVar += linkLanguage;
    arFinishVar += "_ARCHIVE_FINISH";
691
692

    arFinishVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
693
      arFinishVar, linkLanguage, this->ConfigName);
694

695
    if (const char* rule = this->Makefile->GetDefinition(arFinishVar)) {
696
      cmSystemTools::ExpandListArgument(rule, archiveFinishCommands);
697
    }
698
  }
699
700

  // Decide whether to use archiving rules.
701
  bool useArchiveRules = !haveStaticLibraryRule &&
702
    !archiveCreateCommands.empty() && !archiveAppendCommands.empty();
703
  if (useArchiveRules) {
704
705
    // Archiving rules are always run with a link script.
    useLinkScript = true;
706

707
    // Archiving rules never use a response file.
708
    useResponseFileForObjects = false;
709

710
711
712
713
714
715
716
717
    // Limit the length of individual object lists to less than half of
    // the command line length limit (leaving half for other flags).
    // This may result in several calls to the archiver.
    if (size_t limit = cmSystemTools::CalculateCommandLineLengthLimit()) {
      archiveCommandLimit = limit / 2;
    } else {
      archiveCommandLimit = 8000;
    }
718
  }
719

720
  // Expand the rule variables.
721
  std::vector<std::string> real_link_commands;
722
  {
723
724
725
726
727
728
729
730
    bool useWatcomQuote =
      this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE");

    // Set path conversion for link script shells.
    this->LocalGenerator->SetLinkScriptShell(useLinkScript);

    // Collect up flags to link in needed libraries.
    std::string linkLibs;
731
    if (this->GeneratorTarget->GetType() != cmStateEnums::STATIC_LIBRARY) {
732

733
      std::unique_ptr<cmLinkLineComputer> linkLineComputer(
734
        this->CreateLinkLineComputer(
735
          this->LocalGenerator,
736
          this->LocalGenerator->GetStateSnapshot().GetDirectory()));
737
738
      linkLineComputer->SetForResponse(useResponseFileForLibs);
      linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
739
      linkLineComputer->SetRelink(relink);
740

741
      this->CreateLinkLibs(linkLineComputer.get(), linkLibs,
742
                           useResponseFileForLibs, depends);
743
744
745
746
747
748
749
750
    }

    // Construct object file lists that may be needed to expand the
    // rule.
    std::string buildObjs;
    this->CreateObjectLists(useLinkScript, useArchiveRules,
                            useResponseFileForObjects, buildObjs, depends,
                            useWatcomQuote);
751
752
753
754
755
756
757
758
    if (!this->DeviceLinkObject.empty()) {
      buildObjs += " " +
        this->LocalGenerator->ConvertToOutputFormat(
          this->LocalGenerator->MaybeConvertToRelativePath(
            this->LocalGenerator->GetCurrentBinaryDirectory(),
            this->DeviceLinkObject),
          cmOutputConverter::SHELL);
    }
759
760

    // maybe create .def file from list of objects
761
    this->GenDefFile(real_link_commands);
762

763
764
    std::string manifests = this->GetManifests();

765
    cmRulePlaceholderExpander::RuleVariables vars;
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
    vars.TargetPDB = targetOutPathPDB.c_str();

    // Setup the target version.
    std::string targetVersionMajor;
    std::string targetVersionMinor;
    {
      std::ostringstream majorStream;
      std::ostringstream minorStream;
      int major;
      int minor;
      this->GeneratorTarget->GetTargetVersion(major, minor);
      majorStream << major;
      minorStream << minor;
      targetVersionMajor = majorStream.str();
      targetVersionMinor = minorStream.str();
    }
    vars.TargetVersionMajor = targetVersionMajor.c_str();
    vars.TargetVersionMinor = targetVersionMinor.c_str();

785
786
787
    vars.CMTargetName = this->GeneratorTarget->GetName().c_str();
    vars.CMTargetType =
      cmState::GetTargetTypeName(this->GeneratorTarget->GetType());
788
789
790
    vars.Language = linkLanguage.c_str();
    vars.Objects = buildObjs.c_str();
    std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
791
792

    objectDir = this->LocalGenerator->ConvertToOutputFormat(
793
      this->LocalGenerator->MaybeConvertToRelativePath(
794
795
796
        this->LocalGenerator->GetCurrentBinaryDirectory(), objectDir),
      cmOutputConverter::SHELL);

797
    vars.ObjectDir = objectDir.c_str();
798
799
800
    cmOutputConverter::OutputFormat output = (useWatcomQuote)
      ? cmOutputConverter::WATCOMQUOTE
      : cmOutputConverter::SHELL;
801
    std::string target = this->LocalGenerator->ConvertToOutputFormat(
802
      this->LocalGenerator->MaybeConvertToRelativePath(
803
804
        this->LocalGenerator->GetCurrentBinaryDirectory(), targetFullPathReal),
      output);
805
806
807
808
809
810
811
812
813
814
815
816
817
    vars.Target = target.c_str();
    vars.LinkLibraries = linkLibs.c_str();
    vars.ObjectsQuoted = buildObjs.c_str();
    if (this->GeneratorTarget->HasSOName(this->ConfigName)) {
      vars.SONameFlag = this->Makefile->GetSONameFlag(linkLanguage);
      vars.TargetSOName = targetNameSO.c_str();
    }
    vars.LinkFlags = linkFlags.c_str();

    vars.Manifests = manifests.c_str();

    // Compute the directory portion of the install_name setting.
    std::string install_name_dir;
818
    if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY) {
819
      // Get the install_name directory for the build tree.
820
      install_name_dir =
821
822
823
824
825
826
827
        this->GeneratorTarget->GetInstallNameDirForBuildTree(this->ConfigName);

      // Set the rule variable replacement value.
      if (install_name_dir.empty()) {
        vars.TargetInstallNameDir = "";
      } else {
        // Convert to a path for the native build tool.
828
829
        install_name_dir = this->LocalGenerator->ConvertToOutputFormat(
          install_name_dir, cmOutputConverter::SHELL);
830
        vars.TargetInstallNameDir = install_name_dir.c_str();
831
832
      }
    }
833

834
    // Add language-specific flags.
835
    std::string langFlags;
836
837
    this->LocalGenerator->AddLanguageFlagsForLinking(
      langFlags, this->GeneratorTarget, linkLanguage, this->ConfigName);
838

839
840
    this->LocalGenerator->AddArchitectureFlags(
      langFlags, this->GeneratorTarget, linkLanguage, this->ConfigName);
841

842
    vars.LanguageCompileFlags = langFlags.c_str();
843

844
845
846
847
848
849
850
851
    std::string launcher;
    const char* val = this->LocalGenerator->GetRuleLauncher(
      this->GeneratorTarget, "RULE_LAUNCH_LINK");
    if (val && *val) {
      launcher = val;
      launcher += " ";
    }

852
    std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
853
      this->LocalGenerator->CreateRulePlaceholderExpander());
854
    // Construct the main link rule and expand placeholders.
855
    rulePlaceholderExpander->SetTargetImpLib(targetOutPathImport);
856
857
858
859
    if (useArchiveRules) {
      // Construct the individual object list strings.
      std::vector<std::string> object_strings;
      this->WriteObjectsStrings(object_strings, archiveCommandLimit);
860

861
862
863
864
865
866
867
868
869
870
      // Add the cuda device object to the list of archive files. This will
      // only occur on archives which have CUDA_RESOLVE_DEVICE_SYMBOLS enabled
      if (!this->DeviceLinkObject.empty()) {
        object_strings.push_back(this->LocalGenerator->ConvertToOutputFormat(
          this->LocalGenerator->MaybeConvertToRelativePath(
            this->LocalGenerator->GetCurrentBinaryDirectory(),
            this->DeviceLinkObject),
          cmOutputConverter::SHELL));
      }

871
872
      // Create the archive with the first set of objects.
      std::vector<std::string>::iterator osi = object_strings.begin();
873
      {
874
        vars.Objects = osi->c_str();
875
876
        for (std::string const& acc : archiveCreateCommands) {
          std::string cmd = launcher + acc;
877
878
          rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
                                                       cmd, vars);
879
          real_link_commands.push_back(std::move(cmd));
880
        }
881
      }
882
883
884
      // Append to the archive with the other object sets.
      for (++osi; osi != object_strings.end(); ++osi) {
        vars.Objects = osi->c_str();
885
886
        for (std::string const& aac : archiveAppendCommands) {
          std::string cmd = launcher + aac;
887
888
          rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
                                                       cmd, vars);
889
          real_link_commands.push_back(std::move(cmd));
890
891
892
893
        }
      }
      // Finish the archive.
      vars.Objects = "";
894
895
      for (std::string const& afc : archiveFinishCommands) {
        std::string cmd = launcher + afc;
896
897
        rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, cmd,
                                                     vars);
898
899
        // If there is no ranlib the command will be ":".  Skip it.
        if (!cmd.empty() && cmd[0] != ':') {
900
          real_link_commands.push_back(std::move(cmd));
901
        }
902
      }
903
904
905
906
    } else {
      // Get the set of commands.
      std::string linkRule = this->GetLinkRule(linkRuleVar);
      cmSystemTools::ExpandListArgument(linkRule, real_link_commands);
907
      if (this->GeneratorTarget->GetPropertyAsBool("LINK_WHAT_YOU_USE") &&
908
          (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY)) {
909
910
        std::string cmakeCommand = this->LocalGenerator->ConvertToOutputFormat(
          cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL);
911
        cmakeCommand += " -E __run_co_compile --lwyu=";
912
        cmakeCommand += targetOutPathReal;
913
        real_link_commands.push_back(std::move(cmakeCommand));
914
      }
915
916

      // Expand placeholders.
917
918
919
920
      for (std::string& real_link_command : real_link_commands) {
        real_link_command = launcher + real_link_command;
        rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
                                                     real_link_command, vars);
921
      }
922
    }
923

924
925
    // Restore path conversion to normal shells.
    this->LocalGenerator->SetLinkScriptShell(false);
926
  }
927