Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmExportInstallCMakeConfigGenerator.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 "cmExportInstallCMakeConfigGenerator.h"
4
5
#include <functional>
6
#include <map>
7
#include <memory>
8
#include <ostream>
9
#include <set>
10
#include <utility>
11
#include <vector>
12
13
#include "cmExportFileGenerator.h"
14
#include "cmExportSet.h"
15
#include "cmGenExContext.h"
16
#include "cmGeneratedFileStream.h"
17
#include "cmGeneratorExpression.h"
18
#include "cmGeneratorFileSet.h"
19
#include "cmGeneratorTarget.h"
20
#include "cmInstallExportGenerator.h"
21
#include "cmInstallFileSetGenerator.h"
22
#include "cmLocalGenerator.h"
23
#include "cmMakefile.h"
24
#include "cmOutputConverter.h"
25
#include "cmStringAlgorithms.h"
26
#include "cmSystemTools.h"
27
#include "cmTargetExport.h"
28
#include "cmTargetTypes.h"
29
#include "cmValue.h"
30
31
cmExportInstallCMakeConfigGenerator::cmExportInstallCMakeConfigGenerator(
32
  cmInstallExportGenerator* iegen)
33
0
  : cmExportInstallFileGenerator(iegen)
34
0
{
35
0
}
Unexecuted instantiation: cmExportInstallCMakeConfigGenerator::cmExportInstallCMakeConfigGenerator(cmInstallExportGenerator*)
Unexecuted instantiation: cmExportInstallCMakeConfigGenerator::cmExportInstallCMakeConfigGenerator(cmInstallExportGenerator*)
36
37
std::string cmExportInstallCMakeConfigGenerator::GetConfigImportFileGlob()
38
  const
39
0
{
40
0
  std::string glob = cmStrCat(this->FileBase, "-*", this->FileExt);
41
0
  return glob;
42
0
}
43
44
bool cmExportInstallCMakeConfigGenerator::GenerateMainFile(std::ostream& os)
45
0
{
46
0
  std::vector<cmTargetExport const*> allTargets;
47
0
  {
48
0
    std::string expectedTargets;
49
0
    std::string sep;
50
0
    auto visitor = [&](cmTargetExport const* te) {
51
0
      allTargets.push_back(te);
52
0
      expectedTargets = cmStrCat(std::move(expectedTargets), sep,
53
0
                                 this->Namespace, te->Target->GetExportName());
54
0
      sep = " ";
55
0
    };
56
57
0
    if (!this->CollectExports(visitor)) {
58
0
      return false;
59
0
    }
60
61
0
    this->GenerateExpectedTargetsCode(os, expectedTargets);
62
0
  }
63
64
  // Compute the relative import prefix for the file
65
0
  this->GenerateImportPrefix(os);
66
67
0
  bool requiresConfigFiles = false;
68
  // Create all the imported targets.
69
0
  for (cmTargetExport const* te : allTargets) {
70
0
    cmGeneratorTarget* gt = te->Target;
71
0
    cm::TargetType targetType = this->GetExportTargetType(te);
72
73
0
    requiresConfigFiles =
74
0
      requiresConfigFiles || targetType != cm::TargetType::INTERFACE_LIBRARY;
75
76
0
    this->GenerateImportTargetCode(os, gt, targetType);
77
78
0
    ImportPropertyMap properties;
79
0
    if (!this->PopulateInterfaceProperties(te, properties)) {
80
0
      return false;
81
0
    }
82
83
0
    ImportFileSetPropertyMap fsProperties;
84
0
    if (!this->PopulateFileSetInterfaceProperties(te, fsProperties)) {
85
0
      return false;
86
0
    }
87
88
0
    if (this->PopulateInterfaceLinkLibrariesProperty(
89
0
          gt, cmGeneratorExpression::InstallInterface, properties) &&
90
0
        !this->ExportOld) {
91
0
      this->SetRequiredCMakeVersion(2, 8, 12);
92
0
    }
93
0
    if (targetType == cm::TargetType::INTERFACE_LIBRARY) {
94
0
      this->SetRequiredCMakeVersion(3, 0, 0);
95
0
    }
96
0
    if (gt->GetProperty("INTERFACE_SOURCES")) {
97
      // We can only generate INTERFACE_SOURCES in CMake 3.3, but CMake 3.1
98
      // can consume them.
99
0
      this->SetRequiredCMakeVersion(3, 1, 0);
100
0
    }
101
102
0
    this->GenerateInterfaceProperties(gt, os, properties);
103
104
0
    this->GenerateTargetFileSets(gt, os, fsProperties, te);
105
0
  }
106
107
0
  this->LoadConfigFiles(os);
108
109
0
  bool result = true;
110
111
0
  std::string cxx_modules_name = this->GetExportSet()->GetName();
112
0
  this->GenerateCxxModuleInformation(cxx_modules_name, os);
113
0
  if (requiresConfigFiles) {
114
0
    for (std::string const& c : this->Configurations) {
115
0
      if (!this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name,
116
0
                                                              c)) {
117
0
        result = false;
118
0
      }
119
0
    }
120
0
  }
