cmInstallTargetGenerator.cxx 29.2 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 "cmInstallTargetGenerator.h"

5
6
7
8
9
10
#include <assert.h>
#include <map>
#include <set>
#include <sstream>
#include <utility>

11
#include "cmComputeLinkInformation.h"
12
#include "cmGeneratorExpression.h"
13
#include "cmGeneratorTarget.h"
14
#include "cmGlobalGenerator.h"
15
#include "cmInstallType.h"
16
#include "cmLocalGenerator.h"
17
#include "cmMakefile.h"
18
#include "cmStateTypes.h"
19
20
#include "cmSystemTools.h"
#include "cmTarget.h"
21
#include "cmake.h"
22

23
24
25
26
27
28
29
30
cmInstallTargetGenerator::cmInstallTargetGenerator(
  const std::string& targetName, const char* dest, bool implib,
  const char* file_permissions, std::vector<std::string> const& configurations,
  const char* component, MessageLevel message, bool exclude_from_all,
  bool optional)
  : cmInstallGenerator(dest, configurations, component, message,
                       exclude_from_all)
  , TargetName(targetName)
Daniel Pfeifer's avatar
Daniel Pfeifer committed
31
  , Target(nullptr)
32
33
34
  , FilePermissions(file_permissions)
  , ImportLibrary(implib)
  , Optional(optional)
35
{
36
  this->ActionsPerConfig = true;
37
  this->NamelinkMode = NamelinkModeNone;
38
39
}

40
cmInstallTargetGenerator::~cmInstallTargetGenerator()
41
42
43
44
45
{
}

void cmInstallTargetGenerator::GenerateScript(std::ostream& os)
{
46
  // Warn if installing an exclude-from-all target.
47
  if (this->Target->GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
48
    std::ostringstream msg;
49
50
51
52
53
    msg << "WARNING: Target \"" << this->Target->GetName()
        << "\" has EXCLUDE_FROM_ALL set and will not be built by default "
        << "but an install rule has been provided for it.  CMake does "
        << "not define behavior for this case.";
    cmSystemTools::Message(msg.str().c_str(), "Warning");
54
  }
55

56
57
58
59
  // Perform the main install script generation.
  this->cmInstallGenerator::GenerateScript(os);
}

60
void cmInstallTargetGenerator::GenerateScriptForConfig(
61
  std::ostream& os, const std::string& config, Indent indent)
