Coverage Report

Created: 2026-10-28 06:38

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmPackageInfoReader.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 "cmPackageInfoReader.h"
4
5
#include <algorithm>
6
#include <initializer_list>
7
#include <limits>
8
#include <unordered_map>
9
#include <utility>
10
11
#include <cmext/algorithm>
12
#include <cmext/string_view>
13
14
#include <cm3p/json/value.h>
15
#include <cm3p/json/version.h>
16
17
#include "cmsys/RegularExpression.hxx"
18
19
#include "cmCxxModuleMetadata.h"
20
#include "cmExecutionStatus.h"
21
#include "cmFileSet.h"
22
#include "cmFileSetMetadata.h"
23
#include "cmJSONState.h"
24
#include "cmList.h"
25
#include "cmListFileCache.h"
26
#include "cmMakefile.h"
27
#include "cmMessageType.h"
28
#include "cmStringAlgorithms.h"
29
#include "cmSystemTools.h"
30
#include "cmTarget.h"
31
#include "cmTargetTypes.h"
32
#include "cmValue.h"
33
34
// When adding or changing CPS schema versions, component types, languages, or
35
// branch-selecting attributes, review Tests/Fuzzing/cmPackageInfoReader.dict.
36
// Add tokens including keys that help mutations reach the new paths; keep the
37
// dictionary focused on useful parser inputs, not an exhaustive list of CPS
38
// keywords.
39
40
namespace {
41
42
// Map of CPS language names to CMake language name.  Case insensitivity is
43
// achieved by converting the CPS value to lower case, so keys in this map must
44
// be lower case.
45
std::unordered_map<std::string, std::string> Languages = {
46
  // clang-format off
47
  { "c", "C" },
48
  { "c++", "CXX" },
49
  { "cpp", "CXX" },
50
  { "cxx", "CXX" },
51
  { "objc", "OBJC" },
52
  { "objc++", "OBJCXX" },
53
  { "objcpp", "OBJCXX" },
54
  { "objcxx", "OBJCXX" },
55
  { "swift", "swift" },
56
  { "hip", "HIP" },
57
  { "cuda", "CUDA" },
58
  { "ispc", "ISPC" },
59
  { "c#", "CSharp" },
60
  { "csharp", "CSharp" },
61
  { "fortran", "Fortran" },
62
  // clang-format on
63
};
64
65
enum LanguageGlobOption
66
{
67
  DisallowGlob,
68
  AllowGlob,
69
};
70
71
cm::string_view MapLanguage(cm::string_view lang,
72
                            LanguageGlobOption glob = AllowGlob)
73
35.4k
{
74
35.4k
  if (glob == AllowGlob && lang == "*"_s) {
75
22.6k
    return "*"_s;
76
22.6k
  }
77
12.7k
  auto const li = Languages.find(cmSystemTools::LowerCase(lang));
78
12.7k
  if (li != Languages.end()) {
79
1.13k
    return li->second;
80
1.13k
  }
81
11.5k
  return {};
82
12.7k
}
83
84
std::string GetRealPath(std::string const& path)
85
166
{
86
166
  return cmSystemTools::GetRealPath(path);
87
166
}
88
89
std::string GetRealDir(std::string const& path)
90
166
{
91
166
  return cmSystemTools::GetFilenamePath(cmSystemTools::GetRealPath(path));
92
166
}
93
94
Json::Value ReadJson(std::string const& fileName)
95
18.2k
{
96
18.2k
  Json::Value data;
97
18.2k
  cmJSONState parseState(fileName, &data, cmJSONState::StrictMode::Relaxed);
98
18.2k
  if (!parseState.errors.empty()) {
99
#if JSONCPP_VERSION_HEXA < 0x01070300
100
    return Json::Value::null;
101
#else
102
3.52k
    return Json::Value::nullSingleton();
103
3.52k
#endif
104
3.52k
  }
105
106
14.7k
  return data;
107
18.2k
}
108
109
std::string ToString(Json::Value const& value)
110
109k
{
111
109k
  if (value.isString()) {
112
79.6k
    return value.asString();
113
79.6k
  }
114
29.5k
  return {};
115
109k
}
116
117
bool CheckSchemaVersion(Json::Value const& data)
118
9.52k
{
119
9.52k
  std::string const& version = ToString(data["cps_version"]);
120
121
  // Check that a valid version is specified.
122
9.52k
  if (version.empty()) {
123
126
    return false;
124
126
  }
125
126
  // Check that we understand this version.
127
9.39k
  return cmSystemTools::VersionCompare(cmSystemTools::OP_GREATER_EQUAL,
128
9.39k
                                       version, "0.13") &&
129
9.35k
    cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, version, "0.16");
130
131
  // TODO Eventually this probably needs to return the version tuple, and
132
  // should share code with cmPackageInfoReader::ParseVersion.
133
9.52k
}
134
135
bool ComparePathSuffix(std::string const& path, std::string const& suffix)
136
8.82k
{
137
8.82k
  std::string::size_type const ps = path.size();
138
8.82k
  std::string::size_type const ss = suffix.size();
139
140
8.82k
  if (ss > ps) {
141
206
    return false;
142
206
  }
143
144
8.61k
  return cmSystemTools::ComparePath(path.substr(ps - ss), suffix);
145
8.82k
}
146
147
std::string DeterminePrefix(std::string const& filepath,
148
                            Json::Value const& data)