121
122
0
  this->CleanupTemporaryVariables(os);
123
0
  this->GenerateImportedFileCheckLoop(os);
124
125
  // Generate an import file for each configuration.
126
  // Don't do this if we only export INTERFACE_LIBRARY targets.
127
0
  if (requiresConfigFiles) {
128
0
    for (std::string const& c : this->Configurations) {
129
0
      if (!this->GenerateImportFileConfig(c)) {
130
0
        result = false;
131
0
      }
132
0
    }
133
0
  }
134
135
0
  this->GenerateMissingTargetsCheckCode(os);
136
137
0
  return result;
138
0
}
139
140
void cmExportInstallCMakeConfigGenerator::GenerateImportPrefix(
141
  std::ostream& os)
142
0
{
143
  // Set an _IMPORT_PREFIX variable for import location properties
144
  // to reference if they are relative to the install prefix.
145
0
  std::string installPrefix =
146
0
    this->IEGen->GetLocalGenerator()->GetMakefile()->GetSafeDefinition(
147
0
      "CMAKE_INSTALL_PREFIX");
148
0
  std::string const& expDest = this->IEGen->GetDestination();
149
0
  if (cmSystemTools::FileIsFullPath(expDest)) {
150
    // The export file is being installed to an absolute path so the
151
    // package is not relocatable.  Use the configured install prefix.
152
    /* clang-format off */
153
0
    os <<
154
0
      "# The installation prefix configured by this project.\n"
155
0
      "set(_IMPORT_PREFIX \"" << installPrefix << "\")\n"
156
0
      "\n";
157
    /* clang-format on */
158
0
  } else {
159
    // Add code to compute the installation prefix relative to the
160
    // import file location.
161
0
    std::string absDest = cmStrCat(installPrefix, '/', expDest);
162
0
    std::string absDestS = absDest + '/';
163
0
    os << "# Compute the installation prefix relative to this file.\n"
164
0
          "get_filename_component(_IMPORT_PREFIX"
165
0
          " \"${CMAKE_CURRENT_LIST_FILE}\" PATH)\n";
166
0
    if (cmHasLiteralPrefix(absDestS, "/lib/") ||
167
0
        cmHasLiteralPrefix(absDestS, "/lib64/") ||
168
0
        cmHasLiteralPrefix(absDestS, "/libx32/") ||
169
0
        cmHasLiteralPrefix(absDestS, "/usr/lib/") ||
170
0
        cmHasLiteralPrefix(absDestS, "/usr/lib64/") ||
171
0
        cmHasLiteralPrefix(absDestS, "/usr/libx32/")) {
172
      // Handle "/usr move" symlinks created by some Linux distros.
173
      /* clang-format off */
174
0
      os <<
175
0
        "# Use original install prefix when loaded through a\n"
176
0
        "# cross-prefix symbolic link such as /lib -> /usr/lib.\n"
177
0
        "get_filename_component(_realCurr \"${_IMPORT_PREFIX}\" REALPATH)\n"
178
0
        "get_filename_component(_realOrig \"" << absDest << "\" REALPATH)\n"
179
0
        "if(_realCurr STREQUAL _realOrig)\n"
180
0
        "  set(_IMPORT_PREFIX \"" << absDest << "\")\n"
181
0
        "endif()\n"
182
0
        "unset(_realOrig)\n"
183
0
        "unset(_realCurr)\n";
184
      /* clang-format on */
185
0
    }
186
0
    std::string dest = expDest;
187
0
    while (!dest.empty()) {
188
0
      os << "get_filename_component(_IMPORT_PREFIX \"${_IMPORT_PREFIX}\" "
189
0
            "PATH)\n";
190
0
      dest = cmSystemTools::GetFilenamePath(dest);
191
0
    }
192
0
    os << "if(_IMPORT_PREFIX STREQUAL \"/\")\n"
193
0
          "  set(_IMPORT_PREFIX \"\")\n"
194
0
          "endif()\n"
195
0
          "\n";
196
0
  }
197
0
}
198
199
void cmExportInstallCMakeConfigGenerator::CleanupTemporaryVariables(
200
  std::ostream& os)
