Coverage Report

Created: 2026-10-28 06:38

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.15.0";
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
      if (!requirement.second.Domains.empty()) {
232
0
        auto requirementDomains = Json::Value{ Json::arrayValue };
233
0
        for (cm::PackageDomain domain : requirement.second.Domains) {
234
0
          if (domain == cm::PackageDomain::PkgConfig) {
235
0
            requirementDomains.append("pkg-config");
236
0
          }
237
0
        }
238
0
        if (!requirementDomains.empty()) {
239
0
          data["extensions"]["cmake"]["domains@v1"] =
240
0
            std::move(requirementDomains);
241
0
        }
242
0
      }
243
244
0
      requirements[requirement.first] = data;
245
0
    }
246
0
  }
247
0
}
248
249
Json::Value* cmExportPackageInfoGenerator::GenerateImportTarget(
250
  Json::Value& components, cmGeneratorTarget const* target,
251
  cm::TargetType targetType) const
252
0
{
253
0
  auto const& name = target->GetExportName();
254
0
  if (name.empty()) {
255
0
    return nullptr;
256
0
  }
257
258
0
  Json::Value& component = components[name];
259
0
  Json::Value& type = component["type"];
260
261
0
  switch (targetType) {
262
0
    case cm::TargetType::EXECUTABLE:
263
0
      type = "executable";
264
0
      break;
265
0
    case cm::TargetType::STATIC_LIBRARY:
266
0
      type = "archive";
267
0
      break;
268
0
    case cm::TargetType::SHARED_LIBRARY:
269
0
      type = "dylib";
270
0
      break;
271
0
    case cm::TargetType::MODULE_LIBRARY:
272
0
      type = "module";
273
0
      break;
274
0
    case cm::TargetType::INTERFACE_LIBRARY:
275
0
      type = target->IsSymbolic() ? "symbolic" : "interface";
276
0
      break;
277
0
    default:
278
0
      type = "unknown";
279
0
      break;
280
0
  }
281
0
  return &component;
282
0
}
283
284
bool cmExportPackageInfoGenerator::GenerateInterfaceProperties(
285
  Json::Value& component, cmGeneratorTarget const* target,
286
  ImportPropertyMap const& properties) const
287
0
{
288
0
  bool result = true;
289
290
0
  this->GenerateInterfaceLinkProperties(result, component, target, properties);
291
292
0
  this->GenerateInterfaceCompileFeatures(result, component, target,
293
0
                                         properties);
294
0
  this->GenerateInterfaceCompileDefines(result, component, target, properties);
295
296
0
  this->GenerateInterfaceListProperty(result, component, target,
297
0
                                      "compile_flags", "COMPILE_OPTIONS"_s,
298
0
                                      properties);
299
0
  this->GenerateInterfaceListProperty(result, component, target, "link_flags",
300
0
                                      "LINK_OPTIONS"_s, properties);
301
0
  this->GenerateInterfaceListProperty(result, component, target,
302
0
                                      "link_directories", "LINK_DIRECTORIES"_s,
303
0
                                      properties);
304
0
  this->GenerateInterfaceListProperty(result, component, target, "includes",
305
0
                                      "INCLUDE_DIRECTORIES"_s, properties);
306
307
0
  this->GenerateProperty(result, component, target, "license", "SPDX_LICENSE",
308
0
                         properties);
309
310
  // TODO: description
311
312
0
  return result;
313
0
}
314
315
void cmExportPackageInfoGenerator::GenerateTargetFileSets(
316
  Json::Value& component, cmGeneratorTarget const* target,
317
  cmTargetExport const* targetExport) const