149
9.25k
{
150
  // First check if an absolute prefix was supplied.
151
9.25k
  std::string prefix = ToString(data["prefix"]);
152
9.25k
  if (!prefix.empty()) {
153
    // Ensure that the specified prefix is valid.
154
2
    if (cmsys::SystemTools::FileIsFullPath(prefix) &&
155
0
        cmsys::SystemTools::FileIsDirectory(prefix)) {
156
0
      cmSystemTools::ConvertToUnixSlashes(prefix);
157
0
      return prefix;
158
0
    }
159
    // The specified absolute prefix is not valid.
160
2
    return {};
161
2
  }
162
163
  // Get and validate prefix-relative path.
164
9.24k
  std::string const& absPath = cmSystemTools::GetFilenamePath(filepath);
165
9.24k
  std::string relPath = ToString(data["cps_path"]);
166
9.24k
  cmSystemTools::ConvertToUnixSlashes(relPath);
167
9.24k
  if (relPath.empty() || !cmHasLiteralPrefix(relPath, "@prefix@")) {
168
    // The relative prefix is not valid.
169
490
    return {};
170
490
  }
171
8.75k
  if (relPath.size() == 8) {
172
    // The relative path is exactly "@prefix@".
173
68
    return absPath;
174
68
  }
175
8.69k
  if (relPath[8] != '/') {
176
    // The relative prefix is not valid.
177
36
    return {};
178
36
  }
179
8.65k
  relPath = relPath.substr(8);
180
181
  // Get directory portion of the absolute path.
182
8.65k
  if (ComparePathSuffix(absPath, relPath)) {
183
8.48k
    return absPath.substr(0, absPath.size() - relPath.size());
184
8.48k
  }
185
186
332
  for (auto* const f : { GetRealPath, GetRealDir }) {
187
332
    std::string const& tmpPath = (*f)(absPath);
188
332
    if (!cmSystemTools::ComparePath(tmpPath, absPath) &&
189
166
        ComparePathSuffix(tmpPath, relPath)) {
190
0
      return tmpPath.substr(0, tmpPath.size() - relPath.size());
191
0
    }
192
332
  }
193
194
166
  return {};
195
166
}
196
197
// Extract key name from value iterator as string_view.
198
cm::string_view IterKey(Json::Value::const_iterator iter)
199
87.7k
{
200
87.7k
  char const* end;
201
87.7k
  char const* const start = iter.memberName(&end);
202
87.7k
  return { start, static_cast<std::string::size_type>(end - start) };
203
87.7k
}
204
205
// Get list-of-strings value from object.
206
std::vector<std::string> ReadList(Json::Value const& arr)
207
467k
{
208
467k
  std::vector<std::string> result;
209
210
467k
  if (arr.isArray()) {
211
181k
    for (Json::Value const& val : arr) {
212
181k
      if (val.isString()) {
213
180k
        result.push_back(val.asString());
214
180k
      }
215
181k
    }
216
14.0k
  }
217
218
467k
  return result;
219
467k
}
220
221
std::vector<std::string> ReadList(Json::Value const& data, char const* key)
222
462k
{
223
462k
  return ReadList(data[key]);
224
462k
}
225
226
Json::Value GetExtensions(Json::Value const& data)
227
8.60k
{
228
8.60k
  if (data.isObject()) {
229
7.82k
    Json::Value const& extensions = data["extensions"];
230
7.82k
    if (extensions.isObject()) {
231
2.17k
      Json::Value const& cmake = extensions["cmake"];
232
2.17k
      if (cmake.isObject()) {
233
1.93k
        return cmake;
234
1.93k
      }
235
2.17k
    }
236
7.82k
  }
237
6.67k
  return Json::Value{ Json::objectValue };
238
8.60k
}
239
240
std::string NormalizeTargetName(std::string const& name,
241
                                std::string const& context)
242
56.8k
{
243
56.8k
  if (cmHasPrefix(name, ':')) {
244
4.23k
    return cmStrCat(context, ':', name);
245
4.23k
  }
246
247
52.5k
  std::string::size_type const n = name.find_first_of(':');
248
52.5k
  if (n != std::string::npos) {
249
4.45k
    cm::string_view v{ name };
250
4.45k
    return cmStrCat(v.substr(0, n), ':', v.substr(n));
251
4.45k
  }
252
48.1k
  return name;
253
52.5k
}
254
255
void AppendProperty(cmMakefile* makefile, cmTarget* target,
256
                    cm::string_view property, cm::string_view configuration,
257
                    std::string const& value)
258
142k
{
259
142k
  std::string const fullprop = cmStrCat("INTERFACE_", property);
260
142k
  if (!configuration.empty()) {
261
98.2k
    std::string const genexValue =
262
98.2k
      cmStrCat("$<$<CONFIG:", configuration, ">:", value, '>');
263
98.2k
    target->AppendProperty(fullprop, genexValue, makefile->GetBacktrace());
264
98.2k
  } else {
265
44.0k
    target->AppendProperty(fullprop, value, makefile->GetBacktrace());
266
44.0k
  }
267
142k
}
268
269
void AppendImportProperty(cmMakefile* makefile, cmTarget* target,
270
                          cm::string_view property,
271
                          cm::string_view configuration,
272
                          std::string const& value)
273
7.40k
{
274
7.40k
  if (!configuration.empty()) {
275
6.70k
    std::string const fullprop = cmStrCat(
276
6.70k
      "IMPORTED_", property, '_', cmSystemTools::UpperCase(configuration));
277
6.70k
    target->AppendProperty(fullprop, value, makefile->GetBacktrace());
278
6.70k
  } else {
279
706
    std::string const fullprop = cmStrCat("IMPORTED_", property);
280
706
    target->AppendProperty(fullprop, value, makefile->GetBacktrace());
281
706
  }
282
7.40k
}
283
284
template <typename Transform>
285
void AppendLanguageProperties(cmMakefile* makefile, cmTarget* target,
286
                              cm::string_view property,
287
                              cm::string_view configuration,
288
                              Json::Value const& data, char const* key,
289
                              Transform transform)
290
95.7k
{
291
95.7k
  Json::Value const& value = data[key];
292
95.7k
  if (value.isArray()) {
293
53.9k
    for (std::string v : ReadList(value)) {
294
53.9k
      AppendProperty(makefile, target, property, configuration,
295
53.9k
                     transform(std::move(v)));
296
53.9k
    }
297
95.2k
  } else if (value.isObject()) {
298
10
    for (auto vi = value.begin(), ve = value.end(); vi != ve; ++vi) {
299
4
      cm::string_view const originalLang = IterKey(vi);
300
4
      cm::string_view const lang = MapLanguage(originalLang);
301
4
      if (lang.empty()) {
302
4
        makefile->IssueMessage(MessageType::WARNING,
303
4
                               cmStrCat(R"(ignoring unknown language ")"_s,
304
4
                                        originalLang, R"(" in )"_s, key,
305
4
                                        " for "_s, target->GetName()));
306
4
        continue;
307
4
      }
308
309
0
      if (lang == "*"_s) {
310
0
        for (std::string v : ReadList(*vi)) {
311
0
          AppendProperty(makefile, target, property, configuration,
312
0
                         transform(std::move(v)));
313
0
        }
314
0
      } else {
315
0
        for (std::string v : ReadList(*vi)) {
316
0
          v = cmStrCat("$<$<COMPILE_LANGUAGE:"_s, lang, ">:"_s,
317
0
                       transform(std::move(v)), '>');
318
0
          AppendProperty(makefile, target, property, configuration, v);
319
0
        }
320
0
      }
321
0
    }
322
6
  }