201
0
{
202
  /* clang-format off */
203
0
  os << "# Cleanup temporary variables.\n"
204
0
        "set(_IMPORT_PREFIX)\n"
205
0
        "\n";
206
  /* clang-format on */
207
0
}
208
209
void cmExportInstallCMakeConfigGenerator::LoadConfigFiles(std::ostream& os)
210
0
{
211
  // Now load per-configuration properties for them.
212
  /* clang-format off */
213
0
  os << "# Load information for each installed configuration.\n"
214
0
        "file(GLOB _cmake_config_files \"${CMAKE_CURRENT_LIST_DIR}/"
215
0
        << this->GetConfigImportFileGlob() << "\")\n"
216
0
        "foreach(_cmake_config_file IN LISTS _cmake_config_files)\n"
217
0
        "  include(\"${_cmake_config_file}\")\n"
218
0
        "endforeach()\n"
219
0
        "unset(_cmake_config_file)\n"
220
0
        "unset(_cmake_config_files)\n"
221
0
        "\n";
222
  /* clang-format on */
223
0
}
224
225
void cmExportInstallCMakeConfigGenerator::GenerateImportConfig(
226
  std::ostream& os, std::string const& config)
227
0
{
228
  // Start with the import file header.
229
0
  this->GenerateImportHeaderCode(os, config);
230
231
  // Generate the per-config target information.
232
0
  this->cmExportFileGenerator::GenerateImportConfig(os, config);
233
234
  // End with the import file footer.
235
0
  this->GenerateImportFooterCode(os);
236
0
}
237
238
void cmExportInstallCMakeConfigGenerator::GenerateImportTargetsConfig(
239
  std::ostream& os, std::string const& config, std::string const& suffix)
