Coverage Report

Created: 2026-04-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmExportBuildCMakeConfigGenerator.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 "cmExportBuildCMakeConfigGenerator.h"
4
5
#include <cstddef>
6
#include <functional>
7
#include <map>
8
#include <ostream>
9
#include <set>
10
#include <utility>
11
#include <vector>
12
13
#include <cm/string_view>
14
15
#include "cmCryptoHash.h"
16
#include "cmExportSet.h"
17
#include "cmFileSetMetadata.h"
18
#include "cmGenExContext.h"
19
#include "cmGeneratedFileStream.h"
20
#include "cmGeneratorExpression.h"
21
#include "cmGeneratorFileSet.h"
22
#include "cmGeneratorTarget.h"
23
#include "cmLocalGenerator.h"
24
#include "cmMakefile.h"
25
#include "cmMessageType.h"
26
#include "cmOutputConverter.h"
27
#include "cmStateTypes.h"
28
#include "cmStringAlgorithms.h"
29
#include "cmSystemTools.h"
30
#include "cmTarget.h"
31
32
cmExportBuildCMakeConfigGenerator::cmExportBuildCMakeConfigGenerator()
33
0
{
34
0
  this->LG = nullptr;
35
0
  this->ExportSet = nullptr;
36
0
}
Unexecuted instantiation: cmExportBuildCMakeConfigGenerator::cmExportBuildCMakeConfigGenerator()
Unexecuted instantiation: cmExportBuildCMakeConfigGenerator::cmExportBuildCMakeConfigGenerator()
37
38
bool cmExportBuildCMakeConfigGenerator::GenerateMainFile(std::ostream& os)
39
0
{
40
0
  {
41
0
    std::string expectedTargets;
42
0
    std::string sep;
43
0
    bool generatedInterfaceRequired = false;
44
0
    auto visitor = [&](cmGeneratorTarget const* te) {
45
0
      expectedTargets += sep + this->Namespace + te->GetExportName();
46
0
      sep = " ";
47
48
0
      generatedInterfaceRequired |=
49
0
        this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY;
50
0
    };
51
52
0
    if (!this->CollectExports(visitor)) {
53
0
      return false;
54
0
    }
55
56
0
    if (generatedInterfaceRequired) {
57
0
      this->SetRequiredCMakeVersion(3, 0, 0);
58
0
    }
59
0
    this->GenerateExpectedTargetsCode(os, expectedTargets);
60
0
  }
61
62
  // Create all the imported targets.
63
0
  for (auto const& exp : this->Exports) {
64
0
    cmGeneratorTarget* gte = exp.Target;
65
0
    this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
66
67
0
    gte->Target->AppendBuildInterfaceIncludes();
68
69
0
    ImportPropertyMap properties;
70
0
    if (!this->PopulateInterfaceProperties(gte, properties)) {
71
0
      return false;
72
0
    }
73
74
0
    ImportFileSetPropertyMap fsProperties;
75
0
    if (!this->PopulateFileSetInterfaceProperties(gte, fsProperties)) {
76
0
      return false;
77
0
    }
78
79
0
    this->PopulateInterfaceLinkLibrariesProperty(
80
0
      gte, cmGeneratorExpression::BuildInterface, properties);
81
82
0
    this->GenerateInterfaceProperties(gte, os, properties);
83
84
0
    this->GenerateTargetFileSets(gte, os, fsProperties);
85
0
  }
86
87
0
  std::string cxx_modules_name;
88
0
  if (this->ExportSet) {
89
0
    cxx_modules_name = this->ExportSet->GetName();
90
0
  } else {
91
0
    cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
92
0
    constexpr std::size_t HASH_TRUNCATION = 12;
93
0
    for (auto const& target : this->Targets) {
94
0
      hasher.Append(target.Name);
95
0
    }
96
0
    cxx_modules_name = hasher.FinalizeHex().substr(0, HASH_TRUNCATION);
97
0
  }
98
99
0
  this->GenerateCxxModuleInformation(cxx_modules_name, os);
100
101
  // Generate import file content for each configuration.
102
0
  for (std::string const& c : this->Configurations) {
103
0
    this->GenerateImportConfig(os, c);
104
0
  }
105
106
  // Generate import file content for each configuration.
107
0
  for (std::string const& c : this->Configurations) {
108
0
    this->GenerateImportCxxModuleConfigTargetInclusion(cxx_modules_name, c);
109
0
  }
110
111
0
  this->GenerateMissingTargetsCheckCode(os);
112
113
0
  return true;
114
0
}
115
116
void cmExportBuildCMakeConfigGenerator::GenerateImportTargetsConfig(
117
  std::ostream& os, std::string const& config, std::string const& suffix)
