Coverage Report

Created: 2026-04-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmCMakePresetsGraphReadJSON.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 <algorithm>
4
#include <cstddef>
5
#include <functional>
6
#include <map>
7
#include <string>
8
#include <unordered_set>
9
#include <utility>
10
#include <vector>
11
12
#include <cm/memory>
13
#include <cm/optional>
14
#include <cmext/string_view>
15
16
#include <cm3p/json/value.h>
17
18
#include "cmCMakePresetsErrors.h"
19
#include "cmCMakePresetsGraph.h"
20
#include "cmCMakePresetsGraphInternal.h"
21
#include "cmJSONHelpers.h"
22
#include "cmJSONState.h"
23
#include "cmStringAlgorithms.h"
24
#include "cmSystemTools.h"
25
#include "cmVersion.h"
26
27
namespace {
28
using CacheVariable = cmCMakePresetsGraph::CacheVariable;
29
using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
30
using BuildPreset = cmCMakePresetsGraph::BuildPreset;
31
using TestPreset = cmCMakePresetsGraph::TestPreset;
32
using PackagePreset = cmCMakePresetsGraph::PackagePreset;
33
using WorkflowPreset = cmCMakePresetsGraph::WorkflowPreset;
34
using ArchToolsetStrategy = cmCMakePresetsGraph::ArchToolsetStrategy;
35
using JSONHelperBuilder = cmJSONHelperBuilder;
36
using ExpandMacroResult = cmCMakePresetsGraphInternal::ExpandMacroResult;
37
using MacroExpander = cmCMakePresetsGraphInternal::MacroExpander;
38
using MacroExpanderVector = cmCMakePresetsGraphInternal::MacroExpanderVector;
39
using cmCMakePresetsGraphInternal::BaseMacroExpander;
40
using cmCMakePresetsGraphInternal::ExpandMacros;
41
using cmCMakePresetsGraphInternal::ExpandImmediateMacros;
42
43
constexpr int MIN_VERSION = 1;
44
constexpr int MAX_VERSION = 12;
45
46
struct CMakeVersion
47
{
48
  unsigned int Major = 0;
49
  unsigned int Minor = 0;
50
  unsigned int Patch = 0;
51
};
52
53
struct RootPresets
54
{
55
  CMakeVersion CMakeMinimumRequired;
56
  std::vector<ConfigurePreset> ConfigurePresets;
57
  std::vector<BuildPreset> BuildPresets;
58
  std::vector<TestPreset> TestPresets;
59
  std::vector<PackagePreset> PackagePresets;
60
  std::vector<WorkflowPreset> WorkflowPresets;
61
  std::vector<std::string> Include;
62
};
63
64
std::unique_ptr<cmCMakePresetsGraphInternal::NotCondition> InvertCondition(
65
  std::unique_ptr<cmCMakePresetsGraph::Condition> condition)
66
0
{
67
0
  auto retval = cm::make_unique<cmCMakePresetsGraphInternal::NotCondition>();
68
0
  retval->SubCondition = std::move(condition);
69
0
  return retval;
70
0
}
71
72
auto const ConditionStringHelper = JSONHelperBuilder::String();
73
74
auto const ConditionBoolHelper = JSONHelperBuilder::Bool();
75
76
auto const ConditionStringListHelper = JSONHelperBuilder::Vector<std::string>(
77
  cmCMakePresetsErrors::INVALID_CONDITION, ConditionStringHelper);
78
79
auto const ConstConditionHelper =
80
  JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::ConstCondition>(
81
    cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
82
    .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
83
    .Bind("value"_s, &cmCMakePresetsGraphInternal::ConstCondition::Value,
84
          ConditionBoolHelper, true);
85
86
auto const EqualsConditionHelper =
87
  JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::EqualsCondition>(
88
    cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
89
    .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
90
    .Bind("lhs"_s, &cmCMakePresetsGraphInternal::EqualsCondition::Lhs,
91
          ConditionStringHelper, true)
92
    .Bind("rhs"_s, &cmCMakePresetsGraphInternal::EqualsCondition::Rhs,
93
          ConditionStringHelper, true);
94
95
auto const InListConditionHelper =
96
  JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::InListCondition>(
97
    cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
98
    .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
99
    .Bind("string"_s, &cmCMakePresetsGraphInternal::InListCondition::String,
100
          ConditionStringHelper, true)
101
    .Bind("list"_s, &cmCMakePresetsGraphInternal::InListCondition::List,
102
          ConditionStringListHelper, true);
103
104
auto const MatchesConditionHelper =
105
  JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::MatchesCondition>(
106
    cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
107
    .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
108
    .Bind("string"_s, &cmCMakePresetsGraphInternal::MatchesCondition::String,
109
          ConditionStringHelper, true)
110
    .Bind("regex"_s, &cmCMakePresetsGraphInternal::MatchesCondition::Regex,
111
          ConditionStringHelper, true);
112
113
bool SubConditionHelper(std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
114
                        Json::Value const* value, cmJSONState* state);
115
116
auto const ListConditionVectorHelper =
117
  JSONHelperBuilder::Vector<std::unique_ptr<cmCMakePresetsGraph::Condition>>(
118
    cmCMakePresetsErrors::INVALID_CONDITION, SubConditionHelper);
119
auto const AnyAllOfConditionHelper =
120
  JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::AnyAllOfCondition>(
121
    cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
122
    .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
123
    .Bind("conditions"_s,
124
          &cmCMakePresetsGraphInternal::AnyAllOfCondition::Conditions,
125
          ListConditionVectorHelper);
126
127
auto const NotConditionHelper =
128
  JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::NotCondition>(
129
    cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
130
    .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
131
    .Bind("condition"_s,
132
          &cmCMakePresetsGraphInternal::NotCondition::SubCondition,
133
          SubConditionHelper);
134
135
bool ConditionHelper(std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
136
                     Json::Value const* value, cmJSONState* state)
137
0
{
138
0
  if (!value) {
139
0
    out.reset();
140
0
    return true;
141
0
  }
142
143
0
  if (value->isBool()) {
144
0
    auto c = cm::make_unique<cmCMakePresetsGraphInternal::ConstCondition>();
145
0
    c->Value = value->asBool();
146
0
    out = std::move(c);
147
0
    return true;
148
0
  }
149
150
0
  if (value->isNull()) {
151
0
    out = cm::make_unique<cmCMakePresetsGraphInternal::NullCondition>();
152
0
    return true;
153
0
  }
154
155
0
  if (value->isObject()) {
156
0
    if (!value->isMember("type")) {
157
0
      cmCMakePresetsErrors::INVALID_CONDITION(value, state);
158
0
      return false;
159
0
    }
160
161
0
    if (!(*value)["type"].isString()) {
162
0
      cmCMakePresetsErrors::INVALID_CONDITION(value, state);
163
0
      return false;
164
0
    }
165
0
    auto type = (*value)["type"].asString();
166
167
0
    if (type == "const") {
168
0
      auto c = cm::make_unique<cmCMakePresetsGraphInternal::ConstCondition>();
169
0
      CHECK_OK(ConstConditionHelper(*c, value, state));
170
0
      out = std::move(c);
171
0
      return true;
172
0
    }
173
174
0
    if (type == "equals" || type == "notEquals") {
175
0
      auto c = cm::make_unique<cmCMakePresetsGraphInternal::EqualsCondition>();
176
0
      CHECK_OK(EqualsConditionHelper(*c, value, state));
177
0
      out = std::move(c);
178
0
      if (type == "notEquals") {
179
0
        out = InvertCondition(std::move(out));
180
0
      }
181
0
      return true;
182
0
    }
183
184
0
    if (type == "inList" || type == "notInList") {
185
0
      auto c = cm::make_unique<cmCMakePresetsGraphInternal::InListCondition>();
186
0
      CHECK_OK(InListConditionHelper(*c, value, state));
187
0
      out = std::move(c);
188
0
      if (type == "notInList") {
189
0
        out = InvertCondition(std::move(out));
190
0
      }
191
0
      return true;
192
0
    }
193
194
0
    if (type == "matches" || type == "notMatches") {
195
0
      auto c =
196
0
        cm::make_unique<cmCMakePresetsGraphInternal::MatchesCondition>();
197
0
      CHECK_OK(MatchesConditionHelper(*c, value, state));
198
0
      out = std::move(c);
199
0
      if (type == "notMatches") {
200
0
        out = InvertCondition(std::move(out));
201
0
      }
202
0
      return true;
203
0
    }
204
205
0
    if (type == "anyOf" || type == "allOf") {
206
0
      auto c =
207
0
        cm::make_unique<cmCMakePresetsGraphInternal::AnyAllOfCondition>();
208
0
      c->StopValue = (type == "anyOf");
209
0
      CHECK_OK(AnyAllOfConditionHelper(*c, value, state));
210
0
      out = std::move(c);
211
0
      return true;
212
0
    }
213
214
0
    if (type == "not") {
215
0
      auto c = cm::make_unique<cmCMakePresetsGraphInternal::NotCondition>();
216
0
      CHECK_OK(NotConditionHelper(*c, value, state));
217
0
      out = std::move(c);
218
0
      return true;
219
0
    }
220
0
  }
221
222
0
  cmCMakePresetsErrors::INVALID_CONDITION(value, state);
223
0
  return false;
224
0
}
225
226
bool SubConditionHelper(std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
227
                        Json::Value const* value, cmJSONState* state)
228
0
{
229
0
  std::unique_ptr<cmCMakePresetsGraph::Condition> ptr;
230
0
  auto result = ConditionHelper(ptr, value, state);
231
0
  if (ptr && ptr->IsNull()) {
232
0
    cmCMakePresetsErrors::INVALID_CONDITION(value, state);
233
0
    return false;
234
0
  }
235
0
  out = std::move(ptr);
236
0
  return result;
237
0
}
238
239
bool EnvironmentHelper(cm::optional<std::string>& out,
240
                       Json::Value const* value, cmJSONState* state)
241
0
{
242
0
  if (!value || value->isNull()) {
243
0
    out = cm::nullopt;
244
0
    return true;
245
0
  }
246
0
  if (value->isString()) {
247
0
    out = value->asString();
248
0
    return true;
249
0
  }
250
0
  cmCMakePresetsErrors::INVALID_PRESET(value, state);
251
0
  return false;
252
0
}
253
254
auto const VersionIntHelper =
255
  JSONHelperBuilder::Int(cmCMakePresetsErrors::INVALID_VERSION);
256
257
auto const VersionHelper = JSONHelperBuilder::Required<int>(
258
  cmCMakePresetsErrors::NO_VERSION, VersionIntHelper);
259
260
auto const VersionRangeHelper = JSONHelperBuilder::Checked<int>(
261
  cmCMakePresetsErrors::UNRECOGNIZED_VERSION_RANGE(MIN_VERSION, MAX_VERSION),
262
  VersionHelper,
263
0
  [](int const v) -> bool { return v >= MIN_VERSION && v <= MAX_VERSION; });
264
265
auto const RootVersionHelper =
266
  JSONHelperBuilder::Object<int>(cmCMakePresetsErrors::INVALID_ROOT_OBJECT)
267
    .Bind("version"_s, VersionRangeHelper, false);
268
269
auto const CMakeVersionUIntHelper =
270
  JSONHelperBuilder::UInt(cmCMakePresetsErrors::INVALID_VERSION);
271
272
auto const CMakeVersionHelper =
273
  JSONHelperBuilder::Object<CMakeVersion>(JsonErrors::INVALID_NAMED_OBJECT_KEY,
274
                                          false)
275
    .Bind("major"_s, &CMakeVersion::Major, CMakeVersionUIntHelper, false)
276
    .Bind("minor"_s, &CMakeVersion::Minor, CMakeVersionUIntHelper, false)
277
    .Bind("patch"_s, &CMakeVersion::Patch, CMakeVersionUIntHelper, false);
278
279
auto const IncludeHelper =
280
  JSONHelperBuilder::String(cmCMakePresetsErrors::INVALID_INCLUDE);
281
282
auto const IncludeVectorHelper = JSONHelperBuilder::Vector<std::string>(
283
  cmCMakePresetsErrors::INVALID_INCLUDE, IncludeHelper);
284
285
auto const RootPresetsHelper =
286
  JSONHelperBuilder::Object<RootPresets>(
287
    cmCMakePresetsErrors::INVALID_ROOT_OBJECT, false)
288
    .Bind<int>("version"_s, nullptr, VersionHelper)
289
    .Bind("configurePresets"_s, &RootPresets::ConfigurePresets,
290
          cmCMakePresetsGraphInternal::ConfigurePresetsHelper, false)
291
    .Bind("buildPresets"_s, &RootPresets::BuildPresets,
292
          cmCMakePresetsGraphInternal::BuildPresetsHelper, false)
293
    .Bind("testPresets"_s, &RootPresets::TestPresets,
294
          cmCMakePresetsGraphInternal::TestPresetsHelper, false)
295
    .Bind("packagePresets"_s, &RootPresets::PackagePresets,
296
          cmCMakePresetsGraphInternal::PackagePresetsHelper, false)
297
    .Bind("workflowPresets"_s, &RootPresets::WorkflowPresets,
298
          cmCMakePresetsGraphInternal::WorkflowPresetsHelper, false)
299
    .Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired,
300
          CMakeVersionHelper, false)
301
    .Bind("include"_s, &RootPresets::Include, IncludeVectorHelper, false)
302
    .Bind<std::nullptr_t>("vendor"_s, nullptr,
303
                          cmCMakePresetsGraphInternal::VendorHelper(
304
                            cmCMakePresetsErrors::INVALID_ROOT),
305
                          false)
306
    .Bind<std::nullptr_t>("$schema"_s, nullptr,
307
                          cmCMakePresetsGraphInternal::SchemaHelper(), false);
308
309
class EnvironmentMacroExpander : public MacroExpander
310
{
311
public:
312
  ExpandMacroResult operator()(std::string const& macroNamespace,
313
                               std::string const& macroName,
314
                               std::string& macroOut,
315
                               int /*version*/) const override
316
0
  {
317
0
    if (macroNamespace == "penv") {
318
0
      if (macroName.empty()) {
319
0
        return ExpandMacroResult::Error;
320
0
      }
321
0
      if (cm::optional<std::string> value =
322
0
            cmSystemTools::GetEnvVar(macroName)) {
323
0
        macroOut += *value;
324
0
      }
325
0
      return ExpandMacroResult::Ok;
326
0
    }
327
328
0
    return ExpandMacroResult::Ignore;
329
0
  }
330
};
331
}
332
333
namespace cmCMakePresetsGraphInternal {
334
bool PresetStringHelper(std::string& out, Json::Value const* value,
335
                        cmJSONState* state)
336
0
{
337
0
  static auto const helper = JSONHelperBuilder::String();
338
0
  return helper(out, value, state);
339
0
}
340
341
bool PresetNameHelper(std::string& out, Json::Value const* value,
342
                      cmJSONState* state)
343
0
{
344
0
  if (!value || !value->isString() || value->asString().empty()) {
345
0
    cmCMakePresetsErrors::INVALID_PRESET_NAME(value, state);
346
0
    return false;
347
0
  }
348
0
  out = value->asString();
349
0
  return true;
350
0
}
351
352
bool PresetVectorStringHelper(std::vector<std::string>& out,
353
                              Json::Value const* value, cmJSONState* state)
354
0
{
355
0
  static auto const helper = JSONHelperBuilder::Vector<std::string>(
356
0
    cmCMakePresetsErrors::INVALID_PRESET,
357
0
    cmCMakePresetsGraphInternal::PresetStringHelper);
358
0
  return helper(out, value, state);
359
0
}
360
361
bool PresetBoolHelper(bool& out, Json::Value const* value, cmJSONState* state)
362
0
{
363
0
  static auto const helper = JSONHelperBuilder::Bool();
364
0
  return helper(out, value, state);
365
0
}
366
367
bool PresetOptionalBoolHelper(cm::optional<bool>& out,
368
                              Json::Value const* value, cmJSONState* state)
369
0
{
370
0
  static auto const helper =
371
0
    JSONHelperBuilder::Optional<bool>(PresetBoolHelper);
372
0
  return helper(out, value, state);
373
0
}
374
375
bool PresetIntHelper(int& out, Json::Value const* value, cmJSONState* state)
376
0
{
377
0
  static auto const helper = JSONHelperBuilder::Int();
378
0
  return helper(out, value, state);
379
0
}
380
381
bool PresetOptionalIntHelper(cm::optional<int>& out, Json::Value const* value,
382
                             cmJSONState* state)
383
0
{
384
0
  static auto const helper = JSONHelperBuilder::Optional<int>(PresetIntHelper);
385
0
  return helper(out, value, state);
386
0
}
387
388
bool PresetUIntHelper(unsigned int& out, Json::Value const* value,
389
                      cmJSONState* state)
390
0
{
391
0
  static auto const helper = JSONHelperBuilder::UInt();
392
0
  return helper(out, value, state);
393
0
}
394
395
bool PresetOptionalUIntHelper(cm::optional<unsigned int>& out,
396
                              Json::Value const* value, cmJSONState* state)
397
0
{
398
0
  static auto const helper =
399
0
    JSONHelperBuilder::Optional<unsigned int>(PresetUIntHelper);
400
0
  return helper(out, value, state);
401
0
}
402
403
bool PresetVectorIntHelper(std::vector<int>& out, Json::Value const* value,
404
                           cmJSONState* state)
405
0
{
406
0
  static auto const helper = JSONHelperBuilder::Vector<int>(
407
0
    cmCMakePresetsErrors::INVALID_PRESET, PresetIntHelper);
408
0
  return helper(out, value, state);
409
0
}
410
411
cmJSONHelper<std::nullptr_t> VendorHelper(ErrorGenerator const& error)
412
24
{
413
24
  return [error](std::nullptr_t& /*out*/, Json::Value const* value,
414
24
                 cmJSONState* state) -> bool {
415
0
    if (!value) {
416
0
      return true;
417
0
    }
418
419
0
    if (!value->isObject()) {
420
0
      error(value, state);
421
0
      return false;
422
0
    }
423
424
0
    return true;
425
0
  };
426
24
}
427
428
bool PresetConditionHelper(
429
  std::shared_ptr<cmCMakePresetsGraph::Condition>& out,
430
  Json::Value const* value, cmJSONState* state)
431
0
{
432
0
  std::unique_ptr<cmCMakePresetsGraph::Condition> ptr;
433
0
  auto result = ConditionHelper(ptr, value, state);
434
0
  out = std::move(ptr);
435
0
  return result;
436
0
}
437
438
bool PresetVectorOneOrMoreStringHelper(std::vector<std::string>& out,
439
                                       Json::Value const* value,
440
                                       cmJSONState* state)
441
0
{
442
0
  out.clear();
443
0
  if (!value) {
444
0
    return true;
445
0
  }
446
447
0
  if (value->isString()) {
448
0
    out.push_back(value->asString());
449
0
    return true;
450
0
  }
451
452
0
  return PresetVectorStringHelper(out, value, state);
453
0
}
454
455
bool EnvironmentMapHelper(
456
  std::map<std::string, cm::optional<std::string>>& out,
457
  Json::Value const* value, cmJSONState* state)
458
0
{
459
0
  static auto const helper = JSONHelperBuilder::Map<cm::optional<std::string>>(
460
0
    cmCMakePresetsErrors::INVALID_PRESET, EnvironmentHelper);
461
462
0
  return helper(out, value, state);
463
0
}
464
465
cmJSONHelper<std::nullptr_t> SchemaHelper()
466
4
{
467
4
  return [](std::nullptr_t&, Json::Value const*, cmJSONState*) -> bool {
468
0
    return true;
469
0
  };
470
4
}
471
}
472
473
bool cmCMakePresetsGraph::ReadJSONFile(std::string const& filename,
474
                                       RootType rootType,
475
                                       ReadReason readReason,
476
                                       std::vector<File*>& inProgressFiles,
477
                                       File*& file, std::string& errMsg)
478
0
{
479
0
  bool result;
480
481
0
  for (auto const& f : this->Files) {
482
0
    if (cmSystemTools::SameFile(filename, f->Filename)) {
483
0
      file = f.get();
484
0
      auto fileIt =
485
0
        std::find(inProgressFiles.begin(), inProgressFiles.end(), file);
486
0
      if (fileIt != inProgressFiles.end()) {
487
0
        cmCMakePresetsErrors::CYCLIC_INCLUDE(filename, &this->parseState);
488
0
        return false;
489
0
      }
490
491
0
      return true;
492
0
    }
493
0
  }
494
495
0
  Json::Value root;
496
0
  this->parseState = cmJSONState(filename, &root);
497
0
  if (!this->parseState.errors.empty()) {
498
0
    return false;
499
0
  }
500
501
0
  int v = 0;
502
0
  if ((result = RootVersionHelper(v, &root, &parseState)) != true) {
503
0
    return result;
504
0
  }
505
506
  // Support for build and test presets added in version 2.
507
0
  if (v < 2) {
508
0
    if (root.isMember("buildPresets")) {
509
0
      cmCMakePresetsErrors::BUILD_TEST_PRESETS_UNSUPPORTED(
510
0
        &root["buildPresets"], &this->parseState);
511
0
      return false;
512
0
    }
513
0
    if (root.isMember("testPresets")) {
514
0
      cmCMakePresetsErrors::BUILD_TEST_PRESETS_UNSUPPORTED(
515
0
        &root["testPresets"], &this->parseState);
516
0
      return false;
517
0
    }
518
0
  }
519
520
  // Support for package presets added in version 6.
521
0
  if (v < 6 && root.isMember("packagePresets")) {
522
0
    cmCMakePresetsErrors::PACKAGE_PRESETS_UNSUPPORTED(&root["packagePresets"],
523
0
                                                      &this->parseState);
524
0
    return false;
525
0
  }
526
527
  // Support for workflow presets added in version 6.
528
0
  if (v < 6 && root.isMember("workflowPresets")) {
529
0
    cmCMakePresetsErrors::WORKFLOW_PRESETS_UNSUPPORTED(
530
0
      &root["workflowPresets"], &this->parseState);
531
0
    return false;
532
0
  }
533
534
  // Support for include added in version 4.
535
0
  if (v < 4 && root.isMember("include")) {
536
0
    cmCMakePresetsErrors::INCLUDE_UNSUPPORTED(&root["include"],
537
0
                                              &this->parseState);
538
0
    return false;
539
0
  }
540
541
  // Support for $schema added in version 8.
542
0
  if (v < 8 && root.isMember("$schema")) {
543
0
    cmCMakePresetsErrors::SCHEMA_UNSUPPORTED(&this->parseState);
544
0
    return false;
545
0
  }
546
547
  // Support for $comment added in version 10.
548
0
  this->parseState.allowComments = (v >= 10);
549
550
0
  RootPresets presets;
551
0
  if ((result = RootPresetsHelper(presets, &root, &parseState)) != true) {
552
0
    return result;
553
0
  }
554
555
0
  unsigned int currentMajor = cmVersion::GetMajorVersion();
556
0
  unsigned int currentMinor = cmVersion::GetMinorVersion();
557
0
  unsigned int currentPatch = cmVersion::GetPatchVersion();
558
0
  auto const& required = presets.CMakeMinimumRequired;
559
0
  if (required.Major > currentMajor) {
560
0
    ErrorGenerator error = cmCMakePresetsErrors::UNRECOGNIZED_CMAKE_VERSION(
561
0
      "major", currentMajor, required.Major);
562
0
    error(&root["cmakeMinimumRequired"]["major"], &this->parseState);
563
0
    return false;
564
0
  }
565
0
  if (required.Major == currentMajor) {
566
0
    if (required.Minor > currentMinor) {
567
0
      ErrorGenerator error = cmCMakePresetsErrors::UNRECOGNIZED_CMAKE_VERSION(
568
0
        "minor", currentMinor, required.Minor);
569
0
      error(&root["cmakeMinimumRequired"]["minor"], &this->parseState);
570
0
      return false;
571
0
    }
572
0
    if (required.Minor == currentMinor && required.Patch > currentPatch) {
573
0
      ErrorGenerator error = cmCMakePresetsErrors::UNRECOGNIZED_CMAKE_VERSION(
574
0
        "patch", currentPatch, required.Patch);
575
0
      error(&root["cmakeMinimumRequired"]["patch"], &this->parseState);
576
0
      return false;
577
0
    }
578
0
  }
579
580
0
  auto filePtr = cm::make_unique<File>();
581
0
  file = filePtr.get();
582
0
  this->Files.emplace_back(std::move(filePtr));
583
0
  inProgressFiles.emplace_back(file);
584
0
  file->Filename = filename;
585
0
  file->Version = v;
586
0
  file->ReachableFiles.insert(file);
587
588
0
  for (auto& preset : presets.ConfigurePresets) {
589
0
    preset.OriginFile = file;
590
0
    if (preset.Name.empty()) {
591
      // No error, already handled by PresetNameHelper
592
0
      return false;
593
0
    }
594
595
0
    if (!ExpandImmediateMacros<ConfigurePreset>(preset)) {
596
0
      cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name,
597
0
                                                    &this->parseState);
598
0
      return false;
599
0
    }
600
601
    // Support for installDir presets added in version 3.
602
0
    if (v < 3 && !preset.InstallDir.empty()) {
603
0
      cmCMakePresetsErrors::INSTALL_PREFIX_UNSUPPORTED(&root["installDir"],
604
0
                                                       &this->parseState);
605
0
      return false;
606
0
    }
607
608
    // Support for conditions added in version 3.
609
0
    if (v < 3 && preset.ConditionEvaluator) {
610
0
      cmCMakePresetsErrors::CONDITION_UNSUPPORTED(&this->parseState);
611
0
      return false;
612
0
    }
613
614
    // Support for toolchainFile presets added in version 3.
615
0
    if (v < 3 && !preset.ToolchainFile.empty()) {
616
0
      cmCMakePresetsErrors::TOOLCHAIN_FILE_UNSUPPORTED(&this->parseState);
617
0
      return false;
618
0
    }
619
620
    // Support for trace presets added in version 7.
621
0
    if (v < 7 &&
622
0
        (preset.TraceMode.has_value() || preset.TraceFormat.has_value() ||
623
0
         !preset.TraceRedirect.empty() || !preset.TraceSource.empty())) {
624
0
      cmCMakePresetsErrors::TRACE_UNSUPPORTED(&this->parseState);
625
0
      return false;
626
0
    }
627
628
    // Support for graphviz argument added in version 10.
629
0
    if (v < 10 && !preset.GraphVizFile.empty()) {
630
0
      cmCMakePresetsErrors::GRAPHVIZ_FILE_UNSUPPORTED(&this->parseState);
631
0
      return false;
632
0
    }
633
634
    // Support for diagnostics.
635
0
    if (!cmCMakePresetsGraphInternal::CheckDiagnostics(&this->parseState, v,
636
0
                                                       preset)) {
637
0
      return false;
638
0
    }
639
640
0
    PresetPair<ConfigurePreset> presetPair;
641
0
    presetPair.Unexpanded = preset;
642
0
    presetPair.Expanded = cm::nullopt;
643
0
    if (!this->ConfigurePresets.emplace(preset.Name, presetPair).second) {
644
0
      cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
645
0
      return false;
646
0
    }
647
648
0
    this->ConfigurePresetOrder.push_back(preset.Name);
649
0
  }
650
651
0
  for (auto& preset : presets.BuildPresets) {
652
0
    preset.OriginFile = file;
653
0
    if (preset.Name.empty()) {
654
      // No error, already handled by PresetNameHelper
655
0
      return false;
656
0
    }
657
658
0
    if (!ExpandImmediateMacros<BuildPreset>(preset)) {
659
0
      cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name,
660
0
                                                    &this->parseState);
661
0
      return false;
662
0
    }
663
664
0
    PresetPair<BuildPreset> presetPair;
665
0
    presetPair.Unexpanded = preset;
666
0
    presetPair.Expanded = cm::nullopt;
667
0
    if (!this->BuildPresets.emplace(preset.Name, presetPair).second) {
668
0
      cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
669
0
      return false;
670
0
    }
671
672
    // Support for conditions added in version 3.
673
0
    if (v < 3 && preset.ConditionEvaluator) {
674
0
      cmCMakePresetsErrors::CONDITION_UNSUPPORTED(&this->parseState);
675
0
      return false;
676
0
    }
677
678
0
    this->BuildPresetOrder.push_back(preset.Name);
679
0
  }
680
681
0
  for (auto& preset : presets.TestPresets) {
682
0
    preset.OriginFile = file;
683
0
    if (preset.Name.empty()) {
684
      // No error, already handled by PresetNameHelper
685
0
      return false;
686
0
    }
687
688
0
    if (!ExpandImmediateMacros<TestPreset>(preset)) {
689
0
      cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name,
690
0
                                                    &this->parseState);
691
0
      return false;
692
0
    }
693
694
0
    PresetPair<TestPreset> presetPair;
695
0
    presetPair.Unexpanded = preset;
696
0
    presetPair.Expanded = cm::nullopt;
697
0
    if (!this->TestPresets.emplace(preset.Name, presetPair).second) {
698
0
      cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
699
0
      return false;
700
0
    }
701
702
    // Support for conditions added in version 3.
703
0
    if (v < 3 && preset.ConditionEvaluator) {
704
0
      cmCMakePresetsErrors::CONDITION_UNSUPPORTED(&this->parseState);
705
0
      return false;
706
0
    }
707
708
    // Support for TestOutputTruncation added in version 5.
709
0
    if (v < 5 && preset.Output && preset.Output->TestOutputTruncation) {
710
0
      cmCMakePresetsErrors::TEST_OUTPUT_TRUNCATION_UNSUPPORTED(
711
0
        &this->parseState);
712
0
      return false;
713
0
    }
714
715
    // Support for outputJUnitFile added in version 6.
716
0
    if (v < 6 && preset.Output && !preset.Output->OutputJUnitFile.empty()) {
717
0
      cmCMakePresetsErrors::CTEST_JUNIT_UNSUPPORTED(&this->parseState);
718
0
      return false;
719
0
    }
720
721
    // Support for processor-count-based jobs added in version 11.
722
0
    if (v < 11 && preset.Execution && preset.Execution->Jobs.has_value() &&
723
0
        !preset.Execution->Jobs->has_value()) {
724
0
      cmCMakePresetsErrors::JOBS_PROC_UNSUPPORTED(&this->parseState);
725
0
      return false;
726
0
    }
727
728
    // Support for testPassthroughArguments added in version 12.
729
0
    if (v < 12 && preset.Execution &&
730
0
        !preset.Execution->TestPassthroughArguments.empty()) {
731
0
      cmCMakePresetsErrors::PASSTHROUGH_ARGS_UNSUPPORTED(&this->parseState);
732
0
      return false;
733
0
    }
734
735
0
    this->TestPresetOrder.push_back(preset.Name);
736
0
  }
737
738
0
  for (auto& preset : presets.PackagePresets) {
739
0
    preset.OriginFile = file;
740
0
    if (preset.Name.empty()) {
741
      // No error, already handled by PresetNameHelper
742
0
      return false;
743
0
    }
744
745
0
    if (!ExpandImmediateMacros<PackagePreset>(preset)) {
746
0
      cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name,
747
0
                                                    &this->parseState);
748
0
      return false;
749
0
    }
750
751
0
    PresetPair<PackagePreset> presetPair;
752
0
    presetPair.Unexpanded = preset;
753
0
    presetPair.Expanded = cm::nullopt;
754
0
    if (!this->PackagePresets.emplace(preset.Name, presetPair).second) {
755
0
      cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
756
0
      return false;
757
0
    }
758
759
    // Support for conditions added in version 3, but this requires version 5
760
    // already, so no action needed.
761
762
0
    this->PackagePresetOrder.push_back(preset.Name);
763
0
  }
764
765
0
  for (auto& preset : presets.WorkflowPresets) {
766
0
    preset.OriginFile = file;
767
0
    if (preset.Name.empty()) {
768
      // No error, already handled by PresetNameHelper
769
0
      return false;
770
0
    }
771
772
0
    if (!ExpandImmediateMacros<WorkflowPreset>(preset)) {
773
0
      cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name,
774
0
                                                    &this->parseState);
775
0
      return false;
776
0
    }
777
778
0
    PresetPair<WorkflowPreset> presetPair;
779
0
    presetPair.Unexpanded = preset;
780
0
    presetPair.Expanded = cm::nullopt;
781
0
    if (!this->WorkflowPresets.emplace(preset.Name, presetPair).second) {
782
0
      cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
783
0
      return false;
784
0
    }
785
786
    // Support for conditions added in version 3, but this requires version 6
787
    // already, so no action needed.
788
789
0
    this->WorkflowPresetOrder.push_back(preset.Name);
790
0
  }
791
792
0
  auto const includeFile = [this, &inProgressFiles,
793
0
                            file](std::string const& include,
794
0
                                  RootType rootType2, ReadReason readReason2,
795
0
                                  std::string& FailureMessage) -> bool {
796
0
    bool r;
797
0
    File* includedFile;
798
0
    if ((r =
799
0
           this->ReadJSONFile(include, rootType2, readReason2, inProgressFiles,
800
0
                              includedFile, FailureMessage)) != true) {
801
0
      return r;
802
0
    }
803
804
0
    file->ReachableFiles.insert(includedFile->ReachableFiles.begin(),
805
0
                                includedFile->ReachableFiles.end());
806
0
    return true;
807
0
  };
808
809
0
  MacroExpanderVector macroExpanders{};
810
811
0
  if (v >= 9) {
812
0
    macroExpanders.push_back(
813
0
      cm::make_unique<BaseMacroExpander>(*this, filename));
814
0
  }
815
0
  macroExpanders.push_back(cm::make_unique<EnvironmentMacroExpander>());
816
817
0
  for (Json::ArrayIndex i = 0; i < presets.Include.size(); ++i) {
818
0
    auto include = presets.Include[i];
819
820
    // Support for macro expansion in includes added in version 7
821
0
    if (v >= 7) {
822
0
      if (ExpandMacros(include, macroExpanders, v) != ExpandMacroResult::Ok) {
823
0
        cmCMakePresetsErrors::INVALID_INCLUDE(&root["include"][i],
824
0
                                              &this->parseState);
825
0
        return false;
826
0
      }
827
0
    }
828
829
0
    if (!cmSystemTools::FileIsFullPath(include)) {
830
0
      auto directory = cmSystemTools::GetFilenamePath(filename);
831
0
      include = cmStrCat(directory, '/', include);
832
0
    }
833
834
0
    if ((result = includeFile(include, rootType, ReadReason::Included,
835
0
                              errMsg)) != true) {
836
0
      return result;
837
0
    }
838
0
  }
839
840
0
  if (rootType == RootType::User && readReason == ReadReason::Root) {
841
0
    auto cmakePresetsFilename = GetFilename(this->SourceDir);
842
0
    if (cmSystemTools::FileExists(cmakePresetsFilename)) {
843
0
      if ((result = includeFile(cmakePresetsFilename, RootType::Project,
844
0
                                ReadReason::Root, errMsg)) != true) {
845
0
        return result;
846
0
      }
847
0
    }
848
0
  }
849
850
0
  inProgressFiles.pop_back();
851
0
  return true;
852
0
}