62
{
63
  cmStateEnums::TargetType targetType = this->Target->GetType();
64
  cmInstallType type = cmInstallType();
65
  switch (targetType) {
66
    case cmStateEnums::EXECUTABLE:
67
68
      type = cmInstallType_EXECUTABLE;
      break;
69
    case cmStateEnums::STATIC_LIBRARY:
70
71
      type = cmInstallType_STATIC_LIBRARY;
      break;
72
    case cmStateEnums::SHARED_LIBRARY:
73
74
      type = cmInstallType_SHARED_LIBRARY;
      break;
75
    case cmStateEnums::MODULE_LIBRARY:
76
77
      type = cmInstallType_MODULE_LIBRARY;
      break;
78
    case cmStateEnums::INTERFACE_LIBRARY:
79
80
      // Not reachable. We never create a cmInstallTargetGenerator for
      // an INTERFACE_LIBRARY.
81
82
      assert(false &&
             "INTERFACE_LIBRARY targets have no installable outputs.");
83
      break;
84

85
    case cmStateEnums::OBJECT_LIBRARY:
86
87
88
      this->GenerateScriptForConfigObjectLibrary(os, config, indent);
      return;

89
90
91
    case cmStateEnums::UTILITY:
    case cmStateEnums::GLOBAL_TARGET:
    case cmStateEnums::UNKNOWN_LIBRARY:
92
93
      this->Target->GetLocalGenerator()->IssueMessage(
        cmake::INTERNAL_ERROR,
94
95
        "cmInstallTargetGenerator created with non-installable target.");
      return;
96
  }
97
98
99
100
101
102
103
104
105

  // Compute the build tree directory from which to copy the target.
  std::string fromDirConfig;
  if (this->Target->NeedRelinkBeforeInstall(config)) {
    fromDirConfig =
      this->Target->GetLocalGenerator()->GetCurrentBinaryDirectory();
    fromDirConfig += cmake::GetCMakeFilesDirectory();
    fromDirConfig += "/CMakeRelink.dir/";
  } else {
106
107
108
109
    cmStateEnums::ArtifactType artifact = this->ImportLibrary
      ? cmStateEnums::ImportLibraryArtifact
      : cmStateEnums::RuntimeBinaryArtifact;
    fromDirConfig = this->Target->GetDirectory(config, artifact);
110
111
112
113
114
115
116
117
118
119
120
121
    fromDirConfig += "/";
  }

  std::string toDir =
    this->ConvertToAbsoluteDestination(this->GetDestination(config));
  toDir += "/";

  // Compute the list of files to install for this target.
  std::vector<std::string> filesFrom;
  std::vector<std::string> filesTo;
  std::string literal_args;

122
  if (targetType == cmStateEnums::EXECUTABLE) {
123
124
125
    // There is a bug in cmInstallCommand if this fails.
    assert(this->NamelinkMode == NamelinkModeNone);

126
127
128
129
    std::string targetName;
    std::string targetNameReal;
    std::string targetNameImport;
    std::string targetNamePDB;
130
    this->Target->GetExecutableNames(targetName, targetNameReal,
131
132
                                     targetNameImport, targetNamePDB, config);
    if (this->ImportLibrary) {
133
134
      std::string from1 = fromDirConfig + targetNameImport;
      std::string to1 = toDir + targetNameImport;
135
136
      filesFrom.push_back(std::move(from1));
      filesTo.push_back(std::move(to1));
137
      std::string targetNameImportLib;
138
      if (this->Target->GetImplibGNUtoMS(config, targetNameImport,
139
                                         targetNameImportLib)) {
140
141
        filesFrom.push_back(fromDirConfig + targetNameImportLib);
        filesTo.push_back(toDir + targetNameImportLib);
142
      }
143
144

      // An import library looks like a static library.
145
      type = cmInstallType_STATIC_LIBRARY;
146
    } else {
147
148
      std::string from1 = fromDirConfig + targetName;
      std::string to1 = toDir + targetName;
149
150

      // Handle OSX Bundles.
151
      if (this->Target->IsAppBundleOnApple()) {
Gregor Jasny's avatar
Gregor Jasny committed
152
153
        cmMakefile const* mf = this->Target->Target->GetMakefile();

154
155
156
157
158
159
        // Get App Bundle Extension
        const char* ext = this->Target->GetProperty("BUNDLE_EXTENSION");
        if (!ext) {
          ext = "app";
        }

160
        // Install the whole app bundle directory.
161
        type = cmInstallType_DIRECTORY;
162
        literal_args += " USE_SOURCE_PERMISSIONS";
163
164
        from1 += ".";
        from1 += ext;
165
166

        // Tweaks apply to the binary inside the bundle.
167
168
169
        to1 += ".";
        to1 += ext;
        to1 += "/";
170
        if (!mf->PlatformIsAppleEmbedded()) {
Gregor Jasny's avatar
Gregor Jasny committed
171
          to1 += "Contents/MacOS/";
172
        }
173
174
        to1 += targetName;
      } else {
175
        // Tweaks apply to the real file, so list it first.
176
        if (targetNameReal != targetName) {
177
178
          std::string from2 = fromDirConfig + targetNameReal;
          std::string to2 = toDir += targetNameReal;
179
180
          filesFrom.push_back(std::move(from2));
          filesTo.push_back(std::move(to2));
181
        }
182
      }
183

184
185
      filesFrom.push_back(std::move(from1));
      filesTo.push_back(std::move(to1));
186
    }
187
  } else {
188
189
190
191
192
    std::string targetName;
    std::string targetNameSO;
    std::string targetNameReal;
    std::string targetNameImport;
    std::string targetNamePDB;
193
194
195
    this->Target->GetLibraryNames(targetName, targetNameSO, targetNameReal,
                                  targetNameImport, targetNamePDB, config);
    if (this->ImportLibrary) {
196
197
198
      // There is a bug in cmInstallCommand if this fails.
      assert(this->NamelinkMode == NamelinkModeNone);

199
200
      std::string from1 = fromDirConfig + targetNameImport;
      std::string to1 = toDir + targetNameImport;
201
202
      filesFrom.push_back(std::move(from1));
      filesTo.push_back(std::move(to1));
203
      std::string targetNameImportLib;
204
      if (this->Target->GetImplibGNUtoMS(config, targetNameImport,
205
                                         targetNameImportLib)) {
206
207
        filesFrom.push_back(fromDirConfig + targetNameImportLib);
        filesTo.push_back(toDir + targetNameImportLib);
208
      }
209
210

      // An import library looks like a static library.
211
      type = cmInstallType_STATIC_LIBRARY;
212
    } else if (this->Target->IsFrameworkOnApple()) {
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
      // FIXME: In principle we should be able to
      //   assert(this->NamelinkMode == NamelinkModeNone);
      // but since the current install() command implementation checks
      // the FRAMEWORK property immediately instead of delaying until
      // generate time, it is possible for project code to set the
      // property after calling install().  In such a case, the install()
      // command will use the LIBRARY code path and create two install
      // generators, one for the namelink component (NamelinkModeOnly)
      // and one for the primary artifact component (NamelinkModeSkip).
      // Historically this was not diagnosed and resulted in silent
      // installation of a framework to the LIBRARY destination.
      // Retain that behavior.
      switch (this->NamelinkMode) {
        case NamelinkModeNone:
          // Normal case.
          break;
        case NamelinkModeOnly:
          // Assume the NamelinkModeSkip instance will install.
          return;
        case NamelinkModeSkip: {
          // Proceed to install in the LIBRARY destination for compatibility.
        } break;
      }
236

237
      // Install the whole framework directory.
238
      type = cmInstallType_DIRECTORY;
239
      literal_args += " USE_SOURCE_PERMISSIONS";
240
241
242

      std::string from1 = fromDirConfig + targetName;
      from1 = cmSystemTools::GetFilenamePath(from1);
243

244
      // Tweaks apply to the binary inside the bundle.
245
      std::string to1 = toDir + targetNameReal;
246

247
248
      filesFrom.push_back(std::move(from1));
      filesTo.push_back(std::move(to1));
249
    } else if (this->Target->IsCFBundleOnApple()) {
250
251
252
253
254
255
256
257
258
      // Install the whole app bundle directory.
      type = cmInstallType_DIRECTORY;
      literal_args += " USE_SOURCE_PERMISSIONS";

      std::string targetNameBase = targetName.substr(0, targetName.find('/'));

      std::string from1 = fromDirConfig + targetNameBase;
      std::string to1 = toDir + targetName;

259
260
      filesFrom.push_back(std::move(from1));
      filesTo.push_back(std::move(to1));
261
    } else {
262
      bool haveNamelink = false;
263
264
265
266
267
268

      // Library link name.
      std::string fromName = fromDirConfig + targetName;
      std::string toName = toDir + targetName;

      // Library interface name.
269
      std::string fromSOName;
270
      std::string toSOName;
271
      if (targetNameSO != targetName) {
272
        haveNamelink = true;
273
274
        fromSOName = fromDirConfig + targetNameSO;
        toSOName = toDir + targetNameSO;
275
      }
276
277
278
279

      // Library implementation name.
      std::string fromRealName;
      std::string toRealName;
280
      if (targetNameReal != targetName && targetNameReal != targetNameSO) {
281
        haveNamelink = true;
282
283
        fromRealName = fromDirConfig + targetNameReal;
        toRealName = toDir + targetNameReal;
284
      }
285
286

      // Add the names based on the current namelink mode.
287
      if (haveNamelink) {
288
        // With a namelink we need to check the mode.
289
        if (this->NamelinkMode == NamelinkModeOnly) {
290
          // Install the namelink only.
291
292
          filesFrom.push_back(fromName);
          filesTo.push_back(toName);
293
        } else {
294
          // Install the real file if it has its own name.
295
          if (!fromRealName.empty()) {
296
297
            filesFrom.push_back(fromRealName);
            filesTo.push_back(toRealName);
298
          }
299
300

          // Install the soname link if it has its own name.
301
          if (!fromSOName.empty()) {
302
303
            filesFrom.push_back(fromSOName);
            filesTo.push_back(toSOName);
304
          }
305
306

          // Install the namelink if it is not to be skipped.
307
          if (this->NamelinkMode != NamelinkModeSkip) {
308
309
            filesFrom.push_back(fromName);
            filesTo.push_back(toName);
310
311
          }
        }
312
      } else {
313
314
        // Without a namelink there will be only one file.  Install it
        // if this is not a namelink-only rule.
315
        if (this->NamelinkMode != NamelinkModeOnly) {
316
317
          filesFrom.push_back(fromName);
          filesTo.push_back(toName);
318
319
        }
      }
320
    }
321
  }
322

323
324
325
  // If this fails the above code is buggy.
  assert(filesFrom.size() == filesTo.size());

326
  // Skip this rule if no files are to be installed for the target.
327
  if (filesFrom.empty()) {
328
    return;
329
  }
330

331
  // Add pre-installation tweaks.
332
333
  this->AddTweak(os, indent, config, filesTo,
                 &cmInstallTargetGenerator::PreReplacementTweaks);
334

335
  // Write code to install the target file.
Daniel Pfeifer's avatar
Daniel Pfeifer committed
336
337
  const char* no_dir_permissions = nullptr;
  const char* no_rename = nullptr;
338
  bool optional = this->Optional || this->ImportLibrary;
339
340
341
  this->AddInstallRule(os, this->GetDestination(config), type, filesFrom,
                       optional, this->FilePermissions.c_str(),
                       no_dir_permissions, no_rename, literal_args.c_str(),
342
                       indent);
343

344
  // Add post-installation tweaks.
345
346
  this->AddTweak(os, indent, config, filesTo,
                 &cmInstallTargetGenerator::PostReplacementTweaks);
347
348
}

