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