cmInstallTargetGenerator.cxx 29.6 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 "cmMessageType.h"
19
#include "cmStateTypes.h"
20 21
#include "cmSystemTools.h"
#include "cmTarget.h"
22
#include "cmake.h"
23

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

wahikihiki's avatar
wahikihiki committed
42
cmInstallTargetGenerator::~cmInstallTargetGenerator() = default;
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
    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.";
53
    cmSystemTools::Message(msg.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
      this->Target->GetLocalGenerator()->IssueMessage(
93
        MessageType::INTERNAL_ERROR,
94 95
        "cmInstallTargetGenerator created with non-installable target.");
      return;
96
  }
97 98 99 100 101 102

  // Compute the build tree directory from which to copy the target.
  std::string fromDirConfig;
  if (this->Target->NeedRelinkBeforeInstall(config)) {
    fromDirConfig =
      this->Target->GetLocalGenerator()->GetCurrentBinaryDirectory();
103
    fromDirConfig += "/CMakeFiles";
104 105
    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
    cmGeneratorTarget::Names targetNames =
      this->Target->GetExecutableNames(config);
128
    if (this->ImportLibrary) {
129 130
      std::string from1 = fromDirConfig + targetNames.ImportLibrary;
      std::string to1 = toDir + targetNames.ImportLibrary;
131 132
      filesFrom.push_back(std::move(from1));
      filesTo.push_back(std::move(to1));
133
      std::string targetNameImportLib;
134
      if (this->Target->GetImplibGNUtoMS(config, targetNames.ImportLibrary,
135
                                         targetNameImportLib)) {
136 137
        filesFrom.push_back(fromDirConfig + targetNameImportLib);
        filesTo.push_back(toDir + targetNameImportLib);
138
      }
139 140

      // An import library looks like a static library.
141
      type = cmInstallType_STATIC_LIBRARY;
142
    } else {
143 144
      std::string from1 = fromDirConfig + targetNames.Output;
      std::string to1 = toDir + targetNames.Output;
145 146

      // Handle OSX Bundles.
147
      if (this->Target->IsAppBundleOnApple()) {
148 149
        cmMakefile const* mf = this->Target->Target->GetMakefile();

150 151 152 153 154 155
        // Get App Bundle Extension
        const char* ext = this->Target->GetProperty("BUNDLE_EXTENSION");
        if (!ext) {
          ext = "app";
        }

156
        // Install the whole app bundle directory.
157
        type = cmInstallType_DIRECTORY;
158
        literal_args += " USE_SOURCE_PERMISSIONS";
159 160
        from1 += ".";
        from1 += ext;
161 162

        // Tweaks apply to the binary inside the bundle.
163 164 165
        to1 += ".";
        to1 += ext;
        to1 += "/";
166
        if (!mf->PlatformIsAppleEmbedded()) {
167
          to1 += "Contents/MacOS/";
168
        }
169
        to1 += targetNames.Output;
170
      } else {
171
        // Tweaks apply to the real file, so list it first.
172 173 174
        if (targetNames.Real != targetNames.Output) {
          std::string from2 = fromDirConfig + targetNames.Real;
          std::string to2 = toDir += targetNames.Real;
175 176
          filesFrom.push_back(std::move(from2));
          filesTo.push_back(std::move(to2));
177
        }
178
      }
179

180 181
      filesFrom.push_back(std::move(from1));
      filesTo.push_back(std::move(to1));
182
    }
183
  } else {
184 185
    cmGeneratorTarget::Names targetNames =
      this->Target->GetLibraryNames(config);
186
    if (this->ImportLibrary) {
187 188 189
      // There is a bug in cmInstallCommand if this fails.
      assert(this->NamelinkMode == NamelinkModeNone);

190 191
      std::string from1 = fromDirConfig + targetNames.ImportLibrary;
      std::string to1 = toDir + targetNames.ImportLibrary;
192 193
      filesFrom.push_back(std::move(from1));
      filesTo.push_back(std::move(to1));
194
      std::string targetNameImportLib;
195
      if (this->Target->GetImplibGNUtoMS(config, targetNames.ImportLibrary,
196
                                         targetNameImportLib)) {
197 198
        filesFrom.push_back(fromDirConfig + targetNameImportLib);
        filesTo.push_back(toDir + targetNameImportLib);
199
      }
200 201

      // An import library looks like a static library.
202
      type = cmInstallType_STATIC_LIBRARY;
203
    } else if (this->Target->IsFrameworkOnApple()) {
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
      // 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 and warn about the case.
      switch (this->NamelinkMode) {
        case NamelinkModeNone:
          // Normal case.
          break;
        case NamelinkModeOnly:
          // Assume the NamelinkModeSkip instance will warn and install.
          return;
        case NamelinkModeSkip: {
          std::string e = "Target '" + this->Target->GetName() +
            "' was changed to a FRAMEWORK sometime after install().  "
            "This may result in the wrong install DESTINATION.  "
            "Set the FRAMEWORK property earlier.";
          this->Target->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
            MessageType::AUTHOR_WARNING, e, this->GetBacktrace());
        } break;
      }
232

233
      // Install the whole framework directory.
234
      type = cmInstallType_DIRECTORY;
235
      literal_args += " USE_SOURCE_PERMISSIONS";
236

237
      std::string from1 = fromDirConfig + targetNames.Output;
238
      from1 = cmSystemTools::GetFilenamePath(from1);
239

240
      // Tweaks apply to the binary inside the bundle.
241
      std::string to1 = toDir + targetNames.Real;
242

243 244
      filesFrom.push_back(std::move(from1));
      filesTo.push_back(std::move(to1));
245
    } else if (this->Target->IsCFBundleOnApple()) {
246 247 248 249
      // Install the whole app bundle directory.
      type = cmInstallType_DIRECTORY;
      literal_args += " USE_SOURCE_PERMISSIONS";

250 251
      std::string targetNameBase =
        targetNames.Output.substr(0, targetNames.Output.find('/'));
252 253

      std::string from1 = fromDirConfig + targetNameBase;
254
      std::string to1 = toDir + targetNames.Output;
255

256 257
      filesFrom.push_back(std::move(from1));
      filesTo.push_back(std::move(to1));
258
    } else {
259
      bool haveNamelink = false;
260 261

      // Library link name.
262 263
      std::string fromName = fromDirConfig + targetNames.Output;
      std::string toName = toDir + targetNames.Output;
264 265

      // Library interface name.
266
      std::string fromSOName;
267
      std::string toSOName;
268
      if (targetNames.SharedObject != targetNames.Output) {
269
        haveNamelink = true;
270 271
        fromSOName = fromDirConfig + targetNames.SharedObject;
        toSOName = toDir + targetNames.SharedObject;
272
      }
273 274 275 276

      // Library implementation name.
      std::string fromRealName;
      std::string toRealName;
277 278
      if (targetNames.Real != targetNames.Output &&
          targetNames.Real != targetNames.SharedObject) {
279
        haveNamelink = true;
280 281
        fromRealName = fromDirConfig + targetNames.Real;
        toRealName = toDir + targetNames.Real;
282
      }
283 284

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

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

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

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

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

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

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

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

347 348 349 350 351 352 353 354 355 356 357 358 359 360
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(
361
  std::ostream& os, const std::string& config, Indent indent)
362 363 364 365 366 367 368 369 370 371 372
{
  // 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
373 374
  const char* no_dir_permissions = nullptr;
  const char* no_rename = nullptr;
375 376 377 378 379 380 381 382 383
  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);
384 385
  for (std::string& o : objects) {
    o = computeInstallObjectDir(this->Target, config) + "/" + o;
386 387 388
  }
}

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

