Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmInstallTargetGenerator.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
#include "cmInstallTargetGenerator.h"
4
5
#include <algorithm>
6
#include <cassert>
7
#include <cstddef>
8
#include <functional>
9
#include <map>
10
#include <set>
11
#include <sstream>
12
#include <utility>
13
#include <vector>
14
15
#include <cm/optional>
16
17
#include "cmComputeLinkInformation.h"
18
#include "cmGeneratorExpression.h"
19
#include "cmGeneratorTarget.h"
20
#include "cmGlobalGenerator.h"
21
#include "cmInstallType.h"
22
#include "cmLocalGenerator.h"
23
#include "cmMakefile.h"
24
#include "cmMessageType.h"
25
#include "cmObjectLocation.h"
26
#include "cmPolicies.h"
27
#include "cmScriptGenerator.h"
28
#include "cmStateTypes.h"
29
#include "cmStringAlgorithms.h"
30
#include "cmSystemTools.h"
31
#include "cmTarget.h"
32
#include "cmValue.h"
33
#include "cmake.h"
34
35
namespace {
36
std::string computeInstallObjectDir(cmGeneratorTarget* gt,
37
                                    std::string const& config)
38
0
{
39
0
  std::string objectDir = "objects";
40
0
  if (!config.empty()) {
41
0
    objectDir += "-";
42
0
    objectDir += config;
43
0
  }
44
0
  objectDir += "/";
45
0
  objectDir += gt->GetName();
46
0
  return objectDir;
47
0
}
48
49
void computeFilesToInstall(
50
  cmInstallTargetGenerator::Files& files,
51
  cmInstallTargetGenerator::NamelinkModeType namelinkMode,
52
  std::string const& fromDirConfig, std::string const& output,
53
  std::string const& library, std::string const& real,
54
  cm::optional<std::function<void(std::string const&)>> GNUToMS = cm::nullopt)
55
0
{
56
0
  bool haveNamelink = false;
57
0
  auto convert = [&GNUToMS](std::string const& file) {
58
0
    if (GNUToMS) {
59
0
      (*GNUToMS)(file);
60
0
    }
61
0
  };
62
63
  // Library link name.
64
0
  std::string fromName = cmStrCat(fromDirConfig, output);
65
0
  std::string toName = output;
66
67
  // Library interface name.
68
0
  std::string fromSOName;
69
0
  std::string toSOName;
70
0
  if (!library.empty() && library != output) {
71
0
    haveNamelink = true;
72
0
    fromSOName = cmStrCat(fromDirConfig, library);
73
0
    toSOName = library;
74
0
  }
75
76
  // Library implementation name.
77
0
  std::string fromRealName;
78
0
  std::string toRealName;
79
0
  if (real != output && real != library) {
80
0
    haveNamelink = true;
81
0
    fromRealName = cmStrCat(fromDirConfig, real);
82
0
    toRealName = real;
83
0
  }
84
85
  // Add the names based on the current namelink mode.
86
0
  if (haveNamelink) {
87
0
    files.NamelinkMode = namelinkMode;
88
    // With a namelink we need to check the mode.
89
0
    if (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) {
90
      // Install the namelink only.
91
0
      files.From.emplace_back(fromName);
92
0
      files.To.emplace_back(toName);
93
0
      convert(output);
94
0
    } else {
95
      // Install the real file if it has its own name.
96
0
      if (!fromRealName.empty()) {
97
0
        files.From.emplace_back(fromRealName);
98
0
        files.To.emplace_back(toRealName);
99
0
        convert(real);
100
0
      }
101
102
      // Install the soname link if it has its own name.
103
0
      if (!fromSOName.empty()) {
104
0
        files.From.emplace_back(fromSOName);
105
0
        files.To.emplace_back(toSOName);
106
0
        convert(library);
107
0
      }
108
109
      // Install the namelink if it is not to be skipped.
110
0
      if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) {
111
0
        files.From.emplace_back(fromName);
112
0
        files.To.emplace_back(toName);
113
0
        convert(output);
114
0
      }
115
0
    }
116
0
  } else {
117
    // Without a namelink there will be only one file.  Install it
118
    // if this is not a namelink-only rule.
119
0
    if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) {
120
0
      files.From.emplace_back(fromName);
121
0
      files.To.emplace_back(toName);
122
0
      convert(output);
123
0
    }
124
0
  }
125
0
}
126
}
127
128
cmInstallTargetGenerator::cmInstallTargetGenerator(
129
  std::string targetName, std::string const& dest, bool implib,
130
  std::string file_permissions, std::vector<std::string> const& configurations,
131
  std::string const& component, MessageLevel message, bool exclude_from_all,
132
  bool optional, cmListFileBacktrace backtrace)
133
0
  : cmInstallGenerator(dest, configurations, component, message,
134
0
                       exclude_from_all, false, std::move(backtrace))
135
0
  , TargetName(std::move(targetName))
136
0
  , FilePermissions(std::move(file_permissions))
137
0
  , ImportLibrary(implib)
138
0
  , Optional(optional)
139
0
{
140
0
  this->ActionsPerConfig = true;
141
0
  this->NamelinkMode = NamelinkModeNone;
142
0
  this->ImportlinkMode = NamelinkModeNone;
143
0
}
144
145
0
cmInstallTargetGenerator::~cmInstallTargetGenerator() = default;
146
147
void cmInstallTargetGenerator::GenerateScriptForConfig(
148
  std::ostream& os, std::string const& config, Indent indent)
