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 "cmCMakePresetsGraph.h"
13 #include <cm/string_view>
15 #include "cmsys/RegularExpression.hxx"
17 #include "cmCMakePresetsGraphInternal.h"
18 #include "cmStringAlgorithms.h"
19 #include "cmSystemTools.h"
21 #define CHECK_EXPAND(out, field, expanders, version) \
23 switch (ExpandMacros(field, expanders, version)) { \
24 case ExpandMacroResult::Error: \
26 case ExpandMacroResult::Ignore: \
29 case ExpandMacroResult::Ok: \
35 enum class CycleStatus
42 using ReadFileResult = cmCMakePresetsGraph::ReadFileResult;
43 using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
44 using BuildPreset = cmCMakePresetsGraph::BuildPreset;
45 using TestPreset = cmCMakePresetsGraph::TestPreset;
46 using ExpandMacroResult = cmCMakePresetsGraphInternal::ExpandMacroResult;
47 using MacroExpander = cmCMakePresetsGraphInternal::MacroExpander;
49 void InheritString(std::string& child, const std::string& parent)
57 void InheritOptionalValue(cm::optional<T>& child,
58 const cm::optional<T>& parent)
66 void InheritVector(std::vector<T>& child, const std::vector<T>& parent)
74 * Check preset inheritance for cycles (using a DAG check algorithm) while
75 * also bubbling up fields through the inheritance hierarchy, then verify
76 * that each preset has the required fields, either directly or through
80 ReadFileResult VisitPreset(
82 std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets,
83 std::map<std::string, CycleStatus> cycleStatus,
84 const cmCMakePresetsGraph& graph)
86 switch (cycleStatus[preset.Name]) {
87 case CycleStatus::InProgress:
88 return ReadFileResult::CYCLIC_PRESET_INHERITANCE;
89 case CycleStatus::Verified:
90 return ReadFileResult::READ_OK;
95 cycleStatus[preset.Name] = CycleStatus::InProgress;
97 if (preset.Environment.count("") != 0) {
98 return ReadFileResult::INVALID_PRESET;
101 CHECK_OK(preset.VisitPresetBeforeInherit());
103 for (auto const& i : preset.Inherits) {
104 auto parent = presets.find(i);
105 if (parent == presets.end()) {
106 return ReadFileResult::INVALID_PRESET;
109 auto& parentPreset = parent->second.Unexpanded;
110 if (!preset.OriginFile->ReachableFiles.count(parentPreset.OriginFile)) {
111 return ReadFileResult::INHERITED_PRESET_UNREACHABLE_FROM_FILE;
114 auto result = VisitPreset(parentPreset, presets, cycleStatus, graph);
115 if (result != ReadFileResult::READ_OK) {
119 CHECK_OK(preset.VisitPresetInherit(parentPreset));
121 for (auto const& v : parentPreset.Environment) {
122 preset.Environment.insert(v);
125 if (!preset.ConditionEvaluator) {
126 preset.ConditionEvaluator = parentPreset.ConditionEvaluator;
130 if (preset.ConditionEvaluator && preset.ConditionEvaluator->IsNull()) {
131 preset.ConditionEvaluator.reset();
134 CHECK_OK(preset.VisitPresetAfterInherit(graph.GetVersion(preset)));
136 cycleStatus[preset.Name] = CycleStatus::Verified;
137 return ReadFileResult::READ_OK;
141 ReadFileResult ComputePresetInheritance(
142 std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets,
143 const cmCMakePresetsGraph& graph)
145 std::map<std::string, CycleStatus> cycleStatus;
146 for (auto const& it : presets) {
147 cycleStatus[it.first] = CycleStatus::Unvisited;
150 for (auto& it : presets) {
151 auto& preset = it.second.Unexpanded;
152 auto result = VisitPreset<T>(preset, presets, cycleStatus, graph);
153 if (result != ReadFileResult::READ_OK) {
158 return ReadFileResult::READ_OK;
161 constexpr const char* ValidPrefixes[] = {
168 bool PrefixesValidMacroNamespace(const std::string& str)
171 std::begin(ValidPrefixes), std::end(ValidPrefixes),
172 [&str](const char* prefix) -> bool { return cmHasPrefix(prefix, str); });
175 bool IsValidMacroNamespace(const std::string& str)
178 std::begin(ValidPrefixes), std::end(ValidPrefixes),
179 [&str](const char* prefix) -> bool { return str == prefix; });
182 ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status,
183 const std::vector<MacroExpander>& macroExpanders,
185 ExpandMacroResult ExpandMacros(
186 std::string& out, const std::vector<MacroExpander>& macroExpanders,
188 ExpandMacroResult ExpandMacro(std::string& out,
189 const std::string& macroNamespace,
190 const std::string& macroName,
191 const std::vector<MacroExpander>& macroExpanders,
194 bool ExpandMacros(const cmCMakePresetsGraph& graph,
195 const ConfigurePreset& preset,
196 cm::optional<ConfigurePreset>& out,
197 const std::vector<MacroExpander>& macroExpanders)
199 std::string binaryDir = preset.BinaryDir;
200 CHECK_EXPAND(out, binaryDir, macroExpanders, graph.GetVersion(preset));
202 if (!binaryDir.empty()) {
203 if (!cmSystemTools::FileIsFullPath(binaryDir)) {
204 binaryDir = cmStrCat(graph.SourceDir, '/', binaryDir);
206 out->BinaryDir = cmSystemTools::CollapseFullPath(binaryDir);
207 cmSystemTools::ConvertToUnixSlashes(out->BinaryDir);
210 if (!preset.InstallDir.empty()) {
211 std::string installDir = preset.InstallDir;
212 CHECK_EXPAND(out, installDir, macroExpanders, graph.GetVersion(preset));
214 if (!cmSystemTools::FileIsFullPath(installDir)) {
215 installDir = cmStrCat(graph.SourceDir, '/', installDir);
217 out->InstallDir = cmSystemTools::CollapseFullPath(installDir);
218 cmSystemTools::ConvertToUnixSlashes(out->InstallDir);
221 if (!preset.ToolchainFile.empty()) {
222 std::string toolchain = preset.ToolchainFile;
223 CHECK_EXPAND(out, toolchain, macroExpanders, graph.GetVersion(preset));
224 out->ToolchainFile = toolchain;
227 for (auto& variable : out->CacheVariables) {
228 if (variable.second) {
229 CHECK_EXPAND(out, variable.second->Value, macroExpanders,
230 graph.GetVersion(preset));
237 bool ExpandMacros(const cmCMakePresetsGraph& graph, const BuildPreset& preset,
238 cm::optional<BuildPreset>& out,
239 const std::vector<MacroExpander>& macroExpanders)
241 for (auto& target : out->Targets) {
242 CHECK_EXPAND(out, target, macroExpanders, graph.GetVersion(preset));
245 for (auto& nativeToolOption : out->NativeToolOptions) {
246 CHECK_EXPAND(out, nativeToolOption, macroExpanders,
247 graph.GetVersion(preset));
253 bool ExpandMacros(const cmCMakePresetsGraph& graph, const TestPreset& preset,
254 cm::optional<TestPreset>& out,
255 const std::vector<MacroExpander>& macroExpanders)
257 for (auto& overwrite : out->OverwriteConfigurationFile) {
258 CHECK_EXPAND(out, overwrite, macroExpanders, graph.GetVersion(preset));
262 CHECK_EXPAND(out, out->Output->OutputLogFile, macroExpanders,
263 graph.GetVersion(preset));
267 if (out->Filter->Include) {
268 CHECK_EXPAND(out, out->Filter->Include->Name, macroExpanders,
269 graph.GetVersion(preset));
270 CHECK_EXPAND(out, out->Filter->Include->Label, macroExpanders,
271 graph.GetVersion(preset));
273 if (out->Filter->Include->Index) {
274 CHECK_EXPAND(out, out->Filter->Include->Index->IndexFile,
275 macroExpanders, graph.GetVersion(preset));
279 if (out->Filter->Exclude) {
280 CHECK_EXPAND(out, out->Filter->Exclude->Name, macroExpanders,
281 graph.GetVersion(preset));
282 CHECK_EXPAND(out, out->Filter->Exclude->Label, macroExpanders,
283 graph.GetVersion(preset));
285 if (out->Filter->Exclude->Fixtures) {
286 CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Any, macroExpanders,
287 graph.GetVersion(preset));
288 CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Setup,
289 macroExpanders, graph.GetVersion(preset));
290 CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Cleanup,
291 macroExpanders, graph.GetVersion(preset));
296 if (out->Execution) {
297 CHECK_EXPAND(out, out->Execution->ResourceSpecFile, macroExpanders,
298 graph.GetVersion(preset));
305 bool ExpandMacros(const cmCMakePresetsGraph& graph, const T& preset,
306 cm::optional<T>& out)
310 std::map<std::string, CycleStatus> envCycles;
311 for (auto const& v : out->Environment) {
312 envCycles[v.first] = CycleStatus::Unvisited;
315 std::vector<MacroExpander> macroExpanders;
317 MacroExpander defaultMacroExpander =
318 [&graph, &preset](const std::string& macroNamespace,
319 const std::string& macroName, std::string& macroOut,
320 int version) -> ExpandMacroResult {
321 if (macroNamespace.empty()) {
322 if (macroName == "sourceDir") {
323 macroOut += graph.SourceDir;
324 return ExpandMacroResult::Ok;
326 if (macroName == "sourceParentDir") {
327 macroOut += cmSystemTools::GetParentDirectory(graph.SourceDir);
328 return ExpandMacroResult::Ok;
330 if (macroName == "sourceDirName") {
331 macroOut += cmSystemTools::GetFilenameName(graph.SourceDir);
332 return ExpandMacroResult::Ok;
334 if (macroName == "presetName") {
335 macroOut += preset.Name;
336 return ExpandMacroResult::Ok;
338 if (macroName == "generator") {
339 // Generator only makes sense if preset is not hidden.
340 if (!preset.Hidden) {
341 macroOut += graph.GetGeneratorForPreset(preset.Name);
343 return ExpandMacroResult::Ok;
345 if (macroName == "dollar") {
347 return ExpandMacroResult::Ok;
349 if (macroName == "hostSystemName") {
351 return ExpandMacroResult::Error;
353 macroOut += cmSystemTools::GetSystemName();
354 return ExpandMacroResult::Ok;
356 if (macroName == "fileDir") {
358 return ExpandMacroResult::Error;
361 cmSystemTools::GetParentDirectory(preset.OriginFile->Filename);
362 return ExpandMacroResult::Ok;
364 if (macroName == "pathListSep") {
366 return ExpandMacroResult::Error;
368 macroOut += cmSystemTools::GetSystemPathlistSeparator();
369 return ExpandMacroResult::Ok;
373 return ExpandMacroResult::Ignore;
376 MacroExpander environmentMacroExpander =
377 [¯oExpanders, &out, &envCycles](
378 const std::string& macroNamespace, const std::string& macroName,
379 std::string& result, int version) -> ExpandMacroResult {
380 if (macroNamespace == "env" && !macroName.empty() && out) {
381 auto v = out->Environment.find(macroName);
382 if (v != out->Environment.end() && v->second) {
384 VisitEnv(*v->second, envCycles[macroName], macroExpanders, version);
385 if (e != ExpandMacroResult::Ok) {
388 result += *v->second;
389 return ExpandMacroResult::Ok;
393 if (macroNamespace == "env" || macroNamespace == "penv") {
394 if (macroName.empty()) {
395 return ExpandMacroResult::Error;
397 const char* value = std::getenv(macroName.c_str());
401 return ExpandMacroResult::Ok;
404 return ExpandMacroResult::Ignore;
407 macroExpanders.push_back(defaultMacroExpander);
408 macroExpanders.push_back(environmentMacroExpander);
410 for (auto& v : out->Environment) {
412 switch (VisitEnv(*v.second, envCycles[v.first], macroExpanders,
413 graph.GetVersion(preset))) {
414 case ExpandMacroResult::Error:
416 case ExpandMacroResult::Ignore:
419 case ExpandMacroResult::Ok:
425 if (preset.ConditionEvaluator) {
426 cm::optional<bool> result;
427 if (!preset.ConditionEvaluator->Evaluate(
428 macroExpanders, graph.GetVersion(preset), result)) {
435 out->ConditionResult = *result;
438 return ExpandMacros(graph, preset, out, macroExpanders);
441 ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status,
442 const std::vector<MacroExpander>& macroExpanders,
445 if (status == CycleStatus::Verified) {
446 return ExpandMacroResult::Ok;
448 if (status == CycleStatus::InProgress) {
449 return ExpandMacroResult::Error;
452 status = CycleStatus::InProgress;
453 auto e = ExpandMacros(value, macroExpanders, version);
454 if (e != ExpandMacroResult::Ok) {
457 status = CycleStatus::Verified;
458 return ExpandMacroResult::Ok;
461 ExpandMacroResult ExpandMacros(
462 std::string& out, const std::vector<MacroExpander>& macroExpanders,
466 std::string macroNamespace;
467 std::string macroName;
474 } state = State::Default;
480 state = State::MacroNamespace;
486 case State::MacroNamespace:
488 if (IsValidMacroNamespace(macroNamespace)) {
489 state = State::MacroName;
492 result += macroNamespace;
494 macroNamespace.clear();
495 state = State::Default;
499 if (!PrefixesValidMacroNamespace(macroNamespace)) {
501 result += macroNamespace;
502 macroNamespace.clear();
503 state = State::Default;
508 case State::MacroName:
510 auto e = ExpandMacro(result, macroNamespace, macroName,
511 macroExpanders, version);
512 if (e != ExpandMacroResult::Ok) {
515 macroNamespace.clear();
517 state = State::Default;
528 case State::MacroNamespace:
530 result += macroNamespace;
532 case State::MacroName:
533 return ExpandMacroResult::Error;
536 out = std::move(result);
537 return ExpandMacroResult::Ok;
540 ExpandMacroResult ExpandMacro(std::string& out,
541 const std::string& macroNamespace,
542 const std::string& macroName,
543 const std::vector<MacroExpander>& macroExpanders,
546 for (auto const& macroExpander : macroExpanders) {
547 auto result = macroExpander(macroNamespace, macroName, out, version);
548 if (result != ExpandMacroResult::Ignore) {
553 if (macroNamespace == "vendor") {
554 return ExpandMacroResult::Ignore;
557 return ExpandMacroResult::Error;
561 bool cmCMakePresetsGraphInternal::EqualsCondition::Evaluate(
562 const std::vector<MacroExpander>& expanders, int version,
563 cm::optional<bool>& out) const
565 std::string lhs = this->Lhs;
566 CHECK_EXPAND(out, lhs, expanders, version);
568 std::string rhs = this->Rhs;
569 CHECK_EXPAND(out, rhs, expanders, version);
575 bool cmCMakePresetsGraphInternal::InListCondition::Evaluate(
576 const std::vector<MacroExpander>& expanders, int version,
577 cm::optional<bool>& out) const
579 std::string str = this->String;
580 CHECK_EXPAND(out, str, expanders, version);
582 for (auto item : this->List) {
583 CHECK_EXPAND(out, item, expanders, version);
594 bool cmCMakePresetsGraphInternal::MatchesCondition::Evaluate(
595 const std::vector<MacroExpander>& expanders, int version,
596 cm::optional<bool>& out) const
598 std::string str = this->String;
599 CHECK_EXPAND(out, str, expanders, version);
600 std::string regexStr = this->Regex;
601 CHECK_EXPAND(out, regexStr, expanders, version);
603 cmsys::RegularExpression regex;
604 if (!regex.compile(regexStr)) {
608 out = regex.find(str);
612 bool cmCMakePresetsGraphInternal::AnyAllOfCondition::Evaluate(
613 const std::vector<MacroExpander>& expanders, int version,
614 cm::optional<bool>& out) const
616 for (auto const& condition : this->Conditions) {
617 cm::optional<bool> result;
618 if (!condition->Evaluate(expanders, version, result)) {
628 if (result == this->StopValue) {
634 out = !this->StopValue;
638 bool cmCMakePresetsGraphInternal::NotCondition::Evaluate(
639 const std::vector<MacroExpander>& expanders, int version,
640 cm::optional<bool>& out) const
643 if (!this->SubCondition->Evaluate(expanders, version, out)) {
653 cmCMakePresetsGraph::ReadFileResult
654 cmCMakePresetsGraph::ConfigurePreset::VisitPresetInherit(
655 const cmCMakePresetsGraph::Preset& parentPreset)
657 auto& preset = *this;
658 const ConfigurePreset& parent =
659 static_cast<const ConfigurePreset&>(parentPreset);
660 InheritString(preset.Generator, parent.Generator);
661 InheritString(preset.Architecture, parent.Architecture);
662 InheritString(preset.Toolset, parent.Toolset);
663 if (!preset.ArchitectureStrategy) {
664 preset.ArchitectureStrategy = parent.ArchitectureStrategy;
666 if (!preset.ToolsetStrategy) {
667 preset.ToolsetStrategy = parent.ToolsetStrategy;
669 InheritString(preset.BinaryDir, parent.BinaryDir);
670 InheritString(preset.InstallDir, parent.InstallDir);
671 InheritString(preset.ToolchainFile, parent.ToolchainFile);
672 InheritOptionalValue(preset.WarnDev, parent.WarnDev);
673 InheritOptionalValue(preset.ErrorDev, parent.ErrorDev);
674 InheritOptionalValue(preset.WarnDeprecated, parent.WarnDeprecated);
675 InheritOptionalValue(preset.ErrorDeprecated, parent.ErrorDeprecated);
676 InheritOptionalValue(preset.WarnUninitialized, parent.WarnUninitialized);
677 InheritOptionalValue(preset.WarnUnusedCli, parent.WarnUnusedCli);
678 InheritOptionalValue(preset.WarnSystemVars, parent.WarnSystemVars);
680 for (auto const& v : parent.CacheVariables) {
681 preset.CacheVariables.insert(v);
684 return ReadFileResult::READ_OK;
687 cmCMakePresetsGraph::ReadFileResult
688 cmCMakePresetsGraph::ConfigurePreset::VisitPresetBeforeInherit()
690 auto& preset = *this;
691 if (preset.Environment.count("") != 0) {
692 return ReadFileResult::INVALID_PRESET;
695 return ReadFileResult::READ_OK;
698 cmCMakePresetsGraph::ReadFileResult
699 cmCMakePresetsGraph::ConfigurePreset::VisitPresetAfterInherit(int version)
701 auto& preset = *this;
702 if (!preset.Hidden) {
704 if (preset.Generator.empty()) {
705 return ReadFileResult::INVALID_PRESET;
707 if (preset.BinaryDir.empty()) {
708 return ReadFileResult::INVALID_PRESET;
712 if (preset.WarnDev == false && preset.ErrorDev == true) {
713 return ReadFileResult::INVALID_PRESET;
715 if (preset.WarnDeprecated == false && preset.ErrorDeprecated == true) {
716 return ReadFileResult::INVALID_PRESET;
718 if (preset.CacheVariables.count("") != 0) {
719 return ReadFileResult::INVALID_PRESET;
723 return ReadFileResult::READ_OK;
726 cmCMakePresetsGraph::ReadFileResult
727 cmCMakePresetsGraph::BuildPreset::VisitPresetInherit(
728 const cmCMakePresetsGraph::Preset& parentPreset)
730 auto& preset = *this;
731 const BuildPreset& parent = static_cast<const BuildPreset&>(parentPreset);
733 InheritString(preset.ConfigurePreset, parent.ConfigurePreset);
734 InheritOptionalValue(preset.InheritConfigureEnvironment,
735 parent.InheritConfigureEnvironment);
736 InheritOptionalValue(preset.Jobs, parent.Jobs);
737 InheritVector(preset.Targets, parent.Targets);
738 InheritString(preset.Configuration, parent.Configuration);
739 InheritOptionalValue(preset.CleanFirst, parent.CleanFirst);
740 InheritOptionalValue(preset.Verbose, parent.Verbose);
741 InheritVector(preset.NativeToolOptions, parent.NativeToolOptions);
742 if (!preset.ResolvePackageReferences) {
743 preset.ResolvePackageReferences = parent.ResolvePackageReferences;
746 return ReadFileResult::READ_OK;
749 cmCMakePresetsGraph::ReadFileResult
750 cmCMakePresetsGraph::BuildPreset::VisitPresetAfterInherit(int /* version */)
752 auto& preset = *this;
753 if (!preset.Hidden && preset.ConfigurePreset.empty()) {
754 return ReadFileResult::INVALID_PRESET;
756 return ReadFileResult::READ_OK;
759 cmCMakePresetsGraph::ReadFileResult
760 cmCMakePresetsGraph::TestPreset::VisitPresetInherit(
761 const cmCMakePresetsGraph::Preset& parentPreset)
763 auto& preset = *this;
764 const TestPreset& parent = static_cast<const TestPreset&>(parentPreset);
766 InheritString(preset.ConfigurePreset, parent.ConfigurePreset);
767 InheritOptionalValue(preset.InheritConfigureEnvironment,
768 parent.InheritConfigureEnvironment);
769 InheritString(preset.Configuration, parent.Configuration);
770 InheritVector(preset.OverwriteConfigurationFile,
771 parent.OverwriteConfigurationFile);
775 auto& output = preset.Output.value();
776 const auto& parentOutput = parent.Output.value();
777 InheritOptionalValue(output.ShortProgress, parentOutput.ShortProgress);
778 InheritOptionalValue(output.Verbosity, parentOutput.Verbosity);
779 InheritOptionalValue(output.Debug, parentOutput.Debug);
780 InheritOptionalValue(output.OutputOnFailure,
781 parentOutput.OutputOnFailure);
782 InheritOptionalValue(output.Quiet, parentOutput.Quiet);
783 InheritString(output.OutputLogFile, parentOutput.OutputLogFile);
784 InheritOptionalValue(output.LabelSummary, parentOutput.LabelSummary);
785 InheritOptionalValue(output.SubprojectSummary,
786 parentOutput.SubprojectSummary);
787 InheritOptionalValue(output.MaxPassedTestOutputSize,
788 parentOutput.MaxPassedTestOutputSize);
789 InheritOptionalValue(output.MaxFailedTestOutputSize,
790 parentOutput.MaxFailedTestOutputSize);
791 InheritOptionalValue(output.TestOutputTruncation,
792 parentOutput.TestOutputTruncation);
793 InheritOptionalValue(output.MaxTestNameWidth,
794 parentOutput.MaxTestNameWidth);
796 preset.Output = parent.Output;
801 if (parent.Filter->Include) {
802 if (preset.Filter && preset.Filter->Include) {
803 auto& include = *preset.Filter->Include;
804 const auto& parentInclude = *parent.Filter->Include;
805 InheritString(include.Name, parentInclude.Name);
806 InheritString(include.Label, parentInclude.Label);
807 InheritOptionalValue(include.Index, parentInclude.Index);
809 if (!preset.Filter) {
810 preset.Filter.emplace();
812 preset.Filter->Include = parent.Filter->Include;
816 if (parent.Filter->Exclude) {
817 if (preset.Filter && preset.Filter->Exclude) {
818 auto& exclude = *preset.Filter->Exclude;
819 const auto& parentExclude = *parent.Filter->Exclude;
820 InheritString(exclude.Name, parentExclude.Name);
821 InheritString(exclude.Label, parentExclude.Label);
822 InheritOptionalValue(exclude.Fixtures, parentExclude.Fixtures);
824 if (!preset.Filter) {
825 preset.Filter.emplace();
827 preset.Filter->Exclude = parent.Filter->Exclude;
832 if (parent.Execution) {
833 if (preset.Execution) {
834 auto& execution = *preset.Execution;
835 const auto& parentExecution = *parent.Execution;
836 InheritOptionalValue(execution.StopOnFailure,
837 parentExecution.StopOnFailure);
838 InheritOptionalValue(execution.EnableFailover,
839 parentExecution.EnableFailover);
840 InheritOptionalValue(execution.Jobs, parentExecution.Jobs);
841 InheritString(execution.ResourceSpecFile,
842 parentExecution.ResourceSpecFile);
843 InheritOptionalValue(execution.TestLoad, parentExecution.TestLoad);
844 InheritOptionalValue(execution.ShowOnly, parentExecution.ShowOnly);
845 InheritOptionalValue(execution.Repeat, parentExecution.Repeat);
846 InheritOptionalValue(execution.InteractiveDebugging,
847 parentExecution.InteractiveDebugging);
848 InheritOptionalValue(execution.ScheduleRandom,
849 parentExecution.ScheduleRandom);
850 InheritOptionalValue(execution.Timeout, parentExecution.Timeout);
851 InheritOptionalValue(execution.NoTestsAction,
852 parentExecution.NoTestsAction);
854 preset.Execution = parent.Execution;
858 return ReadFileResult::READ_OK;
861 cmCMakePresetsGraph::ReadFileResult
862 cmCMakePresetsGraph::TestPreset::VisitPresetAfterInherit(int /* version */)
864 auto& preset = *this;
865 if (!preset.Hidden && preset.ConfigurePreset.empty()) {
866 return ReadFileResult::INVALID_PRESET;
868 return ReadFileResult::READ_OK;
871 std::string cmCMakePresetsGraph::GetFilename(const std::string& sourceDir)
873 return cmStrCat(sourceDir, "/CMakePresets.json");
876 std::string cmCMakePresetsGraph::GetUserFilename(const std::string& sourceDir)
878 return cmStrCat(sourceDir, "/CMakeUserPresets.json");
881 cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadProjectPresets(
882 const std::string& sourceDir, bool allowNoFiles)
884 this->SourceDir = sourceDir;
885 this->ClearPresets();
887 auto result = this->ReadProjectPresetsInternal(allowNoFiles);
888 if (result != ReadFileResult::READ_OK) {
889 this->ClearPresets();
895 cmCMakePresetsGraph::ReadFileResult
896 cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
898 bool haveOneFile = false;
901 std::string filename = GetUserFilename(this->SourceDir);
902 std::vector<File*> inProgressFiles;
903 if (cmSystemTools::FileExists(filename)) {
904 auto result = this->ReadJSONFile(filename, RootType::User,
905 ReadReason::Root, inProgressFiles, file);
906 if (result != ReadFileResult::READ_OK) {
911 filename = GetFilename(this->SourceDir);
912 if (cmSystemTools::FileExists(filename)) {
913 auto result = this->ReadJSONFile(
914 filename, RootType::Project, ReadReason::Root, inProgressFiles, file);
915 if (result != ReadFileResult::READ_OK) {
921 assert(inProgressFiles.empty());
924 return allowNoFiles ? ReadFileResult::READ_OK
925 : ReadFileResult::FILE_NOT_FOUND;
928 CHECK_OK(ComputePresetInheritance(this->ConfigurePresets, *this));
929 CHECK_OK(ComputePresetInheritance(this->BuildPresets, *this));
930 CHECK_OK(ComputePresetInheritance(this->TestPresets, *this));
932 for (auto& it : this->ConfigurePresets) {
933 if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
934 return ReadFileResult::INVALID_MACRO_EXPANSION;
938 for (auto& it : this->BuildPresets) {
939 if (!it.second.Unexpanded.Hidden) {
940 const auto configurePreset =
941 this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
942 if (configurePreset == this->ConfigurePresets.end()) {
943 return ReadFileResult::INVALID_CONFIGURE_PRESET;
945 if (!it.second.Unexpanded.OriginFile->ReachableFiles.count(
946 configurePreset->second.Unexpanded.OriginFile)) {
947 return ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE;
950 if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true)) {
951 it.second.Unexpanded.Environment.insert(
952 configurePreset->second.Unexpanded.Environment.begin(),
953 configurePreset->second.Unexpanded.Environment.end());
957 if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
958 return ReadFileResult::INVALID_MACRO_EXPANSION;
962 for (auto& it : this->TestPresets) {
963 if (!it.second.Unexpanded.Hidden) {
964 const auto configurePreset =
965 this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
966 if (configurePreset == this->ConfigurePresets.end()) {
967 return ReadFileResult::INVALID_CONFIGURE_PRESET;
969 if (!it.second.Unexpanded.OriginFile->ReachableFiles.count(
970 configurePreset->second.Unexpanded.OriginFile)) {
971 return ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE;
974 if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true)) {
975 it.second.Unexpanded.Environment.insert(
976 configurePreset->second.Unexpanded.Environment.begin(),
977 configurePreset->second.Unexpanded.Environment.end());
981 if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
982 return ReadFileResult::INVALID_MACRO_EXPANSION;
986 return ReadFileResult::READ_OK;
989 const char* cmCMakePresetsGraph::ResultToString(ReadFileResult result)
992 case ReadFileResult::READ_OK:
994 case ReadFileResult::FILE_NOT_FOUND:
995 return "File not found";
996 case ReadFileResult::JSON_PARSE_ERROR:
997 return "JSON parse error";
998 case ReadFileResult::INVALID_ROOT:
999 return "Invalid root object";
1000 case ReadFileResult::NO_VERSION:
1001 return "No \"version\" field";
1002 case ReadFileResult::INVALID_VERSION:
1003 return "Invalid \"version\" field";
1004 case ReadFileResult::UNRECOGNIZED_VERSION:
1005 return "Unrecognized \"version\" field";
1006 case ReadFileResult::INVALID_CMAKE_VERSION:
1007 return "Invalid \"cmakeMinimumRequired\" field";
1008 case ReadFileResult::UNRECOGNIZED_CMAKE_VERSION:
1009 return "\"cmakeMinimumRequired\" version too new";
1010 case ReadFileResult::INVALID_PRESETS:
1011 return "Invalid \"configurePresets\" field";
1012 case ReadFileResult::INVALID_PRESET:
1013 return "Invalid preset";
1014 case ReadFileResult::INVALID_VARIABLE:
1015 return "Invalid CMake variable definition";
1016 case ReadFileResult::DUPLICATE_PRESETS:
1017 return "Duplicate presets";
1018 case ReadFileResult::CYCLIC_PRESET_INHERITANCE:
1019 return "Cyclic preset inheritance";
1020 case ReadFileResult::INHERITED_PRESET_UNREACHABLE_FROM_FILE:
1021 return "Inherited preset is unreachable from preset's file";
1022 case ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE:
1023 return "Configure preset is unreachable from preset's file";
1024 case ReadFileResult::INVALID_MACRO_EXPANSION:
1025 return "Invalid macro expansion";
1026 case ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED:
1027 return "File version must be 2 or higher for build and test preset "
1029 case ReadFileResult::INCLUDE_UNSUPPORTED:
1030 return "File version must be 4 or higher for include support";
1031 case ReadFileResult::INVALID_INCLUDE:
1032 return "Invalid \"include\" field";
1033 case ReadFileResult::INVALID_CONFIGURE_PRESET:
1034 return "Invalid \"configurePreset\" field";
1035 case ReadFileResult::INSTALL_PREFIX_UNSUPPORTED:
1036 return "File version must be 3 or higher for installDir preset "
1038 case ReadFileResult::INVALID_CONDITION:
1039 return "Invalid preset condition";
1040 case ReadFileResult::CONDITION_UNSUPPORTED:
1041 return "File version must be 3 or higher for condition support";
1042 case ReadFileResult::TOOLCHAIN_FILE_UNSUPPORTED:
1043 return "File version must be 3 or higher for toolchainFile preset "
1045 case ReadFileResult::CYCLIC_INCLUDE:
1046 return "Cyclic include among preset files";
1047 case ReadFileResult::TEST_OUTPUT_TRUNCATION_UNSUPPORTED:
1048 return "File version must be 5 or higher for testOutputTruncation "
1052 return "Unknown error";
1055 void cmCMakePresetsGraph::ClearPresets()
1057 this->ConfigurePresets.clear();
1058 this->BuildPresets.clear();
1059 this->TestPresets.clear();
1061 this->ConfigurePresetOrder.clear();
1062 this->BuildPresetOrder.clear();
1063 this->TestPresetOrder.clear();
1065 this->Files.clear();
1068 void cmCMakePresetsGraph::PrintPresets(
1069 const std::vector<const cmCMakePresetsGraph::Preset*>& presets)
1071 if (presets.empty()) {
1075 auto longestPresetName =
1076 std::max_element(presets.begin(), presets.end(),
1077 [](const cmCMakePresetsGraph::Preset* a,
1078 const cmCMakePresetsGraph::Preset* b) {
1079 return a->Name.length() < b->Name.length();
1081 auto longestLength = (*longestPresetName)->Name.length();
1083 for (const auto* preset : presets) {
1084 std::cout << " \"" << preset->Name << '"';
1085 const auto& description = preset->DisplayName;
1086 if (!description.empty()) {
1087 for (std::size_t i = 0; i < longestLength - preset->Name.length(); ++i) {
1090 std::cout << " - " << description;
1096 void cmCMakePresetsGraph::PrintConfigurePresetList() const
1098 PrintConfigurePresetList([](const ConfigurePreset&) { return true; });
1101 void cmCMakePresetsGraph::PrintConfigurePresetList(
1102 const std::function<bool(const ConfigurePreset&)>& filter) const
1104 std::vector<const cmCMakePresetsGraph::Preset*> presets;
1105 for (auto const& p : this->ConfigurePresetOrder) {
1106 auto const& preset = this->ConfigurePresets.at(p);
1107 if (!preset.Unexpanded.Hidden && preset.Expanded &&
1108 preset.Expanded->ConditionResult && filter(preset.Unexpanded)) {
1110 static_cast<const cmCMakePresetsGraph::Preset*>(&preset.Unexpanded));
1114 if (!presets.empty()) {
1115 std::cout << "Available configure presets:\n\n";
1116 cmCMakePresetsGraph::PrintPresets(presets);
1120 void cmCMakePresetsGraph::PrintBuildPresetList() const
1122 std::vector<const cmCMakePresetsGraph::Preset*> presets;
1123 for (auto const& p : this->BuildPresetOrder) {
1124 auto const& preset = this->BuildPresets.at(p);
1125 if (!preset.Unexpanded.Hidden && preset.Expanded &&
1126 preset.Expanded->ConditionResult) {
1128 static_cast<const cmCMakePresetsGraph::Preset*>(&preset.Unexpanded));
1132 if (!presets.empty()) {
1133 std::cout << "Available build presets:\n\n";
1134 cmCMakePresetsGraph::PrintPresets(presets);
1138 void cmCMakePresetsGraph::PrintTestPresetList() const
1140 std::vector<const cmCMakePresetsGraph::Preset*> presets;
1141 for (auto const& p : this->TestPresetOrder) {
1142 auto const& preset = this->TestPresets.at(p);
1143 if (!preset.Unexpanded.Hidden && preset.Expanded &&
1144 preset.Expanded->ConditionResult) {
1146 static_cast<const cmCMakePresetsGraph::Preset*>(&preset.Unexpanded));
1150 if (!presets.empty()) {
1151 std::cout << "Available test presets:\n\n";
1152 cmCMakePresetsGraph::PrintPresets(presets);
1156 void cmCMakePresetsGraph::PrintAllPresets() const
1158 this->PrintConfigurePresetList();
1159 std::cout << std::endl;
1160 this->PrintBuildPresetList();
1161 std::cout << std::endl;
1162 this->PrintTestPresetList();