397 398
std::string cmInstallTargetGenerator::GetInstallFilename(
  const std::string& config) const
399
{
400 401 402
  NameType nameType = this->ImportLibrary ? NameImplib : NameNormal;
  return cmInstallTargetGenerator::GetInstallFilename(this->Target, config,
                                                      nameType);
403 404
}

405 406 407
std::string cmInstallTargetGenerator::GetInstallFilename(
  cmGeneratorTarget const* target, const std::string& config,
  NameType nameType)
408 409 410
{
  std::string fname;
  // Compute the name of the library.
411
  if (target->GetType() == cmStateEnums::EXECUTABLE) {
412
    cmGeneratorTarget::Names targetNames = target->GetExecutableNames(config);
413
    if (nameType == NameImplib) {
414
      // Use the import library name.
415
      if (!target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, fname,
416
                                    "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
417
        fname = targetNames.ImportLibrary;
418
      }
419
    } else if (nameType == NameReal) {
420
      // Use the canonical name.
421
      fname = targetNames.Real;
422
    } else {
423
      // Use the canonical name.
424
      fname = targetNames.Output;
425
    }
426
  } else {
427
    cmGeneratorTarget::Names targetNames = target->GetLibraryNames(config);
428
    if (nameType == NameImplib) {
429
      // Use the import library name.
430
      if (!target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, fname,
431
                                    "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
432
        fname = targetNames.ImportLibrary;
433
      }
434
    } else if (nameType == NameSO) {
435
      // Use the soname.
436
      fname = targetNames.SharedObject;
437
    } else if (nameType == NameReal) {
438
      // Use the real name.
439
      fname = targetNames.Real;
440
    } else {
441
      // Use the canonical name.
442
      fname = targetNames.Output;
443
    }
444
  }
445 446 447 448

  return fname;
}

