Coverage Report

Created: 2026-10-28 06:38

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