Coverage Report

Created: 2026-07-14 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmExportInstallPackageInfoGenerator.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 "cmExportInstallPackageInfoGenerator.h"
4
5
#include <functional>
6
#include <map>
7
#include <memory>
8
#include <set>
9
#include <utility>
10
#include <vector>
11
12
#include <cm/optional>
13
#include <cm/string_view>
14
#include <cmext/algorithm>
15
#include <cmext/string_view>
16
17
#include <cm3p/json/value.h>
18
19
#include "cmAlgorithms.h"
20
#include "cmDiagnostics.h"
21
#include "cmExportSet.h"
22
#include "cmFileSetMetadata.h"
23
#include "cmGenExContext.h"
24
#include "cmGeneratorExpression.h"
25
#include "cmGeneratorFileSet.h"
26
#include "cmGeneratorTarget.h"
27
#include "cmInstallExportGenerator.h"
28
#include "cmInstallFileSetGenerator.h"
29
#include "cmList.h"
30
#include "cmLocalGenerator.h"
31
#include "cmMakefile.h"
32
#include "cmMessageType.h"
33
#include "cmOutputConverter.h"
34
#include "cmPackageInfoArguments.h"
35
#include "cmStringAlgorithms.h"
36
#include "cmSystemTools.h"
37
#include "cmTarget.h"
38
#include "cmTargetExport.h"
39
#include "cmTargetTypes.h"
40
41
cmExportInstallPackageInfoGenerator::cmExportInstallPackageInfoGenerator(
42
  cmInstallExportGenerator* iegen, cmPackageInfoArguments arguments)
43
0
  : cmExportPackageInfoGenerator(std::move(arguments))
44
0
  , cmExportInstallFileGenerator(iegen)
45
0
{
46
0
}
Unexecuted instantiation: cmExportInstallPackageInfoGenerator::cmExportInstallPackageInfoGenerator(cmInstallExportGenerator*, cmPackageInfoArguments)
Unexecuted instantiation: cmExportInstallPackageInfoGenerator::cmExportInstallPackageInfoGenerator(cmInstallExportGenerator*, cmPackageInfoArguments)
47
48
std::string cmExportInstallPackageInfoGenerator::GetConfigImportFileGlob()
49
  const
50
0
{
51
0
  std::string glob = cmStrCat(this->FileBase, "@*", this->FileExt);
52
0
  return glob;
53
0
}
54
55
std::string const& cmExportInstallPackageInfoGenerator::GetExportName() const
56
0
{
57
0
  return this->GetPackageName();
58
0
}
59
60
bool cmExportInstallPackageInfoGenerator::GenerateMainFile(std::ostream& os)
61
0
{
62
0
  std::vector<cmTargetExport const*> allTargets;
63
0
  {
64
0
    auto visitor = [&](cmTargetExport const* te) { allTargets.push_back(te); };
65
66
0
    if (!this->CollectExports(visitor)) {
67
0
      return false;
68
0
    }
69
0
  }
70
71
0
  if (!this->CheckPackage()) {
72
0
    return false;
73
0
  }
74
75
0
  Json::Value root = this->GeneratePackageInfo();
76
0
  Json::Value& components = root["components"];
77
78
  // Compute the relative import prefix for the file
79
0
  std::string const& packagePath = this->GenerateImportPrefix();
80
0
  if (packagePath.empty()) {
81
0
    return false;
82
0
  }
83
0
  root["cps_path"] = packagePath;
84
85
  // Create all the imported targets.
86
0
  for (cmTargetExport const* te : allTargets) {
87
0
    cmGeneratorTarget* gt = te->Target;
88
0
    cm::TargetType targetType = this->GetExportTargetType(te);
89
90
0
    Json::Value* const component =
91
0
      this->GenerateImportTarget(components, gt, targetType);
92
0
    if (!component) {
93
0
      return false;
94
0
    }
95
96
0
    ImportPropertyMap properties;
97
0
    if (!this->PopulateInterfaceProperties(te, properties)) {
98
0
      return false;
99
0
    }
100
0
    this->PopulateInterfaceLinkLibrariesProperty(
101
0
      gt, cmGeneratorExpression::InstallInterface, properties);
102
103
0
    if (targetType != cm::TargetType::INTERFACE_LIBRARY) {
104
0
      this->RequiresConfigFiles = true;
105
0
    }
106
107
    // De-duplicate include directories prior to generation.
108
0
    auto it = properties.find("INTERFACE_INCLUDE_DIRECTORIES");
109
0
    if (it != properties.end()) {
110
0
      auto list = cmList{ it->second };
111
0
      list = cmList{ list.begin(), cmRemoveDuplicates(list) };
112
0
      properties["INTERFACE_INCLUDE_DIRECTORIES"] = list.to_string();
113
0
    }
114
115
    // Set configuration-agnostic properties for component.
116
0
    this->GenerateInterfaceProperties(*component, gt, properties);
117
0
    if (!this->GenerateFileSetProperties(*component, gt, te, packagePath)) {
118
0
      return false;
119
0
    }
120
0
    this->GenerateTargetFileSets(*component, gt, te);
121
0
  }
122
123
0
  this->GeneratePackageRequires(root);
124
125
  // Write the primary packing information file.
126
0
  this->WritePackageInfo(root, os);
127
128
0
  bool result = true;
129
130
  // Generate an import file for each configuration.
131
0
  if (this->RequiresConfigFiles) {
132
0
    for (std::string const& c : this->Configurations) {
133
0
      if (!this->GenerateImportFileConfig(c)) {
134
0
        result = false;
135
0
      }
136
0
    }
137
0
  }
138
139
0
  return result;
140
0
}
141
142
void cmExportInstallPackageInfoGenerator::GenerateImportTargetsConfig(
143
  std::ostream& os, std::string const& config, std::string const& suffix)