449 450
void cmInstallTargetGenerator::Compute(cmLocalGenerator* lg)
{
451
  // Lookup this target in the current directory.
452
  this->Target = lg->FindLocalNonAliasGeneratorTarget(this->TargetName);
453 454 455 456 457
  if (!this->Target) {
    // If no local target has been found, find it in the global scope.
    this->Target =
      lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName);
  }
458 459
}

460
void cmInstallTargetGenerator::AddTweak(std::ostream& os, Indent indent,
461 462 463
                                        const std::string& config,
                                        std::string const& file,
                                        TweakMethod tweak)
464
{
465
  std::ostringstream tw;
466 467
  (this->*tweak)(tw, indent.Next(), config, file);
  std::string tws = tw.str();
468
  if (!tws.empty()) {
469
    os << indent << "if(EXISTS \"" << file << "\" AND\n"
470 471
       << indent << "   NOT IS_SYMLINK \"" << file << "\")\n";
    os << tws;
472
    os << indent << "endif()\n";
473
  }
474 475
}

476
void cmInstallTargetGenerator::AddTweak(std::ostream& os, Indent indent,
477 478 479
                                        const std::string& config,
                                        std::vector<std::string> const& files,
                                        TweakMethod tweak)
480
{
481
  if (files.size() == 1) {
482 483
    // Tweak a single file.
    this->AddTweak(os, indent, config, this->GetDestDirPath(files[0]), tweak);
484
  } else {
485
    // Generate a foreach loop to tweak multiple files.
486
    std::ostringstream tw;
487 488
    this->AddTweak(tw, indent.Next(), config, "${file}", tweak);
    std::string tws = tw.str();
489
    if (!tws.empty()) {
490
      Indent indent2 = indent.Next().Next();
491
      os << indent << "foreach(file\n";
492 493
      for (std::string const& f : files) {
        os << indent2 << "\"" << this->GetDestDirPath(f) << "\"\n";
494
      }
495 496
      os << indent2 << ")\n";
      os << tws;
497
      os << indent << "endforeach()\n";
498
    }
499
  }
500 501 502 503 504 505 506
}

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}";
507
  if (file[0] != '/' && file[0] != '$') {
508
    toDestDirPath += "/";
509
  }
510 511 512 513 514
  toDestDirPath += file;
  return toDestDirPath;
}

void cmInstallTargetGenerator::PreReplacementTweaks(std::ostream& os,
515
                                                    Indent indent,
516
                                                    const std::string& config,
517 518 519 520 521 522
                                                    std::string const& file)
{
  this->AddRPathCheckRule(os, indent, config, file);
}