349
350
351
352
353
354
355
356
357
358
359
360
361
362
static std::string computeInstallObjectDir(cmGeneratorTarget* gt,
                                           std::string const& config)
{
  std::string objectDir = "objects";
  if (!config.empty()) {
    objectDir += "-";
    objectDir += config;
  }
  objectDir += "/";
  objectDir += gt->GetName();
  return objectDir;
}

void cmInstallTargetGenerator::GenerateScriptForConfigObjectLibrary(
363
  std::ostream& os, const std::string& config, Indent indent)
364
365
366
367
368
369
370
371
372
373
374
{
  // Compute all the object files inside this target
  std::vector<std::string> objects;
  this->Target->GetTargetObjectNames(config, objects);

  std::string const dest = this->GetDestination(config) + "/" +
    computeInstallObjectDir(this->Target, config);

  std::string const obj_dir = this->Target->GetObjectDirectory(config);
  std::string const literal_args = " FILES_FROM_DIR \"" + obj_dir + "\"";

Daniel Pfeifer's avatar
Daniel Pfeifer committed
375
376
  const char* no_dir_permissions = nullptr;
  const char* no_rename = nullptr;
377
378
379
380
381
382
383
384
385
  this->AddInstallRule(os, dest, cmInstallType_FILES, objects, this->Optional,
                       this->FilePermissions.c_str(), no_dir_permissions,
                       no_rename, literal_args.c_str(), indent);
}

