Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmInstallGenerator.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 "cmInstallGenerator.h"
4
5
#include <algorithm>
6
#include <sstream>
7
#include <utility>
8
9
#include <cm/string_view>
10
11
#include "cmDiagnostics.h"
12
#include "cmGeneratorTarget.h"
13
#include "cmGlobalGenerator.h"
14
#include "cmLocalGenerator.h"
15
#include "cmMakefile.h"
16
#include "cmMessageType.h"
17
#include "cmStringAlgorithms.h"
18
#include "cmSystemTools.h"
19
20
cmInstallGenerator::cmInstallGenerator(
21
  std::string destination, std::vector<std::string> const& configurations,
22
  std::string component, MessageLevel message, bool excludeFromAll,
23
  bool allComponents, cmDiagnosticContext context)
24
0
  : cmScriptGenerator("CMAKE_INSTALL_CONFIG_NAME", configurations)
25
0
  , Destination(std::move(destination))
26
0
  , Component(std::move(component))
27
0
  , Message(message)
28
0
  , ExcludeFromAll(excludeFromAll)
29
0
  , AllComponents(allComponents)
30
0
  , Context{ std::move(context) }
31
0
{
32
0
}
33
34
0
cmInstallGenerator::~cmInstallGenerator() = default;
35
36
bool cmInstallGenerator::HaveInstall()
37
0
{
38
0
  return true;
39
0
}
40
41
void cmInstallGenerator::CheckCMP0082(bool& haveSubdirectoryInstall,
42
                                      bool& haveInstallAfterSubdirectory)
43
0
{
44
0
  if (haveSubdirectoryInstall) {
45
0
    haveInstallAfterSubdirectory = true;
46
0
  }
47
0
}
48
49
void cmInstallGenerator::AddInstallRule(
50
  std::ostream& os, std::string const& dest, cmInstallType type,
51
  std::vector<std::string> const& files, bool optional /* = false */,
52
  char const* permissionsFile /* = nullptr */,
53
  char const* permissionsDir /* = nullptr */,
54
  char const* rename /* = nullptr */, char const* literalArgs /* = nullptr */,
55
  Indent indent, char const* filesVar /* = nullptr */)