323
95.7k
}
cmPackageInfoReader.cxx:void (anonymous namespace)::AppendLanguageProperties<cmPackageInfoReader::SetTargetProperties(cmMakefile*, cmTarget*, Json::Value const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >) const::$_0>(cmMakefile*, cmTarget*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, Json::Value const&, char const*, cmPackageInfoReader::SetTargetProperties(cmMakefile*, cmTarget*, Json::Value const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >) const::$_0)
Line
Count
Source
290
47.8k
{
291
47.8k
  Json::Value const& value = data[key];
292
47.8k
  if (value.isArray()) {
293
2
    for (std::string v : ReadList(value)) {
294
2
      AppendProperty(makefile, target, property, configuration,
295
2
                     transform(std::move(v)));
296
2
    }
297
47.8k
  } else if (value.isObject()) {
298
0
    for (auto vi = value.begin(), ve = value.end(); vi != ve; ++vi) {
299
0
      cm::string_view const originalLang = IterKey(vi);
300
0
      cm::string_view const lang = MapLanguage(originalLang);
301
0
      if (lang.empty()) {
302
0
        makefile->IssueMessage(MessageType::WARNING,
303
0
                               cmStrCat(R"(ignoring unknown language ")"_s,
304
0
                                        originalLang, R"(" in )"_s, key,
305
0
                                        " for "_s, target->GetName()));
306
0
        continue;
307
0
      }
308
309
0
      if (lang == "*"_s) {
310
0
        for (std::string v : ReadList(*vi)) {
311
0
          AppendProperty(makefile, target, property, configuration,
312
0
                         transform(std::move(v)));
313
0
        }
314
0
      } else {
315
0
        for (std::string v : ReadList(*vi)) {
316
0
          v = cmStrCat("$<$<COMPILE_LANGUAGE:"_s, lang, ">:"_s,
317
0
                       transform(std::move(v)), '>');
318
0
          AppendProperty(makefile, target, property, configuration, v);
319
0
        }
320
0
      }
321
0
    }
322
0
  }
323
47.8k
}
cmPackageInfoReader.cxx:void (anonymous namespace)::AppendLanguageProperties<cmPackageInfoReader::SetTargetProperties(cmMakefile*, cmTarget*, Json::Value const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >) const::$_1>(cmMakefile*, cmTarget*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, Json::Value const&, char const*, cmPackageInfoReader::SetTargetProperties(cmMakefile*, cmTarget*, Json::Value const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string_view<char, std::__1::char_traits<char> >) const::$_1)
Line
Count
Source
290
47.8k
{
291
47.8k
  Json::Value const& value = data[key];
292
47.8k
  if (value.isArray()) {
293
53.9k
    for (std::string v : ReadList(value)) {
294
53.9k
      AppendProperty(makefile, target, property, configuration,
295
53.9k
                     transform(std::move(v)));
296
53.9k
    }
297
47.3k
  } else if (value.isObject()) {
298
10
    for (auto vi = value.begin(), ve = value.end(); vi != ve; ++vi) {
299
4
      cm::string_view const originalLang = IterKey(vi);
300
4
      cm::string_view const lang = MapLanguage(originalLang);
301
4
      if (lang.empty()) {
302
4
        makefile->IssueMessage(MessageType::WARNING,
303
4
                               cmStrCat(R"(ignoring unknown language ")"_s,
304
4
                                        originalLang, R"(" in )"_s, key,
305
4
                                        " for "_s, target->GetName()));
306
4
        continue;
307
4
      }
308
309
0
      if (lang == "*"_s) {
310
0
        for (std::string v : ReadList(*vi)) {
311
0
          AppendProperty(makefile, target, property, configuration,
312
0
                         transform(std::move(v)));
313
0
        }
314
0
      } else {
315
0
        for (std::string v : ReadList(*vi)) {
316
0
          v = cmStrCat("$<$<COMPILE_LANGUAGE:"_s, lang, ">:"_s,
317
0
                       transform(std::move(v)), '>');
318
0
          AppendProperty(makefile, target, property, configuration, v);
319
0
        }
320
0
      }
321
0
    }
322
6
  }
323
47.8k
}
324
325
void AddCompileFeature(cmMakefile* makefile, cmTarget* target,
326
                       cm::string_view configuration, std::string const& value)
327
7.83k
{
328
7.83k
  auto reLanguageLevel = []() -> cmsys::RegularExpression {
329
7.83k
    static cmsys::RegularExpression re{ "^[Cc]([+][+])?([0-9][0-9])$" };
330
7.83k
    return re;
331
7.83k
  }();
332
333
7.83k
  if (reLanguageLevel.find(value)) {
334
8
    std::string::size_type const n = reLanguageLevel.end() - 2;
335
8
    cm::string_view const featurePrefix = (n == 3 ? "cxx_std_"_s : "c_std_"_s);
336
8
    if (configuration.empty()) {
337
8
      AppendProperty(makefile, target, "COMPILE_FEATURES"_s, {},
338
8
                     cmStrCat(featurePrefix, value.substr(n)));
339
8
    } else {
340
0
      std::string const& feature =
341
0
        cmStrCat("$<$<CONFIG:"_s, configuration, ">:"_s, featurePrefix,
342
0
                 value.substr(n), '>');
343
0
      AppendProperty(makefile, target, "COMPILE_FEATURES"_s, {}, feature);
344
0
    }
345
7.83k
  } else if (cmStrCaseEq(value, "gnu"_s)) {
346
    // Not implemented in CMake at this time
347
7.83k
  } else if (cmStrCaseEq(value, "threads"_s)) {
348
0
    AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
349
0
                   "Threads::Threads");
350
0
  }
351
7.83k
}
352
353
void AddLinkFeature(cmMakefile* makefile, cmTarget* target,
354
                    cm::string_view configuration, std::string const& value)
355
1.46k
{
356
1.46k
  if (cmStrCaseEq(value, "thread"_s)) {
357
0
    AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
358
0
                   "Threads::Threads");
359
0
  }
360
1.46k
}
361
362
std::string BuildDefinition(std::string const& name, Json::Value const& value)
363
37.3k
{
364
37.3k
  if (!value.isNull() && value.isConvertibleTo(Json::stringValue)) {
365
28.4k
    return cmStrCat(name, '=', value.asString());
366
28.4k
  }
367
8.96k
  return name;
368
37.3k
}
369
370
void AddDefinition(cmMakefile* makefile, cmTarget* target,
371
                   cm::string_view configuration,
372
                   std::string const& definition)
373
37.3k
{
374
37.3k
  AppendProperty(makefile, target, "COMPILE_DEFINITIONS"_s, configuration,
375
37.3k
                 definition);
376
37.3k
}
377
378
using DefinitionLanguageMap = std::map<cm::string_view, Json::Value>;
379
using DefinitionsMap = std::map<std::string, DefinitionLanguageMap>;
380
381
void AddDefinitions(cmMakefile* makefile, cmTarget* target,
382
                    cm::string_view configuration,
383
                    DefinitionsMap const& definitions)
