Coverage Report

Created: 2026-07-14 07:09

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