Coverage Report

Created: 2026-06-15 07:03

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