144
0
{
145
0
  Json::Value root;
146
0
  root["name"] = this->GetPackageName();
147
0
  root["configuration"] = (config.empty() ? "noconfig" : config);
148
149
0
  std::string const& packagePath = this->GenerateImportPrefix();
150
151
0
  Json::Value& components = root["components"];
152
153
0
  for (auto const& te : this->GetExportSet()->GetTargetExports()) {
154
    // Collect import properties for this target.
155
0
    ImportPropertyMap properties;
156
0
    std::set<std::string> importedLocations;
157
158
0
    if (this->GetExportTargetType(te.get()) !=
159
0
        cm::TargetType::INTERFACE_LIBRARY) {
160
0
      this->PopulateImportProperties(config, suffix, te.get(), properties,
161
0
                                     importedLocations);
162
0
    }
163
164
0
    Json::Value component =
165
0
      this->GenerateInterfaceConfigProperties(suffix, properties);
166
0
    this->GenerateFileSetProperties(component, te->Target, te.get(),
167
0
                                    packagePath, config);
168
169
0
    if (!component.empty()) {
170
0
      components[te->Target->GetExportName()] = std::move(component);
171
0
    }
172
0
  }
173
174
0
  this->WritePackageInfo(root, os);
175
0
}
176
177
std::string cmExportInstallPackageInfoGenerator::GenerateImportPrefix() const
178
0
{
179
0
  std::string expDest = this->IEGen->GetDestination();
180
0
  if (cmSystemTools::FileIsFullPath(expDest)) {
181
0
    std::string const& installPrefix =
182
0
      this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition(
183
0
        "CMAKE_INSTALL_PREFIX");
184
0
    if (cmHasPrefix(expDest, installPrefix)) {
185
0
      auto n = installPrefix.length();
186
0
      while (n < expDest.length() && expDest[n] == '/') {
187
0
        ++n;
188
0
      }
189
0
      expDest = expDest.substr(n);
190
0
    } else {
191
0
      this->ReportError(
192
0
        cmStrCat("install(PACKAGE_INFO \"", this->GetExportName(),
193
0
                 "\" ...) specifies DESTINATION \"", expDest,
194
0
                 "\" which is not a subdirectory of the install prefix."));
195
0
      return {};
196
0
    }
197
0
  }
198
199
0
  if (expDest.empty()) {
200
0
    return this->GetInstallPrefix();
201
0
  }
202
0
  return cmStrCat(this->GetImportPrefixWithSlash(), expDest);
203
0
}
204
205
std::string cmExportInstallPackageInfoGenerator::InstallNameDir(
206
  cmGeneratorTarget const* target, std::string const& config)
207
0
{
208
0
  std::string install_name_dir;
209
210
0
  cmMakefile* mf = target->Target->GetMakefile();
211
0
  if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
212
0
    install_name_dir =
213
0
      target->GetInstallNameDirForInstallTree(config, "@prefix@");
214
0
  }
215
216
0
  return install_name_dir;
217
0
}
218
219
std::string cmExportInstallPackageInfoGenerator::GetCxxModulesDirectory() const
220
0
{
221
0
  return IEGen->GetCxxModuleDirectory();
222
0
}
223
224
cm::optional<std::string>
225
cmExportInstallPackageInfoGenerator::GetFileSetDirectory(
226
  cmGeneratorTarget const* target, cmTargetExport const* targetExport,
227
  cmGeneratorFileSet const* fileSet, cm::optional<std::string> const& config)