void cmInstallTargetGenerator::PostReplacementTweaks(std::ostream& os,
523
                                                     Indent indent,
524 525
                                                     const std::string& config,
                                                     std::string const& file)
526 527 528
{
  this->AddInstallNamePatchRule(os, indent, config, file);
  this->AddChrpathPatchRule(os, indent, config, file);
529
  this->AddUniversalInstallRule(os, indent, file);
530 531 532 533
  this->AddRanlibRule(os, indent, file);
  this->AddStripRule(os, indent, file);
}

534
void cmInstallTargetGenerator::AddInstallNamePatchRule(
535
  std::ostream& os, Indent indent, const std::string& config,
536
  std::string const& toDestDirPath)
537
{
538
  if (this->ImportLibrary ||
539 540 541
      !(this->Target->GetType() == cmStateEnums::SHARED_LIBRARY ||
        this->Target->GetType() == cmStateEnums::MODULE_LIBRARY ||
        this->Target->GetType() == cmStateEnums::EXECUTABLE)) {
542
    return;
543
  }
544

545
  // Fix the install_name settings in installed binaries.
546 547 548
  std::string installNameTool =
    this->Target->Target->GetMakefile()->GetSafeDefinition(
      "CMAKE_INSTALL_NAME_TOOL");
549

550
  if (installNameTool.empty()) {
551
    return;
552
  }
553

554 555
  // Build a map of build-tree install_name to install-tree install_name for
  // shared libraries linked to this target.
556
  std::map<std::string, std::string> install_name_remap;
557 558 559 560
  if (cmComputeLinkInformation* cli =
        this->Target->GetLinkInformation(config)) {
    std::set<cmGeneratorTarget const*> const& sharedLibs =
      cli->GetSharedLibrariesLinked();
561
    for (cmGeneratorTarget const* tgt : sharedLibs) {
562
      // The install_name of an imported target does not change.
563
      if (tgt->IsImported()) {
564
        continue;
565
      }
566

567 568 569
      // 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.
570 571
      std::string for_build = tgt->GetInstallNameDirForBuildTree(config);
      std::string for_install = tgt->GetInstallNameDirForInstallTree();
572
      if (for_build != for_install) {
573 574
        // The directory portions differ.  Append the filename to
        // create the mapping.
575
        std::string fname = this->GetInstallFilename(tgt, config, NameSO);
576

577 578
        // Map from the build-tree install_name.
        for_build += fname;
579

580
        // Map to the install-tree install_name.
581 582 583 584
        for_install += fname;

        // Store the mapping entry.
        install_name_remap[for_build] = for_install;
585 586
      }
    }
587
  }
588 589 590

  // Edit the install_name of the target itself if necessary.
  std::string new_id;
591
  if (this->Target->GetType() == cmStateEnums::SHARED_LIBRARY) {
592
    std::string for_build =
593
      this->Target->GetInstallNameDirForBuildTree(config);
594
    std::string for_install = this->Target->GetInstallNameDirForInstallTree();
595

596
    if (this->Target->IsFrameworkOnApple() && for_install.empty()) {
597 598 599 600
      // Frameworks seem to have an id corresponding to their own full
      // path.
      // ...
      // for_install = fullDestPath_without_DESTDIR_or_name;
601
    }
602 603 604

    // If the install name will change on installation set the new id
    // on the installed file.
605
    if (for_build != for_install) {
606 607
      // Prepare to refer to the install-tree install_name.
      new_id = for_install;
608
      new_id += this->GetInstallFilename(this->Target, config, NameSO);
609
    }
610
  }
611 612 613

  // Write a rule to run install_name_tool to set the install-tree
  // install_name value and references.
614
  if (!new_id.empty() || !install_name_remap.empty()) {
615
    os << indent << "execute_process(COMMAND \"" << installNameTool;
616
    os << "\"";
617
    if (!new_id.empty()) {
618
      os << "\n" << indent << "  -id \"" << new_id << "\"";
619
    }
620
    for (auto const& i : install_name_remap) {
621
      os << "\n"
622
         << indent << "  -change \"" << i.first << "\" \"" << i.second << "\"";
623 624 625
    }
    os << "\n" << indent << "  \"" << toDestDirPath << "\")\n";
  }
626
}
627

