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 <functional>
4 #include <map>
5 #include <string>
6 #include <utility>
7 #include <vector>
8
9 #include <cm/memory>
10 #include <cm/optional>
11 #include <cmext/string_view>
12
13 #include <cm3p/json/reader.h>
14 #include <cm3p/json/value.h>
15
16 #include "cmsys/FStream.hxx"
17
18 #include "cmCMakePresetsFile.h"
19 #include "cmCMakePresetsFileInternal.h"
20 #include "cmJSONHelpers.h"
21 #include "cmVersion.h"
22
23 namespace {
24 using ReadFileResult = cmCMakePresetsFile::ReadFileResult;
25 using CacheVariable = cmCMakePresetsFile::CacheVariable;
26 using ConfigurePreset = cmCMakePresetsFile::ConfigurePreset;
27 using BuildPreset = cmCMakePresetsFile::BuildPreset;
28 using TestPreset = cmCMakePresetsFile::TestPreset;
29 using ArchToolsetStrategy = cmCMakePresetsFile::ArchToolsetStrategy;
30
31 constexpr int MIN_VERSION = 1;
32 constexpr int MAX_VERSION = 3;
33
34 struct CMakeVersion
35 {
36 unsigned int Major = 0;
37 unsigned int Minor = 0;
38 unsigned int Patch = 0;
39 };
40
41 struct RootPresets
42 {
43 CMakeVersion CMakeMinimumRequired;
44 std::vector<cmCMakePresetsFile::ConfigurePreset> ConfigurePresets;
45 std::vector<cmCMakePresetsFile::BuildPreset> BuildPresets;
46 std::vector<cmCMakePresetsFile::TestPreset> TestPresets;
47 };
48
InvertCondition(std::unique_ptr<cmCMakePresetsFile::Condition> condition)49 std::unique_ptr<cmCMakePresetsFileInternal::NotCondition> InvertCondition(
50 std::unique_ptr<cmCMakePresetsFile::Condition> condition)
51 {
52 auto retval = cm::make_unique<cmCMakePresetsFileInternal::NotCondition>();
53 retval->SubCondition = std::move(condition);
54 return retval;
55 }
56
57 auto const ConditionStringHelper = cmJSONStringHelper<ReadFileResult>(
58 ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION);
59
60 auto const ConditionBoolHelper = cmJSONBoolHelper<ReadFileResult>(
61 ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION);
62
63 auto const ConditionStringListHelper =
64 cmJSONVectorHelper<std::string, ReadFileResult>(
65 ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION,
66 ConditionStringHelper);
67
68 auto const ConstConditionHelper =
69 cmJSONObjectHelper<cmCMakePresetsFileInternal::ConstCondition,
70 ReadFileResult>(ReadFileResult::READ_OK,
71 ReadFileResult::INVALID_CONDITION, false)
72 .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
73 .Bind("value"_s, &cmCMakePresetsFileInternal::ConstCondition::Value,
74 ConditionBoolHelper, true);
75
76 auto const EqualsConditionHelper =
77 cmJSONObjectHelper<cmCMakePresetsFileInternal::EqualsCondition,
78 ReadFileResult>(ReadFileResult::READ_OK,
79 ReadFileResult::INVALID_CONDITION, false)
80 .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
81 .Bind("lhs"_s, &cmCMakePresetsFileInternal::EqualsCondition::Lhs,
82 ConditionStringHelper, true)
83 .Bind("rhs"_s, &cmCMakePresetsFileInternal::EqualsCondition::Rhs,
84 ConditionStringHelper, true);
85
86 auto const InListConditionHelper =
87 cmJSONObjectHelper<cmCMakePresetsFileInternal::InListCondition,
88 ReadFileResult>(ReadFileResult::READ_OK,
89 ReadFileResult::INVALID_CONDITION, false)
90 .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
91 .Bind("string"_s, &cmCMakePresetsFileInternal::InListCondition::String,
92 ConditionStringHelper, true)
93 .Bind("list"_s, &cmCMakePresetsFileInternal::InListCondition::List,
94 ConditionStringListHelper, true);
95
96 auto const MatchesConditionHelper =
97 cmJSONObjectHelper<cmCMakePresetsFileInternal::MatchesCondition,
98 ReadFileResult>(ReadFileResult::READ_OK,
99 ReadFileResult::INVALID_CONDITION, false)
100 .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
101 .Bind("string"_s, &cmCMakePresetsFileInternal::MatchesCondition::String,
102 ConditionStringHelper, true)
103 .Bind("regex"_s, &cmCMakePresetsFileInternal::MatchesCondition::Regex,
104 ConditionStringHelper, true);
105
106 ReadFileResult SubConditionHelper(
107 std::unique_ptr<cmCMakePresetsFile::Condition>& out,
108 const Json::Value* value);
109
110 auto const ListConditionVectorHelper =
111 cmJSONVectorHelper<std::unique_ptr<cmCMakePresetsFile::Condition>,
112 ReadFileResult>(ReadFileResult::READ_OK,
113 ReadFileResult::INVALID_CONDITION,
114 SubConditionHelper);
115 auto const AnyAllOfConditionHelper =
116 cmJSONObjectHelper<cmCMakePresetsFileInternal::AnyAllOfCondition,
117 ReadFileResult>(ReadFileResult::READ_OK,
118 ReadFileResult::INVALID_CONDITION, false)
119 .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
120 .Bind("conditions"_s,
121 &cmCMakePresetsFileInternal::AnyAllOfCondition::Conditions,
122 ListConditionVectorHelper);
123
124 auto const NotConditionHelper =
125 cmJSONObjectHelper<cmCMakePresetsFileInternal::NotCondition, ReadFileResult>(
126 ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
127 .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
128 .Bind("condition"_s,
129 &cmCMakePresetsFileInternal::NotCondition::SubCondition,
130 SubConditionHelper);
131
ConditionHelper(std::unique_ptr<cmCMakePresetsFile::Condition> & out,const Json::Value * value)132 ReadFileResult ConditionHelper(
133 std::unique_ptr<cmCMakePresetsFile::Condition>& out,
134 const Json::Value* value)
135 {
136 if (!value) {
137 out.reset();
138 return ReadFileResult::READ_OK;
139 }
140
141 if (value->isBool()) {
142 auto c = cm::make_unique<cmCMakePresetsFileInternal::ConstCondition>();
143 c->Value = value->asBool();
144 out = std::move(c);
145 return ReadFileResult::READ_OK;
146 }
147
148 if (value->isNull()) {
149 out = cm::make_unique<cmCMakePresetsFileInternal::NullCondition>();
150 return ReadFileResult::READ_OK;
151 }
152
153 if (value->isObject()) {
154 if (!value->isMember("type")) {
155 return ReadFileResult::INVALID_CONDITION;
156 }
157
158 if (!(*value)["type"].isString()) {
159 return ReadFileResult::INVALID_CONDITION;
160 }
161 auto type = (*value)["type"].asString();
162
163 if (type == "const") {
164 auto c = cm::make_unique<cmCMakePresetsFileInternal::ConstCondition>();
165 CHECK_OK(ConstConditionHelper(*c, value));
166 out = std::move(c);
167 return ReadFileResult::READ_OK;
168 }
169
170 if (type == "equals" || type == "notEquals") {
171 auto c = cm::make_unique<cmCMakePresetsFileInternal::EqualsCondition>();
172 CHECK_OK(EqualsConditionHelper(*c, value));
173 out = std::move(c);
174 if (type == "notEquals") {
175 out = InvertCondition(std::move(out));
176 }
177 return ReadFileResult::READ_OK;
178 }
179
180 if (type == "inList" || type == "notInList") {
181 auto c = cm::make_unique<cmCMakePresetsFileInternal::InListCondition>();
182 CHECK_OK(InListConditionHelper(*c, value));
183 out = std::move(c);
184 if (type == "notInList") {
185 out = InvertCondition(std::move(out));
186 }
187 return ReadFileResult::READ_OK;
188 }
189
190 if (type == "matches" || type == "notMatches") {
191 auto c = cm::make_unique<cmCMakePresetsFileInternal::MatchesCondition>();
192 CHECK_OK(MatchesConditionHelper(*c, value));
193 out = std::move(c);
194 if (type == "notMatches") {
195 out = InvertCondition(std::move(out));
196 }
197 return ReadFileResult::READ_OK;
198 }
199
200 if (type == "anyOf" || type == "allOf") {
201 auto c =
202 cm::make_unique<cmCMakePresetsFileInternal::AnyAllOfCondition>();
203 c->StopValue = (type == "anyOf");
204 CHECK_OK(AnyAllOfConditionHelper(*c, value));
205 out = std::move(c);
206 return ReadFileResult::READ_OK;
207 }
208
209 if (type == "not") {
210 auto c = cm::make_unique<cmCMakePresetsFileInternal::NotCondition>();
211 CHECK_OK(NotConditionHelper(*c, value));
212 out = std::move(c);
213 return ReadFileResult::READ_OK;
214 }
215 }
216
217 return ReadFileResult::INVALID_CONDITION;
218 }
219
PresetConditionHelper(std::shared_ptr<cmCMakePresetsFile::Condition> & out,const Json::Value * value)220 ReadFileResult PresetConditionHelper(
221 std::shared_ptr<cmCMakePresetsFile::Condition>& out,
222 const Json::Value* value)
223 {
224 std::unique_ptr<cmCMakePresetsFile::Condition> ptr;
225 auto result = ConditionHelper(ptr, value);
226 out = std::move(ptr);
227 return result;
228 }
229
SubConditionHelper(std::unique_ptr<cmCMakePresetsFile::Condition> & out,const Json::Value * value)230 ReadFileResult SubConditionHelper(
231 std::unique_ptr<cmCMakePresetsFile::Condition>& out,
232 const Json::Value* value)
233 {
234 std::unique_ptr<cmCMakePresetsFile::Condition> ptr;
235 auto result = ConditionHelper(ptr, value);
236 if (ptr && ptr->IsNull()) {
237 return ReadFileResult::INVALID_CONDITION;
238 }
239 out = std::move(ptr);
240 return result;
241 }
242
VendorHelper(ReadFileResult error)243 cmJSONHelper<std::nullptr_t, ReadFileResult> VendorHelper(ReadFileResult error)
244 {
245 return [error](std::nullptr_t& /*out*/,
246 const Json::Value* value) -> ReadFileResult {
247 if (!value) {
248 return ReadFileResult::READ_OK;
249 }
250
251 if (!value->isObject()) {
252 return error;
253 }
254
255 return ReadFileResult::READ_OK;
256 };
257 }
258
259 auto const VersionIntHelper = cmJSONIntHelper<ReadFileResult>(
260 ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
261
262 auto const VersionHelper = cmJSONRequiredHelper<int, ReadFileResult>(
263 ReadFileResult::NO_VERSION, VersionIntHelper);
264
265 auto const RootVersionHelper =
266 cmJSONObjectHelper<int, ReadFileResult>(ReadFileResult::READ_OK,
267 ReadFileResult::INVALID_ROOT)
268 .Bind("version"_s, VersionHelper, false);
269
270 auto const VariableStringHelper = cmJSONStringHelper<ReadFileResult>(
271 ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE);
272
VariableValueHelper(std::string & out,const Json::Value * value)273 ReadFileResult VariableValueHelper(std::string& out, const Json::Value* value)
274 {
275 if (!value) {
276 out.clear();
277 return ReadFileResult::READ_OK;
278 }
279
280 if (value->isBool()) {
281 out = value->asBool() ? "TRUE" : "FALSE";
282 return ReadFileResult::READ_OK;
283 }
284
285 return VariableStringHelper(out, value);
286 }
287
288 auto const VariableObjectHelper =
289 cmJSONObjectHelper<CacheVariable, ReadFileResult>(
290 ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE, false)
291 .Bind("type"_s, &CacheVariable::Type, VariableStringHelper, false)
292 .Bind("value"_s, &CacheVariable::Value, VariableValueHelper);
293
VariableHelper(cm::optional<CacheVariable> & out,const Json::Value * value)294 ReadFileResult VariableHelper(cm::optional<CacheVariable>& out,
295 const Json::Value* value)
296 {
297 if (value->isBool()) {
298 out = CacheVariable{
299 /*Type=*/"BOOL",
300 /*Value=*/value->asBool() ? "TRUE" : "FALSE",
301 };
302 return ReadFileResult::READ_OK;
303 }
304 if (value->isString()) {
305 out = CacheVariable{
306 /*Type=*/"",
307 /*Value=*/value->asString(),
308 };
309 return ReadFileResult::READ_OK;
310 }
311 if (value->isObject()) {
312 out.emplace();
313 return VariableObjectHelper(*out, value);
314 }
315 if (value->isNull()) {
316 out = cm::nullopt;
317 return ReadFileResult::READ_OK;
318 }
319 return ReadFileResult::INVALID_VARIABLE;
320 }
321
322 auto const VariablesHelper =
323 cmJSONMapHelper<cm::optional<CacheVariable>, ReadFileResult>(
324 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, VariableHelper);
325
326 auto const PresetStringHelper = cmJSONStringHelper<ReadFileResult>(
327 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
328
EnvironmentHelper(cm::optional<std::string> & out,const Json::Value * value)329 ReadFileResult EnvironmentHelper(cm::optional<std::string>& out,
330 const Json::Value* value)
331 {
332 if (!value || value->isNull()) {
333 out = cm::nullopt;
334 return ReadFileResult::READ_OK;
335 }
336 if (value->isString()) {
337 out = value->asString();
338 return ReadFileResult::READ_OK;
339 }
340 return ReadFileResult::INVALID_PRESET;
341 }
342
343 auto const EnvironmentMapHelper =
344 cmJSONMapHelper<cm::optional<std::string>, ReadFileResult>(
345 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
346 EnvironmentHelper);
347
348 auto const PresetVectorStringHelper =
349 cmJSONVectorHelper<std::string, ReadFileResult>(
350 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
351 PresetStringHelper);
352
PresetVectorOneOrMoreStringHelper(std::vector<std::string> & out,const Json::Value * value)353 ReadFileResult PresetVectorOneOrMoreStringHelper(std::vector<std::string>& out,
354 const Json::Value* value)
355 {
356 out.clear();
357 if (!value) {
358 return ReadFileResult::READ_OK;
359 }
360
361 if (value->isString()) {
362 out.push_back(value->asString());
363 return ReadFileResult::READ_OK;
364 }
365
366 return PresetVectorStringHelper(out, value);
367 }
368
369 auto const PresetBoolHelper = cmJSONBoolHelper<ReadFileResult>(
370 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
371
372 auto const PresetOptionalBoolHelper =
373 cmJSONOptionalHelper<bool, ReadFileResult>(ReadFileResult::READ_OK,
374 PresetBoolHelper);
375
376 auto const PresetIntHelper = cmJSONIntHelper<ReadFileResult>(
377 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
378
379 auto const PresetOptionalIntHelper = cmJSONOptionalHelper<int, ReadFileResult>(
380 ReadFileResult::READ_OK, PresetIntHelper);
381
382 auto const PresetVectorIntHelper = cmJSONVectorHelper<int, ReadFileResult>(
383 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, PresetIntHelper);
384
385 auto const PresetWarningsHelper =
386 cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
387 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
388 .Bind("dev"_s, &ConfigurePreset::WarnDev, PresetOptionalBoolHelper, false)
389 .Bind("deprecated"_s, &ConfigurePreset::WarnDeprecated,
390 PresetOptionalBoolHelper, false)
391 .Bind("uninitialized"_s, &ConfigurePreset::WarnUninitialized,
392 PresetOptionalBoolHelper, false)
393 .Bind("unusedCli"_s, &ConfigurePreset::WarnUnusedCli,
394 PresetOptionalBoolHelper, false)
395 .Bind("systemVars"_s, &ConfigurePreset::WarnSystemVars,
396 PresetOptionalBoolHelper, false);
397
398 auto const PresetErrorsHelper =
399 cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
400 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
401 .Bind("dev"_s, &ConfigurePreset::ErrorDev, PresetOptionalBoolHelper, false)
402 .Bind("deprecated"_s, &ConfigurePreset::ErrorDeprecated,
403 PresetOptionalBoolHelper, false);
404
405 auto const PresetDebugHelper =
406 cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
407 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
408 .Bind("output"_s, &ConfigurePreset::DebugOutput, PresetOptionalBoolHelper,
409 false)
410 .Bind("tryCompile"_s, &ConfigurePreset::DebugTryCompile,
411 PresetOptionalBoolHelper, false)
412 .Bind("find"_s, &ConfigurePreset::DebugFind, PresetOptionalBoolHelper,
413 false);
414
ArchToolsetStrategyHelper(cm::optional<ArchToolsetStrategy> & out,const Json::Value * value)415 ReadFileResult ArchToolsetStrategyHelper(
416 cm::optional<ArchToolsetStrategy>& out, const Json::Value* value)
417 {
418 if (!value) {
419 out = cm::nullopt;
420 return ReadFileResult::READ_OK;
421 }
422
423 if (!value->isString()) {
424 return ReadFileResult::INVALID_PRESET;
425 }
426
427 if (value->asString() == "set") {
428 out = ArchToolsetStrategy::Set;
429 return ReadFileResult::READ_OK;
430 }
431
432 if (value->asString() == "external") {
433 out = ArchToolsetStrategy::External;
434 return ReadFileResult::READ_OK;
435 }
436
437 return ReadFileResult::INVALID_PRESET;
438 }
439
440 std::function<ReadFileResult(ConfigurePreset&, const Json::Value*)>
ArchToolsetHelper(std::string ConfigurePreset::* valueField,cm::optional<ArchToolsetStrategy> ConfigurePreset::* strategyField)441 ArchToolsetHelper(
442 std::string ConfigurePreset::*valueField,
443 cm::optional<ArchToolsetStrategy> ConfigurePreset::*strategyField)
444 {
445 auto const objectHelper =
446 cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
447 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
448 .Bind("value", valueField, PresetStringHelper, false)
449 .Bind("strategy", strategyField, ArchToolsetStrategyHelper, false);
450 return [valueField, strategyField, objectHelper](
451 ConfigurePreset& out, const Json::Value* value) -> ReadFileResult {
452 if (!value) {
453 (out.*valueField).clear();
454 out.*strategyField = cm::nullopt;
455 return ReadFileResult::READ_OK;
456 }
457
458 if (value->isString()) {
459 out.*valueField = value->asString();
460 out.*strategyField = cm::nullopt;
461 return ReadFileResult::READ_OK;
462 }
463
464 if (value->isObject()) {
465 return objectHelper(out, value);
466 }
467
468 return ReadFileResult::INVALID_PRESET;
469 };
470 }
471
472 auto const ArchitectureHelper = ArchToolsetHelper(
473 &ConfigurePreset::Architecture, &ConfigurePreset::ArchitectureStrategy);
474 auto const ToolsetHelper = ArchToolsetHelper(
475 &ConfigurePreset::Toolset, &ConfigurePreset::ToolsetStrategy);
476
477 auto const ConfigurePresetHelper =
478 cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
479 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
480 .Bind("name"_s, &ConfigurePreset::Name, PresetStringHelper)
481 .Bind("inherits"_s, &ConfigurePreset::Inherits,
482 PresetVectorOneOrMoreStringHelper, false)
483 .Bind("hidden"_s, &ConfigurePreset::Hidden, PresetBoolHelper, false)
484 .Bind<std::nullptr_t>("vendor"_s, nullptr,
485 VendorHelper(ReadFileResult::INVALID_PRESET), false)
486 .Bind("displayName"_s, &ConfigurePreset::DisplayName, PresetStringHelper,
487 false)
488 .Bind("description"_s, &ConfigurePreset::Description, PresetStringHelper,
489 false)
490 .Bind("generator"_s, &ConfigurePreset::Generator, PresetStringHelper,
491 false)
492 .Bind("architecture"_s, ArchitectureHelper, false)
493 .Bind("toolset"_s, ToolsetHelper, false)
494 .Bind("toolchainFile"_s, &ConfigurePreset::ToolchainFile,
495 PresetStringHelper, false)
496 .Bind("binaryDir"_s, &ConfigurePreset::BinaryDir, PresetStringHelper,
497 false)
498 .Bind("installDir"_s, &ConfigurePreset::InstallDir, PresetStringHelper,
499 false)
500 .Bind<std::string>("cmakeExecutable"_s, nullptr, PresetStringHelper, false)
501 .Bind("cacheVariables"_s, &ConfigurePreset::CacheVariables,
502 VariablesHelper, false)
503 .Bind("environment"_s, &ConfigurePreset::Environment, EnvironmentMapHelper,
504 false)
505 .Bind("warnings"_s, PresetWarningsHelper, false)
506 .Bind("errors"_s, PresetErrorsHelper, false)
507 .Bind("debug"_s, PresetDebugHelper, false)
508 .Bind("condition"_s, &ConfigurePreset::ConditionEvaluator,
509 PresetConditionHelper, false);
510
511 auto const BuildPresetHelper =
512 cmJSONObjectHelper<BuildPreset, ReadFileResult>(
513 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
514 .Bind("name"_s, &BuildPreset::Name, PresetStringHelper)
515 .Bind("inherits"_s, &BuildPreset::Inherits,
516 PresetVectorOneOrMoreStringHelper, false)
517 .Bind("hidden"_s, &BuildPreset::Hidden, PresetBoolHelper, false)
518 .Bind<std::nullptr_t>("vendor"_s, nullptr,
519 VendorHelper(ReadFileResult::INVALID_PRESET), false)
520 .Bind("displayName"_s, &BuildPreset::DisplayName, PresetStringHelper,
521 false)
522 .Bind("description"_s, &BuildPreset::Description, PresetStringHelper,
523 false)
524 .Bind("environment"_s, &BuildPreset::Environment, EnvironmentMapHelper,
525 false)
526 .Bind("configurePreset"_s, &BuildPreset::ConfigurePreset,
527 PresetStringHelper, false)
528 .Bind("inheritConfigureEnvironment"_s,
529 &BuildPreset::InheritConfigureEnvironment, PresetOptionalBoolHelper,
530 false)
531 .Bind("jobs"_s, &BuildPreset::Jobs, PresetOptionalIntHelper, false)
532 .Bind("targets"_s, &BuildPreset::Targets,
533 PresetVectorOneOrMoreStringHelper, false)
534 .Bind("configuration"_s, &BuildPreset::Configuration, PresetStringHelper,
535 false)
536 .Bind("cleanFirst"_s, &BuildPreset::CleanFirst, PresetOptionalBoolHelper,
537 false)
538 .Bind("verbose"_s, &BuildPreset::Verbose, PresetOptionalBoolHelper, false)
539 .Bind("nativeToolOptions"_s, &BuildPreset::NativeToolOptions,
540 PresetVectorStringHelper, false)
541 .Bind("condition"_s, &BuildPreset::ConditionEvaluator,
542 PresetConditionHelper, false);
543
TestPresetOutputVerbosityHelper(TestPreset::OutputOptions::VerbosityEnum & out,const Json::Value * value)544 ReadFileResult TestPresetOutputVerbosityHelper(
545 TestPreset::OutputOptions::VerbosityEnum& out, const Json::Value* value)
546 {
547 if (!value) {
548 out = TestPreset::OutputOptions::VerbosityEnum::Default;
549 return ReadFileResult::READ_OK;
550 }
551
552 if (!value->isString()) {
553 return ReadFileResult::INVALID_PRESET;
554 }
555
556 if (value->asString() == "default") {
557 out = TestPreset::OutputOptions::VerbosityEnum::Default;
558 return ReadFileResult::READ_OK;
559 }
560
561 if (value->asString() == "verbose") {
562 out = TestPreset::OutputOptions::VerbosityEnum::Verbose;
563 return ReadFileResult::READ_OK;
564 }
565
566 if (value->asString() == "extra") {
567 out = TestPreset::OutputOptions::VerbosityEnum::Extra;
568 return ReadFileResult::READ_OK;
569 }
570
571 return ReadFileResult::INVALID_PRESET;
572 }
573
574 auto const TestPresetOptionalOutputVerbosityHelper =
575 cmJSONOptionalHelper<TestPreset::OutputOptions::VerbosityEnum,
576 ReadFileResult>(ReadFileResult::READ_OK,
577 TestPresetOutputVerbosityHelper);
578
579 auto const TestPresetOptionalOutputHelper =
580 cmJSONOptionalHelper<TestPreset::OutputOptions, ReadFileResult>(
581 ReadFileResult::READ_OK,
582 cmJSONObjectHelper<TestPreset::OutputOptions, ReadFileResult>(
583 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
584 .Bind("shortProgress"_s, &TestPreset::OutputOptions::ShortProgress,
585 PresetOptionalBoolHelper, false)
586 .Bind("verbosity"_s, &TestPreset::OutputOptions::Verbosity,
587 TestPresetOptionalOutputVerbosityHelper, false)
588 .Bind("debug"_s, &TestPreset::OutputOptions::Debug,
589 PresetOptionalBoolHelper, false)
590 .Bind("outputOnFailure"_s, &TestPreset::OutputOptions::OutputOnFailure,
591 PresetOptionalBoolHelper, false)
592 .Bind("quiet"_s, &TestPreset::OutputOptions::Quiet,
593 PresetOptionalBoolHelper, false)
594 .Bind("outputLogFile"_s, &TestPreset::OutputOptions::OutputLogFile,
595 PresetStringHelper, false)
596 .Bind("labelSummary"_s, &TestPreset::OutputOptions::LabelSummary,
597 PresetOptionalBoolHelper, false)
598 .Bind("subprojectSummary"_s,
599 &TestPreset::OutputOptions::SubprojectSummary,
600 PresetOptionalBoolHelper, false)
601 .Bind("maxPassedTestOutputSize"_s,
602 &TestPreset::OutputOptions::MaxPassedTestOutputSize,
603 PresetOptionalIntHelper, false)
604 .Bind("maxFailedTestOutputSize"_s,
605 &TestPreset::OutputOptions::MaxFailedTestOutputSize,
606 PresetOptionalIntHelper, false)
607 .Bind("maxTestNameWidth"_s, &TestPreset::OutputOptions::MaxTestNameWidth,
608 PresetOptionalIntHelper, false));
609
610 auto const TestPresetOptionalFilterIncludeIndexObjectHelper =
611 cmJSONOptionalHelper<TestPreset::IncludeOptions::IndexOptions,
612 ReadFileResult>(
613 ReadFileResult::READ_OK,
614 cmJSONObjectHelper<TestPreset::IncludeOptions::IndexOptions,
615 ReadFileResult>(ReadFileResult::READ_OK,
616 ReadFileResult::INVALID_PRESET)
617 .Bind("start"_s, &TestPreset::IncludeOptions::IndexOptions::Start,
618 PresetOptionalIntHelper, false)
619 .Bind("end"_s, &TestPreset::IncludeOptions::IndexOptions::End,
620 PresetOptionalIntHelper, false)
621 .Bind("stride"_s, &TestPreset::IncludeOptions::IndexOptions::Stride,
622 PresetOptionalIntHelper, false)
623 .Bind("specificTests"_s,
624 &TestPreset::IncludeOptions::IndexOptions::SpecificTests,
625 PresetVectorIntHelper, false));
626
TestPresetOptionalFilterIncludeIndexHelper(cm::optional<TestPreset::IncludeOptions::IndexOptions> & out,const Json::Value * value)627 ReadFileResult TestPresetOptionalFilterIncludeIndexHelper(
628 cm::optional<TestPreset::IncludeOptions::IndexOptions>& out,
629 const Json::Value* value)
630 {
631 if (!value) {
632 out = cm::nullopt;
633 return ReadFileResult::READ_OK;
634 }
635
636 if (value->isString()) {
637 out.emplace();
638 out->IndexFile = value->asString();
639 return ReadFileResult::READ_OK;
640 }
641
642 if (value->isObject()) {
643 return TestPresetOptionalFilterIncludeIndexObjectHelper(out, value);
644 }
645
646 return ReadFileResult::INVALID_PRESET;
647 }
648
649 auto const TestPresetOptionalFilterIncludeHelper =
650 cmJSONOptionalHelper<TestPreset::IncludeOptions, ReadFileResult>(
651 ReadFileResult::READ_OK,
652 cmJSONObjectHelper<TestPreset::IncludeOptions, ReadFileResult>(
653 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
654 .Bind("name"_s, &TestPreset::IncludeOptions::Name, PresetStringHelper,
655 false)
656 .Bind("label"_s, &TestPreset::IncludeOptions::Label, PresetStringHelper,
657 false)
658 .Bind("index"_s, &TestPreset::IncludeOptions::Index,
659 TestPresetOptionalFilterIncludeIndexHelper, false)
660 .Bind("useUnion"_s, &TestPreset::IncludeOptions::UseUnion,
661 PresetOptionalBoolHelper, false));
662
663 auto const TestPresetOptionalFilterExcludeFixturesHelper =
664 cmJSONOptionalHelper<TestPreset::ExcludeOptions::FixturesOptions,
665 ReadFileResult>(
666 ReadFileResult::READ_OK,
667 cmJSONObjectHelper<TestPreset::ExcludeOptions::FixturesOptions,
668 ReadFileResult>(ReadFileResult::READ_OK,
669 ReadFileResult::INVALID_PRESET)
670 .Bind("any"_s, &TestPreset::ExcludeOptions::FixturesOptions::Any,
671 PresetStringHelper, false)
672 .Bind("setup"_s, &TestPreset::ExcludeOptions::FixturesOptions::Setup,
673 PresetStringHelper, false)
674 .Bind("cleanup"_s, &TestPreset::ExcludeOptions::FixturesOptions::Cleanup,
675 PresetStringHelper, false));
676
677 auto const TestPresetOptionalFilterExcludeHelper =
678 cmJSONOptionalHelper<TestPreset::ExcludeOptions, ReadFileResult>(
679 ReadFileResult::READ_OK,
680 cmJSONObjectHelper<TestPreset::ExcludeOptions, ReadFileResult>(
681 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
682 .Bind("name"_s, &TestPreset::ExcludeOptions::Name, PresetStringHelper,
683 false)
684 .Bind("label"_s, &TestPreset::ExcludeOptions::Label, PresetStringHelper,
685 false)
686 .Bind("fixtures"_s, &TestPreset::ExcludeOptions::Fixtures,
687 TestPresetOptionalFilterExcludeFixturesHelper, false));
688
TestPresetExecutionShowOnlyHelper(TestPreset::ExecutionOptions::ShowOnlyEnum & out,const Json::Value * value)689 ReadFileResult TestPresetExecutionShowOnlyHelper(
690 TestPreset::ExecutionOptions::ShowOnlyEnum& out, const Json::Value* value)
691 {
692 if (!value || !value->isString()) {
693 return ReadFileResult::INVALID_PRESET;
694 }
695
696 if (value->asString() == "human") {
697 out = TestPreset::ExecutionOptions::ShowOnlyEnum::Human;
698 return ReadFileResult::READ_OK;
699 }
700
701 if (value->asString() == "json-v1") {
702 out = TestPreset::ExecutionOptions::ShowOnlyEnum::JsonV1;
703 return ReadFileResult::READ_OK;
704 }
705
706 return ReadFileResult::INVALID_PRESET;
707 }
708
709 auto const TestPresetOptionalExecutionShowOnlyHelper =
710 cmJSONOptionalHelper<TestPreset::ExecutionOptions::ShowOnlyEnum,
711 ReadFileResult>(ReadFileResult::READ_OK,
712 TestPresetExecutionShowOnlyHelper);
713
TestPresetExecutionModeHelper(TestPreset::ExecutionOptions::RepeatOptions::ModeEnum & out,const Json::Value * value)714 ReadFileResult TestPresetExecutionModeHelper(
715 TestPreset::ExecutionOptions::RepeatOptions::ModeEnum& out,
716 const Json::Value* value)
717 {
718 if (!value) {
719 return ReadFileResult::READ_OK;
720 }
721
722 if (!value->isString()) {
723 return ReadFileResult::INVALID_PRESET;
724 }
725
726 if (value->asString() == "until-fail") {
727 out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::UntilFail;
728 return ReadFileResult::READ_OK;
729 }
730
731 if (value->asString() == "until-pass") {
732 out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::UntilPass;
733 return ReadFileResult::READ_OK;
734 }
735
736 if (value->asString() == "after-timeout") {
737 out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::AfterTimeout;
738 return ReadFileResult::READ_OK;
739 }
740
741 return ReadFileResult::INVALID_PRESET;
742 }
743
744 auto const TestPresetOptionalExecutionRepeatHelper =
745 cmJSONOptionalHelper<TestPreset::ExecutionOptions::RepeatOptions,
746 ReadFileResult>(
747 ReadFileResult::READ_OK,
748 cmJSONObjectHelper<TestPreset::ExecutionOptions::RepeatOptions,
749 ReadFileResult>(ReadFileResult::READ_OK,
750 ReadFileResult::INVALID_PRESET)
751 .Bind("mode"_s, &TestPreset::ExecutionOptions::RepeatOptions::Mode,
752 TestPresetExecutionModeHelper, true)
753 .Bind("count"_s, &TestPreset::ExecutionOptions::RepeatOptions::Count,
754 PresetIntHelper, true));
755
TestPresetExecutionNoTestsActionHelper(TestPreset::ExecutionOptions::NoTestsActionEnum & out,const Json::Value * value)756 ReadFileResult TestPresetExecutionNoTestsActionHelper(
757 TestPreset::ExecutionOptions::NoTestsActionEnum& out,
758 const Json::Value* value)
759 {
760 if (!value) {
761 out = TestPreset::ExecutionOptions::NoTestsActionEnum::Default;
762 return ReadFileResult::READ_OK;
763 }
764
765 if (!value->isString()) {
766 return ReadFileResult::INVALID_PRESET;
767 }
768
769 if (value->asString() == "default") {
770 out = TestPreset::ExecutionOptions::NoTestsActionEnum::Default;
771 return ReadFileResult::READ_OK;
772 }
773
774 if (value->asString() == "error") {
775 out = TestPreset::ExecutionOptions::NoTestsActionEnum::Error;
776 return ReadFileResult::READ_OK;
777 }
778
779 if (value->asString() == "ignore") {
780 out = TestPreset::ExecutionOptions::NoTestsActionEnum::Ignore;
781 return ReadFileResult::READ_OK;
782 }
783
784 return ReadFileResult::INVALID_PRESET;
785 }
786
787 auto const TestPresetOptionalExecutionNoTestsActionHelper =
788 cmJSONOptionalHelper<TestPreset::ExecutionOptions::NoTestsActionEnum,
789 ReadFileResult>(ReadFileResult::READ_OK,
790 TestPresetExecutionNoTestsActionHelper);
791
792 auto const TestPresetExecutionHelper =
793 cmJSONOptionalHelper<TestPreset::ExecutionOptions, ReadFileResult>(
794 ReadFileResult::READ_OK,
795 cmJSONObjectHelper<TestPreset::ExecutionOptions, ReadFileResult>(
796 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
797 .Bind("stopOnFailure"_s, &TestPreset::ExecutionOptions::StopOnFailure,
798 PresetOptionalBoolHelper, false)
799 .Bind("enableFailover"_s, &TestPreset::ExecutionOptions::EnableFailover,
800 PresetOptionalBoolHelper, false)
801 .Bind("jobs"_s, &TestPreset::ExecutionOptions::Jobs,
802 PresetOptionalIntHelper, false)
803 .Bind("resourceSpecFile"_s,
804 &TestPreset::ExecutionOptions::ResourceSpecFile,
805 PresetStringHelper, false)
806 .Bind("testLoad"_s, &TestPreset::ExecutionOptions::TestLoad,
807 PresetOptionalIntHelper, false)
808 .Bind("showOnly"_s, &TestPreset::ExecutionOptions::ShowOnly,
809 TestPresetOptionalExecutionShowOnlyHelper, false)
810 .Bind("repeat"_s, &TestPreset::ExecutionOptions::Repeat,
811 TestPresetOptionalExecutionRepeatHelper, false)
812 .Bind("interactiveDebugging"_s,
813 &TestPreset::ExecutionOptions::InteractiveDebugging,
814 PresetOptionalBoolHelper, false)
815 .Bind("scheduleRandom"_s, &TestPreset::ExecutionOptions::ScheduleRandom,
816 PresetOptionalBoolHelper, false)
817 .Bind("timeout"_s, &TestPreset::ExecutionOptions::Timeout,
818 PresetOptionalIntHelper, false)
819 .Bind("noTestsAction"_s, &TestPreset::ExecutionOptions::NoTestsAction,
820 TestPresetOptionalExecutionNoTestsActionHelper, false));
821
822 auto const TestPresetFilterHelper =
823 cmJSONOptionalHelper<TestPreset::FilterOptions, ReadFileResult>(
824 ReadFileResult::READ_OK,
825 cmJSONObjectHelper<TestPreset::FilterOptions, ReadFileResult>(
826 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
827 .Bind("include"_s, &TestPreset::FilterOptions::Include,
828 TestPresetOptionalFilterIncludeHelper, false)
829 .Bind("exclude"_s, &TestPreset::FilterOptions::Exclude,
830 TestPresetOptionalFilterExcludeHelper, false));
831
832 auto const TestPresetHelper =
833 cmJSONObjectHelper<TestPreset, ReadFileResult>(
834 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
835 .Bind("name"_s, &TestPreset::Name, PresetStringHelper)
836 .Bind("inherits"_s, &TestPreset::Inherits,
837 PresetVectorOneOrMoreStringHelper, false)
838 .Bind("hidden"_s, &TestPreset::Hidden, PresetBoolHelper, false)
839 .Bind<std::nullptr_t>("vendor"_s, nullptr,
840 VendorHelper(ReadFileResult::INVALID_PRESET), false)
841 .Bind("displayName"_s, &TestPreset::DisplayName, PresetStringHelper, false)
842 .Bind("description"_s, &TestPreset::Description, PresetStringHelper, false)
843 .Bind("environment"_s, &TestPreset::Environment, EnvironmentMapHelper,
844 false)
845 .Bind("configurePreset"_s, &TestPreset::ConfigurePreset,
846 PresetStringHelper, false)
847 .Bind("inheritConfigureEnvironment"_s,
848 &TestPreset::InheritConfigureEnvironment, PresetOptionalBoolHelper,
849 false)
850 .Bind("configuration"_s, &TestPreset::Configuration, PresetStringHelper,
851 false)
852 .Bind("overwriteConfigurationFile"_s,
853 &TestPreset::OverwriteConfigurationFile, PresetVectorStringHelper,
854 false)
855 .Bind("output"_s, &TestPreset::Output, TestPresetOptionalOutputHelper,
856 false)
857 .Bind("filter"_s, &TestPreset::Filter, TestPresetFilterHelper, false)
858 .Bind("execution"_s, &TestPreset::Execution, TestPresetExecutionHelper,
859 false)
860 .Bind("condition"_s, &TestPreset::ConditionEvaluator,
861 PresetConditionHelper, false);
862
863 auto const ConfigurePresetsHelper =
864 cmJSONVectorHelper<ConfigurePreset, ReadFileResult>(
865 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
866 ConfigurePresetHelper);
867
868 auto const BuildPresetsHelper =
869 cmJSONVectorHelper<BuildPreset, ReadFileResult>(
870 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
871 BuildPresetHelper);
872
873 auto const TestPresetsHelper = cmJSONVectorHelper<TestPreset, ReadFileResult>(
874 ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS, TestPresetHelper);
875
876 auto const CMakeVersionUIntHelper = cmJSONUIntHelper<ReadFileResult>(
877 ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
878
879 auto const CMakeVersionHelper =
880 cmJSONObjectHelper<CMakeVersion, ReadFileResult>(
881 ReadFileResult::READ_OK, ReadFileResult::INVALID_CMAKE_VERSION, false)
882 .Bind("major"_s, &CMakeVersion::Major, CMakeVersionUIntHelper, false)
883 .Bind("minor"_s, &CMakeVersion::Minor, CMakeVersionUIntHelper, false)
884 .Bind("patch"_s, &CMakeVersion::Patch, CMakeVersionUIntHelper, false);
885
886 auto const RootPresetsHelper =
887 cmJSONObjectHelper<RootPresets, ReadFileResult>(
888 ReadFileResult::READ_OK, ReadFileResult::INVALID_ROOT, false)
889 .Bind<int>("version"_s, nullptr, VersionHelper)
890 .Bind("configurePresets"_s, &RootPresets::ConfigurePresets,
891 ConfigurePresetsHelper, false)
892 .Bind("buildPresets"_s, &RootPresets::BuildPresets, BuildPresetsHelper,
893 false)
894 .Bind("testPresets"_s, &RootPresets::TestPresets, TestPresetsHelper, false)
895 .Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired,
896 CMakeVersionHelper, false)
897 .Bind<std::nullptr_t>("vendor"_s, nullptr,
898 VendorHelper(ReadFileResult::INVALID_ROOT), false);
899 }
900
ReadJSONFile(const std::string & filename,bool user)901 cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile(
902 const std::string& filename, bool user)
903 {
904 cmsys::ifstream fin(filename.c_str());
905 if (!fin) {
906 return ReadFileResult::FILE_NOT_FOUND;
907 }
908 // If there's a BOM, toss it.
909 cmsys::FStream::ReadBOM(fin);
910
911 Json::Value root;
912 Json::CharReaderBuilder builder;
913 Json::CharReaderBuilder::strictMode(&builder.settings_);
914 if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
915 return ReadFileResult::JSON_PARSE_ERROR;
916 }
917
918 int v = 0;
919 auto result = RootVersionHelper(v, &root);
920 if (result != ReadFileResult::READ_OK) {
921 return result;
922 }
923 if (v < MIN_VERSION || v > MAX_VERSION) {
924 return ReadFileResult::UNRECOGNIZED_VERSION;
925 }
926 if (user) {
927 this->UserVersion = v;
928 } else {
929 this->Version = v;
930 }
931
932 // Support for build and test presets added in version 2.
933 if (v < 2 &&
934 (root.isMember("buildPresets") || root.isMember("testPresets"))) {
935 return ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED;
936 }
937
938 RootPresets presets;
939 if ((result = RootPresetsHelper(presets, &root)) !=
940 ReadFileResult::READ_OK) {
941 return result;
942 }
943
944 unsigned int currentMajor = cmVersion::GetMajorVersion();
945 unsigned int currentMinor = cmVersion::GetMinorVersion();
946 unsigned int currentPatch = cmVersion::GetPatchVersion();
947 auto const& required = presets.CMakeMinimumRequired;
948 if (required.Major > currentMajor ||
949 (required.Major == currentMajor &&
950 (required.Minor > currentMinor ||
951 (required.Minor == currentMinor &&
952 (required.Patch > currentPatch))))) {
953 return ReadFileResult::UNRECOGNIZED_CMAKE_VERSION;
954 }
955
956 for (auto& preset : presets.ConfigurePresets) {
957 preset.User = user;
958 if (preset.Name.empty()) {
959 return ReadFileResult::INVALID_PRESET;
960 }
961
962 PresetPair<ConfigurePreset> presetPair;
963 presetPair.Unexpanded = preset;
964 presetPair.Expanded = cm::nullopt;
965 if (!this->ConfigurePresets
966 .emplace(std::make_pair(preset.Name, presetPair))
967 .second) {
968 return ReadFileResult::DUPLICATE_PRESETS;
969 }
970
971 // Support for installDir presets added in version 3.
972 if (v < 3 && !preset.InstallDir.empty()) {
973 return ReadFileResult::INSTALL_PREFIX_UNSUPPORTED;
974 }
975
976 // Support for conditions added in version 3.
977 if (v < 3 && preset.ConditionEvaluator) {
978 return ReadFileResult::CONDITION_UNSUPPORTED;
979 }
980
981 // Support for toolchainFile presets added in version 3.
982 if (v < 3 && !preset.ToolchainFile.empty()) {
983 return ReadFileResult::TOOLCHAIN_FILE_UNSUPPORTED;
984 }
985
986 this->ConfigurePresetOrder.push_back(preset.Name);
987 }
988
989 for (auto& preset : presets.BuildPresets) {
990 preset.User = user;
991 if (preset.Name.empty()) {
992 return ReadFileResult::INVALID_PRESET;
993 }
994
995 PresetPair<BuildPreset> presetPair;
996 presetPair.Unexpanded = preset;
997 presetPair.Expanded = cm::nullopt;
998 if (!this->BuildPresets.emplace(preset.Name, presetPair).second) {
999 return ReadFileResult::DUPLICATE_PRESETS;
1000 }
1001
1002 // Support for conditions added in version 3.
1003 if (v < 3 && preset.ConditionEvaluator) {
1004 return ReadFileResult::CONDITION_UNSUPPORTED;
1005 }
1006
1007 this->BuildPresetOrder.push_back(preset.Name);
1008 }
1009
1010 for (auto& preset : presets.TestPresets) {
1011 preset.User = user;
1012 if (preset.Name.empty()) {
1013 return ReadFileResult::INVALID_PRESET;
1014 }
1015
1016 PresetPair<TestPreset> presetPair;
1017 presetPair.Unexpanded = preset;
1018 presetPair.Expanded = cm::nullopt;
1019 if (!this->TestPresets.emplace(preset.Name, presetPair).second) {
1020 return ReadFileResult::DUPLICATE_PRESETS;
1021 }
1022
1023 // Support for conditions added in version 3.
1024 if (v < 3 && preset.ConditionEvaluator) {
1025 return ReadFileResult::CONDITION_UNSUPPORTED;
1026 }
1027
1028 this->TestPresetOrder.push_back(preset.Name);
1029 }
1030
1031 return ReadFileResult::READ_OK;
1032 }
1033