Coverage Report

Created: 2026-03-12 06:35

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.1";
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
std::vector<std::string> cmExportPackageInfoGenerator::ExtractRequirements(
390
  std::vector<std::string> const& names, bool& result,
391
  std::vector<std::string>& libraryPaths) const
392
0
{
393
0
  std::vector<std::string> output;
394
395
0
  for (auto const& name : names) {
396
0
    auto const& ti = this->LinkTargets.find(name);
397
0
    if (ti != this->LinkTargets.end()) {
398
0
      if (ti->second.empty()) {
399
0
        result = false;
400
0
      } else {
401
0
        output.emplace_back(ti->second);
402
0
      }
403
0
    } else {
404
0
      libraryPaths.emplace_back(name);
405
0
    }
406
0
  }
407
408
0
  return output;
409
0
}
410
411
void cmExportPackageInfoGenerator::GenerateInterfaceLinkProperties(
412
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
413
  ImportPropertyMap const& properties) const
414
0
{
415
0
  auto const& iter = properties.find("INTERFACE_LINK_LIBRARIES");
416
0
  if (iter == properties.end()) {
417
0
    return;
418
0
  }
419
420
  // Extract any $<LINK_ONLY:...> from the link libraries, and assert that no
421
  // other generator expressions are present.
422
0
  std::map<std::string, std::vector<std::string>>
423
0
    allowedGeneratorExpressions = {
424
0
      { "COMPILE_ONLY", {} },
425
0
      { "LINK_ONLY", {} },
426
0
    };
427
0
  std::string interfaceLinkLibraries;
428
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(
429
0
        target, iter->first, iter->second, interfaceLinkLibraries,
430
0
        allowedGeneratorExpressions)) {
431
0
    result = false;
432
0
    return;
433
0
  }
434
435
0
  std::vector<std::string> linkLibraries;
436
0
  std::vector<std::string> buildRequires = this->ExtractRequirements(
437
0
    cmList{ interfaceLinkLibraries }, result, linkLibraries);
438
0
  std::vector<std::string> compileRequires = this->ExtractRequirements(
439
0
    allowedGeneratorExpressions["COMPILE_ONLY"], result, linkLibraries);
440
0
  std::vector<std::string> linkRequires = this->ExtractRequirements(
441
0
    allowedGeneratorExpressions["LINK_ONLY"], result, linkLibraries);
442
443
0
  BuildArray(component, "requires", buildRequires);
444
0
  BuildArray(component, "link_requires", linkRequires);
445
0
  BuildArray(component, "link_libraries", linkLibraries);
446
0
  BuildArray(component, "compile_requires", compileRequires);
447
0
}
448
449
void cmExportPackageInfoGenerator::GenerateInterfaceCompileFeatures(
450
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
451
  ImportPropertyMap const& properties) const
452
0
{
453
0
  auto const& iter = properties.find("INTERFACE_COMPILE_FEATURES");
454
0
  if (iter == properties.end()) {
455
0
    return;
456
0
  }
457
458
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, iter->first,
459
0
                                                         iter->second)) {
460
0
    result = false;
461
0
    return;
462
0
  }
463
464
0
  std::set<std::string> features;
465
0
  for (auto const& value : cmList{ iter->second }) {
466
0
    if (cmHasLiteralPrefix(value, "c_std_")) {
467
0
      auto suffix = cm::string_view{ value }.substr(6, 2);
468
0
      features.emplace(cmStrCat("c", suffix));
469
0
    } else if (cmHasLiteralPrefix(value, "cxx_std_")) {
470
0
      auto suffix = cm::string_view{ value }.substr(8, 2);
471
0
      features.emplace(cmStrCat("c++", suffix));
472
0
    }
473
0
  }
474
475
0
  BuildArray(component, "compile_features", features);
476
0
}
477
478
void cmExportPackageInfoGenerator::GenerateInterfaceCompileDefines(
479
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
480
  ImportPropertyMap const& properties) const
481
0
{
482
0
  auto const& iter = properties.find("INTERFACE_COMPILE_DEFINITIONS");
483
0
  if (iter == properties.end()) {
484
0
    return;
485
0
  }
486
487
  // TODO: Support language-specific defines.
488
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, iter->first,
489
0
                                                         iter->second)) {
490
0
    result = false;
491
0
    return;
492
0
  }
493
494
0
  Json::Value defines;
495
0
  for (auto const& def : cmList{ iter->second }) {
496
0
    auto const n = def.find('=');
497
0
    if (n == std::string::npos) {
498
0
      defines[def] = Json::Value{};
499
0
    } else {
500
0
      defines[def.substr(0, n)] = def.substr(n + 1);
501
0
    }
502
0
  }