118
0
{
119
0
  for (auto const& exp : this->Exports) {
120
0
    cmGeneratorTarget* target = exp.Target;
121
122
    // Collect import properties for this target.
123
0
    ImportPropertyMap properties;
124
125
0
    if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) {
126
0
      this->SetImportLocationProperty(config, suffix, target, properties);
127
0
    }
128
0
    if (!properties.empty()) {
129
      // Get the rest of the target details.
130
0
      if (this->GetExportTargetType(target) !=
131
0
          cmStateEnums::INTERFACE_LIBRARY) {
132
0
        this->SetImportDetailProperties(config, suffix, target, properties);
133
0
        this->SetImportLinkInterface(config, suffix,
134
0
                                     cmGeneratorExpression::BuildInterface,
135
0
                                     target, properties);
136
0
      }
137
138
      // TODO: PUBLIC_HEADER_LOCATION
139
      // This should wait until the build feature propagation stuff
140
      // is done.  Then this can be a propagated include directory.
141
      // this->GenerateImportProperty(config, te->HeaderGenerator,
142
      //                              properties);
143
144
      // Generate code in the export file.
145
0
      std::string importedXcFrameworkLocation = exp.XcFrameworkLocation;
146
0
      if (!importedXcFrameworkLocation.empty()) {
147
0
        importedXcFrameworkLocation = cmGeneratorExpression::Preprocess(
148
0
          importedXcFrameworkLocation,
149
0
          cmGeneratorExpression::PreprocessContext::BuildInterface);
150
0
        importedXcFrameworkLocation = cmGeneratorExpression::Evaluate(
151
0
          importedXcFrameworkLocation, exp.Target->GetLocalGenerator(), config,
152
0
          exp.Target, nullptr, exp.Target);
153
0
        if (!importedXcFrameworkLocation.empty() &&
154
0
            !cmSystemTools::FileIsFullPath(importedXcFrameworkLocation)) {
155
0
          importedXcFrameworkLocation =
156
0
            cmStrCat(this->LG->GetCurrentBinaryDirectory(), '/',
157
0
                     importedXcFrameworkLocation);
158
0
        }
159
0
      }
160
0
      this->GenerateImportPropertyCode(os, config, suffix, target, properties,
161
0
                                       importedXcFrameworkLocation);
162
0
    }
163
0
  }
164
0
}
165
166
std::string cmExportBuildCMakeConfigGenerator::GetFileSetDirectories(
167
  cmGeneratorTarget* gte, cmGeneratorFileSet const* fileSet,
168
  cmTargetExport const* /*te*/)