149
0
{
150
  // Compute the list of files to install for this target.
151
0
  Files files = this->GetFiles(config);
152
153
  // Skip this rule if no files are to be installed for the target.
154
0
  if (files.From.empty()) {
155
0
    return;
156
0
  }
157
158
  // Compute the effective install destination.
159
0
  std::string dest = this->GetDestination(config);
160
0
  if (!files.ToDir.empty()) {
161
0
    dest = cmStrCat(dest, '/', files.ToDir);
162
0
  }
163
164
  // Tweak files located in the destination directory.
165
0
  std::string toDir = cmStrCat(ConvertToAbsoluteDestination(dest), '/');
166
167
  // Add pre-installation tweaks.
168
0
  if (!files.NoTweak) {
169
0
    AddTweak(os, indent, config, toDir, files.To,
170
0
             [this](std::ostream& o, Indent i, std::string const& c,
171
0
                    std::string const& f) {
172
0
               this->PreReplacementTweaks(o, i, c, f);
173
0
             });
174
0
  }
175
176
  // Write code to install the target file.
177
0
  char const* no_dir_permissions = nullptr;
178
0
  bool optional = this->Optional || this->ImportLibrary;
179
0
  std::string literal_args;
180
0
  if (files.UseSourcePermissions) {
181
0
    literal_args += " USE_SOURCE_PERMISSIONS";
182
0
  }
183
0
  if (files.Rename) {
184
0
    if (files.From.size() != files.To.size()) {
185
0
      this->Target->GetLocalGenerator()->IssueMessage(
186
0
        MessageType::INTERNAL_ERROR,
187
0
        "cmInstallTargetGenerator generated a rename request with mismatched "
188
0
        "names.");
189
0
      return;
190
0
    }
191
0
    std::vector<std::string> FileNames;
192
0
    FileNames.resize(1);
193
0
    for (size_t i = 0; i < files.From.size(); ++i) {
194
0
      if (files.FromDir.empty()) {
195
0
        FileNames[0] = files.From[i];
196
0
      } else {
197
0
        FileNames[0] = cmStrCat(files.FromDir, '/', files.From[i]);
198
0
      }
199
0
      this->AddInstallRule(os, dest, files.Type, FileNames, optional,
200
0
                           this->FilePermissions.c_str(), no_dir_permissions,
201
0
                           files.To[i].c_str(), literal_args.c_str(), indent);
202
0
    }
203
0
  } else {
204
0
    char const* no_rename = nullptr;
205
0
    if (!files.FromDir.empty()) {
206
0
      literal_args += " FILES_FROM_DIR \"" + files.FromDir + "\"";
207
0
    }
208
0
    this->AddInstallRule(os, dest, files.Type, files.From, optional,
209
0
                         this->FilePermissions.c_str(), no_dir_permissions,
210
0
                         no_rename, literal_args.c_str(), indent);
211
0
  }
212
213
  // Add post-installation tweaks.
214
0
  if (!files.NoTweak) {
215
0
    AddTweak(os, indent, config, toDir, files.To,
216
0
             [this](std::ostream& o, Indent i, std::string const& c,
217
0
                    std::string const& f) {
218
0
               this->PostReplacementTweaks(o, i, c, f);
219
0
             });
220
0
  }
221
0
}
222
223
cmInstallTargetGenerator::Files cmInstallTargetGenerator::GetFiles(
224
  std::string const& config) const