56
0
{
57
  // Use the FILE command to install the file.
58
0
  std::string stype;
59
0
  switch (type) {
60
0
    case cmInstallType_DIRECTORY:
61
0
      stype = "DIRECTORY";
62
0
      break;
63
0
    case cmInstallType_PROGRAMS:
64
0
      stype = "PROGRAM";
65
0
      break;
66
0
    case cmInstallType_EXECUTABLE:
67
0
      stype = "EXECUTABLE";
68
0
      break;
69
0
    case cmInstallType_STATIC_LIBRARY:
70
0
      stype = "STATIC_LIBRARY";
71
0
      break;
72
0
    case cmInstallType_SHARED_LIBRARY:
73
0
      stype = "SHARED_LIBRARY";
74
0
      break;
75
0
    case cmInstallType_MODULE_LIBRARY:
76
0
      stype = "MODULE";
77
0
      break;
78
0
    case cmInstallType_FILES:
79
0
      stype = "FILE";
80
0
      break;
81
0
  }
82
0
  if (cmSystemTools::FileIsFullPath(dest)) {
83
0
    if (!files.empty()) {
84
0
      os << indent << "list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n";
85
0
      os << indent << " \"";
86
0
      bool firstIteration = true;
87
0
      for (std::string const& file : files) {
88
0
        if (!firstIteration) {
89
0
          os << ";";
90
0
        }
91
0
        os << dest << "/";
92
0
        if (rename && *rename) {
93
0
          os << rename;
94
0
        } else {
95
0
          os << cmSystemTools::GetFilenameNameView(file);
96
0
        }
97
0
        firstIteration = false;
98
0
      }
99
0
      os << "\")\n";
100
0
    }
101
0
    if (filesVar) {
102
0
      os << indent << "foreach(_cmake_abs_file IN LISTS " << filesVar << ")\n";
103
0
      os << indent.Next()
104
0
         << "get_filename_component(_cmake_abs_file_name "
105
0
            "\"${_cmake_abs_file}\" NAME)\n";
106
0
      os << indent.Next() << "list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES \""
107
0
         << dest << "/${_cmake_abs_file_name}\")\n";
108
0
      os << indent << "endforeach()\n";
109
0
      os << indent << "unset(_cmake_abs_file_name)\n";
110
0
      os << indent << "unset(_cmake_abs_file)\n";
111
0
    }
112
0
    os << indent << "if(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n";
113
0
    os << indent.Next()
114
0
       << "message(WARNING \"ABSOLUTE path INSTALL "
115
0
          "DESTINATION : ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n";
116
0
    os << indent << "endif()\n";
117
118
0
    os << indent << "if(CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n";
119
0
    os << indent.Next()
120
0
       << "message(FATAL_ERROR \"ABSOLUTE path INSTALL "
121
0
          "DESTINATION forbidden (by caller): "
122
0
          "${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n";
123
0
    os << indent << "endif()\n";
124
0
  }
125
0
  std::string absDest = ConvertToAbsoluteDestination(dest);
126
0
  os << indent << "file(INSTALL DESTINATION \"" << absDest << "\" TYPE "
127
0
     << stype;
128
0
  if (optional) {
129
0
    os << " OPTIONAL";
130
0
  }
131
0
  switch (this->Message) {
132
0
    case MessageDefault:
133
0
      break;
134
0
    case MessageAlways:
135
0
      os << " MESSAGE_ALWAYS";
136
0
      break;
137
0
    case MessageLazy:
138
0
      os << " MESSAGE_LAZY";
139
0
      break;
140
0
    case MessageNever:
141
0
      os << " MESSAGE_NEVER";
142
0
      break;
143
0
  }
144
0
  if (permissionsFile && *permissionsFile) {
145
0
    os << " PERMISSIONS" << permissionsFile;
146
0
  }
147
0
  if (permissionsDir && *permissionsDir) {
148
0
    os << " DIR_PERMISSIONS" << permissionsDir;
149
0
  }
150
0
  if (rename && *rename) {
151
0
    os << " RENAME \"" << rename << "\"";
152
0
  }
153
0
  os << " FILES";
154
0
  if (files.size() == 1) {
155
0
    os << " \"" << files[0] << "\"";
156
0
  } else {
157
0
    for (std::string const& f : files) {
158
0
      os << "\n" << indent << "  \"" << f << "\"";
159
0
    }
160
0
    if (filesVar) {
161
0
      os << " ${" << filesVar << "}";
162
0
    }
163
0
    os << "\n" << indent << " ";
164
0
    if (!(literalArgs && *literalArgs)) {
165
0
      os << " ";
166
0
    }
167
0
  }
168
0
  if (literalArgs && *literalArgs) {
169
0
    os << literalArgs;
170
0
  }
171
0
  os << ")\n";
172
0
}
173
174
std::string cmInstallGenerator::CreateComponentTest(
175
  std::string const& component, bool excludeFromAll, bool allComponents)
176
0
{
177
0
  if (allComponents) {
178
0
    if (excludeFromAll) {
179
0
      return "CMAKE_INSTALL_COMPONENT";
180
0
    }
181
0
    return {};
182
0
  }
183
184
0
  std::string result = "CMAKE_INSTALL_COMPONENT STREQUAL \"";
185
0
  result += component;
186
0
  result += "\"";
187
0
  if (!excludeFromAll) {
188
0
    result += " OR NOT CMAKE_INSTALL_COMPONENT";
189
0
  }
190
191
0
  return result;
192
0
}
193
194
void cmInstallGenerator::GenerateScript(std::ostream& os)
195
0
{
196
  // Track indentation.
197
0
  Indent indent;
198
199
0
  std::string componentTest = this->CreateComponentTest(
200
0
    this->Component, this->ExcludeFromAll, this->AllComponents);
201
202
  // Begin this block of installation.
203
0
  if (!componentTest.empty()) {
204
0
    os << indent << "if(" << componentTest << ")\n";
205
0
  }
206
207
  // Generate the script possibly with per-configuration code.
208
0
  this->GenerateScriptConfigs(os,
209
0
                              this->AllComponents ? indent : indent.Next());
210
211
  // End this block of installation.
212
0
  if (!componentTest.empty()) {
213
0
    os << indent << "endif()\n\n";
214
0
  }
215
0
}
216
217
bool cmInstallGenerator::InstallsForConfig(std::string const& config)
218
0
{
219
0
  return this->GeneratesForConfig(config);
220
0
}
221
222
std::string cmInstallGenerator::ConvertToAbsoluteDestination(
223
  std::string const& dest)