384
47.8k
{
385
47.8k
  for (auto const& di : definitions) {
386
35.8k
    auto const& g = di.second.find("*"_s);
387
35.8k
    if (g != di.second.end()) {
388
24.5k
      std::string const& def = BuildDefinition(di.first, g->second);
389
24.5k
      if (di.second.size() == 1) {
390
        // Only the non-language-specific definition exists.
391
24.3k
        AddDefinition(makefile, target, configuration, def);
392
24.3k
        continue;
393
24.3k
      }
394
395
      // Create a genex to apply this definition to all languages except
396
      // those that override it.
397
192
      std::vector<cm::string_view> excludedLanguages;
398
390
      for (auto const& li : di.second) {
399
390
        if (li.first != "*"_s) {
400
198
          excludedLanguages.emplace_back(li.first);
401
198
        }
402
390
      }
403
192
      AddDefinition(makefile, target, configuration,
404
192
                    cmStrCat("$<$<NOT:$<COMPILE_LANGUAGE:"_s,
405
192
                             cmJoin(excludedLanguages, ","_s), ">>:"_s, def,
406
192
                             '>'));
407
192
    }
408
409
    // Add language-specific definitions.
410
13.0k
    for (auto const& li : di.second) {
411
13.0k
      if (li.first != "*"_s) {
412
12.8k
        AddDefinition(makefile, target, configuration,
413
12.8k
                      cmStrCat("$<$<COMPILE_LANGUAGE:"_s, li.first, ">:"_s,
414
12.8k
                               BuildDefinition(di.first, li.second), '>'));
415
12.8k
      }
416
13.0k
    }
417
11.5k
  }
418
47.8k
}
419
420
cm::optional<cmPackageInfoReader::Pep440Version> ParseSimpleVersion(
421
  std::string const& version)
422
342
{
423
342
  if (version.empty()) {
424
2
    return cm::nullopt;
425
2
  }
426
427
340
  cmPackageInfoReader::Pep440Version result;
428
340
  result.Simple = true;
429
430
340
  cm::string_view remnant{ version };
431
810
  for (;;) {
432
    // Find the next part separator.
433
810
    std::string::size_type const n = remnant.find_first_of(".+-"_s);
434
810
    if (n == 0) {
435
      // The part is an empty string.
436
34
      return cm::nullopt;
437
34
    }
438
439
    // Extract the part as a number.
440
776
    cm::string_view const part = remnant.substr(0, n);
441
776
    std::string::size_type const l = part.size();
442
776
    std::string::size_type p;
443
776
    unsigned long const value = std::stoul(std::string{ part }, &p);
444
776
    if (p != l || value > std::numeric_limits<unsigned>::max()) {
445
      // The part was not a valid number or is too big.
446
210
      return cm::nullopt;
447
210
    }
448
566
    result.ReleaseComponents.push_back(static_cast<unsigned>(value));
449
450
    // Have we consumed the entire input?
451
566
    if (n == std::string::npos) {
452
60
      return { std::move(result) };
453
60
    }
454
455
    // Lop off the current part.
456
506
    char const sep = remnant[n];
457
506
    remnant = remnant.substr(n + 1);
458
506
    if (sep == '+' || sep == '-') {
459
      // If we hit the local label, we're done.
460
30
      result.LocalLabel = remnant;
461
30
      return { std::move(result) };
462
30
    }
463
464
    // We just consumed a '.'; check that there's more.
465
476
    if (remnant.empty()) {
466
      // A trailing part separator is not allowed.
467
6
      return cm::nullopt;
468
6
    }
469
470
    // Continue with the remaining input.
471
476
  }
472
473
  // Unreachable.
474
340
}
475
476
} // namespace
477
478
std::unique_ptr<cmPackageInfoReader> cmPackageInfoReader::Read(
479
  cmMakefile* makefile, std::string const& path,
480
  cmPackageInfoReader const* parent)
481
18.2k
{
482
  // Read file and perform some basic validation:
483
  //   - the input is valid JSON
484
  //   - the input is a JSON object
485
  //   - the input has a "cps_version" that we (in theory) know how to parse
486
18.2k
  Json::Value data = ReadJson(path);
487
18.2k
  if (!data.isObject() || (!parent && !CheckSchemaVersion(data))) {
488
4.67k
    return nullptr;
489
4.67k
  }
490
491
  //   - the input has a "name" attribute that is a non-empty string
492
13.5k
  Json::Value const& name = data["name"];
493
13.5k
  if (!name.isString() || name.empty()) {
494
24
    return nullptr;
495
24
  }
496
497
  //   - the input has a "components" attribute that is a JSON object
498
13.5k
  if (!data["components"].isObject()) {
499
20
    return nullptr;
500
20
  }
501
502
13.5k
  std::string prefix = (parent ? parent->Prefix : DeterminePrefix(path, data));
503
13.5k
  if (prefix.empty()) {
504
694
    return nullptr;
505
694
  }
506
507
  // Seems sane enough to hand back to the caller.
508
12.8k
  std::unique_ptr<cmPackageInfoReader> reader{ new cmPackageInfoReader };
509
12.8k
  reader->Data = std::move(data);
510
12.8k
  reader->Prefix = std::move(prefix);
511
12.8k
  reader->Path = path;
512
513
  // Determine other information we need to know immediately, or (if this is
514
  // a supplemental reader) copy from the parent.
515
12.8k
  if (parent) {
516
4.27k
    reader->ComponentTargets = parent->ComponentTargets;
517
4.27k
    reader->DefaultConfigurations = parent->DefaultConfigurations;
518
4.27k
    reader->RequirementDomains = parent->RequirementDomains;
519
8.55k
  } else {
520
8.55k
    for (std::string const& config :
521
41.3k
         ReadList(reader->Data, "configurations")) {
522
41.3k
      reader->DefaultConfigurations.emplace_back(
523
41.3k
        cmSystemTools::UpperCase(config));
524
41.3k
    }
525
8.55k
  }
526
527
  // Read package-level requirements. (Configuration supplements are not
528
  // allowed to declare package requirements.)
529
12.8k
  if (cmSystemTools::GetFilenameName(path).find('@') == std::string::npos) {
530
12.8k
    reader->ReadRequirements();
531
12.8k
  }
532
533
  // Check for a default license.
534
12.8k
  Json::Value const& defaultLicense = reader->Data["default_license"];
535
12.8k
  if (!defaultLicense.isNull()) {
536
0
    if (defaultLicense.isString()) {
537
0
      reader->DefaultLicense = defaultLicense.asString();
538
0
    } else {
539
0
      makefile->IssueMessage(
540
0
        MessageType::WARNING,
541
0
        "Package attribute \"default_license\" is not a string.");
542
0
    }
543
12.8k
  } else if (parent) {
544
4.27k
    reader->DefaultLicense = parent->DefaultLicense;
545
8.55k
  } else {
546
    // If there is no 'default_license', check for 'license'. Note that we
547
    // intentionally allow `default_license` on an appendix to override the
548
    // parent, but we do not consider `license` on an appendix. This is
549
    // consistent with not allowing LICENSE and APPENDIX to be used together.
550
8.55k
    Json::Value const& packageLicense = reader->Data["license"];
551
8.55k
    if (!packageLicense.isNull()) {
552
4
      if (packageLicense.isString()) {
553
4
        reader->DefaultLicense = packageLicense.asString();
554
4
      } else {
555
0
        makefile->IssueMessage(
556
0
          MessageType::WARNING,
557
0
          "Package attribute \"license\" is not a string.");
558
0
      }
559
4
    }
560
8.55k
  }
561
562
12.8k
  return reader;
563
13.5k
}
564
565
std::string cmPackageInfoReader::GetName() const
566
17.1k
{
567
17.1k
  return ToString(this->Data["name"]);
568
17.1k
}
569
570
cm::optional<std::string> cmPackageInfoReader::GetVersion() const
571
8.55k
{
572
8.55k
  Json::Value const& version = this->Data["version"];
573
8.55k
  if (version.isString()) {
574
124
    return version.asString();
575
124
  }
576
8.43k
  return cm::nullopt;
577
8.55k
}
578
579
cm::optional<std::string> cmPackageInfoReader::GetCompatVersion() const
580
8.55k
{
581
8.55k
  Json::Value const& version = this->Data["compat_version"];
582
8.55k
  if (version.isString()) {
583
250
    return version.asString();
584
250
  }
585
8.30k
  return cm::nullopt;
586
8.55k
}
587
588
cm::optional<cmPackageInfoReader::Pep440Version>
589
cmPackageInfoReader::ParseVersion(
590
  cm::optional<std::string> const& version) const