240
0
{
241
  // Add each target in the set to the export.
242
0
  for (std::unique_ptr<cmTargetExport> const& te :
243
0
       this->GetExportSet()->GetTargetExports()) {
244
    // Collect import properties for this target.
245
0
    if (this->GetExportTargetType(te.get()) ==
246
0
        cm::TargetType::INTERFACE_LIBRARY) {
247
0
      continue;
248
0
    }
249
250
0
    ImportPropertyMap properties;
251
0
    std::set<std::string> importedLocations;
252
253
0
    this->PopulateImportProperties(config, suffix, te.get(), properties,
254
0
                                   importedLocations);
255
256
    // If any file location was set for the target add it to the
257
    // import file.
258
0
    if (!properties.empty()) {
259
0
      cmGeneratorTarget const* const gtgt = te->Target;
260
0
      std::string const importedXcFrameworkLocation =
261
0
        this->GetImportXcFrameworkLocation(config, te.get());
262
263
0
      this->SetImportLinkInterface(config, suffix,
264
0
                                   cmGeneratorExpression::InstallInterface,
265
0
                                   gtgt, properties);
266
267
0
      this->GenerateImportPropertyCode(os, config, suffix, gtgt, properties,
268
0
                                       importedXcFrameworkLocation);
269
0
      this->GenerateImportedFileChecksCode(
270
0
        os, gtgt, properties, importedLocations, importedXcFrameworkLocation);
271
0
    }
272
0
  }
273
0
}
274
275
std::string cmExportInstallCMakeConfigGenerator::GetFileSetDirectories(
276
  cmGeneratorTarget* gte, cmGeneratorFileSet const* fileSet,
277
  cmTargetExport const* te)
