Coverage Report

Created: 2026-03-12 06:35

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