591
17.1k
{
592
  // Check that we have a version.
593
17.1k
  if (!version) {
594
16.7k
    return cm::nullopt;
595
16.7k
  }
596
597
  // Check if we know how to parse the version.
598
374
  Json::Value const& schema = this->Data["version_schema"];
599
374
  if (schema.isNull() || cmStrCaseEq(ToString(schema), "simple"_s)) {
600
342
    return ParseSimpleVersion(*version);
601
342
  }
602
603
32
  return cm::nullopt;
604
374
}
605
606
void cmPackageInfoReader::ReadRequirements()
607
12.8k
{
608
12.8k
  auto const& requirementObjects = this->Data["requires"];
609
12.8k
  if (!requirementObjects.isObject()) {
610
12.6k
    return;
611
12.6k
  }
612
613
162
  for (auto ri = requirementObjects.begin(), re = requirementObjects.end();
614
5.05k
       ri != re; ++ri) {
615
4.89k
    if (!ri->isObject() && !ri->isNull()) {
616
126
      continue;
617
126
    }
618
4.76k
    cmPackageRequirement r{ ri.name(),
619
4.76k
                            ToString((*ri)["version"]),
620
4.76k
                            ReadList(*ri, "components"),
621
4.76k
                            ReadList(*ri, "hints"),
622
4.76k
                            {} };
623
4.76k
    for (std::string const& domain :
624
4.76k
         ReadList(GetExtensions(*ri), "domains@v1")) {
625
0
      r.Domains.push_back(domain == "pkg-config" ? cm::PackageDomain::PkgConfig
626
0
                                                 : cm::PackageDomain::Unknown);
627
0
    }
628
4.76k
    if (!r.Domains.empty()) {
629
0
      this->RequirementDomains[r.Name] = r.Domains;
630
4.76k
    } else {
631
      // A local declaration without domains clears any inherited domains.
632
4.76k
      this->RequirementDomains.erase(r.Name);
633
4.76k
    }
634
4.76k
    this->Requirements.emplace_back(std::move(r));
635
4.76k
  }
636
162
}
637
638
std::string cmPackageInfoReader::ResolveTargetName(
639
  cmMakefile* makefile, std::string const& name,
640
  std::string const& context) const
641
56.8k
{
642
  // A pkg-config package is exposed as a single component of the same name.
643
  // Only resolve it to a foreign target if this requirement opts in.
644
56.8k
  std::string::size_type const n = name.find(':');
645
56.8k
  if (n != std::string::npos && n > 0 &&
646
4.45k
      name.substr(0, n) == name.substr(n + 1)) {
647
2
    std::string const package = name.substr(0, n);
648
2
    auto const di = this->RequirementDomains.find(package);
649
2
    if (di != this->RequirementDomains.end() &&
650
0
        cm::contains(di->second, cm::PackageDomain::PkgConfig)) {
651
0
      if (cmTarget* foreignTarget =
652
0
            makefile->FindForeignTarget("pkgcfg", package)) {
653
0
        return foreignTarget->GetName();
654
0
      }
655
0
    }
656
2
  }
657
658
56.8k
  return NormalizeTargetName(name, context);
659
56.8k
}
660
661
std::vector<std::string> cmPackageInfoReader::GetComponentNames() const
662
8.55k
{
663
8.55k
  std::vector<std::string> componentNames;
664
665
8.55k
  Json::Value const& components = this->Data["components"];
666
51.4k
  for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) {
667
42.8k
    componentNames.emplace_back(ci.name());
668
42.8k
  }
669
670
8.55k
  return componentNames;
671
8.55k
}
672
673
std::string cmPackageInfoReader::ResolvePath(std::string path) const
674
81.3k
{
675
81.3k
  cmSystemTools::ConvertToUnixSlashes(path);
676
81.3k
  if (cmHasPrefix(path, "@prefix@"_s)) {
677
23.9k
    return cmStrCat(this->Prefix, path.substr(8));
678
23.9k
  }
679
57.4k
  if (!cmSystemTools::FileIsFullPath(path)) {
680
55.5k
    return cmStrCat(cmSystemTools::GetFilenamePath(this->Path), '/', path);
681
55.5k
  }
682
1.89k
  return path;
683
57.4k
}
684
685
void cmPackageInfoReader::AddTargetConfiguration(
686
  cmTarget* target, cm::string_view configuration) const
