Coverage Report

Created: 2026-06-15 07:03

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 <sstream>
6
#include <utility>
7
8
#include <cm/string_view>
9
10
#include "cmDiagnostics.h"
11
#include "cmLocalGenerator.h"
12
#include "cmMakefile.h"
13
#include "cmStringAlgorithms.h"
14
#include "cmSystemTools.h"
15
16
cmInstallGenerator::cmInstallGenerator(
17
  std::string destination, std::vector<std::string> const& configurations,
18
  std::string component, MessageLevel message, bool excludeFromAll,
19
  bool allComponents, cmDiagnosticContext context)
20
0
  : cmScriptGenerator("CMAKE_INSTALL_CONFIG_NAME", configurations)
21
0
  , Destination(std::move(destination))
22
0
  , Component(std::move(component))
23
0
  , Message(message)
24
0
  , ExcludeFromAll(excludeFromAll)
25
0
  , AllComponents(allComponents)
26
0
  , Context{ std::move(context) }
27
0
{
28
0
}
29
30
0
cmInstallGenerator::~cmInstallGenerator() = default;
31
32
bool cmInstallGenerator::HaveInstall()
33
0
{
34
0
  return true;
35
0
}
36
37
void cmInstallGenerator::CheckCMP0082(bool& haveSubdirectoryInstall,
38
                                      bool& haveInstallAfterSubdirectory)
39
0
{
40
0
  if (haveSubdirectoryInstall) {
41
0
    haveInstallAfterSubdirectory = true;
42
0
  }
43
0
}
44
45
void cmInstallGenerator::AddInstallRule(
46
  std::ostream& os, std::string const& dest, cmInstallType type,
47
  std::vector<std::string> const& files, bool optional /* = false */,
48
  char const* permissionsFile /* = nullptr */,
49
  char const* permissionsDir /* = nullptr */,
50
  char const* rename /* = nullptr */, char const* literalArgs /* = nullptr */,
51
  Indent indent, char const* filesVar /* = nullptr */)