225
0
{
226
0
  Files files;
227
228
0
  cmStateEnums::TargetType targetType = this->Target->GetType();
229
0
  switch (targetType) {
230
0
    case cmStateEnums::EXECUTABLE:
231
0
      files.Type = cmInstallType_EXECUTABLE;
232
0
      break;
233
0
    case cmStateEnums::STATIC_LIBRARY:
234
0
      files.Type = cmInstallType_STATIC_LIBRARY;
235
0
      break;
236
0
    case cmStateEnums::SHARED_LIBRARY:
237
0
      files.Type = cmInstallType_SHARED_LIBRARY;
238
0
      break;
239
0
    case cmStateEnums::MODULE_LIBRARY:
240
0
      files.Type = cmInstallType_MODULE_LIBRARY;
241
0
      break;
242
0
    case cmStateEnums::INTERFACE_LIBRARY:
243
      // Not reachable. We never create a cmInstallTargetGenerator for
244
      // an INTERFACE_LIBRARY.
245
0
      assert(false &&
246
0
             "INTERFACE_LIBRARY targets have no installable outputs.");
247
0
      break;
248
249
0
    case cmStateEnums::OBJECT_LIBRARY: {
250
      // Compute all the object files inside this target
251
0
      std::vector<std::pair<cmObjectLocation, cmObjectLocation>> objects;
252
0
      auto storeObjectLocations = [&objects](cmObjectLocation const& build,
253
0
                                             cmObjectLocation const& install) {
254
0
        objects.emplace_back(build, install);
255
0
      };
256
0
      this->Target->GetTargetObjectLocations(config, storeObjectLocations);
257
258
0
      files.Type = cmInstallType_FILES;
259
0
      files.NoTweak = true;
260
0
      files.Rename = true;
261
0
      files.FromDir = this->Target->GetObjectDirectory(config);
262
0
      if (!this->Target->GetPropertyAsBool(
263
0
            "INSTALL_OBJECT_ONLY_USE_DESTINATION")) {
264
0
        files.ToDir = computeInstallObjectDir(this->Target, config);
265
0
      }
266
0
      for (auto const& obj : objects) {
267
0
        files.From.emplace_back(obj.first.GetPath());
268
0
        files.To.emplace_back(obj.second.GetPath());
269
0
      }
270
0
      return files;
271
0
    }
272
273
0
    case cmStateEnums::UTILITY:
274
0
    case cmStateEnums::GLOBAL_TARGET:
275
0
    case cmStateEnums::UNKNOWN_LIBRARY:
276
0
      this->Target->GetLocalGenerator()->IssueMessage(
277
0
        MessageType::INTERNAL_ERROR,
278
0
        "cmInstallTargetGenerator created with non-installable target.");
279
0
      return files;
280
0
  }
281
282
  // Compute the build tree directory from which to copy the target.
283
0
  std::string fromDirConfig;
284
0
  if (this->Target->NeedRelinkBeforeInstall(config)) {
285
0
    fromDirConfig =
286
0
      cmStrCat(this->Target->GetLocalGenerator()->GetCurrentBinaryDirectory(),
287
0
               "/CMakeFiles/CMakeRelink.dir/");
288
0
  } else {
289
0
    cmStateEnums::ArtifactType artifact = this->ImportLibrary
290
0
      ? cmStateEnums::ImportLibraryArtifact
291
0
      : cmStateEnums::RuntimeBinaryArtifact;
292
0
    fromDirConfig =
293
0
      cmStrCat(this->Target->GetDirectory(config, artifact), '/');
294
0
  }
295
296
0
  if (targetType == cmStateEnums::EXECUTABLE) {
297
    // There is a bug in cmInstallCommand if this fails.
298
0
    assert(this->NamelinkMode == NamelinkModeNone);
299
300
0
    cmGeneratorTarget::Names targetNames =
301
0
      this->Target->GetExecutableNames(config);
302
0
    if (this->ImportLibrary) {
303
0
      std::string from1 = fromDirConfig + targetNames.ImportLibrary;
304
0
      std::string to1 = targetNames.ImportLibrary;
305
0
      files.From.emplace_back(std::move(from1));
306
0
      files.To.emplace_back(std::move(to1));
307
0
      std::string targetNameImportLib;
308
0
      if (this->Target->GetImplibGNUtoMS(config, targetNames.ImportLibrary,
309
0
                                         targetNameImportLib)) {
310
0
        files.From.emplace_back(fromDirConfig + targetNameImportLib);
311
0
        files.To.emplace_back(targetNameImportLib);
312
0
      }
313
314
      // An import library looks like a static library.
315
0
      files.Type = cmInstallType_STATIC_LIBRARY;
316
0
    } else {
317
0
      std::string from1 = fromDirConfig + targetNames.Output;
318
0
      std::string to1 = targetNames.Output;
319
320
      // Handle OSX Bundles.
321
0
      if (this->Target->IsAppBundleOnApple()) {
322
0
        cmMakefile const* mf = this->Target->Target->GetMakefile();
323
324
        // Get App Bundle Extension
325
0
        std::string ext;
326
0
        if (cmValue p = this->Target->GetProperty("BUNDLE_EXTENSION")) {
327
0
          ext = *p;
328
0
        } else {
329
0
          ext = "app";
330
0
        }
331
332
        // Install the whole app bundle directory.
333
0
        files.Type = cmInstallType_DIRECTORY;
334
0
        files.UseSourcePermissions = true;
335
0
        from1 += ".";
336
0
        from1 += ext;
337
338
        // Tweaks apply to the binary inside the bundle.
339
0
        to1 += ".";
340
0
        to1 += ext;
341
0
        to1 += "/";
342
0
        if (!mf->PlatformIsAppleEmbedded()) {
343
0
          to1 += "Contents/MacOS/";
344
0
        }
345
0
        to1 += targetNames.Output;
346
0
      } else {
347
        // Tweaks apply to the real file, so list it first.
348
0
        if (targetNames.Real != targetNames.Output) {
349
0
          std::string from2 = fromDirConfig + targetNames.Real;
350
0
          std::string to2 = targetNames.Real;
351
0
          files.From.emplace_back(std::move(from2));
352
0
          files.To.emplace_back(std::move(to2));
353
0
        }
354
0
      }
355
356
0
      files.From.emplace_back(std::move(from1));
357
0
      files.To.emplace_back(std::move(to1));
358
0
    }
359
0
  } else {
360
0
    cmGeneratorTarget::Names targetNames =
361
0
      this->Target->GetLibraryNames(config);
362
0
    if (this->ImportLibrary) {
363
      // There is a bug in cmInstallCommand if this fails.
364
0
      assert(this->Target->Makefile->PlatformSupportsAppleTextStubs() ||
365
0
             this->ImportlinkMode == NamelinkModeNone);
366
367
0
      auto GNUToMS = [this, &config, &files,
368
0
                      &fromDirConfig](std::string const& lib) {
369
0
        std::string importLib;
370
0
        if (this->Target->GetImplibGNUtoMS(config, lib, importLib)) {
371
0
          files.From.emplace_back(fromDirConfig + importLib);
372
0
          files.To.emplace_back(importLib);
373
0
        }
374
0
      };
375
376
0
      computeFilesToInstall(
377
0
        files, this->ImportlinkMode, fromDirConfig, targetNames.ImportOutput,
378
0
        targetNames.ImportLibrary, targetNames.ImportReal, GNUToMS);
379
380
      // An import library looks like a static library.
381
0
      files.Type = cmInstallType_STATIC_LIBRARY;
382
0
    } else if (this->Target->IsFrameworkOnApple()) {
383
      // FIXME: In principle we should be able to
384
      //   assert(this->NamelinkMode == NamelinkModeNone);
385
      // but since the current install() command implementation checks
386
      // the FRAMEWORK property immediately instead of delaying until
387
      // generate time, it is possible for project code to set the
388
      // property after calling install().  In such a case, the install()
389
      // command will use the LIBRARY code path and create two install
390
      // generators, one for the namelink component (NamelinkModeOnly)
391
      // and one for the primary artifact component (NamelinkModeSkip).
392
      // Historically this was not diagnosed and resulted in silent
393
      // installation of a framework to the LIBRARY destination.
394
      // Retain that behavior and warn about the case.
395
0
      switch (this->NamelinkMode) {
396
0
        case NamelinkModeNone:
397
          // Normal case.
398
0
          break;
399
0
        case NamelinkModeOnly:
400
          // Assume the NamelinkModeSkip instance will warn and install.
401
0
          return files;
402
0
        case NamelinkModeSkip: {
403
0
          std::string e = "Target '" + this->Target->GetName() +
404
0
            "' was changed to a FRAMEWORK sometime after install().  "
405
0
            "This may result in the wrong install DESTINATION.  "
406
0
            "Set the FRAMEWORK property earlier.";
407
0
          this->Target->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
408
0
            MessageType::AUTHOR_WARNING, e, this->GetBacktrace());
409
0
        } break;
410
0
      }
411
412
      // Install the whole framework directory.
413
0
      files.Type = cmInstallType_DIRECTORY;
414
0
      files.UseSourcePermissions = true;
415
416
0
      std::string from1 = fromDirConfig + targetNames.Output;
417
0
      from1 = cmSystemTools::GetFilenamePath(from1);
418
419
      // Tweaks apply to the binary inside the bundle.
420
0
      std::string to1 = targetNames.Real;
421
422
0
      files.From.emplace_back(std::move(from1));
423
0
      files.To.emplace_back(std::move(to1));
424
0
    } else if (this->Target->IsCFBundleOnApple()) {
425
      // Install the whole app bundle directory.
426
0
      files.Type = cmInstallType_DIRECTORY;
427
0
      files.UseSourcePermissions = true;
428
429
0
      std::string targetNameBase =
430
0
        targetNames.Output.substr(0, targetNames.Output.find('/'));
431
432
0
      std::string from1 = fromDirConfig + targetNameBase;
433
0
      std::string to1 = targetNames.Output;
434
435
0
      files.From.emplace_back(std::move(from1));
436
0
      files.To.emplace_back(std::move(to1));
437
0
    } else if (this->Target->IsArchivedAIXSharedLibrary()) {
438
      // Install only the archive on AIX.
439
0
      computeFilesToInstall(files, this->NamelinkMode, fromDirConfig,
440
0
                            targetNames.Output, {}, targetNames.Real);
441
0
    } else {
442
0
      computeFilesToInstall(files, this->NamelinkMode, fromDirConfig,
443
0
                            targetNames.Output, targetNames.SharedObject,
444
0
                            targetNames.Real);
445
0
    }
446
0
  }