687
16.2k
{
688
16.2k
  static std::string const icProp = "IMPORTED_CONFIGURATIONS";
689
690
16.2k
  std::string const& configUpper = cmSystemTools::UpperCase(configuration);
691
692
  // Get existing list of imported configurations.
693
16.2k
  cmList configs;
694
16.2k
  if (cmValue v = target->GetProperty(icProp)) {
695
13.0k
    configs.assign(cmSystemTools::UpperCase(*v));
696
13.0k
  } else {
697
    // If the existing list is empty, just add the new one and return.
698
3.25k
    target->SetProperty(icProp, configUpper);
699
3.25k
    return;
700
3.25k
  }
701
702
13.0k
  if (cm::contains(configs, configUpper)) {
703
    // If the configuration is already listed, we don't need to do anything.
704
16
    return;
705
16
  }
706
707
  // Add the new configuration.
708
13.0k
  configs.append(configUpper);
709
710
  // Rebuild the configuration list by extracting any configuration in the
711
  // default configurations and reinserting it at the beginning of the list
712
  // according to the order of the default configurations.
713
13.0k
  std::vector<std::string> newConfigs;
714
61.1k
  for (std::string const& c : this->DefaultConfigurations) {
715
61.1k
    auto ci = std::find(configs.begin(), configs.end(), c);
716
61.1k
    if (ci != configs.end()) {
717
0
      newConfigs.emplace_back(std::move(*ci));
718
0
      configs.erase(ci);
719
0
    }
720
61.1k
  }
721
111k
  for (std::string& c : configs) {
722
111k
    newConfigs.emplace_back(std::move(c));
723
111k
  }
724
725
13.0k
  target->SetProperty("IMPORTED_CONFIGURATIONS", cmJoin(newConfigs, ";"_s));
726
13.0k
}
727
728
void cmPackageInfoReader::SetImportProperty(cmMakefile* makefile,
729
                                            cmTarget* target,
730
                                            cm::string_view property,
731
                                            cm::string_view configuration,
732
                                            Json::Value const& object,
733
                                            std::string const& attribute) const
734
143k
{
735
143k
  Json::Value const& value = object[attribute];
736
143k
  if (!value.isNull()) {
737
22.5k
    std::string fullprop;
738
22.5k
    if (configuration.empty()) {
739
14.9k
      fullprop = cmStrCat("IMPORTED_"_s, property);
740
14.9k
    } else {
741
7.62k
      fullprop = cmStrCat("IMPORTED_"_s, property, '_',
742
7.62k
                          cmSystemTools::UpperCase(configuration));
743
7.62k
    }
744
745
22.5k
    if (value.isString()) {
746
22.4k
      target->SetProperty(fullprop, this->ResolvePath(value.asString()));
747
22.4k
    } else {
748
162
      makefile->IssueMessage(MessageType::WARNING,
749
162
                             cmStrCat("Failed to set property \""_s, property,
750
162
                                      "\" on target \""_s, target->GetName(),
751
162
                                      "\": attribute \"", attribute,
752
162
                                      "\" is not a string."_s));
753
162
    }
754
22.5k
  }
755
143k
}
756
757
void cmPackageInfoReader::SetMetaProperty(
758
  cmMakefile* makefile, cmTarget* target, std::string const& property,
759
  Json::Value const& object, std::string const& attribute,
760
  std::string const& defaultValue) const
761
31.6k
{
762
31.6k
  Json::Value const& value = object[attribute];
763
31.6k
  if (!value.isNull()) {
764
0
    if (value.isString()) {
765
0
      target->SetProperty(property, value.asString());
766
0
    } else {
767
0
      makefile->IssueMessage(MessageType::WARNING,
768
0
                             cmStrCat("Failed to set property \""_s, property,
769
0
                                      "\" on target \""_s, target->GetName(),
770
0
                                      "\": attribute \"", attribute,
771
0
                                      "\" is not a string."_s));
772
0
    }
773
31.6k
  } else if (!defaultValue.empty()) {
774
0
    target->SetProperty(property, defaultValue);
775
0
  }
776
31.6k
}
777
778
void cmPackageInfoReader::SetTargetProperties(
779
  cmMakefile* makefile, cmTarget* target, Json::Value const& data,
780
  std::string const& package, cm::string_view configuration) const
781
47.8k
{
782
  // Add configuration (if applicable).
783
47.8k
  if (!configuration.empty()) {
784
16.2k
    this->AddTargetConfiguration(target, configuration);
785
16.2k
  }
786
787
  // Add compile and link features.
788
47.8k
  for (std::string const& def : ReadList(data, "compile_features")) {
789
7.83k
    AddCompileFeature(makefile, target, configuration, def);
790
7.83k
  }
791
792
47.8k
  for (std::string const& def : ReadList(data, "link_features")) {
793
1.46k
    AddLinkFeature(makefile, target, configuration, def);
794
1.46k
  }
795
796
  // Add compile and link flags.
797
47.8k
  AppendLanguageProperties(
798
47.8k
    makefile, target, "COMPILE_OPTIONS"_s, configuration, data,
799
47.8k
    "compile_flags", [](std::string flag) -> std::string { return flag; });
800
801
47.8k
  for (std::string const& flag : ReadList(data, "link_flags")) {
802
0
    AppendProperty(makefile, target, "LINK_OPTIONS"_s, configuration, flag);
803
0
  }
804
805
  // Add compile definitions.
806
47.8k
  Json::Value const& defs = data["definitions"];
807
47.8k
  DefinitionsMap definitionsMap;
808
75.3k
  for (auto ldi = defs.begin(), lde = defs.end(); ldi != lde; ++ldi) {
809
27.4k
    cm::string_view const originalLang = IterKey(ldi);
810
27.4k
    cm::string_view const lang = MapLanguage(originalLang);
811
27.4k
    if (lang.empty()) {
812
3.65k
      makefile->IssueMessage(
813
3.65k
        MessageType::WARNING,
814
3.65k
        cmStrCat(R"(ignoring unknown language ")"_s, originalLang,
815
3.65k
                 R"(" in definitions for )"_s, target->GetName()));
816
3.65k
      continue;
817
3.65k
    }
818
819
61.2k
    for (auto di = ldi->begin(), de = ldi->end(); di != de; ++di) {
820
37.3k
      definitionsMap[di.name()].emplace(lang, *di);
821
37.3k
    }
822
23.8k
  }
823
47.8k
  AddDefinitions(makefile, target, configuration, definitionsMap);
824
825
  // Add include directories.
826
47.8k
  AppendLanguageProperties(makefile, target, "INCLUDE_DIRECTORIES"_s,
827
47.8k
                           configuration, data, "includes",
828
53.9k
                           [this](std::string p) -> std::string {
829
53.9k
                             return this->ResolvePath(std::move(p));
830
53.9k
                           });
831
832
  // Add link name/location(s).
833
47.8k
  this->SetImportProperty(makefile, target, "LOCATION"_s, // br
834
47.8k
                          configuration, data, "location");
835
836
47.8k
  this->SetImportProperty(makefile, target, "IMPLIB"_s, // br
837
47.8k
                          configuration, data, "link_location");
838
839
47.8k
  this->SetImportProperty(makefile, target, "SONAME"_s, // br
840
47.8k
                          configuration, data, "link_name");
841
842
  // Add link languages.
843
47.8k
  for (std::string const& originalLang : ReadList(data, "link_languages")) {
844
7.93k
    cm::string_view const lang = MapLanguage(originalLang, DisallowGlob);
845
7.93k
    if (!lang.empty()) {
846
0
      AppendProperty(makefile, target, "LINK_LANGUAGES"_s, configuration,
847
0
                     std::string{ lang });
848
0
    }
849
7.93k
  }
850
851
  // Add transitive dependencies.
852
47.8k
  for (std::string const& dep : ReadList(data, "requires")) {
853
36.3k
    AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration,
854
36.3k
                   this->ResolveTargetName(makefile, dep, package));
855
36.3k
  }
