From b1f15344c9eb20c06640ad0fcc6ac8ebbafb33c4 Mon Sep 17 00:00:00 2001 From: Richard Date: Wed, 29 Jul 2026 16:57:01 -0600 Subject: [PATCH] Presets: Clarify workflow step error messages Report both the workflow step type and preset name when workflow preset validation or execution fails. This disambiguates workflows where configure, build, test, and package steps share the same preset name. Update the workflow preset RunCMake expectations. --- Source/cmCMakePresetsErrors.cxx | 39 +++++++++++----- Source/cmCMakePresetsErrors.h | 13 +++--- Source/cmCMakePresetsGraph.cxx | 44 ++++++++++++++----- Source/cmake.cxx | 40 +++++++++++------ .../BadExitCode-stderr.txt | 3 +- .../FirstStepNotConfigure-stderr.txt | 2 +- .../NonexistentStep-stderr.txt | 2 +- .../SecondStepConfigure-stderr.txt | 2 +- .../UnreachableStep-stderr.txt | 2 +- .../WorkflowStepDisabled-stderr.txt | 4 +- .../WorkflowStepHidden-stderr.txt | 4 +- .../WorkflowStepInvalidMacro-stderr.txt | 2 +- 12 files changed, 104 insertions(+), 53 deletions(-) diff --git a/Source/cmCMakePresetsErrors.cxx b/Source/cmCMakePresetsErrors.cxx index 36dceaec79..5679eeaa44 100644 --- a/Source/cmCMakePresetsErrors.cxx +++ b/Source/cmCMakePresetsErrors.cxx @@ -235,10 +235,19 @@ void TEST_OUTPUT_TRUNCATION_UNSUPPORTED(cmJSONState* state) "preset support"); } -void INVALID_WORKFLOW_STEPS(std::string const& workflowStep, - cmJSONState* state) +namespace { +std::string WorkflowStepLabel(cm::string_view stepType, + std::string const& stepName) { - state->AddError(cmStrCat("Invalid workflow step \"", workflowStep, '"')); + return cmStrCat("of type \"", stepType, "\" named \"", stepName, '"'); +} +} + +void INVALID_WORKFLOW_STEPS(cm::string_view stepType, + std::string const& stepName, cmJSONState* state) +{ + state->AddError( + cmStrCat("Invalid workflow step ", WorkflowStepLabel(stepType, stepName))); } void WORKFLOW_STEP_CONFIGURE_PRESET_MISMATCH( @@ -266,25 +275,31 @@ void NO_WORKFLOW_STEPS(std::string const& presetName, cmJSONState* state) cmStrCat("No workflow steps specified for \"", presetName, '"')); } -void FIRST_WORKFLOW_STEP_NOT_CONFIGURE(std::string const& stepName, +void FIRST_WORKFLOW_STEP_NOT_CONFIGURE(cm::string_view stepType, + std::string const& stepName, cmJSONState* state) { - state->AddError(cmStrCat("First workflow step \"", stepName, - "\" must be a configure step")); + state->AddError(cmStrCat("First workflow step ", + WorkflowStepLabel(stepType, stepName), + " must be a configure step")); } -void CONFIGURE_WORKFLOW_STEP_NOT_FIRST(std::string const& stepName, +void CONFIGURE_WORKFLOW_STEP_NOT_FIRST(cm::string_view stepType, + std::string const& stepName, cmJSONState* state) { - state->AddError(cmStrCat("Configure workflow step \"", stepName, - "\" must be the first step")); + state->AddError(cmStrCat("Workflow step ", + WorkflowStepLabel(stepType, stepName), + " must be the first step")); } -void WORKFLOW_STEP_UNREACHABLE_FROM_FILE(std::string const& workflowStep, +void WORKFLOW_STEP_UNREACHABLE_FROM_FILE(cm::string_view stepType, + std::string const& stepName, cmJSONState* state) { - state->AddError(cmStrCat("Workflow step \"", workflowStep, - "\" is unreachable from preset's file")); + state->AddError(cmStrCat("Workflow step ", + WorkflowStepLabel(stepType, stepName), + " is unreachable from preset's file")); } void CTEST_JUNIT_UNSUPPORTED(cmJSONState* state) diff --git a/Source/cmCMakePresetsErrors.h b/Source/cmCMakePresetsErrors.h index 14a3e29e8f..fd1ca03578 100644 --- a/Source/cmCMakePresetsErrors.h +++ b/Source/cmCMakePresetsErrors.h @@ -90,8 +90,8 @@ void CYCLIC_INCLUDE(std::string const& file, cmJSONState* state); void TEST_OUTPUT_TRUNCATION_UNSUPPORTED(cmJSONState* state); -void INVALID_WORKFLOW_STEPS(std::string const& workflowStep, - cmJSONState* state); +void INVALID_WORKFLOW_STEPS(cm::string_view stepType, + std::string const& stepName, cmJSONState* state); void WORKFLOW_STEP_CONFIGURE_PRESET_MISMATCH( std::string const& kind, std::string const& workflowStep, @@ -103,13 +103,16 @@ void INVALID_TEST_FILTER_INCLUDE_INDEX(Json::Value const* value, void NO_WORKFLOW_STEPS(std::string const& presetName, cmJSONState* state); -void FIRST_WORKFLOW_STEP_NOT_CONFIGURE(std::string const& stepName, +void FIRST_WORKFLOW_STEP_NOT_CONFIGURE(cm::string_view stepType, + std::string const& stepName, cmJSONState* state); -void CONFIGURE_WORKFLOW_STEP_NOT_FIRST(std::string const& stepName, +void CONFIGURE_WORKFLOW_STEP_NOT_FIRST(cm::string_view stepType, + std::string const& stepName, cmJSONState* state); -void WORKFLOW_STEP_UNREACHABLE_FROM_FILE(std::string const& workflowStep, +void WORKFLOW_STEP_UNREACHABLE_FROM_FILE(cm::string_view stepType, + std::string const& stepName, cmJSONState* state); void CTEST_JUNIT_UNSUPPORTED(cmJSONState* state); diff --git a/Source/cmCMakePresetsGraph.cxx b/Source/cmCMakePresetsGraph.cxx index 9fb27b2eda..e2d1934bd4 100644 --- a/Source/cmCMakePresetsGraph.cxx +++ b/Source/cmCMakePresetsGraph.cxx @@ -64,6 +64,21 @@ using cmCMakePresetsGraphInternal::ExpandMacros; bool gSkipNewLine = true; +char const* WorkflowStepTypeToString(WorkflowPreset::WorkflowStep::Type type) +{ + switch (type) { + case WorkflowPreset::WorkflowStep::Type::Configure: + return "configure"; + case WorkflowPreset::WorkflowStep::Type::Build: + return "build"; + case WorkflowPreset::WorkflowStep::Type::Test: + return "test"; + case WorkflowPreset::WorkflowStep::Type::Package: + return "package"; + } + return ""; +} + void InheritString(std::string& child, std::string const& parent) { if (child.empty()) { @@ -701,6 +716,7 @@ namespace { template bool SetupWorkflowConfigurePreset(T const& preset, ConfigurePreset const*& configurePreset, + char const*, std::string const&, cmJSONState* state) { if (preset.ConfigurePreset != configurePreset->Name) { @@ -715,7 +731,7 @@ bool SetupWorkflowConfigurePreset(T const& preset, template <> bool SetupWorkflowConfigurePreset( ConfigurePreset const& preset, ConfigurePreset const*& configurePreset, - cmJSONState*) + char const*, std::string const&, cmJSONState*) { configurePreset = &preset; return true; @@ -725,20 +741,23 @@ template bool TryReachPresetFromWorkflow( WorkflowPreset const& origin, std::map> const& presets, std::string const& name, - ConfigurePreset const*& configurePreset, cmJSONState* state) + char const* workflowStepType, ConfigurePreset const*& configurePreset, + cmJSONState* state) { auto it = presets.find(name); if (it == presets.end()) { - cmCMakePresetsErrors::INVALID_WORKFLOW_STEPS(name, state); + cmCMakePresetsErrors::INVALID_WORKFLOW_STEPS(workflowStepType, name, + state); return false; } if (!origin.OriginFile->ReachableFiles.count( it->second.Unexpanded.OriginFile)) { - cmCMakePresetsErrors::WORKFLOW_STEP_UNREACHABLE_FROM_FILE(name, state); + cmCMakePresetsErrors::WORKFLOW_STEP_UNREACHABLE_FROM_FILE(workflowStepType, + name, state); return false; } - return SetupWorkflowConfigurePreset(it->second.Unexpanded, - configurePreset, state); + return SetupWorkflowConfigurePreset( + it->second.Unexpanded, configurePreset, workflowStepType, name, state); } } @@ -1382,14 +1401,15 @@ bool cmCMakePresetsGraph::ReadProjectPresetsInternal( ConfigurePreset const* configurePreset = nullptr; for (auto const& step : it.second.Unexpanded.Steps) { + char const* const stepType = WorkflowStepTypeToString(step.PresetType); if (!configurePreset && step.PresetType != Type::Configure) { cmCMakePresetsErrors::FIRST_WORKFLOW_STEP_NOT_CONFIGURE( - step.PresetName, &this->parseState); + stepType, step.PresetName, &this->parseState); return false; } if (configurePreset && step.PresetType == Type::Configure) { cmCMakePresetsErrors::CONFIGURE_WORKFLOW_STEP_NOT_FIRST( - step.PresetName, &this->parseState); + stepType, step.PresetName, &this->parseState); return false; } @@ -1397,22 +1417,22 @@ bool cmCMakePresetsGraph::ReadProjectPresetsInternal( case Type::Configure: result = TryReachPresetFromWorkflow( it.second.Unexpanded, this->ConfigurePresets, step.PresetName, - configurePreset, &this->parseState); + stepType, configurePreset, &this->parseState); break; case Type::Build: result = TryReachPresetFromWorkflow( it.second.Unexpanded, this->BuildPresets, step.PresetName, - configurePreset, &this->parseState); + stepType, configurePreset, &this->parseState); break; case Type::Test: result = TryReachPresetFromWorkflow( - it.second.Unexpanded, this->TestPresets, step.PresetName, + it.second.Unexpanded, this->TestPresets, step.PresetName, stepType, configurePreset, &this->parseState); break; case Type::Package: result = TryReachPresetFromWorkflow( it.second.Unexpanded, this->PackagePresets, step.PresetName, - configurePreset, &this->parseState); + stepType, configurePreset, &this->parseState); break; } if (!result) { diff --git a/Source/cmake.cxx b/Source/cmake.cxx index fa569374e6..40de2f777a 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -4228,38 +4228,46 @@ bool cmake::Open(std::string const& dir, DryRun dryRun) } #if !defined(CMAKE_BOOTSTRAP) +namespace { +std::string WorkflowStepLabel(cm::static_string_view type, + std::string const& name) +{ + return cmStrCat("of type \"", type, "\" named \"", name, '"'); +} +} + template T const* cmake::FindPresetForWorkflow( cm::static_string_view type, std::map> const& presets, cmCMakePresetsGraph::WorkflowPreset::WorkflowStep const& step) { + std::string const stepLabel = WorkflowStepLabel(type, step.PresetName); auto it = presets.find(step.PresetName); if (it == presets.end()) { - cmSystemTools::Error(cmStrCat("No such ", type, " preset in ", - this->GetHomeDirectory(), ": \"", - step.PresetName, '"')); + cmSystemTools::Error(cmStrCat("No such preset for workflow step ", + stepLabel, " in ", + this->GetHomeDirectory())); return nullptr; } if (it->second.Unexpanded.Hidden) { - cmSystemTools::Error(cmStrCat("Cannot use hidden ", type, " preset in ", - this->GetHomeDirectory(), ": \"", - step.PresetName, '"')); + cmSystemTools::Error( + cmStrCat("Cannot use hidden preset for workflow step ", stepLabel, + " in ", this->GetHomeDirectory())); return nullptr; } if (!it->second.Expanded) { - cmSystemTools::Error(cmStrCat("Could not evaluate ", type, " preset \"", - step.PresetName, - "\": Invalid macro expansion")); + cmSystemTools::Error(cmStrCat("Could not evaluate workflow step ", + stepLabel, ": Invalid macro expansion")); return nullptr; } if (!it->second.Expanded->ConditionResult) { - cmSystemTools::Error(cmStrCat("Cannot use disabled ", type, " preset in ", - this->GetHomeDirectory(), ": \"", - step.PresetName, '"')); + cmSystemTools::Error( + cmStrCat("Cannot use disabled preset for workflow step ", stepLabel, + " in ", this->GetHomeDirectory())); return nullptr; } @@ -4437,13 +4445,17 @@ int cmake::Workflow(cmCMakePresetsWorkflowArgs const& args) << std::flush; cmUVProcessChain::Status const status = step.Action(); if (status.ExitStatus != 0) { + cmSystemTools::Error( + cmStrCat("Workflow step ", WorkflowStepLabel(step.Type, step.Name), + " failed with exit code ", status.ExitStatus)); exitStatus = static_cast(status.ExitStatus); break; } auto const codeReasonPair = status.GetException(); if (codeReasonPair.first != cmUVProcessChain::ExceptionCode::None) { - std::cout << "Step command ended abnormally: " << codeReasonPair.second - << std::endl; + cmSystemTools::Error( + cmStrCat("Workflow step ", WorkflowStepLabel(step.Type, step.Name), + " command ended abnormally: ", codeReasonPair.second)); exitStatus = status.SpawnResult != 0 ? status.SpawnResult : status.TermSignal; break; diff --git a/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-stderr.txt index 0690c694ba..6f597ff7f5 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-stderr.txt +++ b/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode-stderr.txt @@ -1,4 +1,5 @@ ^Errors while running CTest Output from these tests are in: [^ ]*/Tests/RunCMake/CMakePresetsWorkflow/BadExitCode/build/Testing/Temporary/LastTest\.log -Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely\.$ +Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely\. +CMake Error: Workflow step of type "test" named "default" failed with exit code 8$ diff --git a/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-stderr.txt index 776257bd93..cfc3a4a8ca 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-stderr.txt +++ b/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure-stderr.txt @@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresetsWorkflow/FirstStepNotConfigure: -First workflow step "default" must be a configure step$ +First workflow step of type "build" named "default" must be a configure step$ diff --git a/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-stderr.txt index a9029e116b..5f09fed81a 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-stderr.txt +++ b/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep-stderr.txt @@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresetsWorkflow/NonexistentStep: -Invalid workflow step "default"$ +Invalid workflow step of type "configure" named "default"$ diff --git a/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-stderr.txt index 35eac16c38..f211a71e4e 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-stderr.txt +++ b/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure-stderr.txt @@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresetsWorkflow/SecondStepConfigure: -Configure workflow step "default" must be the first step +Workflow step of type "configure" named "default" must be the first step diff --git a/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-stderr.txt index f0a36f898a..711cdb49ae 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-stderr.txt +++ b/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep-stderr.txt @@ -1,3 +1,3 @@ ^CMake Error: Could not read presets from [^ ]*/Tests/RunCMake/CMakePresetsWorkflow/UnreachableStep: -Workflow step "default" is unreachable from preset's file$ +Workflow step of type "configure" named "default" is unreachable from preset's file$ diff --git a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled-stderr.txt index b598b272f1..1fd0dde853 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled-stderr.txt +++ b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled-stderr.txt @@ -1,2 +1,2 @@ -^CMake Error: Cannot use disabled configure preset in [^ -]*/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled: "default"$ +^CMake Error: Cannot use disabled preset for workflow step of type "configure" named "default" in [^ +]*/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepDisabled$ diff --git a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden-stderr.txt index 838ded5141..38b361a4c3 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden-stderr.txt +++ b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden-stderr.txt @@ -1,2 +1,2 @@ -^CMake Error: Cannot use hidden configure preset in [^ -]*/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden: "default"$ +^CMake Error: Cannot use hidden preset for workflow step of type "configure" named "default" in [^ +]*/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepHidden$ diff --git a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro-stderr.txt b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro-stderr.txt index f132a93364..aa1326aa95 100644 --- a/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro-stderr.txt +++ b/Tests/RunCMake/CMakePresetsWorkflow/WorkflowStepInvalidMacro-stderr.txt @@ -1 +1 @@ -^CMake Error: Could not evaluate configure preset "default": Invalid macro expansion$ +^CMake Error: Could not evaluate workflow step of type "configure" named "default": Invalid macro expansion$