Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmExportPackageInfoGenerator.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 "cmExportPackageInfoGenerator.h"
4
5
#include <memory>
6
#include <set>
7
#include <utility>
8
#include <vector>
9
10
#include <cm/optional>
11
#include <cm/string_view>
12
#include <cmext/algorithm>
13
#include <cmext/string_view>
14
15
#include <cm3p/json/value.h>
16
#include <cm3p/json/writer.h>
17
18
#include "cmsys/RegularExpression.hxx"
19
20
#include "cmArgumentParserTypes.h"
21
#include "cmExportSet.h"
22
#include "cmFindPackageStack.h"
23
#include "cmGeneratorExpression.h"
24
#include "cmGeneratorTarget.h"
25
#include "cmList.h"
26
#include "cmMakefile.h"
27
#include "cmMessageType.h"
28
#include "cmPackageInfoArguments.h"
29
#include "cmStringAlgorithms.h"
30
#include "cmSystemTools.h"
31
#include "cmTarget.h"
32
33
static std::string const kCPS_VERSION_STR = "0.14.0";
34
35
cmExportPackageInfoGenerator::cmExportPackageInfoGenerator(
36
  cmPackageInfoArguments arguments)
37
0
  : PackageName(std::move(arguments.PackageName))
38
0
  , PackageVersion(std::move(arguments.Version))
39
0
  , PackageVersionCompat(std::move(arguments.VersionCompat))
40
0
  , PackageVersionSchema(std::move(arguments.VersionSchema))
41
0
  , PackageDescription(std::move(arguments.Description))
42
0
  , PackageWebsite(std::move(arguments.Website))
43
0
  , PackageLicense(std::move(arguments.License))
44
0
  , DefaultLicense(std::move(arguments.DefaultLicense))
45
0
  , DefaultTargets(std::move(arguments.DefaultTargets))
46
0
  , DefaultConfigurations(std::move(arguments.DefaultConfigs))
47
0
{
48
0
}
49
50
cm::string_view cmExportPackageInfoGenerator::GetImportPrefixWithSlash() const
51
0
{
52
0
  return "@prefix@/"_s;
53
0
}
54
55
bool cmExportPackageInfoGenerator::GenerateImportFile(std::ostream& os)
56
0
{
57
0
  return this->GenerateMainFile(os);
58
0
}
59
60
void cmExportPackageInfoGenerator::WritePackageInfo(
61
  Json::Value const& packageInfo, std::ostream& os) const
