Coverage Report

Created: 2026-02-09 06:05

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