Coverage Report

Created: 2026-07-14 07:09

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