224
0
{
225
0
  if (dest == ".") {
226
0
    return "${CMAKE_INSTALL_PREFIX}";
227
0
  }
228
229
0
  std::string result;
230
0
  if (!dest.empty() && !cmSystemTools::FileIsFullPath(dest)) {
231
0
    result = "${CMAKE_INSTALL_PREFIX}/";
232
0
  }
233
0
  result += dest;
234
0
  return result;
235
0
}
236
237
void cmInstallGenerator::CheckAbsoluteDestination(
238
  std::string const& dest, cmLocalGenerator const* lg) const
239
0
{
240
0
  if (!cmSystemTools::FileIsFullPath(dest)) {
241
0
    return;
242
0
  }
243
0
  lg->IssueDiagnostic(
244
0
    cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION,
245
0
    cmStrCat("INSTALL command given absolute DESTINATION path:\n  ", dest),
246
0
    this->Context);
247
0
}
248
249
cmInstallGenerator::MessageLevel cmInstallGenerator::SelectMessageLevel(
250
  cmMakefile* mf, bool never)
251
0
{
252
0
  if (never) {
253
0
    return MessageNever;
254
0
  }
255
0
  std::string m = mf->GetSafeDefinition("CMAKE_INSTALL_MESSAGE");
256
0
  if (m == "ALWAYS") {
257
0
    return MessageAlways;
258
0
  }
259
0
  if (m == "LAZY") {
260
0
    return MessageLazy;
261
0
  }
262
0
  if (m == "NEVER") {
263
0
    return MessageNever;
264
0
  }
265
0
  return MessageDefault;
266
0
}
267
268
cmDiagnosticContext cmInstallGenerator::CaptureContext(cmMakefile const& mf)
269
0
{
270
0
  cmDiagnosticContext context{ mf.GetBacktrace() };
271
0
  context.RecordDiagnostic(cmDiagnostics::CMD_AUTHOR, mf.GetStateSnapshot());
272
0
  context.RecordDiagnostic(cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION,
273
0
                           mf.GetStateSnapshot());
274
0
  return context;
275
0
}
276
277
bool cmInstallGenerator::ResolveBuildDepends(
278
  cmLocalGenerator* lg, std::string const& signature,
279
  std::vector<std::string> const& names, BuildDependsEntries& entries)
280
0
{
281
0
  entries = BuildDependsEntries();
282
0
  cmGlobalGenerator* gg = lg->GetGlobalGenerator();
283
0
  std::vector<std::string> const configs =
284
0
    lg->GetMakefile()->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
285
0
  auto const isArtifact = [gg, &configs](std::string const& file) {
286
0
    return std::any_of(configs.begin(), configs.end(),
287
0
                       [gg, &file](std::string const& config) {
288
0
                         return gg->FindArtifactOwningTarget(file, config);
289
0
                       });
290
0
  };
291
0
  for (std::string const& name : names) {
292
0
    if (cmGeneratorTarget const* target = lg->FindGeneratorTargetToUse(name)) {
293
0
      entries.Targets.push_back(target);
294
0
      continue;
295
0
    }
296
    // A relative path is interpreted with respect to the current source
297
    // directory if the file exists there, and otherwise the current binary
298
    // directory.
299
0
    std::string file = lg->GetRealFileDependency(name);
300
0
    if (gg->IsGeneratedByBuild(file) || isArtifact(file) ||
301
0
        cmSystemTools::FileExists(file)) {
302
0
      entries.Files.push_back(std::move(file));
303
0
    } else {
304
0
      lg->IssueMessage(
305
0
        MessageType::FATAL_ERROR,
306
0
        cmStrCat("install(", signature, ") BUILD_DEPENDS entry \"", name,
307
0
                 "\" is neither a target nor a file that exists or is "
308
0
                 "generated by the build."),
309
0
        this->GetBacktrace());
310
0
      return false;
311
0
    }
312
0
  }
313
0
  return true;
314
0
}
315
316
void cmInstallGenerator::AddBuildDependsDependencies(
317
  cmLocalGenerator* lg, std::string const& config,
318
  BuildDependsEntries const& entries,
319
  std::vector<BuildDependency>& dependencies)
