resolve cyclic dependency with zstd
[platform/upstream/cmake.git] / Source / cmCMakePresetsGraphReadJSON.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include <algorithm>
4 #include <functional>
5 #include <map>
6 #include <string>
7 #include <unordered_set>
8 #include <utility>
9 #include <vector>
10
11 #include <cm/memory>
12 #include <cm/optional>
13 #include <cmext/string_view>
14
15 #include <cm3p/json/reader.h>
16 #include <cm3p/json/value.h>
17
18 #include "cmsys/FStream.hxx"
19
20 #include "cmCMakePresetsGraph.h"
21 #include "cmCMakePresetsGraphInternal.h"
22 #include "cmJSONHelpers.h"
23 #include "cmStringAlgorithms.h"
24 #include "cmSystemTools.h"
25 #include "cmVersion.h"
26
27 namespace {
28 using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
29 using CacheVariable = cmCMakePresetsGraph::CacheVariable;
30 using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
31 using BuildPreset = cmCMakePresetsGraph::BuildPreset;
32 using TestPreset = cmCMakePresetsGraph::TestPreset;
33 using PackagePreset = cmCMakePresetsGraph::PackagePreset;
34 using WorkflowPreset = cmCMakePresetsGraph::WorkflowPreset;
35 using ArchToolsetStrategy = cmCMakePresetsGraph::ArchToolsetStrategy;
36 using JSONHelperBuilder = cmJSONHelperBuilder<ReadFileResult>;
37
38 constexpr int MIN_VERSION = 1;
39 constexpr int MAX_VERSION = 6;
40
41 struct CMakeVersion
42 {
43   unsigned int Major = 0;
44   unsigned int Minor = 0;
45   unsigned int Patch = 0;
46 };
47
48 struct RootPresets
49 {
50   CMakeVersion CMakeMinimumRequired;
51   std::vector<ConfigurePreset> ConfigurePresets;
52   std::vector<BuildPreset> BuildPresets;
53   std::vector<TestPreset> TestPresets;
54   std::vector<PackagePreset> PackagePresets;
55   std::vector<WorkflowPreset> WorkflowPresets;
56   std::vector<std::string> Include;
57 };
58
59 std::unique_ptr<cmCMakePresetsGraphInternal::NotCondition> InvertCondition(
60   std::unique_ptr<cmCMakePresetsGraph::Condition> condition)
61 {
62   auto retval = cm::make_unique<cmCMakePresetsGraphInternal::NotCondition>();
63   retval->SubCondition = std::move(condition);
64   return retval;
65 }
66
67 auto const ConditionStringHelper = JSONHelperBuilder::String(
68   ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION);
69
70 auto const ConditionBoolHelper = JSONHelperBuilder::Bool(
71   ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION);
72
73 auto const ConditionStringListHelper = JSONHelperBuilder::Vector<std::string>(
74   ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION,
75   ConditionStringHelper);
76
77 auto const ConstConditionHelper =
78   JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::ConstCondition>(
79     ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
80     .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
81     .Bind("value"_s, &cmCMakePresetsGraphInternal::ConstCondition::Value,
82           ConditionBoolHelper, true);
83
84 auto const EqualsConditionHelper =
85   JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::EqualsCondition>(
86     ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
87     .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
88     .Bind("lhs"_s, &cmCMakePresetsGraphInternal::EqualsCondition::Lhs,
89           ConditionStringHelper, true)
90     .Bind("rhs"_s, &cmCMakePresetsGraphInternal::EqualsCondition::Rhs,
91           ConditionStringHelper, true);
92
93 auto const InListConditionHelper =
94   JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::InListCondition>(
95     ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
96     .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
97     .Bind("string"_s, &cmCMakePresetsGraphInternal::InListCondition::String,
98           ConditionStringHelper, true)
99     .Bind("list"_s, &cmCMakePresetsGraphInternal::InListCondition::List,
100           ConditionStringListHelper, true);
101
102 auto const MatchesConditionHelper =
103   JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::MatchesCondition>(
104     ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
105     .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
106     .Bind("string"_s, &cmCMakePresetsGraphInternal::MatchesCondition::String,
107           ConditionStringHelper, true)
108     .Bind("regex"_s, &cmCMakePresetsGraphInternal::MatchesCondition::Regex,
109           ConditionStringHelper, true);
110
111 ReadFileResult SubConditionHelper(
112   std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
113   const Json::Value* value);
114
115 auto const ListConditionVectorHelper =
116   JSONHelperBuilder::Vector<std::unique_ptr<cmCMakePresetsGraph::Condition>>(
117     ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION,
118     SubConditionHelper);
119 auto const AnyAllOfConditionHelper =
120   JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::AnyAllOfCondition>(
121     ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, 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     ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
130     .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
131     .Bind("condition"_s,
132           &cmCMakePresetsGraphInternal::NotCondition::SubCondition,
133           SubConditionHelper);
134
135 ReadFileResult ConditionHelper(
136   std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
137   const Json::Value* value)
138 {
139   if (!value) {
140     out.reset();
141     return ReadFileResult::READ_OK;
142   }
143
144   if (value->isBool()) {
145     auto c = cm::make_unique<cmCMakePresetsGraphInternal::ConstCondition>();
146     c->Value = value->asBool();
147     out = std::move(c);
148     return ReadFileResult::READ_OK;
149   }
150
151   if (value->isNull()) {
152     out = cm::make_unique<cmCMakePresetsGraphInternal::NullCondition>();
153     return ReadFileResult::READ_OK;
154   }
155
156   if (value->isObject()) {
157     if (!value->isMember("type")) {
158       return ReadFileResult::INVALID_CONDITION;
159     }
160
161     if (!(*value)["type"].isString()) {
162       return ReadFileResult::INVALID_CONDITION;
163     }
164     auto type = (*value)["type"].asString();
165
166     if (type == "const") {
167       auto c = cm::make_unique<cmCMakePresetsGraphInternal::ConstCondition>();
168       CHECK_OK(ConstConditionHelper(*c, value));
169       out = std::move(c);
170       return ReadFileResult::READ_OK;
171     }
172
173     if (type == "equals" || type == "notEquals") {
174       auto c = cm::make_unique<cmCMakePresetsGraphInternal::EqualsCondition>();
175       CHECK_OK(EqualsConditionHelper(*c, value));
176       out = std::move(c);
177       if (type == "notEquals") {
178         out = InvertCondition(std::move(out));
179       }
180       return ReadFileResult::READ_OK;
181     }
182
183     if (type == "inList" || type == "notInList") {
184       auto c = cm::make_unique<cmCMakePresetsGraphInternal::InListCondition>();
185       CHECK_OK(InListConditionHelper(*c, value));
186       out = std::move(c);
187       if (type == "notInList") {
188         out = InvertCondition(std::move(out));
189       }
190       return ReadFileResult::READ_OK;
191     }
192
193     if (type == "matches" || type == "notMatches") {
194       auto c =
195         cm::make_unique<cmCMakePresetsGraphInternal::MatchesCondition>();
196       CHECK_OK(MatchesConditionHelper(*c, value));
197       out = std::move(c);
198       if (type == "notMatches") {
199         out = InvertCondition(std::move(out));
200       }
201       return ReadFileResult::READ_OK;
202     }
203
204     if (type == "anyOf" || type == "allOf") {
205       auto c =
206         cm::make_unique<cmCMakePresetsGraphInternal::AnyAllOfCondition>();
207       c->StopValue = (type == "anyOf");
208       CHECK_OK(AnyAllOfConditionHelper(*c, value));
209       out = std::move(c);
210       return ReadFileResult::READ_OK;
211     }
212
213     if (type == "not") {
214       auto c = cm::make_unique<cmCMakePresetsGraphInternal::NotCondition>();
215       CHECK_OK(NotConditionHelper(*c, value));
216       out = std::move(c);
217       return ReadFileResult::READ_OK;
218     }
219   }
220
221   return ReadFileResult::INVALID_CONDITION;
222 }
223
224 ReadFileResult SubConditionHelper(
225   std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
226   const Json::Value* value)
227 {
228   std::unique_ptr<cmCMakePresetsGraph::Condition> ptr;
229   auto result = ConditionHelper(ptr, value);
230   if (ptr && ptr->IsNull()) {
231     return ReadFileResult::INVALID_CONDITION;
232   }
233   out = std::move(ptr);
234   return result;
235 }
236
237 ReadFileResult EnvironmentHelper(cm::optional<std::string>& out,
238                                  const Json::Value* value)
239 {
240   if (!value || value->isNull()) {
241     out = cm::nullopt;
242     return ReadFileResult::READ_OK;
243   }
244   if (value->isString()) {
245     out = value->asString();
246     return ReadFileResult::READ_OK;
247   }
248   return ReadFileResult::INVALID_PRESET;
249 }
250
251 auto const VersionIntHelper = JSONHelperBuilder::Int(
252   ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
253
254 auto const VersionHelper = JSONHelperBuilder::Required<int>(
255   ReadFileResult::NO_VERSION, VersionIntHelper);
256
257 auto const RootVersionHelper =
258   JSONHelperBuilder::Object<int>(ReadFileResult::READ_OK,
259                                  ReadFileResult::INVALID_ROOT)
260     .Bind("version"_s, VersionHelper, false);
261
262 auto const CMakeVersionUIntHelper = JSONHelperBuilder::UInt(
263   ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
264
265 auto const CMakeVersionHelper =
266   JSONHelperBuilder::Object<CMakeVersion>(
267     ReadFileResult::READ_OK, ReadFileResult::INVALID_CMAKE_VERSION, false)
268     .Bind("major"_s, &CMakeVersion::Major, CMakeVersionUIntHelper, false)
269     .Bind("minor"_s, &CMakeVersion::Minor, CMakeVersionUIntHelper, false)
270     .Bind("patch"_s, &CMakeVersion::Patch, CMakeVersionUIntHelper, false);
271
272 auto const IncludeHelper = JSONHelperBuilder::String(
273   ReadFileResult::READ_OK, ReadFileResult::INVALID_INCLUDE);
274
275 auto const IncludeVectorHelper = JSONHelperBuilder::Vector<std::string>(
276   ReadFileResult::READ_OK, ReadFileResult::INVALID_INCLUDE, IncludeHelper);
277
278 auto const RootPresetsHelper =
279   JSONHelperBuilder::Object<RootPresets>(ReadFileResult::READ_OK,
280                                          ReadFileResult::INVALID_ROOT, false)
281     .Bind<int>("version"_s, nullptr, VersionHelper)
282     .Bind("configurePresets"_s, &RootPresets::ConfigurePresets,
283           cmCMakePresetsGraphInternal::ConfigurePresetsHelper, false)
284     .Bind("buildPresets"_s, &RootPresets::BuildPresets,
285           cmCMakePresetsGraphInternal::BuildPresetsHelper, false)
286     .Bind("testPresets"_s, &RootPresets::TestPresets,
287           cmCMakePresetsGraphInternal::TestPresetsHelper, false)
288     .Bind("packagePresets"_s, &RootPresets::PackagePresets,
289           cmCMakePresetsGraphInternal::PackagePresetsHelper, false)
290     .Bind("workflowPresets"_s, &RootPresets::WorkflowPresets,
291           cmCMakePresetsGraphInternal::WorkflowPresetsHelper, false)
292     .Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired,
293           CMakeVersionHelper, false)
294     .Bind("include"_s, &RootPresets::Include, IncludeVectorHelper, false)
295     .Bind<std::nullptr_t>(
296       "vendor"_s, nullptr,
297       cmCMakePresetsGraphInternal::VendorHelper(ReadFileResult::INVALID_ROOT),
298       false);
299 }
300
301 namespace cmCMakePresetsGraphInternal {
302 cmCMakePresetsGraph::ReadFileResult PresetStringHelper(
303   std::string& out, const Json::Value* value)
304 {
305   static auto const helper = JSONHelperBuilder::String(
306     ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
307
308   return helper(out, value);
309 }
310
311 cmCMakePresetsGraph::ReadFileResult PresetVectorStringHelper(
312   std::vector<std::string>& out, const Json::Value* value)
313 {
314   static auto const helper = JSONHelperBuilder::Vector<std::string>(
315     ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
316     cmCMakePresetsGraphInternal::PresetStringHelper);
317
318   return helper(out, value);
319 }
320
321 cmCMakePresetsGraph::ReadFileResult PresetBoolHelper(bool& out,
322                                                      const Json::Value* value)
323 {
324   static auto const helper = JSONHelperBuilder::Bool(
325     ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
326
327   return helper(out, value);
328 }
329
330 cmCMakePresetsGraph::ReadFileResult PresetOptionalBoolHelper(
331   cm::optional<bool>& out, const Json::Value* value)
332 {
333   static auto const helper = JSONHelperBuilder::Optional<bool>(
334     ReadFileResult::READ_OK, PresetBoolHelper);
335
336   return helper(out, value);
337 }
338
339 cmCMakePresetsGraph::ReadFileResult PresetIntHelper(int& out,
340                                                     const Json::Value* value)
341 {
342   static auto const helper = JSONHelperBuilder::Int(
343     ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
344
345   return helper(out, value);
346 }
347
348 cmCMakePresetsGraph::ReadFileResult PresetOptionalIntHelper(
349   cm::optional<int>& out, const Json::Value* value)
350 {
351   static auto const helper =
352     JSONHelperBuilder::Optional<int>(ReadFileResult::READ_OK, PresetIntHelper);
353
354   return helper(out, value);
355 }
356
357 cmCMakePresetsGraph::ReadFileResult PresetVectorIntHelper(
358   std::vector<int>& out, const Json::Value* value)
359 {
360   static auto const helper = JSONHelperBuilder::Vector<int>(
361     ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, PresetIntHelper);
362
363   return helper(out, value);
364 }
365
366 cmJSONHelper<std::nullptr_t, ReadFileResult> VendorHelper(ReadFileResult error)
367 {
368   return [error](std::nullptr_t& /*out*/,
369                  const Json::Value* value) -> ReadFileResult {
370     if (!value) {
371       return ReadFileResult::READ_OK;
372     }
373
374     if (!value->isObject()) {
375       return error;
376     }
377
378     return ReadFileResult::READ_OK;
379   };
380 }
381
382 ReadFileResult PresetConditionHelper(
383   std::shared_ptr<cmCMakePresetsGraph::Condition>& out,
384   const Json::Value* value)
385 {
386   std::unique_ptr<cmCMakePresetsGraph::Condition> ptr;
387   auto result = ConditionHelper(ptr, value);
388   out = std::move(ptr);
389   return result;
390 }
391
392 ReadFileResult PresetVectorOneOrMoreStringHelper(std::vector<std::string>& out,
393                                                  const Json::Value* value)
394 {
395   out.clear();
396   if (!value) {
397     return ReadFileResult::READ_OK;
398   }
399
400   if (value->isString()) {
401     out.push_back(value->asString());
402     return ReadFileResult::READ_OK;
403   }
404
405   return PresetVectorStringHelper(out, value);
406 }
407
408 cmCMakePresetsGraph::ReadFileResult EnvironmentMapHelper(
409   std::map<std::string, cm::optional<std::string>>& out,
410   const Json::Value* value)
411 {
412   static auto const helper = JSONHelperBuilder::Map<cm::optional<std::string>>(
413     ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
414     EnvironmentHelper);
415
416   return helper(out, value);
417 }
418 }
419
420 cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadJSONFile(
421   const std::string& filename, RootType rootType, ReadReason readReason,
422   std::vector<File*>& inProgressFiles, File*& file, std::string& errMsg)
423 {
424   ReadFileResult result;
425
426   for (auto const& f : this->Files) {
427     if (cmSystemTools::SameFile(filename, f->Filename)) {
428       file = f.get();
429       auto fileIt =
430         std::find(inProgressFiles.begin(), inProgressFiles.end(), file);
431       if (fileIt != inProgressFiles.end()) {
432         return cmCMakePresetsGraph::ReadFileResult::CYCLIC_INCLUDE;
433       }
434
435       return cmCMakePresetsGraph::ReadFileResult::READ_OK;
436     }
437   }
438
439   cmsys::ifstream fin(filename.c_str());
440   if (!fin) {
441     errMsg = cmStrCat(filename, ": Failed to read file\n", errMsg);
442     return ReadFileResult::FILE_NOT_FOUND;
443   }
444   // If there's a BOM, toss it.
445   cmsys::FStream::ReadBOM(fin);
446
447   Json::Value root;
448   Json::CharReaderBuilder builder;
449   Json::CharReaderBuilder::strictMode(&builder.settings_);
450   if (!Json::parseFromStream(builder, fin, &root, &errMsg)) {
451     errMsg = cmStrCat(filename, ":\n", errMsg);
452     return ReadFileResult::JSON_PARSE_ERROR;
453   }
454
455   int v = 0;
456   if ((result = RootVersionHelper(v, &root)) != ReadFileResult::READ_OK) {
457     return result;
458   }
459   if (v < MIN_VERSION || v > MAX_VERSION) {
460     return ReadFileResult::UNRECOGNIZED_VERSION;
461   }
462
463   // Support for build and test presets added in version 2.
464   if (v < 2 &&
465       (root.isMember("buildPresets") || root.isMember("testPresets"))) {
466     return ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED;
467   }
468
469   // Support for package presets added in version 6.
470   if (v < 6 && root.isMember("packagePresets")) {
471     return ReadFileResult::PACKAGE_PRESETS_UNSUPPORTED;
472   }
473
474   // Support for workflow presets added in version 6.
475   if (v < 6 && root.isMember("workflowPresets")) {
476     return ReadFileResult::WORKFLOW_PRESETS_UNSUPPORTED;
477   }
478
479   // Support for include added in version 4.
480   if (v < 4 && root.isMember("include")) {
481     return ReadFileResult::INCLUDE_UNSUPPORTED;
482   }
483
484   RootPresets presets;
485   if ((result = RootPresetsHelper(presets, &root)) !=
486       ReadFileResult::READ_OK) {
487     return result;
488   }
489
490   unsigned int currentMajor = cmVersion::GetMajorVersion();
491   unsigned int currentMinor = cmVersion::GetMinorVersion();
492   unsigned int currentPatch = cmVersion::GetPatchVersion();
493   auto const& required = presets.CMakeMinimumRequired;
494   if (required.Major > currentMajor ||
495       (required.Major == currentMajor &&
496        (required.Minor > currentMinor ||
497         (required.Minor == currentMinor &&
498          (required.Patch > currentPatch))))) {
499     return ReadFileResult::UNRECOGNIZED_CMAKE_VERSION;
500   }
501
502   auto filePtr = cm::make_unique<File>();
503   file = filePtr.get();
504   this->Files.emplace_back(std::move(filePtr));
505   inProgressFiles.emplace_back(file);
506   file->Filename = filename;
507   file->Version = v;
508   file->ReachableFiles.insert(file);
509
510   for (auto& preset : presets.ConfigurePresets) {
511     preset.OriginFile = file;
512     if (preset.Name.empty()) {
513       errMsg += R"(\n\t)";
514       errMsg += filename;
515       return ReadFileResult::INVALID_PRESET;
516     }
517
518     PresetPair<ConfigurePreset> presetPair;
519     presetPair.Unexpanded = preset;
520     presetPair.Expanded = cm::nullopt;
521     if (!this->ConfigurePresets
522            .emplace(std::make_pair(preset.Name, presetPair))
523            .second) {
524       return ReadFileResult::DUPLICATE_PRESETS;
525     }
526
527     // Support for installDir presets added in version 3.
528     if (v < 3 && !preset.InstallDir.empty()) {
529       return ReadFileResult::INSTALL_PREFIX_UNSUPPORTED;
530     }
531
532     // Support for conditions added in version 3.
533     if (v < 3 && preset.ConditionEvaluator) {
534       return ReadFileResult::CONDITION_UNSUPPORTED;
535     }
536
537     // Support for toolchainFile presets added in version 3.
538     if (v < 3 && !preset.ToolchainFile.empty()) {
539       return ReadFileResult::TOOLCHAIN_FILE_UNSUPPORTED;
540     }
541
542     this->ConfigurePresetOrder.push_back(preset.Name);
543   }
544
545   for (auto& preset : presets.BuildPresets) {
546     preset.OriginFile = file;
547     if (preset.Name.empty()) {
548       errMsg += R"(\n\t)";
549       errMsg += filename;
550       return ReadFileResult::INVALID_PRESET;
551     }
552
553     PresetPair<BuildPreset> presetPair;
554     presetPair.Unexpanded = preset;
555     presetPair.Expanded = cm::nullopt;
556     if (!this->BuildPresets.emplace(preset.Name, presetPair).second) {
557       return ReadFileResult::DUPLICATE_PRESETS;
558     }
559
560     // Support for conditions added in version 3.
561     if (v < 3 && preset.ConditionEvaluator) {
562       return ReadFileResult::CONDITION_UNSUPPORTED;
563     }
564
565     this->BuildPresetOrder.push_back(preset.Name);
566   }
567
568   for (auto& preset : presets.TestPresets) {
569     preset.OriginFile = file;
570     if (preset.Name.empty()) {
571       return ReadFileResult::INVALID_PRESET;
572     }
573
574     PresetPair<TestPreset> presetPair;
575     presetPair.Unexpanded = preset;
576     presetPair.Expanded = cm::nullopt;
577     if (!this->TestPresets.emplace(preset.Name, presetPair).second) {
578       return ReadFileResult::DUPLICATE_PRESETS;
579     }
580
581     // Support for conditions added in version 3.
582     if (v < 3 && preset.ConditionEvaluator) {
583       return ReadFileResult::CONDITION_UNSUPPORTED;
584     }
585
586     // Support for TestOutputTruncation added in version 5.
587     if (v < 5 && preset.Output && preset.Output->TestOutputTruncation) {
588       return ReadFileResult::TEST_OUTPUT_TRUNCATION_UNSUPPORTED;
589     }
590
591     // Support for outputJUnitFile added in version 6.
592     if (v < 6 && preset.Output && !preset.Output->OutputJUnitFile.empty()) {
593       return ReadFileResult::CTEST_JUNIT_UNSUPPORTED;
594     }
595
596     this->TestPresetOrder.push_back(preset.Name);
597   }
598
599   for (auto& preset : presets.PackagePresets) {
600     preset.OriginFile = file;
601     if (preset.Name.empty()) {
602       return ReadFileResult::INVALID_PRESET;
603     }
604
605     PresetPair<PackagePreset> presetPair;
606     presetPair.Unexpanded = preset;
607     presetPair.Expanded = cm::nullopt;
608     if (!this->PackagePresets.emplace(preset.Name, presetPair).second) {
609       return ReadFileResult::DUPLICATE_PRESETS;
610     }
611
612     // Support for conditions added in version 3, but this requires version 5
613     // already, so no action needed.
614
615     this->PackagePresetOrder.push_back(preset.Name);
616   }
617
618   for (auto& preset : presets.WorkflowPresets) {
619     preset.OriginFile = file;
620     if (preset.Name.empty()) {
621       return ReadFileResult::INVALID_PRESET;
622     }
623
624     PresetPair<WorkflowPreset> presetPair;
625     presetPair.Unexpanded = preset;
626     presetPair.Expanded = cm::nullopt;
627     if (!this->WorkflowPresets.emplace(preset.Name, presetPair).second) {
628       return ReadFileResult::DUPLICATE_PRESETS;
629     }
630
631     // Support for conditions added in version 3, but this requires version 6
632     // already, so no action needed.
633
634     this->WorkflowPresetOrder.push_back(preset.Name);
635   }
636
637   auto const includeFile = [this, &inProgressFiles, file](
638                              const std::string& include, RootType rootType2,
639                              ReadReason readReason2,
640                              std::string& FailureMessage) -> ReadFileResult {
641     ReadFileResult r;
642     File* includedFile;
643     if ((r = this->ReadJSONFile(include, rootType2, readReason2,
644                                 inProgressFiles, includedFile,
645                                 FailureMessage)) != ReadFileResult::READ_OK) {
646       return r;
647     }
648
649     file->ReachableFiles.insert(includedFile->ReachableFiles.begin(),
650                                 includedFile->ReachableFiles.end());
651     return ReadFileResult::READ_OK;
652   };
653
654   for (auto include : presets.Include) {
655     if (!cmSystemTools::FileIsFullPath(include)) {
656       auto directory = cmSystemTools::GetFilenamePath(filename);
657       include = cmStrCat(directory, '/', include);
658     }
659
660     if ((result = includeFile(include, rootType, ReadReason::Included,
661                               errMsg)) != ReadFileResult::READ_OK) {
662       return result;
663     }
664   }
665
666   if (rootType == RootType::User && readReason == ReadReason::Root) {
667     auto cmakePresetsFilename = GetFilename(this->SourceDir);
668     if (cmSystemTools::FileExists(cmakePresetsFilename)) {
669       if ((result = includeFile(cmakePresetsFilename, RootType::Project,
670                                 ReadReason::Root, errMsg)) !=
671           ReadFileResult::READ_OK) {
672         return result;
673       }
674     }
675   }
676
677   inProgressFiles.pop_back();
678   return ReadFileResult::READ_OK;
679 }