318
0
{
319
0
  cmGeneratorFileSets const* gfs = target->GetGeneratorFileSets();
320
321
0
  for (auto const& type : gfs->GetInterfaceFileSetTypes()) {
322
    // Do we support this file set type?
323
0
    std::string fsType;
324
0
    if (type == "HEADERS") {
325
0
      fsType = "includes";
326
0
    } else {
327
0
      continue;
328
0
    }
329
330
    // Generate CPS file set(s) for each CMake file set
331
0
    Json::Value fileSets;
332
0
    for (cmGeneratorFileSet const* fileSet : gfs->GetInterfaceFileSets(type)) {
333
0
      this->GenerateTargetFileSets(fileSets, target, fileSet, targetExport,
334
0
                                   fsType);
335
0
    }
336
337
0
    if (!fileSets.empty()) {
338
0
      component["file_sets"] = fileSets;
339
0
    }
340
0
  }
341
0
}
342
343
void cmExportPackageInfoGenerator::GenerateTargetFileSet(
344
  Json::Value& fileSets, cmGeneratorFileSet const* fileSet,
345
  std::string const& type, std::string const& root,
346
  std::vector<std::string> const& files)
347
0
{
348
0
  Json::Value fileSetOut;
349
0
  fileSetOut["type"] = type;
350
0
  fileSetOut["root"] = root;
351
352
0
  Json::Value filesOut;
353
0
  for (auto const& f : files) {
354
0
    filesOut.append(f);
355
0
  }
356
0
  fileSetOut["files"] = filesOut;
357
358
0
  fileSetOut["extensions"]["cmake"]["name@v1"] = fileSet->GetName();
359
360
0
  fileSets.append(fileSetOut);
361
0
}
362
363
bool cmExportPackageInfoGenerator::NoteLinkedTarget(
364
  cmGeneratorTarget const* target, std::string const& linkedName,
365
  cmGeneratorTarget const* linkedTarget)
366
0
{
367
0
  if (cm::contains(this->ExportedTargets, linkedTarget)) {
368
    // Target is internal to this package.
369
0
    this->LinkTargets.emplace(linkedName,
370
0
                              cmStrCat(':', linkedTarget->GetExportName()));
371
0
    return true;
372
0
  }
373
374
0
  auto getRequirement =
375
0
    [&](std::string const& name,
376
0
        cmPackageInformation&& requirement) -> cmPackageInformation* {
377
    // emplace may move from requirement even when the key already exists.
378
0
    auto const domains = requirement.Domains;
379
0
    auto record = this->Requirements.emplace(name, std::move(requirement));
380
0
    if (!record.second) {
381
0
      if (record.first->second.Domains != domains) {
382
0
        target->Makefile->IssueMessage(
383
0
          MessageType::FATAL_ERROR,
384
0
          cmStrCat("Package \"", this->GetPackageName(),
385
0
                   "\" requires package \"", name,
386
0
                   "\" in multiple domain sets. This is not supported."));
387
0
        return nullptr;
388
0
      }
389
0
    }
390
0
    return &record.first->second;
391
0
  };
392
393
0
  if (linkedTarget->IsImported()) {
394
    // Target is imported from a found package.
395
0
    using Package = cm::optional<std::pair<std::string, cmPackageInformation>>;
396
0
    auto pkgInfo = [](cmTarget* t) -> Package {
397
0
      cmFindPackageStack pkgStack = t->GetFindPackageStack();
398
0
      if (!pkgStack.Empty()) {
399
0
        return std::make_pair(pkgStack.Top().Name,
400
0
                              *pkgStack.Top().PackageInfo);
401
0
      }
402
403
0
      cmPackageInformation package;
404
0
      std::string const pkgName =
405
0
        t->GetSafeProperty("EXPORT_FIND_PACKAGE_NAME");
406
0
      if (pkgName.empty()) {
407
0
        return cm::nullopt;
408
0
      }
409
410
0
      return std::make_pair(pkgName, package);
411
0
    }(linkedTarget->Target);
412
413
0
    if (!pkgInfo) {
414
0
      if (cmTarget* foreignTarget = linkedTarget->Target->GetForeignTarget()) {
415
0
        cmPackageInformation reqInit;
416
0
        reqInit.Domains = { cm::PackageDomain::PkgConfig };
417
418
0
        std::string foreignPkgName = foreignTarget->GetForeignPackageName();
419
0
        cmPackageInformation* req =
420
0
          getRequirement(foreignPkgName, std::move(reqInit));
421
0
        if (!req) {
422
0
          return false;
423
0
        }
424
425
        // A pkg-config package has no components of its own; expose it as a
426
        // component named after the package itself.
427
0
        this->LinkTargets.emplace(
428
0
          linkedName, cmStrCat(foreignPkgName, ':', foreignPkgName));
429
430
0
        req->Components.emplace(std::move(foreignPkgName));
431
0
        return true;
432
0
      }
433
434
0
      target->Makefile->IssueMessage(
435
0
        MessageType::FATAL_ERROR,
436
0
        cmStrCat("Target \"", target->GetName(),
437
0
                 "\" references imported target \"", linkedName,
438
0
                 "\" which does not come from any known package."));
439
0
      return false;
440
0
    }
441
442
0
    std::string const& pkgName = pkgInfo->first;
443
444
0
    auto const& prefix = cmStrCat(pkgName, "::");
445
0
    if (!cmHasPrefix(linkedName, prefix)) {
446
0
      target->Makefile->IssueMessage(
447
0
        MessageType::FATAL_ERROR,
448
0
        cmStrCat("Target \"", target->GetName(), "\" references target \"",
449
0
                 linkedName, "\", which comes from the \"", pkgName,
450
0
                 "\" package, but does not belong to the package's "
451
0
                 "canonical namespace (\"",
452
0
                 prefix, "\"). This is not allowed."));
453
0
      return false;
454
0
    }
455
456
0
    std::string component = linkedName.substr(prefix.length());
457
0
    cmPackageInformation* req =
458
0
      getRequirement(pkgName, std::move(pkgInfo->second));
459
0
    if (!req) {
460
0
      return false;
461
0
    }
462
0
    this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component));