228
0
{
229
0
  cmInstallFileSetGenerator const* const fsg =
230
0
    targetExport->FileSetGenerators.at(fileSet->GetName());
231
0
  cmInstallFileSetGenerator::DestinationContext const result =
232
0
    fsg->GetDestination(target, config.value_or(""));
233
234
0
  if (!config == result.HadContextSensitiveCondition) {
235
0
    return {};
236
0
  }
237
238
  // Use cm::optional here to enable NRVO.
239
0
  cm::optional<std::string> dest = cmOutputConverter::EscapeForCMake(
240
0
    result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap);
241
242
0
  if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) {
243
0
    dest = cmStrCat("@prefix@/"_s, *dest);
244
0
  }
245
246
0
  return dest;
247
0
}
248
249
bool cmExportInstallPackageInfoGenerator::GenerateFileSetProperties(
250
  Json::Value& component, cmGeneratorTarget* gte, cmTargetExport const* te,
251
  std::string const& packagePath, cm::optional<std::string> config)
252
0
{
253
0
  bool hasModules = false;
254
0
  std::set<std::string> seenIncludeDirectories;
255
0
  for (auto const& name : gte->Target->GetAllInterfaceFileSets()) {
256
0
    cmGeneratorFileSet const* fileSet = gte->GetFileSet(name);
257
258
0
    if (!fileSet) {
259
0
      this->IssueMessage(MessageType::FATAL_ERROR,
260
0
                         cmStrCat("File set \"", name,
261
0
                                  "\" is listed in interface file sets of ",
262
0
                                  gte->GetName(),
263
0
                                  " but has not been created"));
264
0
      return false;
265
0
    }
266
267
0
    cm::optional<std::string> const& fileSetDirectory =
268
0
      this->GetFileSetDirectory(gte, te, fileSet, config);
269
270
0
    if (fileSet->GetType() == cm::FileSetMetadata::HEADERS) {
271
0
      if (!fileSetDirectory) {
272
0
        if (!config) {
273
0
          this->RequiresConfigFiles = true;
274
0
        }
275
0
      } else if (!cm::contains(seenIncludeDirectories, *fileSetDirectory)) {
276
0
        component["includes"].append(*fileSetDirectory);
277
0
        seenIncludeDirectories.insert(*fileSetDirectory);
278
0
      }
279
0
    } else if (fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES) {
280
0
      hasModules = true;
281
0
      this->RequiresConfigFiles = true;
282
0
    }
283
0
  }
284
285
0
  if (hasModules && config) {
286
0
    std::string const manifestPath =
287
0
      this->GenerateCxxModules(component, gte, packagePath, *config);
288
0
    if (!manifestPath.empty()) {
289
0
      this->ConfigCxxModuleFiles[*config] =
290
0
        cmStrCat(this->FileDir, '/', manifestPath);
291
0
    }
292
0
  }
293
0
  return true;
294
0
}
295
296
void cmExportInstallPackageInfoGenerator::GenerateTargetFileSets(
297
  Json::Value& fileSets, cmGeneratorTarget const* target,
298
  cmGeneratorFileSet const* fileSet, cmTargetExport const* targetExport,
299
  std::string const& type) const
300
0
{
301
0
  cm::optional<std::string> dest =
302
0
    this->GetFileSetDirectory(target, targetExport, fileSet);
303
0
  if (!dest) {
304
0
    this->IssueDiagnostic(
305
0
      cmDiagnostics::CMD_AUTHOR,
306
0
      cmStrCat("The \""_s, target->GetName(),
307
0
               "\" target's interface file set \""_s, fileSet->GetName(),
308
0
               "\" of type \""_s, fileSet->GetType(),
309
0
               "\" has a context-sensitive destination, which is not "
310
0
               "supported.  The file set will not be exported."_s));
311
0
    return;
312
0
  }
313
314
0
  cm::GenEx::Context context{ target->LocalGenerator, {} };
315
316
0
  std::vector<std::string> files;
317
0
  auto eval = [&files](std::string&& /*baseDir*/, std::string&& relPath,
318
0
                       std::string&& /*file*/) {
319
0
    files.emplace_back(std::move(relPath));
320
0
  };
321
322
0
  if (fileSet->EvaluateFiles(context, target, eval)) {
323
0
    this->IssueDiagnostic(
324
0
      cmDiagnostics::CMD_AUTHOR,
325
0
      cmStrCat("The \""_s, target->GetName(),
326
0
               "\" target's interface file set \""_s, fileSet->GetName(),
327
0
               "\" of type \""_s, fileSet->GetType(),
328
0
               "\" contains context-sensitive information, which is not "
329
0
               "supported.  The file set will not be exported."_s));
330
0
    return;
331
0
  }
332
333
0
  this->GenerateTargetFileSet(fileSets, fileSet, type, *dest, files);
334
0
}