1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmInstallTargetGenerator.h"
13 #include "cmComputeLinkInformation.h"
14 #include "cmGeneratorExpression.h"
15 #include "cmGeneratorTarget.h"
16 #include "cmGlobalGenerator.h"
17 #include "cmInstallType.h"
18 #include "cmLocalGenerator.h"
19 #include "cmMakefile.h"
20 #include "cmMessageType.h"
21 #include "cmOutputConverter.h"
22 #include "cmPolicies.h"
23 #include "cmStateTypes.h"
24 #include "cmStringAlgorithms.h"
25 #include "cmSystemTools.h"
31 std::string computeInstallObjectDir(cmGeneratorTarget* gt,
32 std::string const& config)
34 std::string objectDir = "objects";
35 if (!config.empty()) {
40 objectDir += gt->GetName();
45 cmInstallTargetGenerator::cmInstallTargetGenerator(
46 std::string targetName, std::string const& dest, bool implib,
47 std::string file_permissions, std::vector<std::string> const& configurations,
48 std::string const& component, MessageLevel message, bool exclude_from_all,
49 bool optional, cmListFileBacktrace backtrace)
50 : cmInstallGenerator(dest, configurations, component, message,
51 exclude_from_all, false, std::move(backtrace))
52 , TargetName(std::move(targetName))
53 , FilePermissions(std::move(file_permissions))
54 , ImportLibrary(implib)
57 this->ActionsPerConfig = true;
58 this->NamelinkMode = NamelinkModeNone;
61 cmInstallTargetGenerator::~cmInstallTargetGenerator() = default;
63 void cmInstallTargetGenerator::GenerateScriptForConfig(
64 std::ostream& os, const std::string& config, Indent indent)
66 // Compute the list of files to install for this target.
67 Files files = this->GetFiles(config);
69 // Skip this rule if no files are to be installed for the target.
70 if (files.From.empty()) {
74 // Compute the effective install destination.
75 std::string dest = this->GetDestination(config);
76 if (!files.ToDir.empty()) {
77 dest = cmStrCat(dest, '/', files.ToDir);
80 // Tweak files located in the destination directory.
81 std::string toDir = cmStrCat(ConvertToAbsoluteDestination(dest), '/');
83 // Add pre-installation tweaks.
85 AddTweak(os, indent, config, toDir, files.To,
86 [this](std::ostream& o, Indent i, const std::string& c,
87 const std::string& f) {
88 this->PreReplacementTweaks(o, i, c, f);
92 // Write code to install the target file.
93 const char* no_dir_permissions = nullptr;
94 const char* no_rename = nullptr;
95 bool optional = this->Optional || this->ImportLibrary;
96 std::string literal_args;
97 if (!files.FromDir.empty()) {
98 literal_args += " FILES_FROM_DIR \"" + files.FromDir + "\"";
100 if (files.UseSourcePermissions) {
101 literal_args += " USE_SOURCE_PERMISSIONS";
103 this->AddInstallRule(os, dest, files.Type, files.From, optional,
104 this->FilePermissions.c_str(), no_dir_permissions,
105 no_rename, literal_args.c_str(), indent);
107 // Add post-installation tweaks.
108 if (!files.NoTweak) {
109 AddTweak(os, indent, config, toDir, files.To,
110 [this](std::ostream& o, Indent i, const std::string& c,
111 const std::string& f) {
112 this->PostReplacementTweaks(o, i, c, f);
117 cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles(
118 std::string const& config) const
122 cmStateEnums::TargetType targetType = this->Target->GetType();
123 switch (targetType) {
124 case cmStateEnums::EXECUTABLE:
125 files.Type = cmInstallType_EXECUTABLE;
127 case cmStateEnums::STATIC_LIBRARY:
128 files.Type = cmInstallType_STATIC_LIBRARY;
130 case cmStateEnums::SHARED_LIBRARY:
131 files.Type = cmInstallType_SHARED_LIBRARY;
133 case cmStateEnums::MODULE_LIBRARY:
134 files.Type = cmInstallType_MODULE_LIBRARY;
136 case cmStateEnums::INTERFACE_LIBRARY:
137 // Not reachable. We never create a cmInstallTargetGenerator for
138 // an INTERFACE_LIBRARY.
140 "INTERFACE_LIBRARY targets have no installable outputs.");
143 case cmStateEnums::OBJECT_LIBRARY: {
144 // Compute all the object files inside this target
145 std::vector<std::string> objects;
146 this->Target->GetTargetObjectNames(config, objects);
148 files.Type = cmInstallType_FILES;
149 files.NoTweak = true;
150 files.FromDir = this->Target->GetObjectDirectory(config);
151 files.ToDir = computeInstallObjectDir(this->Target, config);
152 for (std::string& obj : objects) {
153 files.From.emplace_back(obj);
154 files.To.emplace_back(std::move(obj));
159 case cmStateEnums::UTILITY:
160 case cmStateEnums::GLOBAL_TARGET:
161 case cmStateEnums::UNKNOWN_LIBRARY:
162 this->Target->GetLocalGenerator()->IssueMessage(
163 MessageType::INTERNAL_ERROR,
164 "cmInstallTargetGenerator created with non-installable target.");
168 // Compute the build tree directory from which to copy the target.
169 std::string fromDirConfig;
170 if (this->Target->NeedRelinkBeforeInstall(config)) {
172 cmStrCat(this->Target->GetLocalGenerator()->GetCurrentBinaryDirectory(),
173 "/CMakeFiles/CMakeRelink.dir/");
175 cmStateEnums::ArtifactType artifact = this->ImportLibrary
176 ? cmStateEnums::ImportLibraryArtifact
177 : cmStateEnums::RuntimeBinaryArtifact;
179 cmStrCat(this->Target->GetDirectory(config, artifact), '/');
182 if (targetType == cmStateEnums::EXECUTABLE) {
183 // There is a bug in cmInstallCommand if this fails.
184 assert(this->NamelinkMode == NamelinkModeNone);
186 cmGeneratorTarget::Names targetNames =
187 this->Target->GetExecutableNames(config);
188 if (this->ImportLibrary) {
189 std::string from1 = fromDirConfig + targetNames.ImportLibrary;
190 std::string to1 = targetNames.ImportLibrary;
191 files.From.emplace_back(std::move(from1));
192 files.To.emplace_back(std::move(to1));
193 std::string targetNameImportLib;
194 if (this->Target->GetImplibGNUtoMS(config, targetNames.ImportLibrary,
195 targetNameImportLib)) {
196 files.From.emplace_back(fromDirConfig + targetNameImportLib);
197 files.To.emplace_back(targetNameImportLib);
200 // An import library looks like a static library.
201 files.Type = cmInstallType_STATIC_LIBRARY;
203 std::string from1 = fromDirConfig + targetNames.Output;
204 std::string to1 = targetNames.Output;
206 // Handle OSX Bundles.
207 if (this->Target->IsAppBundleOnApple()) {
208 cmMakefile const* mf = this->Target->Target->GetMakefile();
210 // Get App Bundle Extension
212 if (cmValue p = this->Target->GetProperty("BUNDLE_EXTENSION")) {
218 // Install the whole app bundle directory.
219 files.Type = cmInstallType_DIRECTORY;
220 files.UseSourcePermissions = true;
224 // Tweaks apply to the binary inside the bundle.
228 if (!mf->PlatformIsAppleEmbedded()) {
229 to1 += "Contents/MacOS/";
231 to1 += targetNames.Output;
233 // Tweaks apply to the real file, so list it first.
234 if (targetNames.Real != targetNames.Output) {
235 std::string from2 = fromDirConfig + targetNames.Real;
236 std::string to2 = targetNames.Real;
237 files.From.emplace_back(std::move(from2));
238 files.To.emplace_back(std::move(to2));
242 files.From.emplace_back(std::move(from1));
243 files.To.emplace_back(std::move(to1));
246 cmGeneratorTarget::Names targetNames =
247 this->Target->GetLibraryNames(config);
248 if (this->ImportLibrary) {
249 // There is a bug in cmInstallCommand if this fails.
250 assert(this->NamelinkMode == NamelinkModeNone);
252 std::string from1 = fromDirConfig + targetNames.ImportLibrary;
253 std::string to1 = targetNames.ImportLibrary;
254 files.From.emplace_back(std::move(from1));
255 files.To.emplace_back(std::move(to1));
256 std::string targetNameImportLib;
257 if (this->Target->GetImplibGNUtoMS(config, targetNames.ImportLibrary,
258 targetNameImportLib)) {
259 files.From.emplace_back(fromDirConfig + targetNameImportLib);
260 files.To.emplace_back(targetNameImportLib);
263 // An import library looks like a static library.
264 files.Type = cmInstallType_STATIC_LIBRARY;
265 } else if (this->Target->IsFrameworkOnApple()) {
266 // FIXME: In principle we should be able to
267 // assert(this->NamelinkMode == NamelinkModeNone);
268 // but since the current install() command implementation checks
269 // the FRAMEWORK property immediately instead of delaying until
270 // generate time, it is possible for project code to set the
271 // property after calling install(). In such a case, the install()
272 // command will use the LIBRARY code path and create two install
273 // generators, one for the namelink component (NamelinkModeOnly)
274 // and one for the primary artifact component (NamelinkModeSkip).
275 // Historically this was not diagnosed and resulted in silent
276 // installation of a framework to the LIBRARY destination.
277 // Retain that behavior and warn about the case.
278 switch (this->NamelinkMode) {
279 case NamelinkModeNone:
282 case NamelinkModeOnly:
283 // Assume the NamelinkModeSkip instance will warn and install.
285 case NamelinkModeSkip: {
286 std::string e = "Target '" + this->Target->GetName() +
287 "' was changed to a FRAMEWORK sometime after install(). "
288 "This may result in the wrong install DESTINATION. "
289 "Set the FRAMEWORK property earlier.";
290 this->Target->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
291 MessageType::AUTHOR_WARNING, e, this->GetBacktrace());
295 // Install the whole framework directory.
296 files.Type = cmInstallType_DIRECTORY;
297 files.UseSourcePermissions = true;
299 std::string from1 = fromDirConfig + targetNames.Output;
300 from1 = cmSystemTools::GetFilenamePath(from1);
302 // Tweaks apply to the binary inside the bundle.
303 std::string to1 = targetNames.Real;
305 files.From.emplace_back(std::move(from1));
306 files.To.emplace_back(std::move(to1));
307 } else if (this->Target->IsCFBundleOnApple()) {
308 // Install the whole app bundle directory.
309 files.Type = cmInstallType_DIRECTORY;
310 files.UseSourcePermissions = true;
312 std::string targetNameBase =
313 targetNames.Output.substr(0, targetNames.Output.find('/'));
315 std::string from1 = fromDirConfig + targetNameBase;
316 std::string to1 = targetNames.Output;
318 files.From.emplace_back(std::move(from1));
319 files.To.emplace_back(std::move(to1));
321 bool haveNamelink = false;
323 // Library link name.
324 std::string fromName = fromDirConfig + targetNames.Output;
325 std::string toName = targetNames.Output;
327 // Library interface name.
328 std::string fromSOName;
329 std::string toSOName;
330 if (targetNames.SharedObject != targetNames.Output) {
332 fromSOName = fromDirConfig + targetNames.SharedObject;
333 toSOName = targetNames.SharedObject;
336 // Library implementation name.
337 std::string fromRealName;
338 std::string toRealName;
339 if (targetNames.Real != targetNames.Output &&
340 targetNames.Real != targetNames.SharedObject) {
342 fromRealName = fromDirConfig + targetNames.Real;
343 toRealName = targetNames.Real;
346 // Add the names based on the current namelink mode.
348 files.NamelinkMode = this->NamelinkMode;
349 // With a namelink we need to check the mode.
350 if (this->NamelinkMode == NamelinkModeOnly) {
351 // Install the namelink only.
352 files.From.emplace_back(fromName);
353 files.To.emplace_back(toName);
355 // Install the real file if it has its own name.
356 if (!fromRealName.empty()) {
357 files.From.emplace_back(fromRealName);
358 files.To.emplace_back(toRealName);
361 // Install the soname link if it has its own name.
362 if (!fromSOName.empty()) {
363 files.From.emplace_back(fromSOName);
364 files.To.emplace_back(toSOName);
367 // Install the namelink if it is not to be skipped.
368 if (this->NamelinkMode != NamelinkModeSkip) {
369 files.From.emplace_back(fromName);
370 files.To.emplace_back(toName);
374 // Without a namelink there will be only one file. Install it
375 // if this is not a namelink-only rule.
376 if (this->NamelinkMode != NamelinkModeOnly) {
377 files.From.emplace_back(fromName);
378 files.To.emplace_back(toName);
384 // If this fails the above code is buggy.
385 assert(files.From.size() == files.To.size());
390 void cmInstallTargetGenerator::GetInstallObjectNames(
391 std::string const& config, std::vector<std::string>& objects) const
393 this->Target->GetTargetObjectNames(config, objects);
394 for (std::string& o : objects) {
395 o = cmStrCat(computeInstallObjectDir(this->Target, config), "/", o);
399 std::string cmInstallTargetGenerator::GetDestination(
400 std::string const& config) const
402 return cmGeneratorExpression::Evaluate(
403 this->Destination, this->Target->GetLocalGenerator(), config);
406 std::string cmInstallTargetGenerator::GetInstallFilename(
407 const std::string& config) const
409 NameType nameType = this->ImportLibrary ? NameImplib : NameNormal;
410 return cmInstallTargetGenerator::GetInstallFilename(this->Target, config,
414 std::string cmInstallTargetGenerator::GetInstallFilename(
415 cmGeneratorTarget const* target, const std::string& config,
419 // Compute the name of the library.
420 if (target->GetType() == cmStateEnums::EXECUTABLE) {
421 cmGeneratorTarget::Names targetNames = target->GetExecutableNames(config);
422 if (nameType == NameImplib) {
423 // Use the import library name.
424 if (!target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, fname,
425 "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
426 fname = targetNames.ImportLibrary;
428 } else if (nameType == NameReal) {
429 // Use the canonical name.
430 fname = targetNames.Real;
432 // Use the canonical name.
433 fname = targetNames.Output;
436 cmGeneratorTarget::Names targetNames = target->GetLibraryNames(config);
437 if (nameType == NameImplib) {
438 // Use the import library name.
439 if (!target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, fname,
440 "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
441 fname = targetNames.ImportLibrary;
443 } else if (nameType == NameSO) {
445 fname = targetNames.SharedObject;
446 } else if (nameType == NameReal) {
447 // Use the real name.
448 fname = targetNames.Real;
450 // Use the canonical name.
451 fname = targetNames.Output;
458 bool cmInstallTargetGenerator::Compute(cmLocalGenerator* lg)
460 // Lookup this target in the current directory.
461 this->Target = lg->FindLocalNonAliasGeneratorTarget(this->TargetName);
463 // If no local target has been found, find it in the global scope.
465 lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName);
471 void cmInstallTargetGenerator::PreReplacementTweaks(std::ostream& os,
473 const std::string& config,
474 std::string const& file)
476 this->AddRPathCheckRule(os, indent, config, file);
479 void cmInstallTargetGenerator::PostReplacementTweaks(std::ostream& os,
481 const std::string& config,
482 std::string const& file)
484 this->AddInstallNamePatchRule(os, indent, config, file);
485 this->AddChrpathPatchRule(os, indent, config, file);
486 this->AddUniversalInstallRule(os, indent, file);
487 this->AddRanlibRule(os, indent, file);
488 this->AddStripRule(os, indent, file);
491 void cmInstallTargetGenerator::AddInstallNamePatchRule(
492 std::ostream& os, Indent indent, const std::string& config,
493 std::string const& toDestDirPath)
495 if (this->ImportLibrary ||
496 !(this->Target->GetType() == cmStateEnums::SHARED_LIBRARY ||
497 this->Target->GetType() == cmStateEnums::MODULE_LIBRARY ||
498 this->Target->GetType() == cmStateEnums::EXECUTABLE)) {
502 // Fix the install_name settings in installed binaries.
503 std::string installNameTool =
504 this->Target->Target->GetMakefile()->GetSafeDefinition(
505 "CMAKE_INSTALL_NAME_TOOL");
507 if (installNameTool.empty()) {
511 // Build a map of build-tree install_name to install-tree install_name for
512 // shared libraries linked to this target.
513 std::map<std::string, std::string> install_name_remap;
514 if (cmComputeLinkInformation* cli =
515 this->Target->GetLinkInformation(config)) {
516 std::set<cmGeneratorTarget const*> const& sharedLibs =
517 cli->GetSharedLibrariesLinked();
518 for (cmGeneratorTarget const* tgt : sharedLibs) {
519 // The install_name of an imported target does not change.
520 if (tgt->IsImported()) {
524 // If the build tree and install tree use different path
525 // components of the install_name field then we need to create a
526 // mapping to be applied after installation.
527 std::string for_build = tgt->GetInstallNameDirForBuildTree(config);
528 std::string for_install = tgt->GetInstallNameDirForInstallTree(
529 config, "${CMAKE_INSTALL_PREFIX}");
530 if (for_build != for_install) {
531 // The directory portions differ. Append the filename to
532 // create the mapping.
533 std::string fname = this->GetInstallFilename(tgt, config, NameSO);
535 // Map from the build-tree install_name.
538 // Map to the install-tree install_name.
539 for_install += fname;
541 // Store the mapping entry.
542 install_name_remap[for_build] = for_install;
547 // Edit the install_name of the target itself if necessary.
549 if (this->Target->GetType() == cmStateEnums::SHARED_LIBRARY) {
550 std::string for_build =
551 this->Target->GetInstallNameDirForBuildTree(config);
552 std::string for_install = this->Target->GetInstallNameDirForInstallTree(
553 config, "${CMAKE_INSTALL_PREFIX}");
555 if (this->Target->IsFrameworkOnApple() && for_install.empty()) {
556 // Frameworks seem to have an id corresponding to their own full
559 // for_install = fullDestPath_without_DESTDIR_or_name;
562 // If the install name will change on installation set the new id
563 // on the installed file.
564 if (for_build != for_install) {
565 // Prepare to refer to the install-tree install_name.
567 for_install, this->GetInstallFilename(this->Target, config, NameSO));
571 // Write a rule to run install_name_tool to set the install-tree
572 // install_name value and references.
573 if (!new_id.empty() || !install_name_remap.empty()) {
574 os << indent << "execute_process(COMMAND \"" << installNameTool;
576 if (!new_id.empty()) {
577 os << "\n" << indent << " -id \"" << new_id << "\"";
579 for (auto const& i : install_name_remap) {
581 << indent << " -change \"" << i.first << "\" \"" << i.second << "\"";
583 os << "\n" << indent << " \"" << toDestDirPath << "\")\n";
587 void cmInstallTargetGenerator::AddRPathCheckRule(
588 std::ostream& os, Indent indent, const std::string& config,
589 std::string const& toDestDirPath)
591 // Skip the chrpath if the target does not need it.
592 if (this->ImportLibrary || !this->Target->IsChrpathUsed(config)) {
596 if (this->Target->Target->GetMakefile()->IsOn(
597 "CMAKE_PLATFORM_HAS_INSTALLNAME")) {
601 // Get the link information for this target.
602 // It can provide the RPATH.
603 cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
608 // Write a rule to remove the installed file if its rpath is not the
609 // new rpath. This is needed for existing build/install trees when
610 // the installed rpath changes but the file is not rebuilt.
611 os << indent << "file(RPATH_CHECK\n"
612 << indent << " FILE \"" << toDestDirPath << "\"\n";
614 // CMP0095: ``RPATH`` entries are properly escaped in the intermediary
615 // CMake install script.
616 switch (this->Target->GetPolicyStatusCMP0095()) {
617 case cmPolicies::WARN:
618 // No author warning needed here, we warn later in
619 // cmInstallTargetGenerator::AddChrpathPatchRule().
621 case cmPolicies::OLD: {
622 // Get the install RPATH from the link information.
623 std::string newRpath = cli->GetChrpathString();
624 os << indent << " RPATH \"" << newRpath << "\")\n";
628 // Get the install RPATH from the link information and
629 // escape any CMake syntax in the install RPATH.
630 std::string escapedNewRpath =
631 cmOutputConverter::EscapeForCMake(cli->GetChrpathString());
632 os << indent << " RPATH " << escapedNewRpath << ")\n";
638 void cmInstallTargetGenerator::AddChrpathPatchRule(
639 std::ostream& os, Indent indent, const std::string& config,
640 std::string const& toDestDirPath)
642 // Skip the chrpath if the target does not need it.
643 if (this->ImportLibrary || !this->Target->IsChrpathUsed(config)) {
647 // Get the link information for this target.
648 // It can provide the RPATH.
649 cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
654 cmMakefile* mf = this->Target->Target->GetMakefile();
656 if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
657 // If using install_name_tool, set up the rules to modify the rpaths.
658 std::string installNameTool =
659 mf->GetSafeDefinition("CMAKE_INSTALL_NAME_TOOL");
661 std::vector<std::string> oldRuntimeDirs;
662 std::vector<std::string> newRuntimeDirs;
663 cli->GetRPath(oldRuntimeDirs, false);
664 cli->GetRPath(newRuntimeDirs, true);
666 std::string darwin_major_version_s =
667 mf->GetSafeDefinition("DARWIN_MAJOR_VERSION");
669 std::istringstream ss(darwin_major_version_s);
670 int darwin_major_version;
671 ss >> darwin_major_version;
672 if (!ss.fail() && darwin_major_version <= 9 &&
673 (!oldRuntimeDirs.empty() || !newRuntimeDirs.empty())) {
674 std::ostringstream msg;
676 << "WARNING: Target \"" << this->Target->GetName()
677 << "\" has runtime paths which cannot be changed during install. "
678 << "To change runtime paths, OS X version 10.6 or newer is required. "
679 << "Therefore, runtime paths will not be changed when installing. "
680 << "CMAKE_BUILD_WITH_INSTALL_RPATH may be used to work around"
682 mf->IssueMessage(MessageType::WARNING, msg.str());
684 // To be consistent with older versions, runpath changes must be ordered,
685 // deleted first, then added, *and* the same path must only appear once.
686 std::map<std::string, std::string> runpath_change;
687 std::vector<std::string> ordered;
688 for (std::string const& dir : oldRuntimeDirs) {
689 // Normalize path and add to map of changes to make
690 auto iter_inserted = runpath_change.insert(
691 { mf->GetGlobalGenerator()->ExpandCFGIntDir(dir, config),
693 if (iter_inserted.second) {
694 // Add path to ordered list of changes
695 ordered.push_back(iter_inserted.first->first);
699 for (std::string const& dir : newRuntimeDirs) {
700 // Normalize path and add to map of changes to make
701 auto iter_inserted = runpath_change.insert(
702 { mf->GetGlobalGenerator()->ExpandCFGIntDir(dir, config), "add" });
703 if (iter_inserted.second) {
704 // Add path to ordered list of changes
705 ordered.push_back(iter_inserted.first->first);
706 } else if (iter_inserted.first->second != "add") {
707 // Rpath was requested to be deleted and then later re-added. Drop it
708 // from the list by marking as an empty value.
709 iter_inserted.first->second.clear();
713 // Remove rpaths that are unchanged (value was set to empty)
715 std::remove_if(ordered.begin(), ordered.end(),
716 [&runpath_change](const std::string& runpath) {
717 return runpath_change.find(runpath)->second.empty();
721 if (!ordered.empty()) {
722 os << indent << "execute_process(COMMAND " << installNameTool << "\n";
723 for (std::string const& runpath : ordered) {
724 // Either 'add_rpath' or 'delete_rpath' since we've removed empty
726 os << indent << " -" << runpath_change.find(runpath)->second
727 << "_rpath \"" << runpath << "\"\n";
729 os << indent << " \"" << toDestDirPath << "\")\n";
733 // Construct the original rpath string to be replaced.
734 std::string oldRpath = cli->GetRPathString(false);
736 // Get the install RPATH from the link information.
737 std::string newRpath = cli->GetChrpathString();
739 // Skip the rule if the paths are identical
740 if (oldRpath == newRpath) {
744 // Escape any CMake syntax in the RPATHs.
745 std::string escapedOldRpath = cmOutputConverter::EscapeForCMake(oldRpath);
746 std::string escapedNewRpath = cmOutputConverter::EscapeForCMake(newRpath);
748 // Write a rule to run chrpath to set the install-tree RPATH
749 os << indent << "file(RPATH_CHANGE\n"
750 << indent << " FILE \"" << toDestDirPath << "\"\n"
751 << indent << " OLD_RPATH " << escapedOldRpath << "\n";
753 // CMP0095: ``RPATH`` entries are properly escaped in the intermediary
754 // CMake install script.
755 switch (this->Target->GetPolicyStatusCMP0095()) {
756 case cmPolicies::WARN:
757 this->IssueCMP0095Warning(newRpath);
759 case cmPolicies::OLD:
760 os << indent << " NEW_RPATH \"" << newRpath << "\"";
763 os << indent << " NEW_RPATH " << escapedNewRpath;
767 if (this->Target->GetPropertyAsBool("INSTALL_REMOVE_ENVIRONMENT_RPATH")) {
768 os << "\n" << indent << " INSTALL_REMOVE_ENVIRONMENT_RPATH)\n";
775 void cmInstallTargetGenerator::AddStripRule(std::ostream& os, Indent indent,
776 const std::string& toDestDirPath)
779 // don't strip static and import libraries, because it removes the only
780 // symbol table they have so you can't link to them anymore
781 if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY ||
782 this->ImportLibrary) {
786 // Don't handle OSX Bundles.
787 if (this->Target->Target->GetMakefile()->IsOn("APPLE") &&
788 this->Target->GetPropertyAsBool("MACOSX_BUNDLE")) {
792 if (!this->Target->Target->GetMakefile()->IsSet("CMAKE_STRIP")) {
796 std::string stripArgs;
798 // macOS 'strip' is picky, executables need '-u -r' and dylibs need '-x'.
799 if (this->Target->Target->GetMakefile()->IsOn("APPLE")) {
800 if (this->Target->GetType() == cmStateEnums::SHARED_LIBRARY ||
801 this->Target->GetType() == cmStateEnums::MODULE_LIBRARY) {
803 } else if (this->Target->GetType() == cmStateEnums::EXECUTABLE) {
804 stripArgs = "-u -r ";
808 os << indent << "if(CMAKE_INSTALL_DO_STRIP)\n";
809 os << indent << " execute_process(COMMAND \""
810 << this->Target->Target->GetMakefile()->GetSafeDefinition("CMAKE_STRIP")
811 << "\" " << stripArgs << "\"" << toDestDirPath << "\")\n";
812 os << indent << "endif()\n";
815 void cmInstallTargetGenerator::AddRanlibRule(std::ostream& os, Indent indent,
816 const std::string& toDestDirPath)
818 // Static libraries need ranlib on this platform.
819 if (this->Target->GetType() != cmStateEnums::STATIC_LIBRARY) {
823 // Perform post-installation processing on the file depending
825 if (!this->Target->Target->GetMakefile()->IsOn("APPLE")) {
829 const std::string& ranlib =
830 this->Target->Target->GetMakefile()->GetRequiredDefinition("CMAKE_RANLIB");
831 if (ranlib.empty()) {
835 os << indent << "execute_process(COMMAND \"" << ranlib << "\" \""
836 << toDestDirPath << "\")\n";
839 void cmInstallTargetGenerator::AddUniversalInstallRule(
840 std::ostream& os, Indent indent, const std::string& toDestDirPath)
842 cmMakefile const* mf = this->Target->Target->GetMakefile();
844 if (!mf->PlatformIsAppleEmbedded() || !mf->IsOn("XCODE")) {
848 cmValue xcodeVersion = mf->GetDefinition("XCODE_VERSION");
850 cmSystemTools::VersionCompareGreater("6", *xcodeVersion)) {
854 switch (this->Target->GetType()) {
855 case cmStateEnums::EXECUTABLE:
856 case cmStateEnums::STATIC_LIBRARY:
857 case cmStateEnums::SHARED_LIBRARY:
858 case cmStateEnums::MODULE_LIBRARY:
865 if (!this->Target->Target->GetPropertyAsBool("IOS_INSTALL_COMBINED")) {
869 os << indent << "include(CMakeIOSInstallCombined)\n";
870 os << indent << "ios_install_combined("
871 << "\"" << this->Target->Target->GetName() << "\" "
872 << "\"" << toDestDirPath << "\")\n";
875 void cmInstallTargetGenerator::IssueCMP0095Warning(
876 const std::string& unescapedRpath)
878 // Reduce warning noise to cases where used RPATHs may actually be affected
879 // by CMP0095. This filter is meant to skip warnings in cases when
880 // non-curly-braces syntax (e.g. $ORIGIN) or no keyword is used which has
881 // worked already before CMP0095. We intend to issue a warning in all cases
882 // with curly-braces syntax, even if the workaround of double-escaping is in
883 // place, since we deprecate the need for it with CMP0095.
884 const bool potentially_affected(unescapedRpath.find("${") !=
887 if (potentially_affected) {
888 std::ostringstream w;
889 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0095) << "\n";
890 w << "RPATH entries for target '" << this->Target->GetName() << "' "
891 << "will not be escaped in the intermediary "
892 << "cmake_install.cmake script.";
893 this->Target->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
894 MessageType::AUTHOR_WARNING, w.str(), this->GetBacktrace());