447
448
  // If this fails the above code is buggy.
449
0
  assert(files.From.size() == files.To.size());
450
451
0
  return files;
452
0
}
453
454
void cmInstallTargetGenerator::GetInstallObjectNames(
455
  std::string const& config, std::vector<std::string>& objects) const
456
0
{
457
0
  std::vector<cmObjectLocation> installedObjects;
458
0
  auto storeObjectLocations =
459
0
    [&installedObjects](cmObjectLocation const&,
460
0
                        cmObjectLocation const& install) {
461
0
      installedObjects.emplace_back(install);
462
0
    };
463
0
  this->Target->GetTargetObjectLocations(config, storeObjectLocations);
464
0
  objects.reserve(installedObjects.size());
465
0
  std::string rootDir;
466
0
  if (!this->Target->GetPropertyAsBool(
467
0
        "INSTALL_OBJECT_ONLY_USE_DESTINATION")) {
468
0
    rootDir = cmStrCat(computeInstallObjectDir(this->Target, config), '/');
469
0
  }
470
0
  for (cmObjectLocation const& o : installedObjects) {
471
0
    objects.emplace_back(cmStrCat(rootDir, o.GetPath()));
472
0
  }
473
0
}
474
475
std::string cmInstallTargetGenerator::GetDestination(
476
  std::string const& config) const
477
0
{
478
0
  return cmGeneratorExpression::Evaluate(
479
0
    this->Destination, this->Target->GetLocalGenerator(), config);
480
0
}
481
482
std::string cmInstallTargetGenerator::GetInstallFilename(
483
  std::string const& config) const
484
0
{
485
0
  NameType nameType = this->ImportLibrary ? NameImplib : NameNormal;
486
0
  return cmInstallTargetGenerator::GetInstallFilename(this->Target, config,
487
0
                                                      nameType);
488
0
}
489
490
std::string cmInstallTargetGenerator::GetInstallFilename(
491
  cmGeneratorTarget const* target, std::string const& config,
492
  NameType nameType)
