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 PackagePreset = cmCMakePresetsGraph::PackagePreset;
47 using WorkflowPreset = cmCMakePresetsGraph::WorkflowPreset;
49 using PresetPair = cmCMakePresetsGraph::PresetPair<T>;
50 using ExpandMacroResult = cmCMakePresetsGraphInternal::ExpandMacroResult;
51 using MacroExpander = cmCMakePresetsGraphInternal::MacroExpander;
53 void InheritString(std::string& child, const std::string& parent)
61 void InheritOptionalValue(cm::optional<T>& child,
62 const cm::optional<T>& parent)
70 void InheritVector(std::vector<T>& child, const std::vector<T>& parent)
78 * Check preset inheritance for cycles (using a DAG check algorithm) while
79 * also bubbling up fields through the inheritance hierarchy, then verify
80 * that each preset has the required fields, either directly or through
84 ReadFileResult VisitPreset(
86 std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets,
87 std::map<std::string, CycleStatus> cycleStatus,
88 const cmCMakePresetsGraph& graph)
90 switch (cycleStatus[preset.Name]) {
91 case CycleStatus::InProgress:
92 return ReadFileResult::CYCLIC_PRESET_INHERITANCE;
93 case CycleStatus::Verified:
94 return ReadFileResult::READ_OK;
99 cycleStatus[preset.Name] = CycleStatus::InProgress;
101 if (preset.Environment.count("") != 0) {
102 return ReadFileResult::INVALID_PRESET;
105 CHECK_OK(preset.VisitPresetBeforeInherit());
107 for (auto const& i : preset.Inherits) {
108 auto parent = presets.find(i);
109 if (parent == presets.end()) {
110 return ReadFileResult::INVALID_PRESET;
113 auto& parentPreset = parent->second.Unexpanded;
114 if (!preset.OriginFile->ReachableFiles.count(parentPreset.OriginFile)) {
115 return ReadFileResult::INHERITED_PRESET_UNREACHABLE_FROM_FILE;
118 auto result = VisitPreset(parentPreset, presets, cycleStatus, graph);
119 if (result != ReadFileResult::READ_OK) {
123 CHECK_OK(preset.VisitPresetInherit(parentPreset));
125 for (auto const& v : parentPreset.Environment) {
126 preset.Environment.insert(v);
129 if (!preset.ConditionEvaluator) {
130 preset.ConditionEvaluator = parentPreset.ConditionEvaluator;
134 if (preset.ConditionEvaluator && preset.ConditionEvaluator->IsNull()) {
135 preset.ConditionEvaluator.reset();
138 CHECK_OK(preset.VisitPresetAfterInherit(graph.GetVersion(preset)));
140 cycleStatus[preset.Name] = CycleStatus::Verified;
141 return ReadFileResult::READ_OK;
145 ReadFileResult ComputePresetInheritance(
146 std::map<std::string, cmCMakePresetsGraph::PresetPair<T>>& presets,
147 const cmCMakePresetsGraph& graph)
149 std::map<std::string, CycleStatus> cycleStatus;
150 for (auto const& it : presets) {
151 cycleStatus[it.first] = CycleStatus::Unvisited;
154 for (auto& it : presets) {
155 auto& preset = it.second.Unexpanded;
156 auto result = VisitPreset<T>(preset, presets, cycleStatus, graph);
157 if (result != ReadFileResult::READ_OK) {
162 return ReadFileResult::READ_OK;
165 constexpr const char* ValidPrefixes[] = {
172 bool PrefixesValidMacroNamespace(const std::string& str)
175 std::begin(ValidPrefixes), std::end(ValidPrefixes),
176 [&str](const char* prefix) -> bool { return cmHasPrefix(prefix, str); });
179 bool IsValidMacroNamespace(const std::string& str)
182 std::begin(ValidPrefixes), std::end(ValidPrefixes),
183 [&str](const char* prefix) -> bool { return str == prefix; });
186 ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status,
187 const std::vector<MacroExpander>& macroExpanders,
189 ExpandMacroResult ExpandMacros(
190 std::string& out, const std::vector<MacroExpander>& macroExpanders,
192 ExpandMacroResult ExpandMacro(std::string& out,
193 const std::string& macroNamespace,
194 const std::string& macroName,
195 const std::vector<MacroExpander>& macroExpanders,
198 bool ExpandMacros(const cmCMakePresetsGraph& graph,
199 const ConfigurePreset& preset,
200 cm::optional<ConfigurePreset>& out,
201 const std::vector<MacroExpander>& macroExpanders)
203 std::string binaryDir = preset.BinaryDir;
204 CHECK_EXPAND(out, binaryDir, macroExpanders, graph.GetVersion(preset));
206 if (!binaryDir.empty()) {
207 if (!cmSystemTools::FileIsFullPath(binaryDir)) {
208 binaryDir = cmStrCat(graph.SourceDir, '/', binaryDir);
210 out->BinaryDir = cmSystemTools::CollapseFullPath(binaryDir);
211 cmSystemTools::ConvertToUnixSlashes(out->BinaryDir);
214 if (!preset.InstallDir.empty()) {
215 std::string installDir = preset.InstallDir;
216 CHECK_EXPAND(out, installDir, macroExpanders, graph.GetVersion(preset));
218 if (!cmSystemTools::FileIsFullPath(installDir)) {
219 installDir = cmStrCat(graph.SourceDir, '/', installDir);
221 out->InstallDir = cmSystemTools::CollapseFullPath(installDir);
222 cmSystemTools::ConvertToUnixSlashes(out->InstallDir);
225 if (!preset.ToolchainFile.empty()) {
226 std::string toolchain = preset.ToolchainFile;
227 CHECK_EXPAND(out, toolchain, macroExpanders, graph.GetVersion(preset));
228 out->ToolchainFile = toolchain;
231 for (auto& variable : out->CacheVariables) {
232 if (variable.second) {
233 CHECK_EXPAND(out, variable.second->Value, macroExpanders,
234 graph.GetVersion(preset));
241 bool ExpandMacros(const cmCMakePresetsGraph& graph, const BuildPreset& preset,
242 cm::optional<BuildPreset>& out,
243 const std::vector<MacroExpander>& macroExpanders)
245 for (auto& target : out->Targets) {
246 CHECK_EXPAND(out, target, macroExpanders, graph.GetVersion(preset));
249 for (auto& nativeToolOption : out->NativeToolOptions) {
250 CHECK_EXPAND(out, nativeToolOption, macroExpanders,
251 graph.GetVersion(preset));
257 bool ExpandMacros(const cmCMakePresetsGraph& graph, const TestPreset& preset,
258 cm::optional<TestPreset>& out,
259 const std::vector<MacroExpander>& macroExpanders)
261 for (auto& overwrite : out->OverwriteConfigurationFile) {
262 CHECK_EXPAND(out, overwrite, macroExpanders, graph.GetVersion(preset));
266 CHECK_EXPAND(out, out->Output->OutputLogFile, macroExpanders,
267 graph.GetVersion(preset));
268 CHECK_EXPAND(out, out->Output->OutputJUnitFile, macroExpanders,
269 graph.GetVersion(preset));
273 if (out->Filter->Include) {
274 CHECK_EXPAND(out, out->Filter->Include->Name, macroExpanders,
275 graph.GetVersion(preset));
276 CHECK_EXPAND(out, out->Filter->Include->Label, macroExpanders,
277 graph.GetVersion(preset));
279 if (out->Filter->Include->Index) {
280 CHECK_EXPAND(out, out->Filter->Include->Index->IndexFile,
281 macroExpanders, graph.GetVersion(preset));
285 if (out->Filter->Exclude) {
286 CHECK_EXPAND(out, out->Filter->Exclude->Name, macroExpanders,
287 graph.GetVersion(preset));
288 CHECK_EXPAND(out, out->Filter->Exclude->Label, macroExpanders,
289 graph.GetVersion(preset));
291 if (out->Filter->Exclude->Fixtures) {
292 CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Any, macroExpanders,
293 graph.GetVersion(preset));
294 CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Setup,
295 macroExpanders, graph.GetVersion(preset));
296 CHECK_EXPAND(out, out->Filter->Exclude->Fixtures->Cleanup,
297 macroExpanders, graph.GetVersion(preset));
302 if (out->Execution) {
303 CHECK_EXPAND(out, out->Execution->ResourceSpecFile, macroExpanders,
304 graph.GetVersion(preset));
310 bool ExpandMacros(const cmCMakePresetsGraph& graph,
311 const PackagePreset& preset,
312 cm::optional<PackagePreset>& out,
313 const std::vector<MacroExpander>& macroExpanders)
315 for (auto& variable : out->Variables) {
316 CHECK_EXPAND(out, variable.second, macroExpanders,
317 graph.GetVersion(preset));
320 CHECK_EXPAND(out, out->ConfigFile, macroExpanders, graph.GetVersion(preset));
321 CHECK_EXPAND(out, out->PackageName, macroExpanders,
322 graph.GetVersion(preset));
323 CHECK_EXPAND(out, out->PackageVersion, macroExpanders,
324 graph.GetVersion(preset));
325 CHECK_EXPAND(out, out->PackageDirectory, macroExpanders,
326 graph.GetVersion(preset));
327 CHECK_EXPAND(out, out->VendorName, macroExpanders, graph.GetVersion(preset));
332 bool ExpandMacros(const cmCMakePresetsGraph& /*graph*/,
333 const WorkflowPreset& /*preset*/,
334 cm::optional<WorkflowPreset>& /*out*/,
335 const std::vector<MacroExpander>& /*macroExpanders*/)
341 bool ExpandMacros(const cmCMakePresetsGraph& graph, const T& preset,
342 cm::optional<T>& out)
346 std::map<std::string, CycleStatus> envCycles;
347 for (auto const& v : out->Environment) {
348 envCycles[v.first] = CycleStatus::Unvisited;
351 std::vector<MacroExpander> macroExpanders;
353 MacroExpander defaultMacroExpander =
354 [&graph, &preset](const std::string& macroNamespace,
355 const std::string& macroName, std::string& macroOut,
356 int version) -> ExpandMacroResult {
357 if (macroNamespace.empty()) {
358 if (macroName == "sourceDir") {
359 macroOut += graph.SourceDir;
360 return ExpandMacroResult::Ok;
362 if (macroName == "sourceParentDir") {
363 macroOut += cmSystemTools::GetParentDirectory(graph.SourceDir);
364 return ExpandMacroResult::Ok;
366 if (macroName == "sourceDirName") {
367 macroOut += cmSystemTools::GetFilenameName(graph.SourceDir);
368 return ExpandMacroResult::Ok;
370 if (macroName == "presetName") {
371 macroOut += preset.Name;
372 return ExpandMacroResult::Ok;
374 if (macroName == "generator") {
375 // Generator only makes sense if preset is not hidden.
376 if (!preset.Hidden) {
377 macroOut += graph.GetGeneratorForPreset(preset.Name);
379 return ExpandMacroResult::Ok;
381 if (macroName == "dollar") {
383 return ExpandMacroResult::Ok;
385 if (macroName == "hostSystemName") {
387 return ExpandMacroResult::Error;
389 macroOut += cmSystemTools::GetSystemName();
390 return ExpandMacroResult::Ok;
392 if (macroName == "fileDir") {
394 return ExpandMacroResult::Error;
397 cmSystemTools::GetParentDirectory(preset.OriginFile->Filename);
398 return ExpandMacroResult::Ok;
400 if (macroName == "pathListSep") {
402 return ExpandMacroResult::Error;
404 macroOut += cmSystemTools::GetSystemPathlistSeparator();
405 return ExpandMacroResult::Ok;
409 return ExpandMacroResult::Ignore;
412 MacroExpander environmentMacroExpander =
413 [¯oExpanders, &out, &envCycles](
414 const std::string& macroNamespace, const std::string& macroName,
415 std::string& result, int version) -> ExpandMacroResult {
416 if (macroNamespace == "env" && !macroName.empty() && out) {
417 auto v = out->Environment.find(macroName);
418 if (v != out->Environment.end() && v->second) {
420 VisitEnv(*v->second, envCycles[macroName], macroExpanders, version);
421 if (e != ExpandMacroResult::Ok) {
424 result += *v->second;
425 return ExpandMacroResult::Ok;
429 if (macroNamespace == "env" || macroNamespace == "penv") {
430 if (macroName.empty()) {
431 return ExpandMacroResult::Error;
433 const char* value = std::getenv(macroName.c_str());
437 return ExpandMacroResult::Ok;
440 return ExpandMacroResult::Ignore;
443 macroExpanders.push_back(defaultMacroExpander);
444 macroExpanders.push_back(environmentMacroExpander);
446 for (auto& v : out->Environment) {
448 switch (VisitEnv(*v.second, envCycles[v.first], macroExpanders,
449 graph.GetVersion(preset))) {
450 case ExpandMacroResult::Error:
452 case ExpandMacroResult::Ignore:
455 case ExpandMacroResult::Ok:
461 if (preset.ConditionEvaluator) {
462 cm::optional<bool> result;
463 if (!preset.ConditionEvaluator->Evaluate(
464 macroExpanders, graph.GetVersion(preset), result)) {
471 out->ConditionResult = *result;
474 return ExpandMacros(graph, preset, out, macroExpanders);
477 ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status,
478 const std::vector<MacroExpander>& macroExpanders,
481 if (status == CycleStatus::Verified) {
482 return ExpandMacroResult::Ok;
484 if (status == CycleStatus::InProgress) {
485 return ExpandMacroResult::Error;
488 status = CycleStatus::InProgress;
489 auto e = ExpandMacros(value, macroExpanders, version);
490 if (e != ExpandMacroResult::Ok) {
493 status = CycleStatus::Verified;
494 return ExpandMacroResult::Ok;
497 ExpandMacroResult ExpandMacros(
498 std::string& out, const std::vector<MacroExpander>& macroExpanders,
502 std::string macroNamespace;
503 std::string macroName;
510 } state = State::Default;
516 state = State::MacroNamespace;
522 case State::MacroNamespace:
524 if (IsValidMacroNamespace(macroNamespace)) {
525 state = State::MacroName;
528 result += macroNamespace;
530 macroNamespace.clear();
531 state = State::Default;
535 if (!PrefixesValidMacroNamespace(macroNamespace)) {
537 result += macroNamespace;
538 macroNamespace.clear();
539 state = State::Default;
544 case State::MacroName:
546 auto e = ExpandMacro(result, macroNamespace, macroName,
547 macroExpanders, version);
548 if (e != ExpandMacroResult::Ok) {
551 macroNamespace.clear();
553 state = State::Default;
564 case State::MacroNamespace:
566 result += macroNamespace;
568 case State::MacroName:
569 return ExpandMacroResult::Error;
572 out = std::move(result);
573 return ExpandMacroResult::Ok;
576 ExpandMacroResult ExpandMacro(std::string& out,
577 const std::string& macroNamespace,
578 const std::string& macroName,
579 const std::vector<MacroExpander>& macroExpanders,
582 for (auto const& macroExpander : macroExpanders) {
583 auto result = macroExpander(macroNamespace, macroName, out, version);
584 if (result != ExpandMacroResult::Ignore) {
589 if (macroNamespace == "vendor") {
590 return ExpandMacroResult::Ignore;
593 return ExpandMacroResult::Error;
596 template <typename T>
597 ReadFileResult SetupWorkflowConfigurePreset(
598 const T& preset, const ConfigurePreset*& configurePreset)
600 if (preset.ConfigurePreset != configurePreset->Name) {
601 return ReadFileResult::INVALID_WORKFLOW_STEPS;
603 return ReadFileResult::READ_OK;
607 ReadFileResult SetupWorkflowConfigurePreset<ConfigurePreset>(
608 const ConfigurePreset& preset, const ConfigurePreset*& configurePreset)
610 configurePreset = &preset;
611 return ReadFileResult::READ_OK;
614 template <typename T>
615 ReadFileResult TryReachPresetFromWorkflow(
616 const WorkflowPreset& origin,
617 const std::map<std::string, PresetPair<T>>& presets, const std::string& name,
618 const ConfigurePreset*& configurePreset)
620 auto it = presets.find(name);
621 if (it == presets.end()) {
622 return ReadFileResult::INVALID_WORKFLOW_STEPS;
624 if (!origin.OriginFile->ReachableFiles.count(
625 it->second.Unexpanded.OriginFile)) {
626 return ReadFileResult::WORKFLOW_STEP_UNREACHABLE_FROM_FILE;
628 return SetupWorkflowConfigurePreset<T>(it->second.Unexpanded,
633 bool cmCMakePresetsGraphInternal::EqualsCondition::Evaluate(
634 const std::vector<MacroExpander>& expanders, int version,
635 cm::optional<bool>& out) const
637 std::string lhs = this->Lhs;
638 CHECK_EXPAND(out, lhs, expanders, version);
640 std::string rhs = this->Rhs;
641 CHECK_EXPAND(out, rhs, expanders, version);
647 bool cmCMakePresetsGraphInternal::InListCondition::Evaluate(
648 const std::vector<MacroExpander>& expanders, int version,
649 cm::optional<bool>& out) const
651 std::string str = this->String;
652 CHECK_EXPAND(out, str, expanders, version);
654 for (auto item : this->List) {
655 CHECK_EXPAND(out, item, expanders, version);
666 bool cmCMakePresetsGraphInternal::MatchesCondition::Evaluate(
667 const std::vector<MacroExpander>& expanders, int version,
668 cm::optional<bool>& out) const
670 std::string str = this->String;
671 CHECK_EXPAND(out, str, expanders, version);
672 std::string regexStr = this->Regex;
673 CHECK_EXPAND(out, regexStr, expanders, version);
675 cmsys::RegularExpression regex;
676 if (!regex.compile(regexStr)) {
680 out = regex.find(str);
684 bool cmCMakePresetsGraphInternal::AnyAllOfCondition::Evaluate(
685 const std::vector<MacroExpander>& expanders, int version,
686 cm::optional<bool>& out) const
688 for (auto const& condition : this->Conditions) {
689 cm::optional<bool> result;
690 if (!condition->Evaluate(expanders, version, result)) {
700 if (result == this->StopValue) {
706 out = !this->StopValue;
710 bool cmCMakePresetsGraphInternal::NotCondition::Evaluate(
711 const std::vector<MacroExpander>& expanders, int version,
712 cm::optional<bool>& out) const
715 if (!this->SubCondition->Evaluate(expanders, version, out)) {
725 cmCMakePresetsGraph::ReadFileResult
726 cmCMakePresetsGraph::ConfigurePreset::VisitPresetInherit(
727 const cmCMakePresetsGraph::Preset& parentPreset)
729 auto& preset = *this;
730 const ConfigurePreset& parent =
731 static_cast<const ConfigurePreset&>(parentPreset);
732 InheritString(preset.Generator, parent.Generator);
733 InheritString(preset.Architecture, parent.Architecture);
734 InheritString(preset.Toolset, parent.Toolset);
735 if (!preset.ArchitectureStrategy) {
736 preset.ArchitectureStrategy = parent.ArchitectureStrategy;
738 if (!preset.ToolsetStrategy) {
739 preset.ToolsetStrategy = parent.ToolsetStrategy;
741 InheritString(preset.BinaryDir, parent.BinaryDir);
742 InheritString(preset.InstallDir, parent.InstallDir);
743 InheritString(preset.ToolchainFile, parent.ToolchainFile);
744 InheritOptionalValue(preset.WarnDev, parent.WarnDev);
745 InheritOptionalValue(preset.ErrorDev, parent.ErrorDev);
746 InheritOptionalValue(preset.WarnDeprecated, parent.WarnDeprecated);
747 InheritOptionalValue(preset.ErrorDeprecated, parent.ErrorDeprecated);
748 InheritOptionalValue(preset.WarnUninitialized, parent.WarnUninitialized);
749 InheritOptionalValue(preset.WarnUnusedCli, parent.WarnUnusedCli);
750 InheritOptionalValue(preset.WarnSystemVars, parent.WarnSystemVars);
752 for (auto const& v : parent.CacheVariables) {
753 preset.CacheVariables.insert(v);
756 return ReadFileResult::READ_OK;
759 cmCMakePresetsGraph::ReadFileResult
760 cmCMakePresetsGraph::ConfigurePreset::VisitPresetBeforeInherit()
762 auto& preset = *this;
763 if (preset.Environment.count("") != 0) {
764 return ReadFileResult::INVALID_PRESET;
767 return ReadFileResult::READ_OK;
770 cmCMakePresetsGraph::ReadFileResult
771 cmCMakePresetsGraph::ConfigurePreset::VisitPresetAfterInherit(int version)
773 auto& preset = *this;
774 if (!preset.Hidden) {
776 if (preset.Generator.empty()) {
777 return ReadFileResult::INVALID_PRESET;
779 if (preset.BinaryDir.empty()) {
780 return ReadFileResult::INVALID_PRESET;
784 if (preset.WarnDev == false && preset.ErrorDev == true) {
785 return ReadFileResult::INVALID_PRESET;
787 if (preset.WarnDeprecated == false && preset.ErrorDeprecated == true) {
788 return ReadFileResult::INVALID_PRESET;
790 if (preset.CacheVariables.count("") != 0) {
791 return ReadFileResult::INVALID_PRESET;
795 return ReadFileResult::READ_OK;
798 cmCMakePresetsGraph::ReadFileResult
799 cmCMakePresetsGraph::BuildPreset::VisitPresetInherit(
800 const cmCMakePresetsGraph::Preset& parentPreset)
802 auto& preset = *this;
803 const BuildPreset& parent = static_cast<const BuildPreset&>(parentPreset);
805 InheritString(preset.ConfigurePreset, parent.ConfigurePreset);
806 InheritOptionalValue(preset.InheritConfigureEnvironment,
807 parent.InheritConfigureEnvironment);
808 InheritOptionalValue(preset.Jobs, parent.Jobs);
809 InheritVector(preset.Targets, parent.Targets);
810 InheritString(preset.Configuration, parent.Configuration);
811 InheritOptionalValue(preset.CleanFirst, parent.CleanFirst);
812 InheritOptionalValue(preset.Verbose, parent.Verbose);
813 InheritVector(preset.NativeToolOptions, parent.NativeToolOptions);
814 if (!preset.ResolvePackageReferences) {
815 preset.ResolvePackageReferences = parent.ResolvePackageReferences;
818 return ReadFileResult::READ_OK;
821 cmCMakePresetsGraph::ReadFileResult
822 cmCMakePresetsGraph::BuildPreset::VisitPresetAfterInherit(int /* version */)
824 auto& preset = *this;
825 if (!preset.Hidden && preset.ConfigurePreset.empty()) {
826 return ReadFileResult::INVALID_PRESET;
828 return ReadFileResult::READ_OK;
831 cmCMakePresetsGraph::ReadFileResult
832 cmCMakePresetsGraph::TestPreset::VisitPresetInherit(
833 const cmCMakePresetsGraph::Preset& parentPreset)
835 auto& preset = *this;
836 const TestPreset& parent = static_cast<const TestPreset&>(parentPreset);
838 InheritString(preset.ConfigurePreset, parent.ConfigurePreset);
839 InheritOptionalValue(preset.InheritConfigureEnvironment,
840 parent.InheritConfigureEnvironment);
841 InheritString(preset.Configuration, parent.Configuration);
842 InheritVector(preset.OverwriteConfigurationFile,
843 parent.OverwriteConfigurationFile);
847 auto& output = preset.Output.value();
848 const auto& parentOutput = parent.Output.value();
849 InheritOptionalValue(output.ShortProgress, parentOutput.ShortProgress);
850 InheritOptionalValue(output.Verbosity, parentOutput.Verbosity);
851 InheritOptionalValue(output.Debug, parentOutput.Debug);
852 InheritOptionalValue(output.OutputOnFailure,
853 parentOutput.OutputOnFailure);
854 InheritOptionalValue(output.Quiet, parentOutput.Quiet);
855 InheritString(output.OutputLogFile, parentOutput.OutputLogFile);
856 InheritString(output.OutputJUnitFile, parentOutput.OutputJUnitFile);
857 InheritOptionalValue(output.LabelSummary, parentOutput.LabelSummary);
858 InheritOptionalValue(output.SubprojectSummary,
859 parentOutput.SubprojectSummary);
860 InheritOptionalValue(output.MaxPassedTestOutputSize,
861 parentOutput.MaxPassedTestOutputSize);
862 InheritOptionalValue(output.MaxFailedTestOutputSize,
863 parentOutput.MaxFailedTestOutputSize);
864 InheritOptionalValue(output.TestOutputTruncation,
865 parentOutput.TestOutputTruncation);
866 InheritOptionalValue(output.MaxTestNameWidth,
867 parentOutput.MaxTestNameWidth);
869 preset.Output = parent.Output;
874 if (parent.Filter->Include) {
875 if (preset.Filter && preset.Filter->Include) {
876 auto& include = *preset.Filter->Include;
877 const auto& parentInclude = *parent.Filter->Include;
878 InheritString(include.Name, parentInclude.Name);
879 InheritString(include.Label, parentInclude.Label);
880 InheritOptionalValue(include.Index, parentInclude.Index);
882 if (!preset.Filter) {
883 preset.Filter.emplace();
885 preset.Filter->Include = parent.Filter->Include;
889 if (parent.Filter->Exclude) {
890 if (preset.Filter && preset.Filter->Exclude) {
891 auto& exclude = *preset.Filter->Exclude;
892 const auto& parentExclude = *parent.Filter->Exclude;
893 InheritString(exclude.Name, parentExclude.Name);
894 InheritString(exclude.Label, parentExclude.Label);
895 InheritOptionalValue(exclude.Fixtures, parentExclude.Fixtures);
897 if (!preset.Filter) {
898 preset.Filter.emplace();
900 preset.Filter->Exclude = parent.Filter->Exclude;
905 if (parent.Execution) {
906 if (preset.Execution) {
907 auto& execution = *preset.Execution;
908 const auto& parentExecution = *parent.Execution;
909 InheritOptionalValue(execution.StopOnFailure,
910 parentExecution.StopOnFailure);
911 InheritOptionalValue(execution.EnableFailover,
912 parentExecution.EnableFailover);
913 InheritOptionalValue(execution.Jobs, parentExecution.Jobs);
914 InheritString(execution.ResourceSpecFile,
915 parentExecution.ResourceSpecFile);
916 InheritOptionalValue(execution.TestLoad, parentExecution.TestLoad);
917 InheritOptionalValue(execution.ShowOnly, parentExecution.ShowOnly);
918 InheritOptionalValue(execution.Repeat, parentExecution.Repeat);
919 InheritOptionalValue(execution.InteractiveDebugging,
920 parentExecution.InteractiveDebugging);
921 InheritOptionalValue(execution.ScheduleRandom,
922 parentExecution.ScheduleRandom);
923 InheritOptionalValue(execution.Timeout, parentExecution.Timeout);
924 InheritOptionalValue(execution.NoTestsAction,
925 parentExecution.NoTestsAction);
927 preset.Execution = parent.Execution;
931 return ReadFileResult::READ_OK;
934 cmCMakePresetsGraph::ReadFileResult
935 cmCMakePresetsGraph::TestPreset::VisitPresetAfterInherit(int /* version */)
937 auto& preset = *this;
938 if (!preset.Hidden && preset.ConfigurePreset.empty()) {
939 return ReadFileResult::INVALID_PRESET;
941 return ReadFileResult::READ_OK;
944 cmCMakePresetsGraph::ReadFileResult
945 cmCMakePresetsGraph::PackagePreset::VisitPresetInherit(
946 const cmCMakePresetsGraph::Preset& parentPreset)
948 auto& preset = *this;
949 const PackagePreset& parent =
950 static_cast<const PackagePreset&>(parentPreset);
952 InheritString(preset.ConfigurePreset, parent.ConfigurePreset);
953 InheritOptionalValue(preset.InheritConfigureEnvironment,
954 parent.InheritConfigureEnvironment);
955 InheritVector(preset.Generators, parent.Generators);
956 InheritVector(preset.Configurations, parent.Configurations);
958 for (auto const& v : parent.Variables) {
959 preset.Variables.insert(v);
962 InheritOptionalValue(preset.DebugOutput, parent.DebugOutput);
963 InheritOptionalValue(preset.VerboseOutput, parent.VerboseOutput);
964 InheritString(preset.PackageName, parent.PackageName);
965 InheritString(preset.PackageVersion, parent.PackageVersion);
966 InheritString(preset.PackageDirectory, parent.PackageDirectory);
967 InheritString(preset.VendorName, parent.VendorName);
969 return ReadFileResult::READ_OK;
972 cmCMakePresetsGraph::ReadFileResult
973 cmCMakePresetsGraph::PackagePreset::VisitPresetAfterInherit(int /* version */)
975 auto& preset = *this;
976 if (!preset.Hidden && preset.ConfigurePreset.empty()) {
977 return ReadFileResult::INVALID_PRESET;
979 return ReadFileResult::READ_OK;
982 cmCMakePresetsGraph::ReadFileResult
983 cmCMakePresetsGraph::WorkflowPreset::VisitPresetInherit(
984 const cmCMakePresetsGraph::Preset& /*parentPreset*/)
986 return ReadFileResult::READ_OK;
989 cmCMakePresetsGraph::ReadFileResult
990 cmCMakePresetsGraph::WorkflowPreset::VisitPresetAfterInherit(int /* version */)
992 return ReadFileResult::READ_OK;
995 std::string cmCMakePresetsGraph::GetFilename(const std::string& sourceDir)
997 return cmStrCat(sourceDir, "/CMakePresets.json");
1000 std::string cmCMakePresetsGraph::GetUserFilename(const std::string& sourceDir)
1002 return cmStrCat(sourceDir, "/CMakeUserPresets.json");
1005 cmCMakePresetsGraph::ReadFileResult cmCMakePresetsGraph::ReadProjectPresets(
1006 const std::string& sourceDir, bool allowNoFiles)
1008 this->SourceDir = sourceDir;
1009 this->ClearPresets();
1011 auto result = this->ReadProjectPresetsInternal(allowNoFiles);
1012 if (result != ReadFileResult::READ_OK) {
1013 this->ClearPresets();
1019 cmCMakePresetsGraph::ReadFileResult
1020 cmCMakePresetsGraph::ReadProjectPresetsInternal(bool allowNoFiles)
1022 bool haveOneFile = false;
1025 std::string filename = GetUserFilename(this->SourceDir);
1026 std::vector<File*> inProgressFiles;
1027 if (cmSystemTools::FileExists(filename)) {
1029 this->ReadJSONFile(filename, RootType::User, ReadReason::Root,
1030 inProgressFiles, file, this->errors);
1031 if (result != ReadFileResult::READ_OK) {
1036 filename = GetFilename(this->SourceDir);
1037 if (cmSystemTools::FileExists(filename)) {
1039 this->ReadJSONFile(filename, RootType::Project, ReadReason::Root,
1040 inProgressFiles, file, this->errors);
1041 if (result != ReadFileResult::READ_OK) {
1047 assert(inProgressFiles.empty());
1050 return allowNoFiles ? ReadFileResult::READ_OK
1051 : ReadFileResult::FILE_NOT_FOUND;
1054 CHECK_OK(ComputePresetInheritance(this->ConfigurePresets, *this));
1055 CHECK_OK(ComputePresetInheritance(this->BuildPresets, *this));
1056 CHECK_OK(ComputePresetInheritance(this->TestPresets, *this));
1057 CHECK_OK(ComputePresetInheritance(this->PackagePresets, *this));
1058 CHECK_OK(ComputePresetInheritance(this->WorkflowPresets, *this));
1060 for (auto& it : this->ConfigurePresets) {
1061 if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
1062 return ReadFileResult::INVALID_MACRO_EXPANSION;
1066 for (auto& it : this->BuildPresets) {
1067 if (!it.second.Unexpanded.Hidden) {
1068 const auto configurePreset =
1069 this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
1070 if (configurePreset == this->ConfigurePresets.end()) {
1071 return ReadFileResult::INVALID_CONFIGURE_PRESET;
1073 if (!it.second.Unexpanded.OriginFile->ReachableFiles.count(
1074 configurePreset->second.Unexpanded.OriginFile)) {
1075 return ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE;
1078 if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true)) {
1079 it.second.Unexpanded.Environment.insert(
1080 configurePreset->second.Unexpanded.Environment.begin(),
1081 configurePreset->second.Unexpanded.Environment.end());
1085 if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
1086 return ReadFileResult::INVALID_MACRO_EXPANSION;
1090 for (auto& it : this->TestPresets) {
1091 if (!it.second.Unexpanded.Hidden) {
1092 const auto configurePreset =
1093 this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
1094 if (configurePreset == this->ConfigurePresets.end()) {
1095 return ReadFileResult::INVALID_CONFIGURE_PRESET;
1097 if (!it.second.Unexpanded.OriginFile->ReachableFiles.count(
1098 configurePreset->second.Unexpanded.OriginFile)) {
1099 return ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE;
1102 if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true)) {
1103 it.second.Unexpanded.Environment.insert(
1104 configurePreset->second.Unexpanded.Environment.begin(),
1105 configurePreset->second.Unexpanded.Environment.end());
1109 if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
1110 return ReadFileResult::INVALID_MACRO_EXPANSION;
1114 for (auto& it : this->PackagePresets) {
1115 if (!it.second.Unexpanded.Hidden) {
1116 const auto configurePreset =
1117 this->ConfigurePresets.find(it.second.Unexpanded.ConfigurePreset);
1118 if (configurePreset == this->ConfigurePresets.end()) {
1119 return ReadFileResult::INVALID_CONFIGURE_PRESET;
1121 if (!it.second.Unexpanded.OriginFile->ReachableFiles.count(
1122 configurePreset->second.Unexpanded.OriginFile)) {
1123 return ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE;
1126 if (it.second.Unexpanded.InheritConfigureEnvironment.value_or(true)) {
1127 it.second.Unexpanded.Environment.insert(
1128 configurePreset->second.Unexpanded.Environment.begin(),
1129 configurePreset->second.Unexpanded.Environment.end());
1133 if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
1134 return ReadFileResult::INVALID_MACRO_EXPANSION;
1138 for (auto& it : this->WorkflowPresets) {
1139 using Type = WorkflowPreset::WorkflowStep::Type;
1141 const ConfigurePreset* configurePreset = nullptr;
1142 for (auto const& step : it.second.Unexpanded.Steps) {
1143 if (configurePreset == nullptr && step.PresetType != Type::Configure) {
1144 return ReadFileResult::INVALID_WORKFLOW_STEPS;
1146 if (configurePreset != nullptr && step.PresetType == Type::Configure) {
1147 return ReadFileResult::INVALID_WORKFLOW_STEPS;
1150 ReadFileResult result;
1151 switch (step.PresetType) {
1152 case Type::Configure:
1153 result = TryReachPresetFromWorkflow(
1154 it.second.Unexpanded, this->ConfigurePresets, step.PresetName,
1158 result = TryReachPresetFromWorkflow(
1159 it.second.Unexpanded, this->BuildPresets, step.PresetName,
1164 TryReachPresetFromWorkflow(it.second.Unexpanded, this->TestPresets,
1165 step.PresetName, configurePreset);
1168 result = TryReachPresetFromWorkflow(
1169 it.second.Unexpanded, this->PackagePresets, step.PresetName,
1173 if (result != ReadFileResult::READ_OK) {
1178 if (configurePreset == nullptr) {
1179 return ReadFileResult::INVALID_WORKFLOW_STEPS;
1182 if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) {
1183 return ReadFileResult::INVALID_MACRO_EXPANSION;
1187 return ReadFileResult::READ_OK;
1190 const char* cmCMakePresetsGraph::ResultToString(ReadFileResult result)
1193 case ReadFileResult::READ_OK:
1195 case ReadFileResult::FILE_NOT_FOUND:
1196 return "File not found";
1197 case ReadFileResult::JSON_PARSE_ERROR:
1198 return "JSON parse error";
1199 case ReadFileResult::INVALID_ROOT:
1200 return "Invalid root object";
1201 case ReadFileResult::NO_VERSION:
1202 return "No \"version\" field";
1203 case ReadFileResult::INVALID_VERSION:
1204 return "Invalid \"version\" field";
1205 case ReadFileResult::UNRECOGNIZED_VERSION:
1206 return "Unrecognized \"version\" field";
1207 case ReadFileResult::INVALID_CMAKE_VERSION:
1208 return "Invalid \"cmakeMinimumRequired\" field";
1209 case ReadFileResult::UNRECOGNIZED_CMAKE_VERSION:
1210 return "\"cmakeMinimumRequired\" version too new";
1211 case ReadFileResult::INVALID_PRESETS:
1212 return "Invalid \"configurePresets\" field";
1213 case ReadFileResult::INVALID_PRESET:
1214 return "Invalid preset";
1215 case ReadFileResult::INVALID_VARIABLE:
1216 return "Invalid CMake variable definition";
1217 case ReadFileResult::DUPLICATE_PRESETS:
1218 return "Duplicate presets";
1219 case ReadFileResult::CYCLIC_PRESET_INHERITANCE:
1220 return "Cyclic preset inheritance";
1221 case ReadFileResult::INHERITED_PRESET_UNREACHABLE_FROM_FILE:
1222 return "Inherited preset is unreachable from preset's file";
1223 case ReadFileResult::CONFIGURE_PRESET_UNREACHABLE_FROM_FILE:
1224 return "Configure preset is unreachable from preset's file";
1225 case ReadFileResult::INVALID_MACRO_EXPANSION:
1226 return "Invalid macro expansion";
1227 case ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED:
1228 return "File version must be 2 or higher for build and test preset "
1230 case ReadFileResult::PACKAGE_PRESETS_UNSUPPORTED:
1231 return "File version must be 6 or higher for package preset support";
1232 case ReadFileResult::WORKFLOW_PRESETS_UNSUPPORTED:
1233 return "File version must be 6 or higher for workflow preset support";
1234 case ReadFileResult::INCLUDE_UNSUPPORTED:
1235 return "File version must be 4 or higher for include support";
1236 case ReadFileResult::INVALID_INCLUDE:
1237 return "Invalid \"include\" field";
1238 case ReadFileResult::INVALID_CONFIGURE_PRESET:
1239 return "Invalid \"configurePreset\" field";
1240 case ReadFileResult::INSTALL_PREFIX_UNSUPPORTED:
1241 return "File version must be 3 or higher for installDir preset "
1243 case ReadFileResult::INVALID_CONDITION:
1244 return "Invalid preset condition";
1245 case ReadFileResult::CONDITION_UNSUPPORTED:
1246 return "File version must be 3 or higher for condition support";
1247 case ReadFileResult::TOOLCHAIN_FILE_UNSUPPORTED:
1248 return "File version must be 3 or higher for toolchainFile preset "
1250 case ReadFileResult::CYCLIC_INCLUDE:
1251 return "Cyclic include among preset files";
1252 case ReadFileResult::TEST_OUTPUT_TRUNCATION_UNSUPPORTED:
1253 return "File version must be 5 or higher for testOutputTruncation "
1255 case ReadFileResult::INVALID_WORKFLOW_STEPS:
1256 return "Invalid workflow steps";
1257 case ReadFileResult::WORKFLOW_STEP_UNREACHABLE_FROM_FILE:
1258 return "Workflow step is unreachable from preset's file";
1259 case ReadFileResult::CTEST_JUNIT_UNSUPPORTED:
1260 return "File version must be 6 or higher for CTest JUnit output support";
1263 return "Unknown error";
1266 void cmCMakePresetsGraph::ClearPresets()
1268 this->ConfigurePresets.clear();
1269 this->BuildPresets.clear();
1270 this->TestPresets.clear();
1271 this->PackagePresets.clear();
1272 this->WorkflowPresets.clear();
1274 this->ConfigurePresetOrder.clear();
1275 this->BuildPresetOrder.clear();
1276 this->TestPresetOrder.clear();
1277 this->PackagePresetOrder.clear();
1278 this->WorkflowPresetOrder.clear();
1280 this->Files.clear();
1283 void cmCMakePresetsGraph::printPrecedingNewline(PrintPrecedingNewline* newline)
1286 if (*newline == PrintPrecedingNewline::True) {
1287 std::cout << std::endl;
1289 *newline = PrintPrecedingNewline::True;
1293 void cmCMakePresetsGraph::PrintPresets(
1294 const std::vector<const cmCMakePresetsGraph::Preset*>& presets)
1296 if (presets.empty()) {
1300 auto longestPresetName =
1301 std::max_element(presets.begin(), presets.end(),
1302 [](const cmCMakePresetsGraph::Preset* a,
1303 const cmCMakePresetsGraph::Preset* b) {
1304 return a->Name.length() < b->Name.length();
1306 auto longestLength = (*longestPresetName)->Name.length();
1308 for (const auto* preset : presets) {
1309 std::cout << " \"" << preset->Name << '"';
1310 const auto& description = preset->DisplayName;
1311 if (!description.empty()) {
1312 for (std::size_t i = 0; i < longestLength - preset->Name.length(); ++i) {
1315 std::cout << " - " << description;
1321 void cmCMakePresetsGraph::PrintConfigurePresetList(
1322 PrintPrecedingNewline* newline) const
1324 PrintConfigurePresetList([](const ConfigurePreset&) { return true; },
1328 void cmCMakePresetsGraph::PrintConfigurePresetList(
1329 const std::function<bool(const ConfigurePreset&)>& filter,
1330 PrintPrecedingNewline* newline) const
1332 std::vector<const cmCMakePresetsGraph::Preset*> presets;
1333 for (auto const& p : this->ConfigurePresetOrder) {
1334 auto const& preset = this->ConfigurePresets.at(p);
1335 if (!preset.Unexpanded.Hidden && preset.Expanded &&
1336 preset.Expanded->ConditionResult && filter(preset.Unexpanded)) {
1338 static_cast<const cmCMakePresetsGraph::Preset*>(&preset.Unexpanded));
1342 if (!presets.empty()) {
1343 printPrecedingNewline(newline);
1344 std::cout << "Available configure presets:\n\n";
1345 cmCMakePresetsGraph::PrintPresets(presets);
1349 void cmCMakePresetsGraph::PrintBuildPresetList(
1350 PrintPrecedingNewline* newline) const
1352 std::vector<const cmCMakePresetsGraph::Preset*> presets;
1353 for (auto const& p : this->BuildPresetOrder) {
1354 auto const& preset = this->BuildPresets.at(p);
1355 if (!preset.Unexpanded.Hidden && preset.Expanded &&
1356 preset.Expanded->ConditionResult) {
1358 static_cast<const cmCMakePresetsGraph::Preset*>(&preset.Unexpanded));
1362 if (!presets.empty()) {
1363 printPrecedingNewline(newline);
1364 std::cout << "Available build presets:\n\n";
1365 cmCMakePresetsGraph::PrintPresets(presets);
1369 void cmCMakePresetsGraph::PrintTestPresetList(
1370 PrintPrecedingNewline* newline) const
1372 std::vector<const cmCMakePresetsGraph::Preset*> presets;
1373 for (auto const& p : this->TestPresetOrder) {
1374 auto const& preset = this->TestPresets.at(p);
1375 if (!preset.Unexpanded.Hidden && preset.Expanded &&
1376 preset.Expanded->ConditionResult) {
1378 static_cast<const cmCMakePresetsGraph::Preset*>(&preset.Unexpanded));
1382 if (!presets.empty()) {
1383 printPrecedingNewline(newline);
1384 std::cout << "Available test presets:\n\n";
1385 cmCMakePresetsGraph::PrintPresets(presets);
1389 void cmCMakePresetsGraph::PrintPackagePresetList(
1390 PrintPrecedingNewline* newline) const
1392 this->PrintPackagePresetList([](const PackagePreset&) { return true; },
1396 void cmCMakePresetsGraph::PrintPackagePresetList(
1397 const std::function<bool(const PackagePreset&)>& filter,
1398 PrintPrecedingNewline* newline) const
1400 std::vector<const cmCMakePresetsGraph::Preset*> presets;
1401 for (auto const& p : this->PackagePresetOrder) {
1402 auto const& preset = this->PackagePresets.at(p);
1403 if (!preset.Unexpanded.Hidden && preset.Expanded &&
1404 preset.Expanded->ConditionResult && filter(preset.Unexpanded)) {
1406 static_cast<const cmCMakePresetsGraph::Preset*>(&preset.Unexpanded));
1410 if (!presets.empty()) {
1411 printPrecedingNewline(newline);
1412 std::cout << "Available package presets:\n\n";
1413 cmCMakePresetsGraph::PrintPresets(presets);
1417 void cmCMakePresetsGraph::PrintWorkflowPresetList(
1418 PrintPrecedingNewline* newline) const
1420 std::vector<const cmCMakePresetsGraph::Preset*> presets;
1421 for (auto const& p : this->WorkflowPresetOrder) {
1422 auto const& preset = this->WorkflowPresets.at(p);
1423 if (!preset.Unexpanded.Hidden && preset.Expanded &&
1424 preset.Expanded->ConditionResult) {
1426 static_cast<const cmCMakePresetsGraph::Preset*>(&preset.Unexpanded));
1430 if (!presets.empty()) {
1431 printPrecedingNewline(newline);
1432 std::cout << "Available workflow presets:\n\n";
1433 cmCMakePresetsGraph::PrintPresets(presets);
1437 void cmCMakePresetsGraph::PrintAllPresets() const
1439 PrintPrecedingNewline newline = PrintPrecedingNewline::False;
1440 this->PrintConfigurePresetList(&newline);
1441 this->PrintBuildPresetList(&newline);
1442 this->PrintTestPresetList(&newline);
1443 this->PrintPackagePresetList(&newline);
1444 this->PrintWorkflowPresetList(&newline);