278
0
{
279
0
  std::vector<std::string> resultVector;
280
281
0
  auto configs =
282
0
    gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
283
284
0
  for (auto const& config : configs) {
285
0
    cmInstallFileSetGenerator::DestinationContext result =
286
0
      te->FileSetGenerators.at(fileSet->GetName())
287
0
        ->GetDestination(gte, config);
288
289
0
    std::string dest = cmOutputConverter::EscapeForCMake(
290
0
      result.UnescapedDestination, cmOutputConverter::WrapQuotes::NoWrap);
291
0
    if (!cmSystemTools::FileIsFullPath(result.UnescapedDestination)) {
292
0
      dest = cmStrCat("${_IMPORT_PREFIX}/", std::move(dest));
293
0
    }
294
295
0
    if (result.HadContextSensitiveCondition && configs.size() != 1) {
296
0
      resultVector.push_back(
297
0
        cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
298
0
    } else {
299
0
      resultVector.emplace_back(cmStrCat('"', dest, '"'));
300
0
      break;
301
0
    }
302
0
  }
303
304
0
  return cmJoin(resultVector, " ");
305
0
}
306
307
std::string cmExportInstallCMakeConfigGenerator::GetFileSetFiles(
308
  cmGeneratorTarget* gte, cmGeneratorFileSet const* fileSet,
309
  cmTargetExport const* te)
310
0
{
311
0
  std::vector<std::string> resultVector;
312
313
0
  auto configs =
314
0
    gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
315
316
0
  cmGeneratorExpression destGe(*gte->Makefile->GetCMakeInstance());
317
0
  auto destCge = destGe.Parse(
318
0
    te->FileSetGenerators.at(fileSet->GetName())->GetDestination());
319
320
0
  for (auto const& config : configs) {
321
0
    cm::GenEx::Context context(gte->LocalGenerator, config);
322
0
    auto files = fileSet->GetFiles(context, gte);
323
324
0
    auto unescapedDest = destCge->Evaluate(gte->LocalGenerator, config, gte);
325
0
    auto dest =
326
0
      cmStrCat(cmOutputConverter::EscapeForCMake(
327
0
                 unescapedDest, cmOutputConverter::WrapQuotes::NoWrap),
328
0
               '/');
329
0
    if (!cmSystemTools::FileIsFullPath(unescapedDest)) {
330
0
      dest = cmStrCat("${_IMPORT_PREFIX}/", std::move(dest));
331
0
    }
332
333
0
    bool const contextSensitive =
334
0
      destCge->GetHadContextSensitiveCondition() || files.second;
335
0
    for (auto const& it : files.first) {
336
0
      auto prefix = it.first.empty() ? "" : cmStrCat(it.first, '/');
337
0
      for (auto const& filename : it.second) {
338
0
        auto relFile =
339
0
          cmStrCat(prefix, cmSystemTools::GetFilenameNameView(filename));
340
0
        auto escapedFile =
341
0
          cmStrCat(dest,
342
0
                   cmOutputConverter::EscapeForCMake(
343
0
                     relFile, cmOutputConverter::WrapQuotes::NoWrap));
344
0
        if (contextSensitive && configs.size() != 1) {
345
0
          resultVector.push_back(
346
0
            cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
347
0
        } else {
348
0
          resultVector.emplace_back(cmStrCat('"', escapedFile, '"'));
349
0
        }
350
0
      }
351
0
    }
352
353
0
    if (!(contextSensitive && configs.size() != 1)) {
354
0
      break;
355
0
    }
356
0
  }
357
358
0
  return cmJoin(resultVector, " ");
359
0
}
360
361
std::string cmExportInstallCMakeConfigGenerator::GetCxxModulesDirectory() const
362
0
{
363
0
  return IEGen->GetCxxModuleDirectory();
364
0
}
365
366
void cmExportInstallCMakeConfigGenerator::GenerateCxxModuleConfigInformation(
367
  std::string const& name, std::ostream& os) const
368
0
{
369
  // Now load per-configuration properties for them.
370
  /* clang-format off */
371
0
  os << "# Load information for each installed configuration.\n"
372
0
        "file(GLOB _cmake_cxx_module_includes \"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << "-*.cmake\")\n"
373
0
        "foreach(_cmake_cxx_module_include IN LISTS _cmake_cxx_module_includes)\n"
374
0
        "  include(\"${_cmake_cxx_module_include}\")\n"
375
0
        "endforeach()\n"
376
0
        "unset(_cmake_cxx_module_include)\n"
377
0
        "unset(_cmake_cxx_module_includes)\n";
378
  /* clang-format on */
379
0
}
380
381
bool cmExportInstallCMakeConfigGenerator::
382
  GenerateImportCxxModuleConfigTargetInclusion(std::string const& name,
383
                                               std::string const& config)
384
0
{
385
0
  auto cxx_modules_dirname = this->GetCxxModulesDirectory();
386
0
  if (cxx_modules_dirname.empty()) {
387
0
    return true;
388
0
  }
389
390
0
  std::string filename_config = config;
391
0
  if (filename_config.empty()) {
392
0
    filename_config = "noconfig";
393
0
  }
394
395
0
  std::string const dest =
396
0
    cmStrCat(this->FileDir, '/', cxx_modules_dirname, '/');
397
0
  std::string fileName =
398
0
    cmStrCat(dest, "cxx-modules-", name, '-', filename_config, ".cmake");
399
400
0
  cmGeneratedFileStream os(fileName, true);
401
0
  if (!os) {
402
0
    std::string se = cmSystemTools::GetLastSystemError();
403
0
    cmSystemTools::Error(
404
0
      cmStrCat("cannot write to file \"", fileName, "\": ", se));
405
0
    return false;
406
0
  }
407
0
  os.SetCopyIfDifferent(true);
408
409
  // Record this per-config import file.
410
0
  this->ConfigCxxModuleFiles[config] = fileName;
411
412
0
  auto& prop_files = this->ConfigCxxModuleTargetFiles[config];
413
0
  for (auto const* tgt : this->ExportedTargets) {
414
    // Only targets with C++ module sources will have a
415
    // collator-generated install script.
416
0
    if (!tgt->HaveCxx20ModuleSources()) {
417
0
      continue;
418
0
    }
419
420
0
    auto prop_filename = cmStrCat("target-", tgt->GetFilesystemExportName(),
421
0
                                  '-', filename_config, ".cmake");
422
0
    prop_files.emplace_back(cmStrCat(dest, prop_filename));
423
0
    os << "include(\"${CMAKE_CURRENT_LIST_DIR}/" << prop_filename << "\")\n";
424
0
  }
425
426
0
  return true;
427
0
}