856
857
47.8k
  for (std::string const& dep : ReadList(data, "compile_requires")) {
858
13.0k
    std::string const& lib =
859
13.0k
      cmStrCat("$<COMPILE_ONLY:"_s,
860
13.0k
               this->ResolveTargetName(makefile, dep, package), '>');
861
13.0k
    AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, lib);
862
13.0k
  }
863
864
47.8k
  for (std::string const& dep : ReadList(data, "link_requires")) {
865
48
    std::string const& lib = cmStrCat(
866
48
      "$<LINK_ONLY:"_s, this->ResolveTargetName(makefile, dep, package), '>');
867
48
    AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, lib);
868
48
  }
869
870
47.8k
  for (std::string const& dep : ReadList(data, "dyld_requires")) {
871
7.40k
    AppendImportProperty(makefile, target, "LINK_DEPENDENT_LIBRARIES"_s,
872
7.40k
                         configuration,
873
7.40k
                         this->ResolveTargetName(makefile, dep, package));
874
7.40k
  }
875
876
47.8k
  for (std::string const& lib : ReadList(data, "link_libraries")) {
877
1.54k
    AppendProperty(makefile, target, "LINK_LIBRARIES"_s, configuration, lib);
878
1.54k
  }
879
880
  // TODO: Handle non-configuration modules
881
  // once IMPORTED_CXX_MODULES supports it
882
47.8k
  if (!configuration.empty()) {
883
16.2k
    this->ReadCxxModulesMetadata(makefile, target, configuration, data);
884
16.2k
  }
885
886
  // Add other information.
887
47.8k
  if (configuration.empty()) {
888
31.6k
    this->SetMetaProperty(makefile, target, "SPDX_LICENSE", data, "license",
889
31.6k
                          this->DefaultLicense);
890
31.6k
  }
891
47.8k
}
892
893
void cmPackageInfoReader::ReadCxxModulesMetadata(
894
  cmMakefile* makefile, cmTarget* target, cm::string_view configuration,
895
  Json::Value const& object) const
896
16.2k
{
897
16.2k
#ifndef CMAKE_BOOTSTRAP
898
16.2k
  Json::Value const& path = object["cpp_module_metadata"];
899
900
16.2k
  if (!path.isString()) {
901
16.2k
    return;
902
16.2k
  }
903
904
0
  cmCxxModuleMetadata::ParseResult result =
905
0
    cmCxxModuleMetadata::LoadFromFile(this->ResolvePath(path.asString()));
906
907
0
  if (!result) {
908
0
    makefile->IssueMessage(
909
0
      MessageType::WARNING,
910
0
      cmStrCat("Error parsing module manifest:\n"_s, result.Error));
911
0
    return;
912
0
  }
913
914
0
  cmCxxModuleMetadata::PopulateTarget(*target, *result.Meta, configuration);
915
0
#endif
916
0
}
917
918
cmTarget* cmPackageInfoReader::AddComponent(
919
  cmMakefile* makefile, cm::TargetType type, std::string const& name,
920
  Json::Value const& data, std::string const& package,
921
  cm::ImportedTargetScope scope) const
922
31.5k
{
923
  // Create the imported target.
924
31.5k
  cmTarget* const target = makefile->AddImportedTarget(name, type, scope);
925
31.5k
  target->SetOrigin(cmTarget::Origin::Cps);
926
927
  // Set target properties.
928
31.5k
  this->SetTargetProperties(makefile, target, data, package, {});
929
31.5k
  auto const& cfgData = data["configurations"];
930
31.5k
  if (cfgData.isObject()) {
931
20.7k
    for (auto ci = cfgData.begin(), ce = cfgData.end(); ci != ce; ++ci) {
932
17.4k
      cm::string_view const cfgName = IterKey(ci);
933
17.4k
      Json::Value const& cfgObj = *ci;
934
17.4k
      if (cfgObj.isObject()) {
935
16.2k
        this->SetTargetProperties(makefile, target, cfgObj, package, cfgName);
936
16.2k
      } else if (!cfgObj.isNull()) {
937
506
        makefile->IssueMessage(MessageType::WARNING,
938
506
                               cmStrCat("Component \""_s, name,
939
506
                                        "\" configuration \""_s, cfgName,
940
506
                                        "\" is not an object."_s));
941
506
      }
942
17.4k
    }
943
28.3k
  } else if (!cfgData.isNull()) {
944
8
    makefile->IssueMessage(
945
8
      MessageType::WARNING,
946
8
      cmStrCat(R"(Component ")"_s, name,
947
8
               R"(" attribute "configurations" is not an object.)"_s));
948
8
  }
949
950
  // Add target sources.
951
31.5k
  this->AddTargetSources(makefile, target, data["file_sets"]);
952
953
31.5k
  return target;
954
31.5k
}
955
956
void cmPackageInfoReader::AddTargetSources(cmMakefile* makefile,
957
                                           cmTarget* target,
958
                                           Json::Value const& data) const
959
31.5k
{
960
31.5k
  if (data.isArray()) {
961
4.98k
    for (Json::Value const& fs : data) {
962
4.98k
      if (fs.isObject()) {
963
4.98k
        std::string const& type = ToString(fs["type"]);
964
4.98k
        std::string const& root = this->ResolvePath(ToString(fs["root"]));
965
4.98k
        std::vector<std::string> files = ReadList(fs["files"]);
966
967
4.98k
        if (files.empty() || root.empty() || type != "includes") {
968
1.14k
          continue;
969
1.14k
        }
970
971
3.84k
        Json::Value const& ext = GetExtensions(fs);
972
3.84k
        std::string const& name = [&] {
973
3.84k
          std::string const& extName = ToString(ext["name@v1"]);
974
3.84k
          if (!extName.empty()) {
975
710
            return extName;
976
710
          }
977
3.13k
          return std::string{ cm::FileSetMetadata::HEADERS };
978
3.84k
        }();
979
980
        // TODO: When we support more than one file set type, check that we
981
        // don't see the same 'name' on sets of different types.
982
3.84k
        auto fileSet = target->GetOrCreateFileSet(
983
3.84k
          name, std::string{ cm::FileSetMetadata::HEADERS },
984
3.84k
          cm::FileSetMetadata::Visibility::Interface);
985
3.84k
        cmListFileBacktrace const& bt = makefile->GetBacktrace();
986
987
9.01k
        for (std::string& file : files) {
988
9.01k
          file = cmStrCat(root, '/', std::move(file));
989
9.01k
        }
990
3.84k
        fileSet.first->AddFileEntry(
991
3.84k
          BT<std::string>{ cmList{ files }.to_string(), bt });
992
993
3.84k
        fileSet.first->AddDirectoryEntry(BT<std::string>{ root, bt });
994
3.84k
      }
995
4.98k
    }
996
576
  }
997
31.5k
}
998
999
bool cmPackageInfoReader::ImportTargets(cmMakefile* makefile,
1000
                                        cmExecutionStatus& status,
1001
                                        cm::ImportedTargetScope scope)