493
0
{
494
0
  std::string fname;
495
  // Compute the name of the library.
496
0
  if (target->GetType() == cmStateEnums::EXECUTABLE) {
497
0
    cmGeneratorTarget::Names targetNames = target->GetExecutableNames(config);
498
0
    if (nameType == NameImplib) {
499
      // Use the import library name.
500
0
      if (!target->GetImplibGNUtoMS(config, targetNames.ImportLibrary, fname,
501
0
                                    "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
502
0
        fname = targetNames.ImportLibrary;
503
0
      }
504
0
    } else if (nameType == NameImplibReal) {
505
      // Use the import library name.
506
0
      if (!target->GetImplibGNUtoMS(config, targetNames.ImportReal, fname,
507
0
                                    "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
508
0
        fname = targetNames.ImportReal;
509
0
      }
510
0
    } else if (nameType == NameReal) {
511
      // Use the canonical name.
512
0
      fname = targetNames.Real;
513
0
    } else {
514
      // Use the canonical name.
515
0
      fname = targetNames.Output;
516
0
    }
517
0
  } else {
518
0
    cmGeneratorTarget::Names targetNames = target->GetLibraryNames(config);
519
0
    if (nameType == NameImplib || nameType == NameImplibReal) {
520
0
      auto const& importName = nameType == NameImplib
521
0
        ? targetNames.ImportLibrary
522
0
        : targetNames.ImportReal;
523
      // Use the import library name.
524
0
      if (!target->GetImplibGNUtoMS(config, importName, fname,
525
0
                                    "${CMAKE_IMPORT_LIBRARY_SUFFIX}")) {
526
0
        fname = importName;
527
0
      }
528
0
    } else if (nameType == NameSO) {
529
      // Use the soname.
530
0
      fname = targetNames.SharedObject;
531
0
    } else if (nameType == NameReal) {
532
      // Use the real name.
533
0
      fname = targetNames.Real;
534
0
    } else {
535
      // Use the canonical name.
536
0
      fname = targetNames.Output;
537
0
    }
538
0
  }
539
540
0
  return fname;
541
0
}
542
543
bool cmInstallTargetGenerator::Compute(cmLocalGenerator* lg)
544
0
{
545
  // Lookup this target in the current directory.
546
0
  this->Target = lg->FindLocalNonAliasGeneratorTarget(this->TargetName);
547
0
  if (!this->Target) {
548
    // If no local target has been found, find it in the global scope.
549
0
    this->Target =
550
0
      lg->GetGlobalGenerator()->FindGeneratorTarget(this->TargetName);
551
0
  }
552
553
0
  return true;
554
0
}
555
556
void cmInstallTargetGenerator::PreReplacementTweaks(std::ostream& os,
557
                                                    Indent indent,
558
                                                    std::string const& config,
559
                                                    std::string const& file)
560
0
{
561
0
  this->AddRPathCheckRule(os, indent, config, file);
562
0
}
563
564
void cmInstallTargetGenerator::PostReplacementTweaks(std::ostream& os,
565
                                                     Indent indent,
566
                                                     std::string const& config,
567
                                                     std::string const& file)
568
0
{
569
0
  this->AddInstallNamePatchRule(os, indent, config, file);
570
0
  this->AddChrpathPatchRule(os, indent, config, file);
571
0
  this->AddUniversalInstallRule(os, indent, file);
572
0
  this->AddRanlibRule(os, indent, file);
573
0
  this->AddStripRule(os, indent, file);
574
0
}
575
576
void cmInstallTargetGenerator::AddInstallNamePatchRule(
577
  std::ostream& os, Indent indent, std::string const& config,
578
  std::string const& toDestDirPath)
579
0
{
580
0
  if (this->ImportLibrary || this->NamelinkMode == NamelinkModeOnly ||
581
0
      !(this->Target->GetType() == cmStateEnums::SHARED_LIBRARY ||
582
0
        this->Target->GetType() == cmStateEnums::MODULE_LIBRARY ||
583
0
        this->Target->GetType() == cmStateEnums::EXECUTABLE)) {
584
0
    return;
585
0
  }
586
587
  // Fix the install_name settings in installed binaries.
588
0
  std::string installNameTool =
589
0
    this->Target->Target->GetMakefile()->GetSafeDefinition(
590
0
      "CMAKE_INSTALL_NAME_TOOL");
591
592
0
  if (installNameTool.empty()) {
593
0
    return;
594
0
  }
595
596
  // Build a map of build-tree install_name to install-tree install_name for
597
  // shared libraries linked to this target.
598
0
  std::map<std::string, std::string> install_name_remap;
599
0
  if (cmComputeLinkInformation* cli =
600
0
        this->Target->GetLinkInformation(config)) {
601
0
    std::set<cmGeneratorTarget const*> const& sharedLibs =
602
0
      cli->GetSharedLibrariesLinked();
603
0
    for (cmGeneratorTarget const* tgt : sharedLibs) {
604
      // The install_name of an imported target does not change.
605
0
      if (tgt->IsImported()) {
606
0
        continue;
607
0
      }
608
609
      // If the build tree and install tree use different path
610
      // components of the install_name field then we need to create a
611
      // mapping to be applied after installation.
612
0
      std::string for_build = tgt->GetInstallNameDirForBuildTree(config);
613
0
      std::string for_install = tgt->GetInstallNameDirForInstallTree(
614
0
        config, "${CMAKE_INSTALL_PREFIX}");
615
0
      if (for_build != for_install) {
616
        // The directory portions differ.  Append the filename to
617
        // create the mapping.
618
0
        std::string fname = this->GetInstallFilename(tgt, config, NameSO);
619
620
        // Map from the build-tree install_name.
621
0
        for_build += fname;
622
623
        // Map to the install-tree install_name.
624
0
        for_install += fname;
625
626
        // Store the mapping entry.
627
0
        install_name_remap[for_build] = for_install;
628
0
      }
629
0
    }
630
0
  }
631
632
  // Edit the install_name of the target itself if necessary.
633
0
  std::string new_id;
634
0
  if (this->Target->GetType() == cmStateEnums::SHARED_LIBRARY) {
635
0
    std::string for_build =
636
0
      this->Target->GetInstallNameDirForBuildTree(config);
637
0
    std::string for_install = this->Target->GetInstallNameDirForInstallTree(
638
0
      config, "${CMAKE_INSTALL_PREFIX}");
639
640
0
    if (this->Target->IsFrameworkOnApple() && for_install.empty()) {
641
      // Frameworks seem to have an id corresponding to their own full
642
      // path.
643
      // ...
644
      // for_install = fullDestPath_without_DESTDIR_or_name;
645
0
    }
646
647
    // If the install name will change on installation set the new id
648
    // on the installed file.
649
0
    if (for_build != for_install) {
650
      // Prepare to refer to the install-tree install_name.
651
0
      new_id = cmStrCat(
652
0
        for_install, this->GetInstallFilename(this->Target, config, NameSO));
653
0
    }
654
0
  }
655
656
  // Write a rule to run install_name_tool to set the install-tree
657
  // install_name value and references.
658
0
  if (!new_id.empty() || !install_name_remap.empty()) {
659
0
    os << indent << "execute_process(COMMAND \"" << installNameTool;
660
0
    os << "\"";
661
0
    if (!new_id.empty()) {
662
0
      os << "\n" << indent << "  -id \"" << new_id << "\"";
663
0
    }
664
0
    for (auto const& i : install_name_remap) {
665
0
      os << "\n"
666
0
         << indent << "  -change \"" << i.first << "\" \"" << i.second << "\"";
667
0
    }
668
0
    os << "\n" << indent << "  \"" << toDestDirPath << "\")\n";
669
0
  }
670
0
}
671
672
void cmInstallTargetGenerator::AddRPathCheckRule(
673
  std::ostream& os, Indent indent, std::string const& config,
674
  std::string const& toDestDirPath)
675
0
{
676
  // Skip the chrpath if the target does not need it.
677
0
  if (this->ImportLibrary || this->NamelinkMode == NamelinkModeOnly ||
678
0
      !this->Target->IsChrpathUsed(config)) {
679
0
    return;
680
0
  }
681
  // Skip if on Apple
682
0
  if (this->Target->Target->GetMakefile()->IsOn(
683
0
        "CMAKE_PLATFORM_HAS_INSTALLNAME")) {
684
0
    return;
685
0
  }
686
687
  // Get the link information for this target.
688
  // It can provide the RPATH.
689
0
  cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
690
0
  if (!cli) {
691
0
    return;
692
0
  }
693
694
  // Write a rule to remove the installed file if its rpath is not the
695
  // new rpath.  This is needed for existing build/install trees when
696
  // the installed rpath changes but the file is not rebuilt.
697
0
  os << indent << "file(RPATH_CHECK\n"
698
0
     << indent << "     FILE \"" << toDestDirPath << "\"\n";
699
700
  // CMP0095: ``RPATH`` entries are properly escaped in the intermediary
701
  // CMake install script.
702
0
  switch (this->Target->GetPolicyStatusCMP0095()) {
703
0
    case cmPolicies::WARN:
704
      // No author warning needed here, we warn later in
705
      // cmInstallTargetGenerator::AddChrpathPatchRule().
706
0
      CM_FALLTHROUGH;
707
0
    case cmPolicies::OLD: {
708
      // Get the install RPATH from the link information.
709
0
      std::string newRpath = cli->GetChrpathString();
710
0
      os << indent << "     RPATH \"" << newRpath << "\")\n";
711
0
      break;
712
0
    }
713
0
    default: {
714
      // Get the install RPATH from the link information and
715
      // escape any CMake syntax in the install RPATH.
716
0
      os << indent << "     RPATH "
717
0
         << cmScriptGenerator::Quote(cli->GetChrpathString()) << ")\n";
718
0
      break;
719
0
    }
720
0
  }
721
0
}
722
723
void cmInstallTargetGenerator::AddChrpathPatchRule(
724
  std::ostream& os, Indent indent, std::string const& config,
725
  std::string const& toDestDirPath)
726
0
{
727
  // Skip the chrpath if the target does not need it.
728
0
  if (this->ImportLibrary || this->NamelinkMode == NamelinkModeOnly ||
729
0
      !this->Target->IsChrpathUsed(config)) {
730
0
    return;
731
0
  }
732
733
  // Get the link information for this target.
734
  // It can provide the RPATH.
735
0
  cmComputeLinkInformation* cli = this->Target->GetLinkInformation(config);
736
0
  if (!cli) {
737
0
    return;
738
0
  }
739
740
0
  cmMakefile* mf = this->Target->Target->GetMakefile();
741
742
0
  if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
743
    // If using install_name_tool, set up the rules to modify the rpaths.
744
0
    std::string installNameTool =
745
0
      mf->GetSafeDefinition("CMAKE_INSTALL_NAME_TOOL");
746
747
0
    std::vector<std::string> oldRuntimeDirs;
748
0
    std::vector<std::string> newRuntimeDirs;
749
0
    cli->GetRPath(oldRuntimeDirs, false);
750
0
    cli->GetRPath(newRuntimeDirs, true);
751
752
0
    std::string darwin_major_version_s =
753
0
      mf->GetSafeDefinition("DARWIN_MAJOR_VERSION");
754
755
0
    std::istringstream ss(darwin_major_version_s);
756
0
    int darwin_major_version;
757
0
    ss >> darwin_major_version;
758
0
    if (!ss.fail() && darwin_major_version <= 9 &&
759
0
        (!oldRuntimeDirs.empty() || !newRuntimeDirs.empty())) {
760
0
      std::ostringstream msg;
761
0
      msg
762
0
        << "WARNING: Target \"" << this->Target->GetName()
763
0
        << "\" has runtime paths which cannot be changed during install.  "
764
0
        << "To change runtime paths, OS X version 10.6 or newer is required.  "
765
0
        << "Therefore, runtime paths will not be changed when installing.  "
766
0
        << "CMAKE_BUILD_WITH_INSTALL_RPATH may be used to work around"
767
0
           " this limitation.";
768
0
      mf->IssueMessage(MessageType::WARNING, msg.str());
769
0
    } else {
770
      // To be consistent with older versions, runpath changes must be ordered,
771
      // deleted first, then added, *and* the same path must only appear once.
772
0
      std::map<std::string, std::string> runpath_change;
773
0
      std::vector<std::string> ordered;
774
0
      for (std::string const& dir : oldRuntimeDirs) {
775
        // Normalize path and add to map of changes to make
776
0
        auto iter_inserted = runpath_change.insert(
777
0
          { mf->GetGlobalGenerator()->ExpandCFGIntDir(dir, config),
778
0
            "delete" });
779
0
        if (iter_inserted.second) {
780
          // Add path to ordered list of changes
781
0
          ordered.push_back(iter_inserted.first->first);
782
0
        }
783
0
      }
784
785
0
      for (std::string const& dir : newRuntimeDirs) {
786
        // Normalize path and add to map of changes to make
787
0
        auto iter_inserted = runpath_change.insert(
788
0
          { mf->GetGlobalGenerator()->ExpandCFGIntDir(dir, config), "add" });
789
0
        if (iter_inserted.second) {
790
          // Add path to ordered list of changes
791
0
          ordered.push_back(iter_inserted.first->first);
792
0
        } else if (iter_inserted.first->second != "add") {
793
          // Rpath was requested to be deleted and then later re-added. Drop it
794
          // from the list by marking as an empty value.
795
0
          iter_inserted.first->second.clear();
796
0
        }
797
0
      }
798
799
      // Remove rpaths that are unchanged (value was set to empty)
800
0
      ordered.erase(
801
0
        std::remove_if(ordered.begin(), ordered.end(),
802
0
                       [&runpath_change](std::string const& runpath) {
803
0
                         return runpath_change.find(runpath)->second.empty();
804
0
                       }),
805
0
        ordered.end());
806
807
0
      if (!ordered.empty()) {
808
0
        os << indent << "execute_process(COMMAND " << installNameTool << "\n";
809
0
        for (std::string const& runpath : ordered) {
810
          // Either 'add_rpath' or 'delete_rpath' since we've removed empty
811
          // entries
812
0
          os << indent << "  -" << runpath_change.find(runpath)->second
813
0
             << "_rpath \"" << runpath << "\"\n";
814
0
        }
815
0
        os << indent << "  \"" << toDestDirPath << "\")\n";
816
0
      }
817
0
    }
818
0
  } else {
819
    // Construct the original rpath string to be replaced.
820
0
    std::string oldRpath = cli->GetRPathString(false);
821
822
    // Get the install RPATH from the link information.
823
0
    std::string newRpath = cli->GetChrpathString();
824
825
    // Skip the rule if the paths are identical
826
0
    if (oldRpath == newRpath) {
827
0
      return;
828
0
    }
829
830
    // Write a rule to run chrpath to set the install-tree RPATH
831
0
    os << indent << "file(RPATH_CHANGE\n"
832
0
       << indent << "     FILE \"" << toDestDirPath << "\"\n"
833
0
       << indent << "     OLD_RPATH " << cmScriptGenerator::Quote(oldRpath)
834
0
       << "\n";
835
836
    // CMP0095: ``RPATH`` entries are properly escaped in the intermediary
837
    // CMake install script.
838
0
    switch (this->Target->GetPolicyStatusCMP0095()) {
839
0
      case cmPolicies::WARN:
840
0
        this->IssueCMP0095Warning(newRpath);
841
0
        CM_FALLTHROUGH;
842
0
      case cmPolicies::OLD:
843
0
        os << indent << "     NEW_RPATH \"" << newRpath << "\"";
844
0
        break;
845
0
      default:
846
0
        os << indent << "     NEW_RPATH "
847
0
           << cmScriptGenerator::Quote(newRpath);
848
0
        break;
849
0
    }
850
851
0
    if (this->Target->GetPropertyAsBool("INSTALL_REMOVE_ENVIRONMENT_RPATH")) {
852
0
      os << "\n" << indent << "     INSTALL_REMOVE_ENVIRONMENT_RPATH)\n";
853
0
    } else {
854
0
      os << ")\n";
855
0
    }
856
0
  }
857
0
}
858
859
void cmInstallTargetGenerator::AddStripRule(std::ostream& os, Indent indent,
860
                                            std::string const& toDestDirPath)
861
0
{
862
863
  // don't strip static and import libraries, because it removes the only
864
  // symbol table they have so you can't link to them anymore
865
0
  if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY ||
866
0
      this->ImportLibrary || this->NamelinkMode == NamelinkModeOnly) {
867
0
    return;
868
0
  }
869
870
  // Don't handle OSX Bundles.
871
0
  if (this->Target->IsApple() &&
872
0
      this->Target->GetPropertyAsBool("MACOSX_BUNDLE")) {
873
0
    return;
874
0
  }
875
876
0
  std::string const& strip =
877
0
    this->Target->Target->GetMakefile()->GetSafeDefinition("CMAKE_STRIP");
878
0
  if (strip.empty()) {
879
0
    return;
880
0
  }
881
882
0
  std::string stripArgs;
883
0
  if (this->Target->IsApple()) {
884
0
    if (this->Target->GetType() == cmStateEnums::SHARED_LIBRARY ||
885
0
        this->Target->GetType() == cmStateEnums::MODULE_LIBRARY) {
886
      // Strip tools need '-x' to strip Apple dylibs correctly.
887
0
      stripArgs = "-x ";
888
0
    } else if (this->Target->GetType() == cmStateEnums::EXECUTABLE &&
889
0
               this->Target->GetGlobalGenerator()->GetStripCommandStyle(
890
0
                 strip) == cmGlobalGenerator::StripCommandStyle::Apple) {
891
      // Apple's strip tool needs '-u -r' to strip executables correctly.
892
0
      stripArgs = "-u -r ";
893
0
    }
894
0
  }
895
896
0
  os << indent << "if(CMAKE_INSTALL_DO_STRIP)\n";
897
0
  os << indent << "  execute_process(COMMAND \"" << strip << "\" " << stripArgs
898
0
     << "\"" << toDestDirPath << "\")\n";
899
0
  os << indent << "endif()\n";
900
0
}
901
902
void cmInstallTargetGenerator::AddRanlibRule(std::ostream& os, Indent indent,
903
                                             std::string const& toDestDirPath)