62
0
{
63
0
  Json::StreamWriterBuilder builder;
64
0
  builder["indentation"] = "  ";
65
0
  builder["commentStyle"] = "None";
66
0
  std::unique_ptr<Json::StreamWriter> const writer(builder.newStreamWriter());
67
0
  writer->write(packageInfo, &os);
68
0
}
69
70
namespace {
71
bool SetProperty(Json::Value& object, std::string const& property,
72
                 std::string const& value)
73
0
{
74
0
  if (!value.empty()) {
75
0
    object[property] = value;
76
0
    return true;
77
0
  }
78
0
  return false;
79
0
}
80
81
template <typename T>
82
void BuildArray(Json::Value& object, std::string const& property,
83
                T const& values)
84
0
{
85
0
  if (!values.empty()) {
86
0
    Json::Value& array = object[property];
87
0
    for (auto const& item : values) {
88
0
      array.append(item);
89
0
    }
90
0
  }
91
0
}
Unexecuted instantiation: cmExportPackageInfoGenerator.cxx:void (anonymous namespace)::BuildArray<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(Json::Value&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&)
Unexecuted instantiation: cmExportPackageInfoGenerator.cxx:void (anonymous namespace)::BuildArray<std::__1::set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(Json::Value&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > const&)
92
93
bool CheckSimpleVersion(std::string const& version)
94
0
{
95
0
  cmsys::RegularExpression regex("^[0-9]+([.][0-9]+)*([-+].*)?$");
96
0
  return regex.find(version);
97
0
}
98
}
99
100
bool cmExportPackageInfoGenerator::CheckVersion() const
101
0
{
102
0
  if (!this->PackageVersion.empty()) {
103
0
    std::string const& schema = [&] {
104
0
      if (this->PackageVersionSchema.empty()) {
105
0
        return std::string{ "simple" };
106
0
      }
107
0
      return cmSystemTools::LowerCase(this->PackageVersionSchema);
108
0
    }();
109
0
    bool (*validator)(std::string const&) = nullptr;
110
0
    bool result = true;
111
112
0
    if (schema == "simple"_s) {
113
0
      validator = &CheckSimpleVersion;
114
0
    } else if (schema == "dpkg"_s || schema == "rpm"_s ||
115
0
               schema == "pep440"_s) {
116
      // TODO
117
      // We don't validate these at this time. Eventually, we would like to do
118
      // so, but will probably need to introduce a policy whether to treat
119
      // invalid versions as an error.
120
0
    } else if (schema != "custom"_s) {
121
0
      this->IssueMessage(MessageType::AUTHOR_WARNING,
122
0
                         cmStrCat("Package \""_s, this->GetPackageName(),
123
0
                                  "\" uses unrecognized version schema \""_s,
124
0
                                  this->PackageVersionSchema, "\"."_s));
125
0
    }
126
127
0
    if (validator) {
128
0
      if (!(*validator)(this->PackageVersion)) {
129
0
        this->ReportError(cmStrCat("Package \""_s, this->GetPackageName(),
130
0
                                   "\" version \""_s, this->PackageVersion,
131
0
                                   "\" does not conform to the \""_s, schema,
132
0
                                   "\" schema."_s));
133
0
        result = false;
134
0
      }
135
0
      if (!this->PackageVersionCompat.empty() &&
136
0
          !(*validator)(this->PackageVersionCompat)) {
137
0
        this->ReportError(
138
0
          cmStrCat("Package \""_s, this->GetPackageName(),
139
0
                   "\" compatibility version \""_s, this->PackageVersionCompat,
140
0
                   "\" does not conform to the \""_s, schema, "\" schema."_s));
141
0
        result = false;
142
0
      }
143
0
    }
144
145
0
    return result;
146
0
  }
147
148
0
  return true;
149
0
}
150
151
bool cmExportPackageInfoGenerator::CheckDefaultTargets() const
152
0
{
153
0
  bool result = true;
154
0
  std::set<std::string> exportedTargetNames;
155
0
  for (auto const* te : this->ExportedTargets) {
156
0
    exportedTargetNames.emplace(te->GetExportName());
157
0
  }
158
159
0
  for (auto const& name : this->DefaultTargets) {
160
0
    if (!cm::contains(exportedTargetNames, name)) {
161
0
      this->ReportError(
162
0
        cmStrCat("Package \"", this->GetPackageName(),
163
0
                 "\" specifies DEFAULT_TARGETS \"", name,
164
0
                 "\", which is not a target in the export set \"",
165
0
                 this->GetExportSet()->GetName(), "\"."));
166
0
      result = false;
167
0
    }
168
0
  }
169
170
0
  return result;
171
0
}
172
173
Json::Value cmExportPackageInfoGenerator::GeneratePackageInfo() const
174
0
{
175
0
  Json::Value package;
176
177
0
  package["name"] = this->GetPackageName();
178
0
  package["cps_version"] = std::string(kCPS_VERSION_STR);
179
180
0
  if (SetProperty(package, "version", this->PackageVersion)) {
181
0
    SetProperty(package, "compat_version", this->PackageVersionCompat);
182
0
    SetProperty(package, "version_schema", this->PackageVersionSchema);
183
0
  }
184
185
0
  BuildArray(package, "default_components", this->DefaultTargets);
186
0
  BuildArray(package, "configurations", this->DefaultConfigurations);
187
188
0
  SetProperty(package, "description", this->PackageDescription);
189
0
  SetProperty(package, "website", this->PackageWebsite);
190
0
  SetProperty(package, "license", this->PackageLicense);
191
0
  SetProperty(package, "default_license", this->DefaultLicense);
192
193
0
  return package;
194
0
}
195
196
void cmExportPackageInfoGenerator::GeneratePackageRequires(
197
  Json::Value& package) const
