diff --git a/Source/CPack/cpack.cxx b/Source/CPack/cpack.cxx index 7dd676a4ce..1ae57ef79f 100644 --- a/Source/CPack/cpack.cxx +++ b/Source/CPack/cpack.cxx @@ -313,9 +313,9 @@ int main(int argc, char const* const* argv) auto resolveResult = presetsGraph.ResolvePreset( presetsArgs.PresetName, presetsGraph.PackagePresets); auto resolveError = cmCMakePresetsGraph::FormatPresetError< - cmCMakePresetsGraph::PackagePreset>(resolveResult.StatusCode, - resolveResult.ErrorPresetName, - workingDirectory); + cmCMakePresetsGraph::PackagePreset>( + resolveResult.StatusCode, resolveResult.ErrorPresetName, + resolveResult.Suggestion, workingDirectory); if (resolveError) { cmCPack_Log(&log, cmCPackLog::LOG_ERROR, *resolveError << "\n"); presetsGraph.PrintPackagePresetList( diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx index 353bfed8e9..bdc7545d81 100644 --- a/Source/CTest/cmCTestBuildCommand.cxx +++ b/Source/CTest/cmCTestBuildCommand.cxx @@ -139,7 +139,7 @@ std::unique_ptr cmCTestBuildCommand::InitializeHandler( presetsGraph.ResolvePreset(effectivePreset, presetsGraph.BuildPresets); auto resolveError = cmCMakePresetsGraph::FormatPresetError( resolveResult.StatusCode, resolveResult.ErrorPresetName, - sourceDirectory); + resolveResult.Suggestion, sourceDirectory); if (resolveError) { status.SetError(*resolveError); return nullptr; diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx index 1d7201853a..91868cb051 100644 --- a/Source/CTest/cmCTestConfigureCommand.cxx +++ b/Source/CTest/cmCTestConfigureCommand.cxx @@ -75,7 +75,7 @@ bool ConstructConfigureCommand( auto resolveError = cmCMakePresetsGraph::FormatPresetError( resolveResult.StatusCode, resolveResult.ErrorPresetName, - sourceDirectory); + resolveResult.Suggestion, sourceDirectory); if (resolveError) { status.SetError(*resolveError); return false; diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx index a6063baaf0..e3992f039a 100644 --- a/Source/CTest/cmCTestTestCommand.cxx +++ b/Source/CTest/cmCTestTestCommand.cxx @@ -95,7 +95,7 @@ cmCTestTestCommand::ResolveTestPreset(cmMakefile& mf, resolved.EffectivePreset, resolved.PresetsGraph->TestPresets); auto resolveError = cmCMakePresetsGraph::FormatPresetError( resolveResult.StatusCode, resolveResult.ErrorPresetName, - resolved.SourceDirectory); + resolveResult.Suggestion, resolved.SourceDirectory); if (resolveError) { status.SetError(*resolveError); return cm::nullopt; diff --git a/Source/cmCMakePresetsGraph.h b/Source/cmCMakePresetsGraph.h index b6c08538b9..d2f39a249d 100644 --- a/Source/cmCMakePresetsGraph.h +++ b/Source/cmCMakePresetsGraph.h @@ -426,6 +426,7 @@ public: Status StatusCode = Status::Success; std::string ErrorPresetName; + std::string Suggestion; T const* Preset = nullptr; }; @@ -439,7 +440,7 @@ public: template static cm::optional FormatPresetError( PresetResolveStatus status, std::string const& errorPresetName, - std::string const& directory); + std::string const& suggestion, std::string const& directory); template bool ResolveDependentPresets(std::map>& presets); @@ -573,24 +574,24 @@ cmCMakePresetsGraph::ResolvePreset( extern template cm::optional cmCMakePresetsGraph::FormatPresetError( cmCMakePresetsGraph::PresetResolveStatus, std::string const&, - std::string const&); + std::string const&, std::string const&); extern template cm::optional cmCMakePresetsGraph::FormatPresetError( cmCMakePresetsGraph::PresetResolveStatus, std::string const&, - std::string const&); + std::string const&, std::string const&); extern template cm::optional cmCMakePresetsGraph::FormatPresetError( cmCMakePresetsGraph::PresetResolveStatus, std::string const&, - std::string const&); + std::string const&, std::string const&); extern template cm::optional cmCMakePresetsGraph::FormatPresetError( cmCMakePresetsGraph::PresetResolveStatus, std::string const&, - std::string const&); + std::string const&, std::string const&); extern template cm::optional cmCMakePresetsGraph::FormatPresetError( cmCMakePresetsGraph::PresetResolveStatus, std::string const&, - std::string const&); + std::string const&, std::string const&); diff --git a/Source/cmCMakePresetsGraphResolve.cxx b/Source/cmCMakePresetsGraphResolve.cxx index 88c73bf62f..be0e8f9d9b 100644 --- a/Source/cmCMakePresetsGraphResolve.cxx +++ b/Source/cmCMakePresetsGraphResolve.cxx @@ -2,6 +2,8 @@ file LICENSE.rst or https://cmake.org/licensing for details. */ #include #include +#include +#include #include @@ -21,6 +23,13 @@ cmCMakePresetsGraph::PresetResolveResult cmCMakePresetsGraph::ResolvePreset( if (presetPair == presets.end()) { result.StatusCode = Result::Status::NotFound; result.ErrorPresetName = presetName; + std::vector candidates; + for (auto const& pair : presets) { + if (!pair.second.Unexpanded.Hidden) { + candidates.push_back(pair.first); + } + } + result.Suggestion = cmFindClosestString(presetName, candidates); return result; } if (presetPair->second.Unexpanded.Hidden) { @@ -82,13 +91,19 @@ cmCMakePresetsGraph::ResolvePreset( template cm::optional cmCMakePresetsGraph::FormatPresetError( PresetResolveStatus status, std::string const& errorPresetName, - std::string const& directory) + std::string const& suggestion, std::string const& directory) { using Status = PresetResolveStatus; switch (status) { - case Status::NotFound: - return cmStrCat("No such ", T::kind(), " preset in ", directory, ": \"", - errorPresetName, '"'); + case Status::NotFound: { + std::string error = cmStrCat("No such ", T::kind(), " preset in ", + directory, ": \"", errorPresetName, '"'); + if (!suggestion.empty()) { + error = + cmStrCat(std::move(error), ". Did you mean: \"", suggestion, "\"?"); + } + return error; + } case Status::Hidden: return cmStrCat("Cannot use hidden ", T::kind(), " preset in ", directory, ": \"", errorPresetName, '"'); @@ -108,20 +123,21 @@ cm::optional cmCMakePresetsGraph::FormatPresetError( // Explicit template instantiations for FormatPresetError template cm::optional cmCMakePresetsGraph::FormatPresetError( - PresetResolveStatus, std::string const&, std::string const&); + PresetResolveStatus, std::string const&, std::string const&, + std::string const&); -template cm::optional -cmCMakePresetsGraph::FormatPresetError( - PresetResolveStatus, std::string const&, std::string const&); +template cm::optional cmCMakePresetsGraph::FormatPresetError< + cmCMakePresetsGraph::BuildPreset>(PresetResolveStatus, std::string const&, + std::string const&, std::string const&); -template cm::optional -cmCMakePresetsGraph::FormatPresetError( - PresetResolveStatus, std::string const&, std::string const&); +template cm::optional cmCMakePresetsGraph::FormatPresetError< + cmCMakePresetsGraph::TestPreset>(PresetResolveStatus, std::string const&, + std::string const&, std::string const&); -template cm::optional -cmCMakePresetsGraph::FormatPresetError( - PresetResolveStatus, std::string const&, std::string const&); +template cm::optional cmCMakePresetsGraph::FormatPresetError< + cmCMakePresetsGraph::PackagePreset>(PresetResolveStatus, std::string const&, + std::string const&, std::string const&); -template cm::optional -cmCMakePresetsGraph::FormatPresetError( - PresetResolveStatus, std::string const&, std::string const&); +template cm::optional cmCMakePresetsGraph::FormatPresetError< + cmCMakePresetsGraph::WorkflowPreset>(PresetResolveStatus, std::string const&, + std::string const&, std::string const&); diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx index a26cece9ac..73b8e5fcd5 100644 --- a/Source/cmCTest.cxx +++ b/Source/cmCTest.cxx @@ -833,7 +833,7 @@ int cmCTest::ProcessSteps() cmCMakePresetsGraph::FormatPresetError< cmCMakePresetsGraph::ConfigurePreset>( resolveResult.StatusCode, resolveResult.ErrorPresetName, - sourceDir); + resolveResult.Suggestion, sourceDir); if (resolveError) { cmCTestLog(this, ERROR_MESSAGE, *resolveError, '\n'); return 12; @@ -1660,7 +1660,7 @@ bool cmCTest::SetArgsFromPreset(cmCMakePresetsArgs const& args) auto resolveError = cmCMakePresetsGraph::FormatPresetError( resolveResult.StatusCode, resolveResult.ErrorPresetName, - workingDirectory); + resolveResult.Suggestion, workingDirectory); if (resolveError) { cmSystemTools::Error(*resolveError); cmake cm(cmState::Role::CTest); diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 4a6486993e..dbba4c3a1e 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -2121,7 +2121,7 @@ bool cmake::SetArgsFromPreset(cmCMakePresetsConfigureArgs const& args, using S = cmCMakePresetsGraph::PresetResolveStatus; auto resolveError = cmCMakePresetsGraph::FormatPresetError( resolveResult.StatusCode, resolveResult.ErrorPresetName, - this->GetHomeDirectory()); + resolveResult.Suggestion, this->GetHomeDirectory()); if (resolveError) { cmSystemTools::Error(*resolveError); if (resolveResult.StatusCode == S::NotFound || @@ -4081,7 +4081,7 @@ int cmake::Build(cmBuildArgs buildArgs, std::vector targets, auto resolveError = cmCMakePresetsGraph::FormatPresetError( resolveResult.StatusCode, resolveResult.ErrorPresetName, - this->GetHomeDirectory()); + resolveResult.Suggestion, this->GetHomeDirectory()); if (resolveError) { cmSystemTools::Error(*resolveError); settingsFile.PrintBuildPresetList(); @@ -4465,9 +4465,9 @@ int cmake::Workflow(cmCMakePresetsWorkflowArgs const& args) auto resolveResult = settingsFile.ResolvePreset(args.PresetName, settingsFile.WorkflowPresets); auto resolveError = cmCMakePresetsGraph::FormatPresetError< - cmCMakePresetsGraph::WorkflowPreset>(resolveResult.StatusCode, - resolveResult.ErrorPresetName, - this->GetHomeDirectory()); + cmCMakePresetsGraph::WorkflowPreset>( + resolveResult.StatusCode, resolveResult.ErrorPresetName, + resolveResult.Suggestion, this->GetHomeDirectory()); if (resolveError) { cmSystemTools::Error(*resolveError); settingsFile.PrintWorkflowPresetList(); diff --git a/Tests/RunCMake/CMakePresets/NoSuchPresetDidYouMean-result.txt b/Tests/RunCMake/CMakePresets/NoSuchPresetDidYouMean-result.txt new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/Tests/RunCMake/CMakePresets/NoSuchPresetDidYouMean-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/CMakePresets/NoSuchPresetDidYouMean-stderr.txt b/Tests/RunCMake/CMakePresets/NoSuchPresetDidYouMean-stderr.txt new file mode 100644 index 0000000000..d1d22297df --- /dev/null +++ b/Tests/RunCMake/CMakePresets/NoSuchPresetDidYouMean-stderr.txt @@ -0,0 +1,2 @@ +^CMake Error: No such configure preset in [^ +]*/Tests/RunCMake/CMakePresets/NoSuchPresetDidYouMean: "GoodNoArg"\. Did you mean: "GoodNoArgs"\?$ diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake index 3eac4a5357..03e49d61bf 100644 --- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake @@ -350,6 +350,9 @@ endif() # Test bad command line arguments run_cmake_presets(NoSuchPreset) +set(CMakePresets_NO_PRESET 1) +run_cmake_presets(NoSuchPresetDidYouMean --preset=GoodNoArg) +unset(CMakePresets_NO_PRESET) run_cmake_presets(NoPresetArgument --preset) run_cmake_presets(NoPresetArgumentEq --preset= -DA=B) run_cmake_presets(UseHiddenPreset)