void cmInstallTargetGenerator::GetInstallObjectNames(
  std::string const& config, std::vector<std::string>& objects) const
{
  this->Target->GetTargetObjectNames(config, objects);
386
387
  for (std::string& o : objects) {
    o = computeInstallObjectDir(this->Target, config) + "/" + o;
388
389
390
  }
}

391
392
std::string cmInstallTargetGenerator::GetDestination(
  std::string const& config) const
393
394
395
{
  cmGeneratorExpression ge;
  return ge.Parse(this->Destination)
396
    ->Evaluate(this->Target->GetLocalGenerator(), config);
397
398
}

399
400
std::string cmInstallTargetGenerator::GetInstallFilename(
  const std::string& config) const
Alexander Neundorf's avatar
   
Alexander Neundorf committed
401
{
402
403
404
  NameType nameType = this->ImportLibrary ? NameImplib : NameNormal;
  return cmInstallTargetGenerator::GetInstallFilename(this->Target, config,
                                                      nameType);
Alexander Neundorf's avatar
   
Alexander Neundorf committed
405
406
}

407
408
409
std::string cmInstallTargetGenerator::GetInstallFilename(
  cmGeneratorTarget const* target, const std::string& config,
  NameType nameType)
Alexander Neundorf's avatar
   
Alexander Neundorf committed
410
411
412
{
  std::string fname;
  // Compute the name of the library.
413
  if (target->GetType() == cmStateEnums::EXECUTABLE) {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
414
415
416
417
    std::string targetName;
    std::string targetNameReal;
    std::string targetNameImport;
    std::string targetNamePDB;
418
419
420
    target->GetExecutableNames(targetName, targetNameReal, targetNameImport,
                               targetNamePDB, config);
    if (nameType == NameImplib) {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
421
      // Use the import library name.
422
      if (!target->GetImplibGNUtoMS(config, targetNameImport, fname,
423
                                    "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
424
        fname = targetNameImport;
Alexander Neundorf's avatar
   
Alexander Neundorf committed
425
      }
426
    } else if (nameType == NameReal) {
427
428
      // Use the canonical name.
      fname = targetNameReal;
429
    } else {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
430
431
432
      // Use the canonical name.
      fname = targetName;
    }
433
  } else {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
434
435
436
437
438
    std::string targetName;
    std::string targetNameSO;
    std::string targetNameReal;
    std::string targetNameImport;
    std::string targetNamePDB;
439
    target->GetLibraryNames(targetName, targetNameSO, targetNameReal,
Alexander Neundorf's avatar
   
Alexander Neundorf committed
440
                            targetNameImport, targetNamePDB, config);
441
    if (nameType == NameImplib) {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
442
      // Use the import library name.
443
      if (!target->GetImplibGNUtoMS(config, targetNameImport, fname,
444
                                    "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
445
        fname = targetNameImport;
Alexander Neundorf's avatar
   
Alexander Neundorf committed
446
      }
447
    } else if (nameType == NameSO) {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
448
449
      // Use the soname.
      fname = targetNameSO;
450
    } else if (nameType == NameReal) {
451
452
      // Use the real name.
      fname = targetNameReal;
453
    } else {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
454
455
456
      // Use the canonical name.
      fname = targetName;
    }
457
  }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
458
459
460
461

  return fname;
}

462
463
void cmInstallTargetGenerator::Compute(cmLocalGenerator* lg)
{
464
  // Lookup this target in the current directory.
465
  this->Target = lg->FindLocalNonAliasGeneratorTarget(this->TargetName);
466
467
468
469
470
  if (!this->Target) {
    // If no local target has been found, find it in the global scope.
    this->Target =
      lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName);
  }
471
472
}

473
void cmInstallTargetGenerator::AddTweak(std::ostream& os, Indent indent,
474
475
476
                                        const std::string& config,
                                        std::string const& file,
                                        TweakMethod tweak)
477
{
478
  std::ostringstream tw;
479
480
  (this->*tweak)(tw, indent.Next(), config, file);
  std::string tws = tw.str();
481
  if (!tws.empty()) {
482
    os << indent << "if(EXISTS \"" << file << "\" AND\n"
483
484
       << indent << "   NOT IS_SYMLINK \"" << file << "\")\n";
    os << tws;
485
    os << indent << "endif()\n";
486
  }
487
488
}

489
void cmInstallTargetGenerator::AddTweak(std::ostream& os, Indent indent,
490
491
492
                                        const std::string& config,
                                        std::vector<std::string> const& files,
                                        TweakMethod tweak)
493
{
494
  if (files.size() == 1) {
495
496
    // Tweak a single file.
    this->AddTweak(os, indent, config, this->GetDestDirPath(files[0]), tweak);
497
  } else {
498
    // Generate a foreach loop to tweak multiple files.
499
    std::ostringstream tw;
500
501
    this->AddTweak(tw, indent.Next(), config, "${file}", tweak);
    std::string tws = tw.str();
502
    if (!tws.empty()) {
503
      Indent indent2 = indent.Next().Next();
504
      os << indent << "foreach(file\n";
505
506
      for (std::string const& f : files) {
        os << indent2 << "\"" << this->GetDestDirPath(f) << "\"\n";
507
      }
508
509
      os << indent2 << ")\n";
      os << tws;
510
      os << indent << "endforeach()\n";
511
    }
512
  }
513
514
515
516
517
518
519
}

std::string cmInstallTargetGenerator::GetDestDirPath(std::string const& file)
{
  // Construct the path of the file on disk after installation on
  // which tweaks may be performed.
  std::string toDestDirPath = "$ENV{DESTDIR}";
520
  if (file[0] != '/' && file[0] != '$') {
521
    toDestDirPath += "/";
522
  }
523
524
525
526
527
  toDestDirPath += file;
  return toDestDirPath;
}

void cmInstallTargetGenerator::PreReplacementTweaks(std::ostream& os,
528
                                                    Indent indent,
529
                                                    const std::string& config,
530
531
532
533
534
535
                                                    std::string const& file)
{
  this->AddRPathCheckRule(os, indent, config, file);
}

void cmInstallTargetGenerator::PostReplacementTweaks(std::ostream& os,
536
                                                     Indent indent,
537
538
                                                     const std::string& config,
                                                     std::string const& file)
539
540
541
{
  this->AddInstallNamePatchRule(os, indent, config, file);
  this->AddChrpathPatchRule(os, indent, config, file);
542
  this->AddUniversalInstallRule(os, indent, file);
543
544
545
546
  this->AddRanlibRule(os, indent, file);
  this->AddStripRule(os, indent, file);
}

547
void cmInstallTargetGenerator::AddInstallNamePatchRule(
548
  std::ostream& os, Indent indent, const std::string& config,
549
  std::string const& toDestDirPath)
550
{
551
  if (this->ImportLibrary ||
552
553
554
      !(this->Target->GetType() == cmStateEnums::SHARED_LIBRARY ||
        this->Target->GetType() == cmStateEnums::MODULE_LIBRARY ||
        this->Target->GetType() == cmStateEnums::EXECUTABLE)) {
555
    return;
556
  }
557

558
  // Fix the install_name settings in installed binaries.
559
560
561
  std::string installNameTool =
    this->Target->Target->GetMakefile()->GetSafeDefinition(
      "CMAKE_INSTALL_NAME_TOOL");
562

563
  if (installNameTool.empty()) {
Alexander Neundorf's avatar
Alexander Neundorf committed
564
    return;
565
  }
Alexander Neundorf's avatar
Alexander Neundorf committed
566

567
568
  // Build a map of build-tree install_name to install-tree install_name for
  // shared libraries linked to this target.
569
  std::map<std::string, std::string> install_name_remap;
570
571
572
573
  if (cmComputeLinkInformation* cli =
        this->Target->GetLinkInformation(config)) {
    std::set<cmGeneratorTarget const*> const& sharedLibs =
      cli->GetSharedLibrariesLinked();
574
    for (cmGeneratorTarget const* tgt : sharedLibs) {
575
      // The install_name of an imported target does not change.
576
      if (tgt->IsImported()) {
577
        continue;
578
      }
579

580
581
582
      // If the build tree and install tree use different path
      // components of the install_name field then we need to create a
      // mapping to be applied after installation.
583
584
      std::string for_build = tgt->GetInstallNameDirForBuildTree(config);
      std::string for_install = tgt->GetInstallNameDirForInstallTree();
585
      if (for_build != for_install) {
586
587
        // The directory portions differ.  Append the filename to
        // create the mapping.
588
        std::string fname = this->GetInstallFilename(tgt, config, NameSO);
589

590
591
        // Map from the build-tree install_name.
        for_build += fname;
592

593
        // Map to the install-tree install_name.
594
595
596
597
        for_install += fname;

        // Store the mapping entry.
        install_name_remap[for_build] = for_install;
598
599
      }
    }
600
  }
601
602
603

  // Edit the install_name of the target itself if necessary.
  std::string new_id;
604
  if (this->Target->GetType() == cmStateEnums::SHARED_LIBRARY) {
605
    std::string for_build =
606
      this->Target->GetInstallNameDirForBuildTree(config);
607
    std::string for_install = this->Target->GetInstallNameDirForInstallTree();
608

609
    if (this->Target->IsFrameworkOnApple() && for_install.empty()) {
610
611
612
613
      // Frameworks seem to have an id corresponding to their own full
      // path.
      // ...
      // for_install = fullDestPath_without_DESTDIR_or_name;
614
    }
615
616
617

    // If the install name will change on installation set the new id
    // on the installed file.
618
    if (for_build != for_install) {
619
620
      // Prepare to refer to the install-tree install_name.
      new_id = for_install;
621
      new_id += this->GetInstallFilename(this->Target, config, NameSO);
622
    }
623
  }
624
625
626

  // Write a rule to run install_name_tool to set the install-tree
  // install_name value and references.
627
  if (!new_id.empty() || !install_name_remap.empty()) {
628
    os << indent << "execute_process(COMMAND \"" << installNameTool;
Alexander Neundorf's avatar
   
Alexander Neundorf committed
629
    os << "\"";
630
    if (!new_id.empty()) {
631
      os << "\n" << indent << "  -id \"" << new_id << "\"";
632
    }
633
    for (auto const& i : install_name_remap) {
634
      os << "\n"
635
         << indent << "  -change \"" << i.first << "\" \"" << i.second << "\"";
636
637
638
    }
    os << "\n" << indent << "  \"" << toDestDirPath << "\")\n";
  }
639
}
Alexander Neundorf's avatar
   
Alexander Neundorf committed
640

641
void cmInstallTargetGenerator::AddRPathCheckRule(
642
  std::ostream& os, Indent indent, const std::string& config,
643
  std::string const& toDestDirPath)
644
645
{
  // Skip the chrpath if the target does not need it.
646
  if (this->ImportLibrary || !this->Target->IsChrpathUsed(config)) {
647
    return;
648
  }
649
  // Skip if on Apple
650
651
  if (this->Target->Target->GetMakefile()->IsOn(
        "CMAKE_PLATFORM_HAS_INSTALLNAME")) {
652
    return;
653
  }
654

655
656
  // Get the link information for this target.
  // It can provide the RPATH.
657
  cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
658
  if (!cli) {
659
    return;
660
  }
661
662
663
664
665
666
667

  // Get the install RPATH from the link information.
  std::string newRpath = cli->GetChrpathString();

  // Write a rule to remove the installed file if its rpath is not the
  // new rpath.  This is needed for existing build/install trees when
  // the installed rpath changes but the file is not rebuilt.
668
  /* clang-format off */
669
  os << indent << "file(RPATH_CHECK\n"
670
671
     << indent << "     FILE \"" << toDestDirPath << "\"\n"
     << indent << "     RPATH \"" << newRpath << "\")\n";
672
  /* clang-format on */
673
674
}

675
void cmInstallTargetGenerator::AddChrpathPatchRule(
676
  std::ostream& os, Indent indent, const std::string& config,
677
  std::string const& toDestDirPath)
678
{
679
  // Skip the chrpath if the target does not need it.
680
  if (this->ImportLibrary || !this->Target->IsChrpathUsed(config)) {
681
    return;
682
  }
683

684
685
  // Get the link information for this target.
  // It can provide the RPATH.
686
  cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
687
  if (!cli) {
688
    return;
689
  }
690

691
  cmMakefile* mf = this->Target->Target->GetMakefile();
692

693
  if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
694
695
    // If using install_name_tool, set up the rules to modify the rpaths.
    std::string installNameTool =
696
      mf->GetSafeDefinition("CMAKE_INSTALL_NAME_TOOL");
697
698
699
700
701

    std::vector<std::string> oldRuntimeDirs, newRuntimeDirs;
    cli->GetRPath(oldRuntimeDirs, false);
    cli->GetRPath(newRuntimeDirs, true);

702
703
    std::string darwin_major_version_s =
      mf->GetSafeDefinition("DARWIN_MAJOR_VERSION");
704

705
    std::istringstream ss(darwin_major_version_s);
706
707
    int darwin_major_version;
    ss >> darwin_major_version;
708
709
    if (!ss.fail() && darwin_major_version <= 9 &&
        (!oldRuntimeDirs.empty() || !newRuntimeDirs.empty())) {
710
      std::ostringstream msg;
711
712
      msg
        << "WARNING: Target \"" << this->Target->GetName()
713
714
715
716
717
718
        << "\" has runtime paths which cannot be changed during install.  "
        << "To change runtime paths, OS X version 10.6 or newer is required.  "
        << "Therefore, runtime paths will not be changed when installing.  "
        << "CMAKE_BUILD_WITH_INSTALL_RPATH may be used to work around"
           " this limitation.";
      mf->IssueMessage(cmake::WARNING, msg.str());
719
    } else {
720
721
722
      // Note: These paths are kept unique to avoid
      // install_name_tool corruption.
      std::set<std::string> runpaths;
723
      for (std::string const& i : oldRuntimeDirs) {
724
        std::string runpath =
725
          mf->GetGlobalGenerator()->ExpandCFGIntDir(i, config);
726

727
        if (runpaths.find(runpath) == runpaths.end()) {
728
          runpaths.insert(runpath);
729
730
          os << indent << "execute_process(COMMAND " << installNameTool
             << "\n";
731
732
733
          os << indent << "  -delete_rpath \"" << runpath << "\"\n";
          os << indent << "  \"" << toDestDirPath << "\")\n";
        }
734
      }
735

736
      runpaths.clear();
737
      for (std::string const& i : newRuntimeDirs) {
738
        std::string runpath =
739
          mf->GetGlobalGenerator()->ExpandCFGIntDir(i, config);
740

741
742
743
        if (runpaths.find(runpath) == runpaths.end()) {
          os << indent << "execute_process(COMMAND " << installNameTool
             << "\n";
744
745
          os << indent << "  -add_rpath \"" << runpath << "\"\n";
          os << indent << "  \"" << toDestDirPath << "\")\n";
746
        }
747
      }
748
    }
749
  } else {
750
751
752
753
754
755
756
    // Construct the original rpath string to be replaced.
    std::string oldRpath = cli->GetRPathString(false);

    // Get the install RPATH from the link information.
    std::string newRpath = cli->GetChrpathString();

    // Skip the rule if the paths are identical
757
    if (oldRpath == newRpath) {
758
      return;
759
    }
760
761

    // Write a rule to run chrpath to set the install-tree RPATH
762
763
764
765
    os << indent << "file(RPATH_CHANGE\n"
       << indent << "     FILE \"" << toDestDirPath << "\"\n"
       << indent << "     OLD_RPATH \"" << oldRpath << "\"\n"
       << indent << "     NEW_RPATH \"" << newRpath << "\")\n";
766
  }
767
768
}