198
0
{
199
0
  if (!this->Requirements.empty()) {
200
0
    Json::Value& requirements = package["requires"];
201
202
    // Build description for each requirement.
203
0
    for (auto const& requirement : this->Requirements) {
204
0
      auto data = Json::Value{ Json::objectValue };
205
206
      // Add required components.
207
0
      if (!requirement.second.Components.empty()) {
208
0
        auto components = Json::Value{ Json::arrayValue };
209
0
        for (std::string const& component : requirement.second.Components) {
210
0
          components.append(component);
211
0
        }
212
0
        data["components"] = components;
213
0
      }
214
215
      // Add additional dependency information.
216
0
      if (requirement.second.Directory) {
217
0
        auto hints = Json::Value{ Json::arrayValue };
218
0
        hints.append(*requirement.second.Directory);
219
0
        data["hints"] = hints;
220
0
      }
221
222
0
      if (requirement.second.Version) {
223
0
        data["version"] = *requirement.second.Version;
224
0
      }
225
226
0
      requirements[requirement.first] = data;
227
0
    }
228
0
  }
229
0
}
230
231
Json::Value* cmExportPackageInfoGenerator::GenerateImportTarget(
232
  Json::Value& components, cmGeneratorTarget const* target,
233
  cmStateEnums::TargetType targetType) const
234
0
{
235
0
  auto const& name = target->GetExportName();
236
0
  if (name.empty()) {
237
0
    return nullptr;
238
0
  }
239
240
0
  Json::Value& component = components[name];
241
0
  Json::Value& type = component["type"];
242
243
0
  switch (targetType) {
244
0
    case cmStateEnums::EXECUTABLE:
245
0
      type = "executable";
246
0
      break;
247
0
    case cmStateEnums::STATIC_LIBRARY:
248
0
      type = "archive";
249
0
      break;
250
0
    case cmStateEnums::SHARED_LIBRARY:
251
0
      type = "dylib";
252
0
      break;
253
0
    case cmStateEnums::MODULE_LIBRARY:
254
0
      type = "module";
255
0
      break;
256
0
    case cmStateEnums::INTERFACE_LIBRARY:
257
0
      type = target->IsSymbolic() ? "symbolic" : "interface";
258
0
      break;
259
0
    default:
260
0
      type = "unknown";
261
0
      break;
262
0
  }
263
0
  return &component;
264
0
}
265
266
bool cmExportPackageInfoGenerator::GenerateInterfaceProperties(
267
  Json::Value& component, cmGeneratorTarget const* target,
268
  ImportPropertyMap const& properties) const
269
0
{
270
0
  bool result = true;
271
272
0
  this->GenerateInterfaceLinkProperties(result, component, target, properties);
273
274
0
  this->GenerateInterfaceCompileFeatures(result, component, target,
275
0
                                         properties);
276
0
  this->GenerateInterfaceCompileDefines(result, component, target, properties);
277
278
0
  this->GenerateInterfaceListProperty(result, component, target,
279
0
                                      "compile_flags", "COMPILE_OPTIONS"_s,
280
0
                                      properties);
281
0
  this->GenerateInterfaceListProperty(result, component, target, "link_flags",
282
0
                                      "LINK_OPTIONS"_s, properties);
283
0
  this->GenerateInterfaceListProperty(result, component, target,
284
0
                                      "link_directories", "LINK_DIRECTORIES"_s,
285
0
                                      properties);
286
0
  this->GenerateInterfaceListProperty(result, component, target, "includes",
287
0
                                      "INCLUDE_DIRECTORIES"_s, properties);
288
289
0
  this->GenerateProperty(result, component, target, "license", "SPDX_LICENSE",
290
0
                         properties);
291
292
  // TODO: description
293
294
0
  return result;
295
0
}
296
297
bool cmExportPackageInfoGenerator::NoteLinkedTarget(
298
  cmGeneratorTarget const* target, std::string const& linkedName,
299
  cmGeneratorTarget const* linkedTarget)