169
0
{
170
0
  std::vector<std::string> resultVector;
171
172
0
  auto configs =
173
0
    gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
174
175
0
  for (auto const& config : configs) {
176
0
    cm::GenEx::Context context(gte->LocalGenerator, config);
177
0
    auto directories = fileSet->GetDirectories(context, gte);
178
0
    bool const contextSensitive = directories.second;
179
180
0
    auto const& type = fileSet->GetType();
181
    // C++ modules do not support interface file sets which are dependent upon
182
    // the configuration.
183
0
    if (contextSensitive && type == cm::FileSetMetadata::CXX_MODULES) {
184
0
      auto* mf = this->LG->GetMakefile();
185
0
      mf->IssueMessage(MessageType::FATAL_ERROR,
186
0
                       cmStrCat("The \"", gte->GetName(),
187
0
                                "\" target's interface file set \"",
188
0
                                fileSet->GetName(), "\" of type \"", type,
189
0
                                "\" contains context-sensitive base directory "
190
0
                                "entries which is not supported."));
191
0
      return std::string{};
192
0
    }
193
194
0
    for (auto const& directory : directories.first) {
195
0
      auto dest = cmOutputConverter::EscapeForCMake(
196
0
        directory, cmOutputConverter::WrapQuotes::NoWrap);
197
198
0
      if (contextSensitive && configs.size() != 1) {
199
0
        resultVector.push_back(
200
0
          cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
201
0
      } else {
202
0
        resultVector.emplace_back(cmStrCat('"', dest, '"'));
203
0
      }
204
0
    }
205
0
  }
206
207
0
  return cmJoin(resultVector, " ");
208
0
}
209
210
std::string cmExportBuildCMakeConfigGenerator::GetFileSetFiles(
211
  cmGeneratorTarget* gte, cmGeneratorFileSet const* fileSet,
212
  cmTargetExport const* /*te*/)
213
0
{
214
0
  std::vector<std::string> resultVector;
215
216
0
  auto configs =
217
0
    gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
218
219
0
  for (auto const& config : configs) {
220
0
    cm::GenEx::Context context(gte->LocalGenerator, config);
221
222
0
    auto files = fileSet->GetFiles(context, gte);
223
0
    bool const contextSensitive = files.second;
224
225
0
    auto const& type = fileSet->GetType();
226
    // C++ modules do not support interface file sets which are dependent upon
227
    // the configuration.
228
0
    if (contextSensitive && type == cm::FileSetMetadata::CXX_MODULES) {
229
0
      auto* mf = this->LG->GetMakefile();
230
0
      mf->IssueMessage(MessageType::FATAL_ERROR,
231
0
                       cmStrCat("The \"", gte->GetName(),
232
0
                                "\" target's interface file set \"",
233
0
                                fileSet->GetName(), "\" of type \"", type,
234
0
                                "\" contains context-sensitive file entries "
235
0
                                "which is not supported."));
236
0
      return std::string{};
237
0
    }
238
239
0
    for (auto const& it : files.first) {
240
0
      for (auto const& filename : it.second) {
241
0
        auto escapedFile = cmOutputConverter::EscapeForCMake(
242
0
          filename, cmOutputConverter::WrapQuotes::NoWrap);
243
0
        if (contextSensitive && configs.size() != 1) {
244
0
          resultVector.push_back(
245
0
            cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
246
0
        } else {
247
0
          resultVector.emplace_back(cmStrCat('"', escapedFile, '"'));
248
0
        }
249
0
      }
250
0
    }
251
252
0
    if (!(contextSensitive && configs.size() != 1)) {
253
0
      break;
254
0
    }
255
0
  }
256
257
0
  return cmJoin(resultVector, " ");
258
0
}
259
260
void cmExportBuildCMakeConfigGenerator::GenerateCxxModuleConfigInformation(
261
  std::string const& name, std::ostream& os) const
262
0
{
263
0
  char const* opt = "";
264
0
  if (this->Configurations.size() > 1) {
265
    // With more than one configuration, each individual file is optional.
266
0
    opt = " OPTIONAL";
267
0
  }
268
269
  // Generate import file content for each configuration.
270
0
  for (std::string c : this->Configurations) {
271
0
    if (c.empty()) {
272
0
      c = "noconfig";
273
0
    }
274
0
    os << "include(\"${CMAKE_CURRENT_LIST_DIR}/cxx-modules-" << name << '-'
275
0
       << c << ".cmake\"" << opt << ")\n";
276
0
  }
277
0
}
278
279
bool cmExportBuildCMakeConfigGenerator::
280
  GenerateImportCxxModuleConfigTargetInclusion(std::string const& name,
281
                                               std::string config) const
282
0
{
283
0
  auto cxx_modules_dirname = this->GetCxxModulesDirectory();
284
0
  if (cxx_modules_dirname.empty()) {
285
0
    return true;
286
0
  }
287
288
0
  if (config.empty()) {
289
0
    config = "noconfig";
290
0
  }
291
292
0
  std::string fileName =
293
0
    cmStrCat(this->FileDir, '/', cxx_modules_dirname, "/cxx-modules-", name,
294
0
             '-', config, ".cmake");
295
296
0
  cmGeneratedFileStream os(fileName, true);
297
0
  if (!os) {
298
0
    std::string se = cmSystemTools::GetLastSystemError();
299
0
    cmSystemTools::Error(
300
0
      cmStrCat("cannot write to file \"", fileName, "\": ", se));
301
0
    return false;
302
0
  }
303
0
  os.SetCopyIfDifferent(true);
304
305
0
  for (auto const* tgt : this->ExportedTargets) {
306
    // Only targets with C++ module sources will have a
307
    // collator-generated install script.
308
0
    if (!tgt->HaveCxx20ModuleSources()) {
309
0
      continue;
310
0
    }
311
312
0
    os << "include(\"${CMAKE_CURRENT_LIST_DIR}/target-"
313
0
       << tgt->GetFilesystemExportName() << '-' << config << ".cmake\")\n";
314
0
  }
315
316
0
  return true;
317
0
}