628
void cmInstallTargetGenerator::AddRPathCheckRule(
629
  std::ostream& os, Indent indent, const std::string& config,
630
  std::string const& toDestDirPath)
631 632
{
  // Skip the chrpath if the target does not need it.
633
  if (this->ImportLibrary || !this->Target->IsChrpathUsed(config)) {
634
    return;
635
  }
636
  // Skip if on Apple
637 638
  if (this->Target->Target->GetMakefile()->IsOn(
        "CMAKE_PLATFORM_HAS_INSTALLNAME")) {
639
    return;
640
  }
641

642 643
  // Get the link information for this target.
  // It can provide the RPATH.
644
  cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
645
  if (!cli) {
646
    return;
647
  }
648 649 650 651 652 653 654

  // 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.
655
  /* clang-format off */
656
  os << indent << "file(RPATH_CHECK\n"
657 658
     << indent << "     FILE \"" << toDestDirPath << "\"\n"
     << indent << "     RPATH \"" << newRpath << "\")\n";
659
  /* clang-format on */
660 661
}

662
void cmInstallTargetGenerator::AddChrpathPatchRule(
663
  std::ostream& os, Indent indent, const std::string& config,
664
  std::string const& toDestDirPath)
665
{
666
  // Skip the chrpath if the target does not need it.
667
  if (this->ImportLibrary || !this->Target->IsChrpathUsed(config)) {
668
    return;
669
  }
670

671 672
  // Get the link information for this target.
  // It can provide the RPATH.
673
  cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
674
  if (!cli) {
675
    return;
676
  }
677

678
  cmMakefile* mf = this->Target->Target->GetMakefile();
679

680
  if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
681 682
    // If using install_name_tool, set up the rules to modify the rpaths.
    std::string installNameTool =
683
      mf->GetSafeDefinition("CMAKE_INSTALL_NAME_TOOL");
684 685 686 687 688

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

689 690
    std::string darwin_major_version_s =
      mf->GetSafeDefinition("DARWIN_MAJOR_VERSION");
691

692
    std::istringstream ss(darwin_major_version_s);
693 694
    int darwin_major_version;
    ss >> darwin_major_version;
695 696
    if (!ss.fail() && darwin_major_version <= 9 &&
        (!oldRuntimeDirs.empty() || !newRuntimeDirs.empty())) {
697
      std::ostringstream msg;
698 699
      msg
        << "WARNING: Target \"" << this->Target->GetName()
700 701 702 703 704
        << "\" 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.";
705
      mf->IssueMessage(MessageType::WARNING, msg.str());
706
    } else {
707 708 709
      // Note: These paths are kept unique to avoid
      // install_name_tool corruption.
      std::set<std::string> runpaths;
710
      for (std::string const& i : oldRuntimeDirs) {
711
        std::string runpath =
712
          mf->GetGlobalGenerator()->ExpandCFGIntDir(i, config);
713

714
        if (runpaths.find(runpath) == runpaths.end()) {
715
          runpaths.insert(runpath);
716 717
          os << indent << "execute_process(COMMAND " << installNameTool
             << "\n";
718 719 720
          os << indent << "  -delete_rpath \"" << runpath << "\"\n";
          os << indent << "  \"" << toDestDirPath << "\")\n";
        }
721
      }
722

723
      runpaths.clear();
724
      for (std::string const& i : newRuntimeDirs) {
725
        std::string runpath =
726
          mf->GetGlobalGenerator()->ExpandCFGIntDir(i, config);
727

728 729 730
        if (runpaths.find(runpath) == runpaths.end()) {
          os << indent << "execute_process(COMMAND " << installNameTool
             << "\n";
731 732
          os << indent << "  -add_rpath \"" << runpath << "\"\n";
          os << indent << "  \"" << toDestDirPath << "\")\n";
733
        }
734
      }
735
    }
736
  } else {
737 738 739 740 741 742 743
    // 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
744
    if (oldRpath == newRpath) {
745
      return;
746
    }
747 748

    // Write a rule to run chrpath to set the install-tree RPATH
749 750 751 752
    os << indent << "file(RPATH_CHANGE\n"
       << indent << "     FILE \"" << toDestDirPath << "\"\n"
       << indent << "     OLD_RPATH \"" << oldRpath << "\"\n"
       << indent << "     NEW_RPATH \"" << newRpath << "\")\n";
753
  }