463
0
    req->Components.emplace(std::move(component));
464
0
    return true;
465
0
  }
466
467
  // Target belongs to another export from this build.
468
0
  auto const& exportInfo = this->FindExportInfo(linkedTarget);
469
0
  if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) {
470
0
    auto const& linkNamespace = *exportInfo.Namespaces.begin();
471
0
    if (!cmHasSuffix(linkNamespace, "::")) {
472
0
      target->Makefile->IssueMessage(
473
0
        MessageType::FATAL_ERROR,
474
0
        cmStrCat("Target \"", target->GetName(), "\" references target \"",
475
0
                 linkedName,
476
0
                 "\", which does not use the standard namespace separator. "
477
0
                 "This is not allowed."));
478
0
      return false;
479
0
    }
480
481
0
    std::string pkgName{ linkNamespace.data(), linkNamespace.size() - 2 };
482
0
    std::string component = linkedTarget->GetExportName();
483
0
    if (pkgName == this->GetPackageName()) {
484
0
      this->LinkTargets.emplace(linkedName, cmStrCat(':', component));
485
0
    } else {
486
0
      cmPackageInformation* req = getRequirement(pkgName, {});
487
0
      if (!req) {
488
0
        return false;
489
0
      }
490
0
      this->LinkTargets.emplace(linkedName, cmStrCat(pkgName, ':', component));
491
0
      req->Components.emplace(std::move(component));
492
0
    }
493
0
    return true;
494
0
  }
495
496
  // Target belongs to multiple namespaces or multiple export sets.
497
  // cmExportFileGenerator::HandleMissingTarget should have complained about
498
  // this already.
499
0
  return false;
500
0
}
501
502
std::vector<std::string> cmExportPackageInfoGenerator::ExtractRequirements(
503
  std::vector<std::string> const& names, bool& result,
504
  std::vector<std::string>& libraryPaths) const
505
0
{
506
0
  std::vector<std::string> output;
507
508
0
  for (auto const& name : names) {
509
0
    auto const& ti = this->LinkTargets.find(name);
510
0
    if (ti != this->LinkTargets.end()) {
511
0
      if (ti->second.empty()) {
512
0
        result = false;
513
0
      } else {
514
0
        output.emplace_back(ti->second);
515
0
      }
516
0
    } else {
517
0
      libraryPaths.emplace_back(name);
518
0
    }
519
0
  }
520
521
0
  return output;
522
0
}
523
524
void cmExportPackageInfoGenerator::GenerateInterfaceLinkProperties(
525
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
526
  ImportPropertyMap const& properties) const
