Coverage Report

Created: 2026-04-29 07:01

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