904
0
{
905
  // Static libraries need ranlib on this platform.
906
0
  if (this->Target->GetType() != cmStateEnums::STATIC_LIBRARY) {
907
0
    return;
908
0
  }
909
910
  // Perform post-installation processing on the file depending
911
  // on its type.
912
0
  if (!this->Target->IsApple()) {
913
0
    return;
914
0
  }
915
916
0
  std::string const& ranlib =
917
0
    this->Target->Target->GetMakefile()->GetRequiredDefinition("CMAKE_RANLIB");
918
0
  if (ranlib.empty()) {
919
0
    return;
920
0
  }
921
922
0
  os << indent << "execute_process(COMMAND \"" << ranlib << "\" \""
923
0
     << toDestDirPath << "\")\n";
924
0
}
925
926
void cmInstallTargetGenerator::AddUniversalInstallRule(
927
  std::ostream& os, Indent indent, std::string const& toDestDirPath)
928
0
{
929
0
  cmMakefile const* mf = this->Target->Target->GetMakefile();
930
931
0
  if (!mf->PlatformIsAppleEmbedded() || !mf->IsOn("XCODE")) {
932
0
    return;
933
0
  }
934
935
0
  cmValue xcodeVersion = mf->GetDefinition("XCODE_VERSION");
936
0
  if (!xcodeVersion ||
937
0
      cmSystemTools::VersionCompareGreater("6", *xcodeVersion)) {
938
0
    return;
939
0
  }
940
941
0
  switch (this->Target->GetType()) {
942
0
    case cmStateEnums::EXECUTABLE:
943
0
    case cmStateEnums::STATIC_LIBRARY:
944
0
    case cmStateEnums::SHARED_LIBRARY:
945
0
    case cmStateEnums::MODULE_LIBRARY:
946
0
      break;
947
948
0
    default:
949
0
      return;
950
0
  }
951
952
0
  if (!this->Target->Target->GetPropertyAsBool("IOS_INSTALL_COMBINED")) {
953
0
    return;
954
0
  }
955
956
0
  os << indent << "include(CMakeIOSInstallCombined)\n";
957
0
  os << indent << "ios_install_combined("
958
0
     << "\"" << this->Target->Target->GetName() << "\" "
959
0
     << "\"" << toDestDirPath << "\")\n";
960
0
}
961
962
void cmInstallTargetGenerator::IssueCMP0095Warning(
963
  std::string const& unescapedRpath)
964
0
{
965
  // Reduce warning noise to cases where used RPATHs may actually be affected
966
  // by CMP0095. This filter is meant to skip warnings in cases when
967
  // non-curly-braces syntax (e.g. $ORIGIN) or no keyword is used which has
968
  // worked already before CMP0095. We intend to issue a warning in all cases
969
  // with curly-braces syntax, even if the workaround of double-escaping is in
970
  // place, since we deprecate the need for it with CMP0095.
971
0
  bool const potentially_affected(unescapedRpath.find("${") !=
972
0
                                  std::string::npos);
973
974
0
  if (potentially_affected) {
975
0
    std::ostringstream w;
976
0
    w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0095) << "\n";
977
0
    w << "RPATH entries for target '" << this->Target->GetName() << "' "
978
0
      << "will not be escaped in the intermediary "
979
0
      << "cmake_install.cmake script.";
980
0
    this->Target->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
981
0
      MessageType::AUTHOR_WARNING, w.str(), this->GetBacktrace());
982
0
  }
983
0
}