754 755
}

756
void cmInstallTargetGenerator::AddStripRule(std::ostream& os, Indent indent,
757
                                            const std::string& toDestDirPath)
758 759
{

760 761
  // don't strip static and import libraries, because it removes the only
  // symbol table they have so you can't link to them anymore
762
  if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY ||
763
      this->ImportLibrary) {
764
    return;
765
  }
766

767
  // Don't handle OSX Bundles.
768 769
  if (this->Target->Target->GetMakefile()->IsOn("APPLE") &&
      this->Target->GetPropertyAsBool("MACOSX_BUNDLE")) {
770
    return;
771
  }
772

773
  if (!this->Target->Target->GetMakefile()->IsSet("CMAKE_STRIP")) {
774
    return;
775
  }
776

777 778 779 780 781 782 783 784 785 786 787 788
  std::string stripArgs;

  // macOS 'strip' is picky, executables need '-u -r' and dylibs need '-x'.
  if (this->Target->Target->GetMakefile()->IsOn("APPLE")) {
    if (this->Target->GetType() == cmStateEnums::SHARED_LIBRARY ||
        this->Target->GetType() == cmStateEnums::MODULE_LIBRARY) {
      stripArgs = "-x ";
    } else if (this->Target->GetType() == cmStateEnums::EXECUTABLE) {
      stripArgs = "-u -r ";
    }
  }

789 790
  os << indent << "if(CMAKE_INSTALL_DO_STRIP)\n";
  os << indent << "  execute_process(COMMAND \""
791
     << this->Target->Target->GetMakefile()->GetDefinition("CMAKE_STRIP")
792
     << "\" " << stripArgs << "\"" << toDestDirPath << "\")\n";
793
  os << indent << "endif()\n";
794 795
}

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

  // Perform post-installation processing on the file depending
  // on its type.
806
  if (!this->Target->Target->GetMakefile()->IsOn("APPLE")) {
807
    return;
808
  }
809

810
  const std::string& ranlib =
811
    this->Target->Target->GetMakefile()->GetRequiredDefinition("CMAKE_RANLIB");
812
  if (ranlib.empty()) {
813
    return;
814
  }
815

816 817
  os << indent << "execute_process(COMMAND \"" << ranlib << "\" \""
     << toDestDirPath << "\")\n";
818
}
819

820
void cmInstallTargetGenerator::AddUniversalInstallRule(
821
  std::ostream& os, Indent indent, const std::string& toDestDirPath)
822 823 824
{
  cmMakefile const* mf = this->Target->Target->GetMakefile();

825
  if (!mf->PlatformIsAppleEmbedded() || !mf->IsOn("XCODE")) {
826
    return;
827
  }
828 829

  const char* xcodeVersion = mf->GetDefinition("XCODE_VERSION");
830 831
  if (!xcodeVersion ||
      cmSystemTools::VersionCompareGreater("6", xcodeVersion)) {
832
    return;
833
  }
834

835
  switch (this->Target->GetType()) {
836 837 838 839
    case cmStateEnums::EXECUTABLE:
    case cmStateEnums::STATIC_LIBRARY:
    case cmStateEnums::SHARED_LIBRARY:
    case cmStateEnums::MODULE_LIBRARY:
840 841 842 843
      break;

    default:
      return;
844
  }
845

846 847 848
  if (!this->Target->Target->GetPropertyAsBool("IOS_INSTALL_COMBINED")) {
    return;
  }
849 850 851

  os << indent << "include(CMakeIOSInstallCombined)\n";
  os << indent << "ios_install_combined("
852 853
     << "\"" << this->Target->Target->GetName() << "\" "
     << "\"" << toDestDirPath << "\")\n";
854
}