300
0
{
301
0
  if (cm::contains(this->ExportedTargets, linkedTarget)) {
302
    // Target is internal to this package.
303
0
    this->LinkTargets.emplace(linkedName,
304
0
                              cmStrCat(':', linkedTarget->GetExportName()));
305
0
    return true;
306
0
  }
307
308
0
  if (linkedTarget->IsImported()) {
309
    // Target is imported from a found package.
310
0
    using Package = cm::optional<std::pair<std::string, cmPackageInformation>>;
311
0
    auto pkgInfo = [](cmTarget* t) -> Package {
312
0
      cmFindPackageStack pkgStack = t->GetFindPackageStack();
313
0
      if (!pkgStack.Empty()) {
314
0
        return std::make_pair(pkgStack.Top().Name, pkgStack.Top().PackageInfo);
315
0
      }
316
317
0
      cmPackageInformation package;
318
0
      std::string const pkgName =
319
0
        t->GetSafeProperty("EXPORT_FIND_PACKAGE_NAME");
320
0
      if (pkgName.empty()) {
321
0
        return cm::nullopt;
322
0
      }
323
324
0
      return std::make_pair(pkgName, package);
325
0
    }(linkedTarget->Target);
326
327
0
    if (!pkgInfo) {
328
0
      target->Makefile->IssueMessage(
329
0
        MessageType::FATAL_ERROR,
330
0
        cmStrCat("Target \"", target->GetName(),
331
0
                 "\" references imported target \"", linkedName,
332
0
                 "\" which does not come from any known package."));
333
0
      return false;
334
0
    }
335
336
0
    std::string const& pkgName = pkgInfo->first;
337
338
0
    auto const& prefix = cmStrCat(pkgName, "::");
339
0
    if (!cmHasPrefix(linkedName, prefix)) {
340
0
      target->Makefile->IssueMessage(
341
0
        MessageType::FATAL_ERROR,
342
0
        cmStrCat("Target \"", target->GetName(), "\" references target \"",
343
0
                 linkedName, "\", which comes from the \"", pkgName,
344
0
                 "\" package, but does not belong to the package's "
345
0
                 "canonical namespace (\"",
346
0
                 prefix, "\"). This is not allowed."));
347
0
      return false;
348
0
    }
349
350
0
    std::string component = linkedName.substr(prefix.length());
351
0
    this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component));
352
0
    cmPackageInformation& req =
353
0
      this->Requirements.insert(std::move(*pkgInfo)).first->second;
354
0
    req.Components.emplace(std::move(component));
355
0
    return true;
356
0
  }
357
358
  // Target belongs to another export from this build.
359
0
  auto const& exportInfo = this->FindExportInfo(linkedTarget);
360
0
  if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) {
361
0
    auto const& linkNamespace = *exportInfo.Namespaces.begin();
362
0
    if (!cmHasSuffix(linkNamespace, "::")) {
363
0
      target->Makefile->IssueMessage(
364
0
        MessageType::FATAL_ERROR,
365
0
        cmStrCat("Target \"", target->GetName(), "\" references target \"",
366
0
                 linkedName,
367
0
                 "\", which does not use the standard namespace separator. "
368
0
                 "This is not allowed."));
369
0
      return false;
370
0
    }
371
372
0
    std::string pkgName{ linkNamespace.data(), linkNamespace.size() - 2 };
373
0
    std::string component = linkedTarget->GetExportName();
374
0
    if (pkgName == this->GetPackageName()) {
375
0
      this->LinkTargets.emplace(linkedName, cmStrCat(':', component));
376
0
    } else {
377
0
      this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component));
378
0
      this->Requirements[pkgName].Components.emplace(std::move(component));
379
0
    }
380
0
    return true;
381
0
  }
382
383
  // Target belongs to multiple namespaces or multiple export sets.
384
  // cmExportFileGenerator::HandleMissingTarget should have complained about
385
  // this already.