1002
8.55k
{
1003
8.55k
  std::string const& package = this->GetName();
1004
1005
  // Read components.
1006
8.55k
  Json::Value const& components = this->Data["components"];
1007
1008
51.4k
  for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) {
1009
42.8k
    cm::string_view const name = IterKey(ci);
1010
1011
    // Get and validate full target name.
1012
42.8k
    std::string const& fullName = cmStrCat(package, "::"_s, name);
1013
42.8k
    {
1014
42.8k
      std::string msg;
1015
42.8k
      if (!makefile->EnforceUniqueName(fullName, msg)) {
1016
0
        status.SetError(msg);
1017
0
        return false;
1018
0
      }
1019
42.8k
    }
1020
1021
42.8k
    Json::Value const& component = *ci;
1022
42.8k
    if (!component.isObject()) {
1023
5.93k
      makefile->IssueMessage(
1024
5.93k
        MessageType::WARNING,
1025
5.93k
        cmStrCat(R"(Component ")"_s, fullName, R"(" is not an object.)"_s));
1026
5.93k
      continue;
1027
5.93k
    }
1028
1029
36.9k
    auto createTarget = [&](cm::TargetType typeEnum) {
1030
31.5k
      return this->AddComponent(makefile, typeEnum, fullName, component,
1031
31.5k
                                package, scope);
1032
31.5k
    };
1033
1034
36.9k
    cmTarget* target = nullptr;
1035
36.9k
    std::string const& type =
1036
36.9k
      cmSystemTools::LowerCase(ToString(component["type"]));
1037
36.9k
    if (type == "symbolic"_s) {
1038
8
      target = createTarget(cm::TargetType::INTERFACE_LIBRARY);
1039
8
      target->SetSymbolic(true);
1040
36.9k
    } else if (type == "executable"_s) {
1041
84
      target = createTarget(cm::TargetType::EXECUTABLE);
1042
36.8k
    } else if (type == "dylib"_s) {
1043
6.66k
      target = createTarget(cm::TargetType::SHARED_LIBRARY);
1044
30.1k
    } else if (type == "module"_s) {
1045
44
      target = createTarget(cm::TargetType::MODULE_LIBRARY);
1046
30.1k
    } else if (type == "archive"_s) {
1047
15.6k
      target = createTarget(cm::TargetType::STATIC_LIBRARY);
1048
15.6k
    } else if (type == "interface"_s) {
1049
9.15k
      target = createTarget(cm::TargetType::INTERFACE_LIBRARY);
1050
9.15k
    } else {
1051
5.31k
      makefile->IssueMessage(MessageType::WARNING,
1052
5.31k
                             cmStrCat(R"(component ")"_s, fullName,
1053
5.31k
                                      R"(" has unknown type ")"_s, type,
1054
5.31k
                                      R"(" and was not imported)"_s));
1055
5.31k
    }
1056
1057
36.9k
    if (target) {
1058
31.5k
      this->ComponentTargets.emplace(std::string{ name }, target);
1059
31.5k
    }
1060
36.9k
  }
1061
1062
  // Read default components.
1063
8.55k
  std::vector<std::string> const& defaultComponents =
1064
8.55k
    ReadList(this->Data, "default_components");
1065
8.55k
  if (!defaultComponents.empty()) {
1066
0
    std::string msg;
1067
0
    if (!makefile->EnforceUniqueName(package, msg)) {
1068
0
      status.SetError(msg);
1069
0
      return false;
1070
0
    }
1071
1072
0
    cmTarget* const target = makefile->AddImportedTarget(
1073
0
      package, cm::TargetType::INTERFACE_LIBRARY, scope);
1074
0
    for (std::string const& name : defaultComponents) {
1075
0
      std::string const& fullName = cmStrCat(package, "::"_s, name);
1076
0
      AppendProperty(makefile, target, "LINK_LIBRARIES"_s, {}, fullName);
1077
0
    }
1078
0
    target->SetExportPassthrough(true);
1079
0
  }
1080
1081
8.55k
  return true;
1082
8.55k
}
1083
1084
bool cmPackageInfoReader::ImportTargetConfigurations(
1085
  cmMakefile* makefile, cmExecutionStatus& status) const
1086
8.55k
{
1087
8.55k
  std::string const& configuration = ToString(this->Data["configuration"]);
1088
1089
8.55k
  if (configuration.empty()) {
1090
8.55k
    makefile->IssueMessage(MessageType::WARNING,
1091
8.55k
                           cmStrCat("supplemental file "_s, this->Path,
1092
8.55k
                                    " does not specify a configuration"_s));
1093
8.55k
    return true;
1094
8.55k
  }
1095
1096
0
  std::string const& package = this->GetName();
1097
0
  Json::Value const& components = this->Data["components"];
1098
1099
0
  for (auto ci = components.begin(), ce = components.end(); ci != ce; ++ci) {
1100
    // Get component name and look up target.
1101
0
    cm::string_view const name = IterKey(ci);
1102
0
    auto const& ti = this->ComponentTargets.find(std::string{ name });
1103
0
    if (ti == this->ComponentTargets.end()) {
1104
0
      status.SetError(cmStrCat("component "_s, name, " was not found"_s));
1105
0
      return false;
1106
0
    }
1107
1108
    // Read supplemental data for component.
1109
0
    if (!ci->isObject()) {
1110
0
      makefile->IssueMessage(
1111
0
        MessageType::WARNING,
1112
0
        cmStrCat("Component \""_s, name, "\" is not an object."_s));
1113
0
      continue;
1114
0
    }
1115
0
    this->SetTargetProperties(makefile, ti->second, *ci, package,
1116
0
                              configuration);
1117
0
  }
1118
1119
0
  return true;
1120
0
}