52
0
{
53
  // Use the FILE command to install the file.
54
0
  std::string stype;
55
0
  switch (type) {
56
0
    case cmInstallType_DIRECTORY:
57
0
      stype = "DIRECTORY";
58
0
      break;
59
0
    case cmInstallType_PROGRAMS:
60
0
      stype = "PROGRAM";
61
0
      break;
62
0
    case cmInstallType_EXECUTABLE:
63
0
      stype = "EXECUTABLE";
64
0
      break;
65
0
    case cmInstallType_STATIC_LIBRARY:
66
0
      stype = "STATIC_LIBRARY";
67
0
      break;
68
0
    case cmInstallType_SHARED_LIBRARY:
69
0
      stype = "SHARED_LIBRARY";
70
0
      break;
71
0
    case cmInstallType_MODULE_LIBRARY:
72
0
      stype = "MODULE";
73
0
      break;
74
0
    case cmInstallType_FILES:
75
0
      stype = "FILE";
76
0
      break;
77
0
  }
78
0
  if (cmSystemTools::FileIsFullPath(dest)) {
79
0
    if (!files.empty()) {
80
0
      os << indent << "list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n";
81
0
      os << indent << " \"";
82
0
      bool firstIteration = true;
83
0
      for (std::string const& file : files) {
84
0
        if (!firstIteration) {
85
0
          os << ";";
86
0
        }
87
0
        os << dest << "/";
88
0
        if (rename && *rename) {
89
0
          os << rename;
90
0
        } else {
91
0
          os << cmSystemTools::GetFilenameNameView(file);
92
0
        }
93
0
        firstIteration = false;
94
0
      }
95
0
      os << "\")\n";
96
0
    }
97
0
    if (filesVar) {
98
0
      os << indent << "foreach(_cmake_abs_file IN LISTS " << filesVar << ")\n";
99
0
      os << indent.Next()
100
0
         << "get_filename_component(_cmake_abs_file_name "
101
0
            "\"${_cmake_abs_file}\" NAME)\n";
102
0
      os << indent.Next() << "list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES \""
103
0
         << dest << "/${_cmake_abs_file_name}\")\n";
104
0
      os << indent << "endforeach()\n";
105
0
      os << indent << "unset(_cmake_abs_file_name)\n";
106
0
      os << indent << "unset(_cmake_abs_file)\n";
107
0
    }
108
0
    os << indent << "if(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n";
109
0
    os << indent.Next()
110
0
       << "message(WARNING \"ABSOLUTE path INSTALL "
111
0
          "DESTINATION : ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n";
112
0
    os << indent << "endif()\n";
113
114
0
    os << indent << "if(CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n";
115
0
    os << indent.Next()
116
0
       << "message(FATAL_ERROR \"ABSOLUTE path INSTALL "
117
0
          "DESTINATION forbidden (by caller): "
118
0
          "${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n";
119
0
    os << indent << "endif()\n";
120
0
  }
121
0
  std::string absDest = ConvertToAbsoluteDestination(dest);
122
0
  os << indent << "file(INSTALL DESTINATION \"" << absDest << "\" TYPE "
123
0
     << stype;
124
0
  if (optional) {
125
0
    os << " OPTIONAL";
126
0
  }
127
0
  switch (this->Message) {
128
0
    case MessageDefault:
129
0
      break;
130
0
    case MessageAlways:
131
0
      os << " MESSAGE_ALWAYS";
132
0
      break;
133
0
    case MessageLazy:
134
0
      os << " MESSAGE_LAZY";
135
0
      break;
136
0
    case MessageNever:
137
0
      os << " MESSAGE_NEVER";
138
0
      break;
139
0
  }
140
0
  if (permissionsFile && *permissionsFile) {
141
0
    os << " PERMISSIONS" << permissionsFile;
142
0
  }
143
0
  if (permissionsDir && *permissionsDir) {
144
0
    os << " DIR_PERMISSIONS" << permissionsDir;
145
0
  }
146
0
  if (rename && *rename) {
147
0
    os << " RENAME \"" << rename << "\"";
148
0
  }
149
0
  os << " FILES";
150
0
  if (files.size() == 1) {
151
0
    os << " \"" << files[0] << "\"";
152
0
  } else {
153
0
    for (std::string const& f : files) {
154
0
      os << "\n" << indent << "  \"" << f << "\"";
155
0
    }
156
0
    if (filesVar) {
157
0
      os << " ${" << filesVar << "}";
158
0
    }
159
0
    os << "\n" << indent << " ";
160
0
    if (!(literalArgs && *literalArgs)) {
161
0
      os << " ";
162
0
    }
163
0
  }
164
0
  if (literalArgs && *literalArgs) {
165
0
    os << literalArgs;
166
0
  }
167
0
  os << ")\n";
168
0
}
169
170
std::string cmInstallGenerator::CreateComponentTest(
171
  std::string const& component, bool excludeFromAll, bool allComponents)
172
0
{
173
0
  if (allComponents) {
174
0
    if (excludeFromAll) {
175
0
      return "CMAKE_INSTALL_COMPONENT";
176
0
    }
177
0
    return {};
178
0
  }
179
180
0
  std::string result = "CMAKE_INSTALL_COMPONENT STREQUAL \"";
181
0
  result += component;
182
0
  result += "\"";
183
0
  if (!excludeFromAll) {
184
0
    result += " OR NOT CMAKE_INSTALL_COMPONENT";
185
0
  }
186
187
0
  return result;
188
0
}
189
190
void cmInstallGenerator::GenerateScript(std::ostream& os)
191
0
{
192
  // Track indentation.
193
0
  Indent indent;
194
195
0
  std::string componentTest = this->CreateComponentTest(
196
0
    this->Component, this->ExcludeFromAll, this->AllComponents);
197
198
  // Begin this block of installation.
199
0
  if (!componentTest.empty()) {
200
0
    os << indent << "if(" << componentTest << ")\n";
201
0
  }
202
203
  // Generate the script possibly with per-configuration code.
204
0
  this->GenerateScriptConfigs(os,
205
0
                              this->AllComponents ? indent : indent.Next());
206
207
  // End this block of installation.
208
0
  if (!componentTest.empty()) {
209
0
    os << indent << "endif()\n\n";
210
0
  }
211
0
}
212
213
bool cmInstallGenerator::InstallsForConfig(std::string const& config)
214
0
{
215
0
  return this->GeneratesForConfig(config);
216
0
}
217
218
std::string cmInstallGenerator::ConvertToAbsoluteDestination(
219
  std::string const& dest)