503
504
0
  if (!defines.empty()) {
505
0
    component["definitions"]["*"] = std::move(defines);
506
0
  }
507
0
}
508
509
void cmExportPackageInfoGenerator::GenerateInterfaceListProperty(
510
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
511
  std::string const& outName, cm::string_view inName,
512
  ImportPropertyMap const& properties) const
513
0
{
514
0
  auto const& prop = cmStrCat("INTERFACE_", inName);
515
0
  auto const& iter = properties.find(prop);
516
0
  if (iter == properties.end()) {
517
0
    return;
518
0
  }
519
520
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, prop,
521
0
                                                         iter->second)) {
522
0
    result = false;
523
0
    return;
524
0
  }
525
526
0
  Json::Value& array = component[outName];
527
0
  for (auto const& value : cmList{ iter->second }) {
528
0
    array.append(value);
529
0
  }
530
0
}
531
532
void cmExportPackageInfoGenerator::GenerateProperty(
533
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
534
  std::string const& outName, std::string const& inName,
535
  ImportPropertyMap const& properties) const
536
0
{
537
0
  auto const& iter = properties.find(inName);
538
0
  if (iter == properties.end()) {
539
0
    return;
540
0
  }
541
542
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, inName,
543
0
                                                         iter->second)) {
544
0
    result = false;
545
0
    return;
546
0
  }
547
548
0
  component[outName] = iter->second;
549
0
}
550
551
Json::Value cmExportPackageInfoGenerator::GenerateInterfaceConfigProperties(
552
  std::string const& suffix, ImportPropertyMap const& properties) const
553
0
{
554
0
  Json::Value component;
555
0
  auto const suffixLength = suffix.length();
556
557
0
  for (auto const& p : properties) {
558
0
    if (!cmHasSuffix(p.first, suffix)) {
559
0
      continue;
560
0
    }
561
0
    auto const n = p.first.length() - suffixLength - 9;
562
0
    auto const prop = cm::string_view{ p.first }.substr(9, n);
563
564
0
    if (prop == "LOCATION") {
565
0
      component["location"] = p.second;
566
0
    } else if (prop == "IMPLIB") {
567
0
      component["link_location"] = p.second;
568
0
    } else if (prop == "LINK_DEPENDENT_LIBRARIES") {
569
0
      bool result;
570
0
      std::vector<std::string> libraries;
571
0
      std::vector<std::string> components =
572
0
        this->ExtractRequirements(cmList{ p.second }, result, libraries);
573
0
      BuildArray(component, "dyld_requires", components);
574
0
      if (!libraries.empty()) {
575
        // In theory this can never happen?
576
0
        this->IssueMessage(
577
0
          MessageType::AUTHOR_WARNING,
578
0
          cmStrCat("Package \""_s, this->GetPackageName(),
579
0
                   "\" has IMPORTED_LINK_DEPENDENT_LIBRARIES \""_s,
580
0
                   cmJoin(libraries, ";"_s), this->PackageVersionSchema,
581
0
                   "\". These cannot be exported. "
582
0
                   "Consumers may encounter link errors."_s));
583
0
      }
584
0
    } else if (prop == "LINK_INTERFACE_LANGUAGES") {
585
0
      std::vector<std::string> languages;
586
0
      for (auto const& lang : cmList{ p.second }) {
587
0
        auto ll = cmSystemTools::LowerCase(lang);
588
0
        if (ll == "cxx") {
589
0
          languages.emplace_back("cpp");
590
0
        } else {
591
0
          languages.emplace_back(std::move(ll));
592
0
        }
593
0
      }
594
0
      BuildArray(component, "link_languages", languages);
595
0
    }
596
0
  }
597
598
0
  return component;
599
0
}
600
601
std::string cmExportPackageInfoGenerator::GenerateCxxModules(
602
  Json::Value& component, cmGeneratorTarget* target,
603
  std::string const& packagePath, std::string const& config)
604
0
{
605
0
  std::string manifestPath;
606
607
0
  std::string const cxxModulesDirName = this->GetCxxModulesDirectory();
608
0
  if (cxxModulesDirName.empty() || !target->HaveCxx20ModuleSources()) {
609
0
    return manifestPath;
610
0
  }
611
612
0
  manifestPath =
613
0
    cmStrCat(cxxModulesDirName, "/target-", target->GetFilesystemExportName(),
614
0
             '-', config.empty() ? "noconfig" : config, ".modules.json");
615
616
0
  component["cpp_module_metadata"] = cmStrCat(packagePath, '/', manifestPath);
617
0
  return manifestPath;
618
0
}