527
0
{
528
0
  auto const& iter = properties.find("INTERFACE_LINK_LIBRARIES");
529
0
  if (iter == properties.end()) {
530
0
    return;
531
0
  }
532
533
  // Extract any $<LINK_ONLY:...> from the link libraries, and assert that no
534
  // other generator expressions are present.
535
0
  std::map<std::string, std::vector<std::string>>
536
0
    allowedGeneratorExpressions = {
537
0
      { "COMPILE_ONLY", {} },
538
0
      { "LINK_ONLY", {} },
539
0
    };
540
0
  std::string interfaceLinkLibraries;
541
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(
542
0
        target, iter->first, iter->second, interfaceLinkLibraries,
543
0
        allowedGeneratorExpressions)) {
544
0
    result = false;
545
0
    return;
546
0
  }
547
548
0
  std::vector<std::string> linkLibraries;
549
0
  std::vector<std::string> buildRequires = this->ExtractRequirements(
550
0
    cmList{ interfaceLinkLibraries }, result, linkLibraries);
551
0
  std::vector<std::string> compileRequires = this->ExtractRequirements(
552
0
    allowedGeneratorExpressions["COMPILE_ONLY"], result, linkLibraries);
553
0
  std::vector<std::string> linkRequires = this->ExtractRequirements(
554
0
    allowedGeneratorExpressions["LINK_ONLY"], result, linkLibraries);
555
556
0
  BuildArray(component, "requires", buildRequires);
557
0
  BuildArray(component, "link_requires", linkRequires);
558
0
  BuildArray(component, "link_libraries", linkLibraries);
559
0
  BuildArray(component, "compile_requires", compileRequires);
560
0
}
561
562
void cmExportPackageInfoGenerator::GenerateInterfaceCompileFeatures(
563
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
564
  ImportPropertyMap const& properties) const
565
0
{
566
0
  auto const& iter = properties.find("INTERFACE_COMPILE_FEATURES");
567
0
  if (iter == properties.end()) {
568
0
    return;
569
0
  }
570
571
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, iter->first,
572
0
                                                         iter->second)) {
573
0
    result = false;
574
0
    return;
575
0
  }
576
577
0
  std::set<std::string> features;
578
0
  for (auto const& value : cmList{ iter->second }) {
579
0
    if (cmHasLiteralPrefix(value, "c_std_")) {
580
0
      auto suffix = cm::string_view{ value }.substr(6, 2);
581
0
      features.emplace(cmStrCat('c', suffix));
582
0
    } else if (cmHasLiteralPrefix(value, "cxx_std_")) {
583
0
      auto suffix = cm::string_view{ value }.substr(8, 2);
584
0
      features.emplace(cmStrCat("c++", suffix));
585
0
    }
586
0
  }
587
588
0
  BuildArray(component, "compile_features", features);
589
0
}
590
591
void cmExportPackageInfoGenerator::GenerateInterfaceCompileDefines(
592
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
593
  ImportPropertyMap const& properties) const
594
0
{
595
0
  auto const& iter = properties.find("INTERFACE_COMPILE_DEFINITIONS");
596
0
  if (iter == properties.end()) {
597
0
    return;
598
0
  }
599
600
  // TODO: Support language-specific defines.
601
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, iter->first,
602
0
                                                         iter->second)) {
603
0
    result = false;
604
0
    return;
605
0
  }
606
607
0
  Json::Value defines;
608
0
  for (auto const& def : cmList{ iter->second }) {
609
0
    auto const n = def.find('=');
610
0
    if (n == std::string::npos) {
611
0
      defines[def] = Json::Value{};
612
0
    } else {
613
0
      defines[def.substr(0, n)] = def.substr(n + 1);
614
0
    }
615
0
  }
616
617
0
  if (!defines.empty()) {
618
0
    component["definitions"]["*"] = std::move(defines);
619
0
  }
620
0
}
621
622
void cmExportPackageInfoGenerator::GenerateInterfaceListProperty(
623
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
624
  std::string const& outName, cm::string_view inName,
625
  ImportPropertyMap const& properties) const
626
0
{
627
0
  auto const& prop = cmStrCat("INTERFACE_", inName);
628
0
  auto const& iter = properties.find(prop);
629
0
  if (iter == properties.end()) {
630
0
    return;
631
0
  }
632
633
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, prop,
634
0
                                                         iter->second)) {
635
0
    result = false;
636
0
    return;
637
0
  }
