Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmInstallExportGenerator.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 "cmInstallExportGenerator.h"
4
5
#include <map>
6
#include <ostream>
7
#include <utility>
8
9
#include "cmCryptoHash.h"
10
#include "cmDiagnosticContext.h"
11
#include "cmExportInstallFileGenerator.h"
12
#include "cmExportSet.h"
13
#include "cmGeneratorTarget.h"
14
#include "cmInstallType.h"
15
#include "cmLocalGenerator.h"
16
#include "cmScriptGenerator.h"
17
#include "cmStringAlgorithms.h"
18
#include "cmSystemTools.h"
19
#include "cmTargetExport.h"
20
21
cmInstallExportGenerator::cmInstallExportGenerator(
22
  cmExportSet* exportSet, std::string destination, std::string filePermissions,
23
  std::vector<std::string> const& configurations, std::string component,
24
  MessageLevel message, bool excludeFromAll, std::string filename,
25
  std::string targetNamespace, std::string cxxModulesDirectory,
26
  cmDiagnosticContext context)
27
0
  : cmInstallGenerator(std::move(destination), configurations,
28
0
                       std::move(component), message, excludeFromAll, false,
29
0
                       std::move(context))
30
0
  , ExportSet(exportSet)
31
0
  , FilePermissions(std::move(filePermissions))
32
0
  , FileName(std::move(filename))
33
0
  , Namespace(std::move(targetNamespace))
34
0
  , CxxModulesDirectory(std::move(cxxModulesDirectory))
35
0
{
36
0
  exportSet->AddInstallation(this);
37
0
}
38
39
0
cmInstallExportGenerator::~cmInstallExportGenerator() = default;
40
41
bool cmInstallExportGenerator::Compute(cmLocalGenerator* lg)
42
0
{
43
0
  this->LocalGenerator = lg;
44
0
  return this->ExportSet->Compute(lg);
45
0
}
46
47
void cmInstallExportGenerator::GetInstallBuildDependencies(
48
  std::string const&, std::vector<BuildDependency>& dependencies) const
49
0
{
50
0
  if (this->CxxModulesDirectory.empty()) {
51
0
    return;
52
0
  }
53
54
  // Module export metadata is generated during the target's build.
55
0
  for (auto const& targetExport : this->ExportSet->GetTargetExports()) {
56
0
    auto const* target = targetExport->Target;
57
0
    if (!targetExport->NamelinkOnly && target &&
58
0
        target->HaveCxx20ModuleSources()) {
59
0
      dependencies.push_back({ target, BuildDependencyType::Normal });
60
0
    }
61
0
  }
62
0
}
63
64
std::string cmInstallExportGenerator::TempDirCalculate() const
65
0
{
66
  // Choose a temporary directory in which to generate the import
67
  // files to be installed.
68
0
  std::string path = cmStrCat(
69
0
    this->LocalGenerator->GetCurrentBinaryDirectory(), "/CMakeFiles/Export");
70
0
  if (this->Destination.empty()) {
71
0
    return path;
72
0
  }
73
74
0
  cmCryptoHash hasher(cmCryptoHash::AlgoMD5);
75
0
  path += '/';
76
  // Replace the destination path with a hash to keep it short.
77
0
  path += hasher.HashString(this->Destination);
78
79
0
  return path;
80
0
}
81
82
void cmInstallExportGenerator::ComputeTempDir()
83
0
{
84
0
  this->TempDir = this->TempDirCalculate();
85
0
}
86
87
std::string cmInstallExportGenerator::GetTempDir() const
88
0
{
89
0
  if (this->TempDir.empty()) {
90
0
    return this->TempDirCalculate();
91
0
  }
92
0
  return this->TempDir;
93
0
}
94
95
void cmInstallExportGenerator::GenerateScript(std::ostream& os)
96
0
{
97
  // Skip empty sets.
98
0
  if (this->ExportSet->GetTargetExports().empty()) {
99
0
    cmSystemTools::Error(cmStrCat("INSTALL(", this->InstallSubcommand(),
100
0
                                  ") given unknown export \"",
101
0
                                  this->ExportSet->GetName(), '"'));
102
0
    return;
103
0
  }
104
105
  // Create the temporary directory in which to store the files.
106
0
  this->ComputeTempDir();
107
0
  cmSystemTools::MakeDirectory(this->TempDir);
108
109
  // Construct a temporary location for the file.
110
0
  this->MainImportFile = cmStrCat(this->TempDir, '/', this->FileName);
111
112
  // Generate the import file for this export set.
113
0
  this->EFGen->SetExportFile(this->MainImportFile.c_str());
114
0
  this->EFGen->SetNamespace(this->Namespace);
115
0
  if (this->ConfigurationTypes->empty()) {
116
0
    if (!this->ConfigurationName.empty()) {
117
0
      this->EFGen->AddConfiguration(this->ConfigurationName);
118
0
    } else {
119
0
      this->EFGen->AddConfiguration("");
120
0
    }
121
0
  } else {
122
0
    for (std::string const& c : *this->ConfigurationTypes) {
123
0
      this->EFGen->AddConfiguration(c);
124
0
    }
125
0
  }
126
0
  this->EFGen->GenerateImportFile();
127
128
  // Perform the main install script generation.
129
0
  this->cmInstallGenerator::GenerateScript(os);
130
0
}
131
132
void cmInstallExportGenerator::GenerateScriptConfigs(std::ostream& os,
133
                                                     Indent indent)