769
void cmInstallTargetGenerator::AddStripRule(std::ostream& os, Indent indent,
770
                                            const std::string& toDestDirPath)
Alexander Neundorf's avatar
   
Alexander Neundorf committed
771
772
{

773
774
  // don't strip static and import libraries, because it removes the only
  // symbol table they have so you can't link to them anymore
775
  if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY ||
776
      this->ImportLibrary) {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
777
    return;
778
  }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
779

Alexander Neundorf's avatar
   
Alexander Neundorf committed
780
  // Don't handle OSX Bundles.
781
782
  if (this->Target->Target->GetMakefile()->IsOn("APPLE") &&
      this->Target->GetPropertyAsBool("MACOSX_BUNDLE")) {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
783
    return;
784
  }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
785

786
  if (!this->Target->Target->GetMakefile()->IsSet("CMAKE_STRIP")) {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
787
    return;
788
  }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
789

790
791
  os << indent << "if(CMAKE_INSTALL_DO_STRIP)\n";
  os << indent << "  execute_process(COMMAND \""
792
     << this->Target->Target->GetMakefile()->GetDefinition("CMAKE_STRIP")
793
     << "\" \"" << toDestDirPath << "\")\n";
794
  os << indent << "endif()\n";
Alexander Neundorf's avatar
   
Alexander Neundorf committed
795
796
}

797
void cmInstallTargetGenerator::AddRanlibRule(std::ostream& os, Indent indent,
798
                                             const std::string& toDestDirPath)
Alexander Neundorf's avatar
   
Alexander Neundorf committed
799
800
{
  // Static libraries need ranlib on this platform.
801
  if (this->Target->GetType() != cmStateEnums::STATIC_LIBRARY) {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
802
    return;
803
  }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
804
805
806

  // Perform post-installation processing on the file depending
  // on its type.
807
  if (!this->Target->Target->GetMakefile()->IsOn("APPLE")) {
Alexander Neundorf's avatar
   
Alexander Neundorf committed
808
    return;
809
  }
Alexander Neundorf's avatar
   
Alexander Neundorf committed
810

811
  std::string ranlib =