386
0
  return false;
387
0
}
388
389
void cmExportPackageInfoGenerator::GenerateInterfaceLinkProperties(
390
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
391
  ImportPropertyMap const& properties) const
392
0
{
393
0
  auto const& iter = properties.find("INTERFACE_LINK_LIBRARIES");
394
0
  if (iter == properties.end()) {
395
0
    return;
396
0
  }
397
398
  // Extract any $<LINK_ONLY:...> from the link libraries, and assert that no
399
  // other generator expressions are present.
400
0
  std::map<std::string, std::vector<std::string>> allowList = {
401
0
    { "COMPILE_ONLY", {} },
402
0
    { "LINK_ONLY", {} },
403
0
  };
404
0
  std::string interfaceLinkLibraries;
405
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(
406
0
        target, iter->first, iter->second, interfaceLinkLibraries,
407
0
        allowList)) {
408
0
    result = false;
409
0
    return;
410
0
  }
411
412
0
  std::vector<std::string> buildRequires;
413
0
  std::vector<std::string> compileRequires;
414
0
  std::vector<std::string> linkRequires;
415
0
  std::vector<std::string> linkLibraries;
416
417
0
  auto addLibraries = [this, &linkLibraries,
418
0
                       &result](std::vector<std::string> const& names,
419
0
                                std::vector<std::string>& output) -> void {
420
0
    for (auto const& name : names) {
421
0
      auto const& ti = this->LinkTargets.find(name);
422
0
      if (ti != this->LinkTargets.end()) {
423
0
        if (ti->second.empty()) {
424
0
          result = false;
425
0
        } else {
426
0
          output.emplace_back(ti->second);
427
0
        }
428
0
      } else {
429
0
        linkLibraries.emplace_back(name);
430
0
      }
431
0
    }
432
0
  };
433
434
0
  addLibraries(allowList["COMPILE_ONLY"], compileRequires);
435
0
  addLibraries(allowList["LINK_ONLY"], linkRequires);
436
0
  addLibraries(cmList{ interfaceLinkLibraries }, buildRequires);
437
438
0
  BuildArray(component, "requires", buildRequires);
439
0
  BuildArray(component, "link_requires", linkRequires);
440
0
  BuildArray(component, "link_libraries", linkLibraries);
441
0
  BuildArray(component, "compile_requires", compileRequires);
442
0
}
443
444
void cmExportPackageInfoGenerator::GenerateInterfaceCompileFeatures(
445
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
446
  ImportPropertyMap const& properties) const
447
0
{
448
0
  auto const& iter = properties.find("INTERFACE_COMPILE_FEATURES");
449
0
  if (iter == properties.end()) {
450
0
    return;
451
0
  }
452
453
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, iter->first,
454
0
                                                         iter->second)) {
455
0
    result = false;
456
0
    return;
457
0
  }
458
459
0
  std::set<std::string> features;
460
0
  for (auto const& value : cmList{ iter->second }) {
461
0
    if (cmHasLiteralPrefix(value, "c_std_")) {
462
0
      auto suffix = cm::string_view{ value }.substr(6, 2);
463
0
      features.emplace(cmStrCat("cxx", suffix));
464
0
    } else if (cmHasLiteralPrefix(value, "cxx_std_")) {
465
0
      auto suffix = cm::string_view{ value }.substr(8, 2);
466
0
      features.emplace(cmStrCat("c++", suffix));
467
0
    }
468
0
  }
469
470
0
  BuildArray(component, "compile_features", features);
471
0
}
472
473
void cmExportPackageInfoGenerator::GenerateInterfaceCompileDefines(
474
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
475
  ImportPropertyMap const& properties) const
476
0
{
477
0
  auto const& iter = properties.find("INTERFACE_COMPILE_DEFINITIONS");
478
0
  if (iter == properties.end()) {
479
0
    return;
480
0
  }
481
482
  // TODO: Support language-specific defines.
483
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, iter->first,
484
0
                                                         iter->second)) {
485
0
    result = false;
486
0
    return;
487
0
  }