134
0
{
135
  // Create the main install rules first.
136
0
  this->cmInstallGenerator::GenerateScriptConfigs(os, indent);
137
138
  // Now create a configuration-specific install rule for the import
139
  // file of each configuration.
140
0
  std::vector<std::string> files;
141
0
  for (auto const& i : this->EFGen->GetConfigImportFiles()) {
142
0
    files.push_back(i.second);
143
0
    std::string configTest = this->CreateConfigTest(i.first);
144
0
    os << indent << "if(" << configTest << ")\n";
145
0
    this->AddInstallRule(os, this->Destination, cmInstallType_FILES, files,
146
0
                         false, this->FilePermissions.c_str(), nullptr,
147
0
                         nullptr, nullptr, indent.Next());
148
0
    os << indent << "endif()\n";
149
0
    files.clear();
150
0
  }
151
152
  // Now create a configuration-specific install rule for the C++ module import
153
  // property file of each configuration.
154
0
  auto const cxxModuleDestination =
155
0
    cmStrCat(this->Destination, '/', this->CxxModulesDirectory);
156
0
  auto const cxxModuleInstallFilePath = this->EFGen->GetCxxModuleFile();
157
0
  auto const configImportFilesGlob = this->EFGen->GetConfigImportFileGlob();
158
0
  if (!cxxModuleInstallFilePath.empty() && !configImportFilesGlob.empty()) {
159
0
    auto const cxxModuleFilename =
160
0
      cmSystemTools::GetFilenameName(cxxModuleInstallFilePath);
161
162
    // Remove old per-configuration export files if the main changes.
163
0
    std::string installedDir =
164
0
      cmStrCat("$ENV{DESTDIR}",
165
0
               ConvertToAbsoluteDestination(cxxModuleDestination), '/');
166
0
    std::string installedFile = cmStrCat(installedDir, cxxModuleFilename);
167
0
    os << indent << "if(EXISTS \"" << installedFile << "\")\n";
168
0
    Indent indentN = indent.Next();
169
0
    Indent indentNN = indentN.Next();
170
0
    Indent indentNNN = indentNN.Next();
171
0
    os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n"
172
0
       << indentN << "     \"" << installedFile << "\"\n"
173
0
       << indentN << "     \"" << cxxModuleInstallFilePath << "\")\n";
174
0
    os << indentN << "if(_cmake_export_file_changed)\n";
175
0
    os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir
176
0
       << configImportFilesGlob << "\")\n";
177
0
    os << indentNN << "if(_cmake_old_config_files)\n";
178
0
    os << indentNNN
179
0
       << "string(REPLACE \";\" \", \" _cmake_old_config_files_text "
180
0
          "\"${_cmake_old_config_files}\")\n";
181
0
    os << indentNNN << R"(message(STATUS "Old C++ module export file \")"
182
0
       << installedFile
183
0
       << "\\\" will be replaced.  "
184
0
          "Removing files [${_cmake_old_config_files_text}].\")\n";
185
0
    os << indentNNN << "unset(_cmake_old_config_files_text)\n";
186
0
    os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n";
187
0
    os << indentNN << "endif()\n";
188
0
    os << indentNN << "unset(_cmake_old_config_files)\n";
189
0
    os << indentN << "endif()\n";
190
0
    os << indentN << "unset(_cmake_export_file_changed)\n";
191
0
    os << indent << "endif()\n";
192
193
    // All of these files are siblings; get its location to know where the
194
    // "anchor" file is.
195
0
    files.push_back(cxxModuleInstallFilePath);
196
0
    this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, files,
197
0
                         false, this->FilePermissions.c_str(), nullptr,
198
0
                         nullptr, nullptr, indent);
199
0
    files.clear();
200
0
  }
