resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmInstallTargetGenerator.cxx
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"
4
5 #include <algorithm>
6 #include <cassert>
7 #include <map>
8 #include <set>
9 #include <sstream>
10 #include <utility>
11 #include <vector>
12
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"
26 #include "cmTarget.h"
27 #include "cmValue.h"
28 #include "cmake.h"
29
30 namespace {
31 std::string computeInstallObjectDir(cmGeneratorTarget* gt,
32                                     std::string const& config)
33 {
34   std::string objectDir = "objects";
35   if (!config.empty()) {
36     objectDir += "-";
37     objectDir += config;
38   }
39   objectDir += "/";
40   objectDir += gt->GetName();
41   return objectDir;
42 }
43 }
44
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)
55   , Optional(optional)
56 {
57   this->ActionsPerConfig = true;
58   this->NamelinkMode = NamelinkModeNone;
59 }
60
61 cmInstallTargetGenerator::~cmInstallTargetGenerator() = default;
62
63 void cmInstallTargetGenerator::GenerateScriptForConfig(
64   std::ostream& os, const std::string& config, Indent indent)
65 {
66   // Compute the list of files to install for this target.
67   Files files = this->GetFiles(config);
68
69   // Skip this rule if no files are to be installed for the target.
70   if (files.From.empty()) {
71     return;
72   }
73
74   // Compute the effective install destination.
75   std::string dest = this->GetDestination(config);
76   if (!files.ToDir.empty()) {
77     dest = cmStrCat(dest, '/', files.ToDir);
78   }
79
80   // Tweak files located in the destination directory.
81   std::string toDir = cmStrCat(ConvertToAbsoluteDestination(dest), '/');
82
83   // Add pre-installation tweaks.
84   if (!files.NoTweak) {
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);
89              });
90   }
91
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 + "\"";
99   }
100   if (files.UseSourcePermissions) {
101     literal_args += " USE_SOURCE_PERMISSIONS";
102   }
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);
106
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);
113              });
114   }
115 }
116
117 cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles(
118   std::string const& config) const
119 {
120   Files files;
121
122   cmStateEnums::TargetType targetType = this->Target->GetType();
123   switch (targetType) {
124     case cmStateEnums::EXECUTABLE:
125       files.Type = cmInstallType_EXECUTABLE;
126       break;
127     case cmStateEnums::STATIC_LIBRARY:
128       files.Type = cmInstallType_STATIC_LIBRARY;
129       break;
130     case cmStateEnums::SHARED_LIBRARY:
131       files.Type = cmInstallType_SHARED_LIBRARY;
132       break;
133     case cmStateEnums::MODULE_LIBRARY:
134       files.Type = cmInstallType_MODULE_LIBRARY;
135       break;
136     case cmStateEnums::INTERFACE_LIBRARY:
137       // Not reachable. We never create a cmInstallTargetGenerator for
138       // an INTERFACE_LIBRARY.
139       assert(false &&
140              "INTERFACE_LIBRARY targets have no installable outputs.");
141       break;
142
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);
147
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));
155       }
156       return files;
157     }
158
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.");
165       return files;
166   }
167
168   // Compute the build tree directory from which to copy the target.
169   std::string fromDirConfig;
170   if (this->Target->NeedRelinkBeforeInstall(config)) {
171     fromDirConfig =
172       cmStrCat(this->Target->GetLocalGenerator()->GetCurrentBinaryDirectory(),
173                "/CMakeFiles/CMakeRelink.dir/");
174   } else {
175     cmStateEnums::ArtifactType artifact = this->ImportLibrary
176       ? cmStateEnums::ImportLibraryArtifact
177       : cmStateEnums::RuntimeBinaryArtifact;
178     fromDirConfig =
179       cmStrCat(this->Target->GetDirectory(config, artifact), '/');
180   }
181
182   if (targetType == cmStateEnums::EXECUTABLE) {
183     // There is a bug in cmInstallCommand if this fails.
184     assert(this->NamelinkMode == NamelinkModeNone);
185
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);
198       }
199
200       // An import library looks like a static library.
201       files.Type = cmInstallType_STATIC_LIBRARY;
202     } else {
203       std::string from1 = fromDirConfig + targetNames.Output;
204       std::string to1 = targetNames.Output;
205
206       // Handle OSX Bundles.
207       if (this->Target->IsAppBundleOnApple()) {
208         cmMakefile const* mf = this->Target->Target->GetMakefile();
209
210         // Get App Bundle Extension
211         std::string ext;
212         if (cmValue p = this->Target->GetProperty("BUNDLE_EXTENSION")) {
213           ext = *p;
214         } else {
215           ext = "app";
216         }
217
218         // Install the whole app bundle directory.
219         files.Type = cmInstallType_DIRECTORY;
220         files.UseSourcePermissions = true;
221         from1 += ".";
222         from1 += ext;
223
224         // Tweaks apply to the binary inside the bundle.
225         to1 += ".";
226         to1 += ext;
227         to1 += "/";
228         if (!mf->PlatformIsAppleEmbedded()) {
229           to1 += "Contents/MacOS/";
230         }
231         to1 += targetNames.Output;
232       } else {
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));
239         }
240       }
241
242       files.From.emplace_back(std::move(from1));
243       files.To.emplace_back(std::move(to1));
244     }
245   } else {
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);
251
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);
261       }
262
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:
280           // Normal case.
281           break;
282         case NamelinkModeOnly:
283           // Assume the NamelinkModeSkip instance will warn and install.
284           return files;
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());
292         } break;
293       }
294
295       // Install the whole framework directory.
296       files.Type = cmInstallType_DIRECTORY;
297       files.UseSourcePermissions = true;
298
299       std::string from1 = fromDirConfig + targetNames.Output;
300       from1 = cmSystemTools::GetFilenamePath(from1);
301
302       // Tweaks apply to the binary inside the bundle.
303       std::string to1 = targetNames.Real;
304
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;
311
312       std::string targetNameBase =
313         targetNames.Output.substr(0, targetNames.Output.find('/'));
314
315       std::string from1 = fromDirConfig + targetNameBase;
316       std::string to1 = targetNames.Output;
317
318       files.From.emplace_back(std::move(from1));
319       files.To.emplace_back(std::move(to1));
320     } else {
321       bool haveNamelink = false;
322
323       // Library link name.
324       std::string fromName = fromDirConfig + targetNames.Output;
325       std::string toName = targetNames.Output;
326
327       // Library interface name.
328       std::string fromSOName;
329       std::string toSOName;
330       if (targetNames.SharedObject != targetNames.Output) {
331         haveNamelink = true;
332         fromSOName = fromDirConfig + targetNames.SharedObject;
333         toSOName = targetNames.SharedObject;
334       }
335
336       // Library implementation name.
337       std::string fromRealName;
338       std::string toRealName;
339       if (targetNames.Real != targetNames.Output &&
340           targetNames.Real != targetNames.SharedObject) {
341         haveNamelink = true;
342         fromRealName = fromDirConfig + targetNames.Real;
343         toRealName = targetNames.Real;
344       }
345
346       // Add the names based on the current namelink mode.
347       if (haveNamelink) {
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);
354         } else {
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);
359           }
360
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);
365           }
366
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);
371           }
372         }
373       } else {
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);
379         }
380       }
381     }
382   }
383
384   // If this fails the above code is buggy.
385   assert(files.From.size() == files.To.size());
386
387   return files;
388 }
389
390 void cmInstallTargetGenerator::GetInstallObjectNames(
391   std::string const& config, std::vector<std::string>& objects) const
392 {
393   this->Target->GetTargetObjectNames(config, objects);
394   for (std::string& o : objects) {
395     o = cmStrCat(computeInstallObjectDir(this->Target, config), "/", o);
396   }
397 }
398
399 std::string cmInstallTargetGenerator::GetDestination(
400   std::string const& config) const
401 {
402   return cmGeneratorExpression::Evaluate(
403     this->Destination, this->Target->GetLocalGenerator(), config);
404 }
405
406 std::string cmInstallTargetGenerator::GetInstallFilename(
407   const std::string& config) const
408 {
409   NameType nameType = this->ImportLibrary ? NameImplib : NameNormal;
410   return cmInstallTargetGenerator::GetInstallFilename(this->Target, config,
411                                                       nameType);
412 }
413
414 std::string cmInstallTargetGenerator::GetInstallFilename(
415   cmGeneratorTarget const* target, const std::string& config,
416   NameType nameType)
417 {
418   std::string fname;
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;
427       }
428     } else if (nameType == NameReal) {
429       // Use the canonical name.
430       fname = targetNames.Real;
431     } else {
432       // Use the canonical name.
433       fname = targetNames.Output;
434     }
435   } else {
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;
442       }
443     } else if (nameType == NameSO) {
444       // Use the soname.
445       fname = targetNames.SharedObject;
446     } else if (nameType == NameReal) {
447       // Use the real name.
448       fname = targetNames.Real;
449     } else {
450       // Use the canonical name.
451       fname = targetNames.Output;
452     }
453   }
454
455   return fname;
456 }
457
458 bool cmInstallTargetGenerator::Compute(cmLocalGenerator* lg)
459 {
460   // Lookup this target in the current directory.
461   this->Target = lg->FindLocalNonAliasGeneratorTarget(this->TargetName);
462   if (!this->Target) {
463     // If no local target has been found, find it in the global scope.
464     this->Target =
465       lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName);
466   }
467
468   return true;
469 }
470
471 void cmInstallTargetGenerator::PreReplacementTweaks(std::ostream& os,
472                                                     Indent indent,
473                                                     const std::string& config,
474                                                     std::string const& file)
475 {
476   this->AddRPathCheckRule(os, indent, config, file);
477 }
478
479 void cmInstallTargetGenerator::PostReplacementTweaks(std::ostream& os,
480                                                      Indent indent,
481                                                      const std::string& config,
482                                                      std::string const& file)
483 {
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);
489 }
490
491 void cmInstallTargetGenerator::AddInstallNamePatchRule(
492   std::ostream& os, Indent indent, const std::string& config,
493   std::string const& toDestDirPath)
494 {
495   if (this->ImportLibrary ||
496       !(this->Target->GetType() == cmStateEnums::SHARED_LIBRARY ||
497         this->Target->GetType() == cmStateEnums::MODULE_LIBRARY ||
498         this->Target->GetType() == cmStateEnums::EXECUTABLE)) {
499     return;
500   }
501
502   // Fix the install_name settings in installed binaries.
503   std::string installNameTool =
504     this->Target->Target->GetMakefile()->GetSafeDefinition(
505       "CMAKE_INSTALL_NAME_TOOL");
506
507   if (installNameTool.empty()) {
508     return;
509   }
510
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()) {
521         continue;
522       }
523
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);
534
535         // Map from the build-tree install_name.
536         for_build += fname;
537
538         // Map to the install-tree install_name.
539         for_install += fname;
540
541         // Store the mapping entry.
542         install_name_remap[for_build] = for_install;
543       }
544     }
545   }
546
547   // Edit the install_name of the target itself if necessary.
548   std::string new_id;
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}");
554
555     if (this->Target->IsFrameworkOnApple() && for_install.empty()) {
556       // Frameworks seem to have an id corresponding to their own full
557       // path.
558       // ...
559       // for_install = fullDestPath_without_DESTDIR_or_name;
560     }
561
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.
566       new_id = cmStrCat(
567         for_install, this->GetInstallFilename(this->Target, config, NameSO));
568     }
569   }
570
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;
575     os << "\"";
576     if (!new_id.empty()) {
577       os << "\n" << indent << "  -id \"" << new_id << "\"";
578     }
579     for (auto const& i : install_name_remap) {
580       os << "\n"
581          << indent << "  -change \"" << i.first << "\" \"" << i.second << "\"";
582     }
583     os << "\n" << indent << "  \"" << toDestDirPath << "\")\n";
584   }
585 }
586
587 void cmInstallTargetGenerator::AddRPathCheckRule(
588   std::ostream& os, Indent indent, const std::string& config,
589   std::string const& toDestDirPath)
590 {
591   // Skip the chrpath if the target does not need it.
592   if (this->ImportLibrary || !this->Target->IsChrpathUsed(config)) {
593     return;
594   }
595   // Skip if on Apple
596   if (this->Target->Target->GetMakefile()->IsOn(
597         "CMAKE_PLATFORM_HAS_INSTALLNAME")) {
598     return;
599   }
600
601   // Get the link information for this target.
602   // It can provide the RPATH.
603   cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
604   if (!cli) {
605     return;
606   }
607
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";
613
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().
620       CM_FALLTHROUGH;
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";
625       break;
626     }
627     default: {
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";
633       break;
634     }
635   }
636 }
637
638 void cmInstallTargetGenerator::AddChrpathPatchRule(
639   std::ostream& os, Indent indent, const std::string& config,
640   std::string const& toDestDirPath)
641 {
642   // Skip the chrpath if the target does not need it.
643   if (this->ImportLibrary || !this->Target->IsChrpathUsed(config)) {
644     return;
645   }
646
647   // Get the link information for this target.
648   // It can provide the RPATH.
649   cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
650   if (!cli) {
651     return;
652   }
653
654   cmMakefile* mf = this->Target->Target->GetMakefile();
655
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");
660
661     std::vector<std::string> oldRuntimeDirs;
662     std::vector<std::string> newRuntimeDirs;
663     cli->GetRPath(oldRuntimeDirs, false);
664     cli->GetRPath(newRuntimeDirs, true);
665
666     std::string darwin_major_version_s =
667       mf->GetSafeDefinition("DARWIN_MAJOR_VERSION");
668
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;
675       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"
681            " this limitation.";
682       mf->IssueMessage(MessageType::WARNING, msg.str());
683     } else {
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),
692             "delete" });
693         if (iter_inserted.second) {
694           // Add path to ordered list of changes
695           ordered.push_back(iter_inserted.first->first);
696         }
697       }
698
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();
710         }
711       }
712
713       // Remove rpaths that are unchanged (value was set to empty)
714       ordered.erase(
715         std::remove_if(ordered.begin(), ordered.end(),
716                        [&runpath_change](const std::string& runpath) {
717                          return runpath_change.find(runpath)->second.empty();
718                        }),
719         ordered.end());
720
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
725           // entries
726           os << indent << "  -" << runpath_change.find(runpath)->second
727              << "_rpath \"" << runpath << "\"\n";
728         }
729         os << indent << "  \"" << toDestDirPath << "\")\n";
730       }
731     }
732   } else {
733     // Construct the original rpath string to be replaced.
734     std::string oldRpath = cli->GetRPathString(false);
735
736     // Get the install RPATH from the link information.
737     std::string newRpath = cli->GetChrpathString();
738
739     // Skip the rule if the paths are identical
740     if (oldRpath == newRpath) {
741       return;
742     }
743
744     // Escape any CMake syntax in the RPATHs.
745     std::string escapedOldRpath = cmOutputConverter::EscapeForCMake(oldRpath);
746     std::string escapedNewRpath = cmOutputConverter::EscapeForCMake(newRpath);
747
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";
752
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);
758         CM_FALLTHROUGH;
759       case cmPolicies::OLD:
760         os << indent << "     NEW_RPATH \"" << newRpath << "\"";
761         break;
762       default:
763         os << indent << "     NEW_RPATH " << escapedNewRpath;
764         break;
765     }
766
767     if (this->Target->GetPropertyAsBool("INSTALL_REMOVE_ENVIRONMENT_RPATH")) {
768       os << "\n" << indent << "     INSTALL_REMOVE_ENVIRONMENT_RPATH)\n";
769     } else {
770       os << ")\n";
771     }
772   }
773 }
774
775 void cmInstallTargetGenerator::AddStripRule(std::ostream& os, Indent indent,
776                                             const std::string& toDestDirPath)
777 {
778
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) {
783     return;
784   }
785
786   // Don't handle OSX Bundles.
787   if (this->Target->Target->GetMakefile()->IsOn("APPLE") &&
788       this->Target->GetPropertyAsBool("MACOSX_BUNDLE")) {
789     return;
790   }
791
792   if (!this->Target->Target->GetMakefile()->IsSet("CMAKE_STRIP")) {
793     return;
794   }
795
796   std::string stripArgs;
797
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) {
802       stripArgs = "-x ";
803     } else if (this->Target->GetType() == cmStateEnums::EXECUTABLE) {
804       stripArgs = "-u -r ";
805     }
806   }
807
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";
813 }
814
815 void cmInstallTargetGenerator::AddRanlibRule(std::ostream& os, Indent indent,
816                                              const std::string& toDestDirPath)
817 {
818   // Static libraries need ranlib on this platform.
819   if (this->Target->GetType() != cmStateEnums::STATIC_LIBRARY) {
820     return;
821   }
822
823   // Perform post-installation processing on the file depending
824   // on its type.
825   if (!this->Target->Target->GetMakefile()->IsOn("APPLE")) {
826     return;
827   }
828
829   const std::string& ranlib =
830     this->Target->Target->GetMakefile()->GetRequiredDefinition("CMAKE_RANLIB");
831   if (ranlib.empty()) {
832     return;
833   }
834
835   os << indent << "execute_process(COMMAND \"" << ranlib << "\" \""
836      << toDestDirPath << "\")\n";
837 }
838
839 void cmInstallTargetGenerator::AddUniversalInstallRule(
840   std::ostream& os, Indent indent, const std::string& toDestDirPath)
841 {
842   cmMakefile const* mf = this->Target->Target->GetMakefile();
843
844   if (!mf->PlatformIsAppleEmbedded() || !mf->IsOn("XCODE")) {
845     return;
846   }
847
848   cmValue xcodeVersion = mf->GetDefinition("XCODE_VERSION");
849   if (!xcodeVersion ||
850       cmSystemTools::VersionCompareGreater("6", *xcodeVersion)) {
851     return;
852   }
853
854   switch (this->Target->GetType()) {
855     case cmStateEnums::EXECUTABLE:
856     case cmStateEnums::STATIC_LIBRARY:
857     case cmStateEnums::SHARED_LIBRARY:
858     case cmStateEnums::MODULE_LIBRARY:
859       break;
860
861     default:
862       return;
863   }
864
865   if (!this->Target->Target->GetPropertyAsBool("IOS_INSTALL_COMBINED")) {
866     return;
867   }
868
869   os << indent << "include(CMakeIOSInstallCombined)\n";
870   os << indent << "ios_install_combined("
871      << "\"" << this->Target->Target->GetName() << "\" "
872      << "\"" << toDestDirPath << "\")\n";
873 }
874
875 void cmInstallTargetGenerator::IssueCMP0095Warning(
876   const std::string& unescapedRpath)
877 {
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("${") !=
885                                   std::string::npos);
886
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());
895   }
896 }