320
0
{
321
0
  for (cmGeneratorTarget const* target : entries.Targets) {
322
0
    dependencies.push_back({ target, BuildDependencyType::Normal });
323
0
  }
324
0
  AddGeneratedFileDependencies(lg->GetGlobalGenerator(), config, nullptr,
325
0
                               entries.Files, dependencies);
326
0
}
327
328
std::string cmInstallGenerator::GetDestDirPath(std::string const& file)
329
0
{
330
  // Construct the path of the file on disk after installation on
331
  // which tweaks may be performed.
332
0
  std::string toDestDirPath = "$ENV{DESTDIR}";
333
0
  if (file[0] != '/' && file[0] != '$') {
334
0
    toDestDirPath += "/";
335
0
  }
336
0
  toDestDirPath += file;
337
0
  return toDestDirPath;
338
0
}
339
340
void cmInstallGenerator::AddGeneratedFileDependencies(
341
  cmGlobalGenerator* gg, std::string const& config,
342
  cmGeneratorTarget const* target, std::vector<std::string> const& files,
343
  std::vector<BuildDependency>& dependencies)
344
0
{
345
0
  for (std::string const& file : files) {
346
0
    if (cmGeneratorTarget const* owner = gg->FindOutputOwningTarget(file)) {
347
0
      dependencies.push_back(
348
0
        { owner, BuildDependencyType::GeneratedFile, file });
349
0
    } else if (cmGeneratorTarget const* linker =
350
0
                 gg->FindArtifactOwningTarget(file, config)) {
351
0
      dependencies.push_back({ linker, BuildDependencyType::Normal });
352
0
    } else if (gg->IsGeneratedByBuild(file)) {
353
0
      dependencies.push_back(
354
0
        { target, BuildDependencyType::GeneratedFile, file });
355
0
    }
356
0
  }
357
0
}
358
359
void cmInstallGenerator::AddTweak(std::ostream& os, Indent indent,
360
                                  std::string const& config,
361
                                  std::string const& file,
362
                                  TweakMethod const& tweak)
363
0
{
364
0
  std::ostringstream tw;
365
0
  tweak(tw, indent.Next(), config, file);
366
0
  std::string tws = tw.str();
367
0
  if (!tws.empty()) {
368
0
    os << indent << "if(EXISTS \"" << file << "\" AND\n"
369
0
       << indent << "   NOT IS_SYMLINK \"" << file << "\")\n";
370
0
    os << tws;
371
0
    os << indent << "endif()\n";
372
0
  }
373
0
}
374
375
void cmInstallGenerator::AddTweak(std::ostream& os, Indent indent,
376
                                  std::string const& config,
377
                                  std::string const& dir,
378
                                  std::vector<std::string> const& files,
379
                                  TweakMethod const& tweak)
380
0
{
381
0
  if (files.size() == 1) {
382
    // Tweak a single file.
383
0
    AddTweak(os, indent, config, GetDestDirPath(cmStrCat(dir, files[0])),
384
0
             tweak);
385
0
  } else {
386
    // Generate a foreach loop to tweak multiple files.
387
0
    std::ostringstream tw;
388
0
    AddTweak(tw, indent.Next(), config, "${file}", tweak);
389
0
    std::string tws = tw.str();
390
0
    if (!tws.empty()) {
391
0
      Indent indent2 = indent.Next().Next();
392
0
      os << indent << "foreach(file\n";
393
0
      for (std::string const& f : files) {
394
0
        os << indent2 << "\"" << GetDestDirPath(cmStrCat(dir, f)) << "\"\n";
395
0
      }
396
0
      os << indent2 << ")\n";
397
0
      os << tws;
398
0
      os << indent << "endforeach()\n";
399
0
    }
400
0
  }
401
0
}