220
0
{
221
0
  if (dest == ".") {
222
0
    return "${CMAKE_INSTALL_PREFIX}";
223
0
  }
224
225
0
  std::string result;
226
0
  if (!dest.empty() && !cmSystemTools::FileIsFullPath(dest)) {
227
0
    result = "${CMAKE_INSTALL_PREFIX}/";
228
0
  }
229
0
  result += dest;
230
0
  return result;
231
0
}
232
233
void cmInstallGenerator::CheckAbsoluteDestination(std::string const& dest,
234
                                                  cmLocalGenerator* lg) const
235
0
{
236
0
  if (!cmSystemTools::FileIsFullPath(dest)) {
237
0
    return;
238
0
  }
239
0
  lg->IssueDiagnostic(
240
0
    cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION,
241
0
    cmStrCat("INSTALL command given absolute DESTINATION path:\n  ", dest),
242
0
    this->Context);
243
0
}
244
245
cmInstallGenerator::MessageLevel cmInstallGenerator::SelectMessageLevel(
246
  cmMakefile* mf, bool never)
247
0
{
248
0
  if (never) {
249
0
    return MessageNever;
250
0
  }
251
0
  std::string m = mf->GetSafeDefinition("CMAKE_INSTALL_MESSAGE");
252
0
  if (m == "ALWAYS") {
253
0
    return MessageAlways;
254
0
  }
255
0
  if (m == "LAZY") {
256
0
    return MessageLazy;
257
0
  }
258
0
  if (m == "NEVER") {
259
0
    return MessageNever;
260
0
  }
261
0
  return MessageDefault;
262
0
}
263
264
cmDiagnosticContext cmInstallGenerator::CaptureContext(cmMakefile const& mf)
265
0
{
266
0
  cmDiagnosticContext context{ mf.GetBacktrace() };
267
0
  context.RecordDiagnostic(cmDiagnostics::CMD_INSTALL_ABSOLUTE_DESTINATION,
268
0
                           mf.GetStateSnapshot());
269
0
  return context;
270
0
}
271
272
std::string cmInstallGenerator::GetDestDirPath(std::string const& file)
273
0
{
274
  // Construct the path of the file on disk after installation on
275
  // which tweaks may be performed.
276
0
  std::string toDestDirPath = "$ENV{DESTDIR}";
277
0
  if (file[0] != '/' && file[0] != '$') {
278
0
    toDestDirPath += "/";
279
0
  }
280
0
  toDestDirPath += file;
281
0
  return toDestDirPath;
282
0
}
283
284
void cmInstallGenerator::AddTweak(std::ostream& os, Indent indent,
285
                                  std::string const& config,
286
                                  std::string const& file,
287
                                  TweakMethod const& tweak)
288
0
{
289
0
  std::ostringstream tw;
290
0
  tweak(tw, indent.Next(), config, file);
291
0
  std::string tws = tw.str();
292
0
  if (!tws.empty()) {
293
0
    os << indent << "if(EXISTS \"" << file << "\" AND\n"
294
0
       << indent << "   NOT IS_SYMLINK \"" << file << "\")\n";
295
0
    os << tws;
296
0
    os << indent << "endif()\n";
297
0
  }
298
0
}
299
300
void cmInstallGenerator::AddTweak(std::ostream& os, Indent indent,
301
                                  std::string const& config,
302
                                  std::string const& dir,
303
                                  std::vector<std::string> const& files,
304
                                  TweakMethod const& tweak)
305
0
{
306
0
  if (files.size() == 1) {
307
    // Tweak a single file.
308
0
    AddTweak(os, indent, config, GetDestDirPath(cmStrCat(dir, files[0])),
309
0
             tweak);
310
0
  } else {
311
    // Generate a foreach loop to tweak multiple files.
312
0
    std::ostringstream tw;
313
0
    AddTweak(tw, indent.Next(), config, "${file}", tweak);
314
0
    std::string tws = tw.str();
315
0
    if (!tws.empty()) {
316
0
      Indent indent2 = indent.Next().Next();
317
0
      os << indent << "foreach(file\n";
318
0
      for (std::string const& f : files) {
319
0
        os << indent2 << "\"" << GetDestDirPath(cmStrCat(dir, f)) << "\"\n";
320
0
      }
321
0
      os << indent2 << ")\n";
322
0
      os << tws;
323
0
      os << indent << "endforeach()\n";
324
0
    }
325
0
  }
326
0
}