638
639
0
  Json::Value& array = component[outName];
640
0
  for (auto const& value : cmList{ iter->second }) {
641
0
    array.append(value);
642
0
  }
643
0
}
644
645
void cmExportPackageInfoGenerator::GenerateProperty(
646
  bool& result, Json::Value& component, cmGeneratorTarget const* target,
647
  std::string const& outName, std::string const& inName,
648
  ImportPropertyMap const& properties) const
649
0
{
650
0
  auto const& iter = properties.find(inName);
651
0
  if (iter == properties.end()) {
652
0
    return;
653
0
  }
654
655
0
  if (!cmGeneratorExpression::ForbidGeneratorExpressions(target, inName,
656
0
                                                         iter->second)) {
657
0
    result = false;
658
0
    return;
659
0
  }
660
661
0
  component[outName] = iter->second;
662
0
}
663
664
Json::Value cmExportPackageInfoGenerator::GenerateInterfaceConfigProperties(
665
  std::string const& suffix, ImportPropertyMap const& properties) const
666
0
{
667
0
  Json::Value component;
668
0
  auto const suffixLength = suffix.length();
669
670
0
  for (auto const& p : properties) {
671
0
    if (!cmHasSuffix(p.first, suffix)) {
672
0
      continue;
673
0
    }
674
0
    auto const n = p.first.length() - suffixLength - 9;
675
0
    auto const prop = cm::string_view{ p.first }.substr(9, n);
676
677
0
    if (prop == "LOCATION") {
678
0
      component["location"] = p.second;
679
0
    } else if (prop == "IMPLIB") {
680
0
      component["link_location"] = p.second;
681
0
    } else if (prop == "LINK_DEPENDENT_LIBRARIES") {
682
0
      bool result;
683
0
      std::vector<std::string> libraries;
684
0
      std::vector<std::string> components =
685
0
        this->ExtractRequirements(cmList{ p.second }, result, libraries);
686
0
      BuildArray(component, "dyld_requires", components);
687
0
      if (!libraries.empty()) {
688
        // In theory this can never happen?
689
0
        this->IssueDiagnostic(
690
0
          cmDiagnostics::CMD_AUTHOR,
691
0
          cmStrCat("Package \""_s, this->GetPackageName(),
692
0
                   "\" has IMPORTED_LINK_DEPENDENT_LIBRARIES \""_s,
693
0
                   cmJoin(libraries, ";"_s), this->PackageVersionSchema,
694
0
                   "\". These cannot be exported. "
695
0
                   "Consumers may encounter link errors."_s));
696
0
      }
697
0
    } else if (prop == "LINK_INTERFACE_LANGUAGES") {
698
0
      std::vector<std::string> languages;
699
0
      for (auto const& lang : cmList{ p.second }) {
700
0
        auto ll = cmSystemTools::LowerCase(lang);
701
0
        if (ll == "cxx") {
702
0
          languages.emplace_back("cpp");
703
0
        } else {
704
0
          languages.emplace_back(std::move(ll));
705
0
        }
706
0
      }
707
0
      BuildArray(component, "link_languages", languages);
708
0
    }
709
0
  }
710
711
0
  return component;
712
0
}
713
714
std::string cmExportPackageInfoGenerator::GenerateCxxModules(
715
  Json::Value& component, cmGeneratorTarget* target,
716
  std::string const& packagePath, std::string const& config)
717
0
{
718
0
  std::string manifestPath;
719
720
0
  std::string const cxxModulesDirName = this->GetCxxModulesDirectory();
721
0
  if (cxxModulesDirName.empty() || !target->HaveCxx20ModuleSources()) {
722
0
    return manifestPath;
723
0
  }
724
725
0
  manifestPath =
726
0
    cmStrCat(cxxModulesDirName, "/target-", target->GetFilesystemExportName(),
727
0
             '-', config.empty() ? "noconfig" : config, ".modules.json");
728
729
0
  component["cpp_module_metadata"] = cmStrCat(packagePath, '/', manifestPath);
730
0
  return manifestPath;
731
0
}