201
0
  for (auto const& i : this->EFGen->GetConfigCxxModuleFiles()) {
202
0
    files.push_back(i.second);
203
0
    std::string configTest = this->CreateConfigTest(i.first);
204
0
    os << indent << "if(" << configTest << ")\n";
205
0
    this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES, files,
206
0
                         false, this->FilePermissions.c_str(), nullptr,
207
0
                         nullptr, nullptr, indent.Next());
208
0
    os << indent << "endif()\n";
209
0
    files.clear();
210
0
  }
211
0
  for (auto const& i : this->EFGen->GetConfigCxxModuleTargetFiles()) {
212
0
    std::string configTest = this->CreateConfigTest(i.first);
213
0
    os << indent << "if(" << configTest << ")\n";
214
0
    this->AddInstallRule(os, cxxModuleDestination, cmInstallType_FILES,
215
0
                         i.second, false, this->FilePermissions.c_str(),
216
0
                         nullptr, nullptr, nullptr, indent.Next());
217
0
    os << indent << "endif()\n";
218
0
    files.clear();
219
0
  }
220
0
}
221
222
void cmInstallExportGenerator::GenerateScriptActions(std::ostream& os,
223
                                                     Indent indent)
224
0
{
225
0
  auto const configImportFilesGlob = this->EFGen->GetConfigImportFileGlob();
226
0
  if (!configImportFilesGlob.empty()) {
227
    // Remove old per-configuration export files if the main changes.
228
0
    std::string installedDir = cmStrCat(
229
0
      "$ENV{DESTDIR}", ConvertToAbsoluteDestination(this->Destination), '/');
230
0
    std::string installedFile = cmStrCat(installedDir, this->FileName);
231
0
    os << indent << "if(EXISTS \"" << installedFile << "\")\n";
232
0
    Indent indentN = indent.Next();
233
0
    Indent indentNN = indentN.Next();
234
0
    Indent indentNNN = indentNN.Next();
235
0
    os << indentN << "file(DIFFERENT _cmake_export_file_changed FILES\n"
236
0
       << indentN << "     \"" << installedFile << "\"\n"
237
0
       << indentN << "     \"" << this->MainImportFile << "\")\n";
238
0
    os << indentN << "if(_cmake_export_file_changed)\n";
239
0
    os << indentNN << "file(GLOB _cmake_old_config_files \"" << installedDir
240
0
       << configImportFilesGlob << "\")\n";
241
0
    os << indentNN << "if(_cmake_old_config_files)\n";
242
0
    os << indentNNN
243
0
       << "string(REPLACE \";\" \", \" _cmake_old_config_files_text "
244
0
          "\"${_cmake_old_config_files}\")\n";
245
0
    os << indentNNN << R"(message(STATUS "Old export file \")" << installedFile
246
0
       << "\\\" will be replaced.  "
247
0
          "Removing files [${_cmake_old_config_files_text}].\")\n";
248
0
    os << indentNNN << "unset(_cmake_old_config_files_text)\n";
249
0
    os << indentNNN << "file(REMOVE ${_cmake_old_config_files})\n";
250
0
    os << indentNN << "endif()\n";
251
0
    os << indentNN << "unset(_cmake_old_config_files)\n";
252
0
    os << indentN << "endif()\n";
253
0
    os << indentN << "unset(_cmake_export_file_changed)\n";
254
0
    os << indent << "endif()\n";
255
0
  }
256
257
  // Install the main export file.
258
0
  std::vector<std::string> files;
259
0
  files.push_back(this->MainImportFile);
260
0
  this->AddInstallRule(os, this->Destination, cmInstallType_FILES, files,
261
0
                       false, this->FilePermissions.c_str(), nullptr, nullptr,
262
0
                       nullptr, indent);
263
0
}
264
265
std::string cmInstallExportGenerator::GetDestinationFile() const
266
0
{
267
0
  return cmStrCat(this->Destination, '/', this->FileName);
268
0
}