488
489
0
  Json::Value defines;
490
0
  for (auto const& def : cmList{ iter->second }) {
491
0
    auto const n = def.find('=');
492
0
    if (n == std::string::npos) {
493
0
      defines[def] = Json::Value{};
494
0
    } else {
495
0
      defines[def.substr(0, n)] = def.substr(n + 1);
496
0
    }
497
0
  }
498
499
0
  if (!defines.empty()) {
500
0
    component["definitions"]["*"] = std::move(defines);
501
0
  }
502
0
}
503
504
void cmExportPackageInfoGenerator::GenerateInterfaceListProperty(
505
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
506
  std::string const& outName, cm::string_view inName,
507
  ImportPropertyMap const& properties) const
508
0
{
509
0
  auto const& prop = cmStrCat("INTERFACE_", inName);
510
0
  auto const& iter = properties.find(prop);
511
0
  if (iter == properties.end()) {
512
0
    return;
513
0
  }
514
515
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, prop,
516
0
                                                         iter->second)) {
517
0
    result = false;
518
0
    return;
519
0
  }
520
521
0
  Json::Value& array = component[outName];
522
0
  for (auto const& value : cmList{ iter->second }) {
523
0
    array.append(value);
524
0
  }
525
0
}
526
527
void cmExportPackageInfoGenerator::GenerateProperty(
528
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
529
  std::string const& outName, std::string const& inName,
530
  ImportPropertyMap const& properties) const
531
0
{
532
0
  auto const& iter = properties.find(inName);
533
0
  if (iter == properties.end()) {
534
0
    return;
535
0
  }
536
537
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, inName,
538
0
                                                         iter->second)) {
539
0
    result = false;
540
0
    return;
541
0
  }
542
543
0
  component[outName] = iter->second;
544
0
}
545
546
Json::Value cmExportPackageInfoGenerator::GenerateInterfaceConfigProperties(
547
  std::string const& suffix, ImportPropertyMap const& properties) const
548
0
{
549
0
  Json::Value component;
550
0
  auto const suffixLength = suffix.length();
551
552
0
  for (auto const& p : properties) {
553
0
    if (!cmHasSuffix(p.first, suffix)) {
554
0
      continue;
555
0
    }
556
0
    auto const n = p.first.length() - suffixLength - 9;
557
0
    auto const prop = cm::string_view{ p.first }.substr(9, n);
558
559
0
    if (prop == "LOCATION") {
560
0
      component["location"] = p.second;
561
0
    } else if (prop == "IMPLIB") {
562
0
      component["link_location"] = p.second;
563
0
    } else if (prop == "LINK_INTERFACE_LANGUAGES") {
564
0
      std::vector<std::string> languages;
565
0
      for (auto const& lang : cmList{ p.second }) {
566
0
        auto ll = cmSystemTools::LowerCase(lang);
567
0
        if (ll == "cxx") {
568
0
          languages.emplace_back("cpp");
569
0
        } else {
570
0
          languages.emplace_back(std::move(ll));
571
0
        }
572
0
      }
573
0
      BuildArray(component, "link_languages", languages);
574
0
    }
575
0
  }
576
577
0
  return component;
578
0
}
579
580
std::string cmExportPackageInfoGenerator::GenerateCxxModules(
581
  Json::Value& component, cmGeneratorTarget* target,
582
  std::string const& packagePath, std::string const& config)
583
0
{
584
0
  std::string manifestPath;
585
586
0
  std::string const cxxModulesDirName = this->GetCxxModulesDirectory();
587
0
  if (cxxModulesDirName.empty() || !target->HaveCxx20ModuleSources()) {
588
0
    return manifestPath;
589
0
  }
590
591
0
  manifestPath =
592
0
    cmStrCat(cxxModulesDirName, "/target-", target->GetFilesystemExportName(),
593
0
             '-', config.empty() ? "noconfig" : config, ".modules.json");
594
595
0
  component["cpp_module_metadata"] = cmStrCat(packagePath, '/', manifestPath);
596
0
  return manifestPath;
597
0
}