From 63b6e7d78580b8d3f52d148c2f71ee715f99ca8e Mon Sep 17 00:00:00 2001 From: Zack Galbreath Date: Tue, 26 May 2026 20:38:16 -0400 Subject: [PATCH] ctest: allow dashboard clients to specify presets via variables Add variables to support specifying PRESET and PRESETS_FILE to dashboard clients from the command-line. --- Help/command/ctest_build.rst | 9 ++- Help/command/ctest_configure.rst | 9 ++- Help/command/ctest_test.rst | 11 +++- Help/manual/cmake-variables.7.rst | 5 ++ Help/release/dev/ctest-script-preset-arg.rst | 9 ++- Help/variable/CTEST_BUILD_PRESET.rst | 10 ++++ Help/variable/CTEST_CONFIGURE_PRESET.rst | 10 ++++ Help/variable/CTEST_PRESET.rst | 22 ++++++++ Help/variable/CTEST_PRESETS_FILE.rst | 16 ++++++ Help/variable/CTEST_TEST_PRESET.rst | 11 ++++ Source/CTest/cmCTestBuildCommand.cxx | 56 +++++++++++++++---- Source/CTest/cmCTestConfigureCommand.cxx | 22 +++++++- Source/CTest/cmCTestHandlerCommand.cxx | 54 ++++++++++++++++++ Source/CTest/cmCTestHandlerCommand.h | 16 ++++++ Source/CTest/cmCTestTestCommand.cxx | 54 +++++++++++++++--- .../BuildPresetFromFileVar-check.cmake | 14 +++++ .../BuildPresetGenericVar-check.cmake | 10 ++++ .../ctest_build/BuildPresetVar-check.cmake | 10 ++++ .../RunCMake/ctest_build/CMakePresets.json.in | 4 ++ Tests/RunCMake/ctest_build/RunCMakeTest.cmake | 35 ++++++++++++ .../ConfigurePresetFromFileVar-check.cmake | 29 ++++++++++ .../ConfigurePresetGenericVar-check.cmake | 25 +++++++++ .../ConfigurePresetVar-check.cmake | 25 +++++++++ .../ctest_configure/RunCMakeTest.cmake | 32 +++++++++++ Tests/RunCMake/ctest_configure/test.cmake.in | 1 + .../RunCMake/ctest_test/CMakePresets.json.in | 4 ++ Tests/RunCMake/ctest_test/RunCMakeTest.cmake | 23 ++++++++ .../TestPresetFromFileVar-stdout.txt | 4 ++ .../TestPresetGenericVar-stderr.txt | 1 + .../TestPresetGenericVar-stdout.txt | 4 ++ .../ctest_test/TestPresetVar-stdout.txt | 4 ++ 31 files changed, 513 insertions(+), 26 deletions(-) create mode 100644 Help/variable/CTEST_BUILD_PRESET.rst create mode 100644 Help/variable/CTEST_CONFIGURE_PRESET.rst create mode 100644 Help/variable/CTEST_PRESET.rst create mode 100644 Help/variable/CTEST_PRESETS_FILE.rst create mode 100644 Help/variable/CTEST_TEST_PRESET.rst create mode 100644 Tests/RunCMake/ctest_build/BuildPresetFromFileVar-check.cmake create mode 100644 Tests/RunCMake/ctest_build/BuildPresetGenericVar-check.cmake create mode 100644 Tests/RunCMake/ctest_build/BuildPresetVar-check.cmake create mode 100644 Tests/RunCMake/ctest_configure/ConfigurePresetFromFileVar-check.cmake create mode 100644 Tests/RunCMake/ctest_configure/ConfigurePresetGenericVar-check.cmake create mode 100644 Tests/RunCMake/ctest_configure/ConfigurePresetVar-check.cmake create mode 100644 Tests/RunCMake/ctest_test/TestPresetFromFileVar-stdout.txt create mode 100644 Tests/RunCMake/ctest_test/TestPresetGenericVar-stderr.txt create mode 100644 Tests/RunCMake/ctest_test/TestPresetGenericVar-stdout.txt create mode 100644 Tests/RunCMake/ctest_test/TestPresetVar-stdout.txt diff --git a/Help/command/ctest_build.rst b/Help/command/ctest_build.rst index 8a83107b0d..07ceadcff1 100644 --- a/Help/command/ctest_build.rst +++ b/Help/command/ctest_build.rst @@ -86,13 +86,20 @@ The options are: argument will override the :preset:`buildPresets.targets` setting from the chosen preset. + See also the :variable:`CTEST_BUILD_PRESET` and + :variable:`CTEST_PRESET` variables. + ``PRESETS_FILE `` .. versionadded:: 4.4 Specify a :manual:`presets ` file to use instead of the default ``CMakePresets.json`` in the source directory. A relative path is interpreted relative to the source directory. - Has no effect unless ``PRESET`` is also specified. + Has no effect unless a preset is selected via the ``PRESET`` argument + or the :variable:`CTEST_BUILD_PRESET` or :variable:`CTEST_PRESET` + variables. + + See also the :variable:`CTEST_PRESETS_FILE` variable. ``RETURN_VALUE `` Store the return value of the native build tool in the given variable. diff --git a/Help/command/ctest_configure.rst b/Help/command/ctest_configure.rst index 7a273cd556..431787543e 100644 --- a/Help/command/ctest_configure.rst +++ b/Help/command/ctest_configure.rst @@ -49,13 +49,20 @@ The options are: This option is ignored when :variable:`CTEST_CONFIGURE_COMMAND` is used. + See also the :variable:`CTEST_CONFIGURE_PRESET` and + :variable:`CTEST_PRESET` variables. + ``PRESETS_FILE `` .. versionadded:: 4.4 Specify a :manual:`presets ` file to use instead of the default ``CMakePresets.json`` in the source directory. A relative path is interpreted relative to the source directory. - Has no effect unless ``PRESET`` is also specified. + Has no effect unless a preset is selected via the ``PRESET`` argument + or the :variable:`CTEST_CONFIGURE_PRESET` or :variable:`CTEST_PRESET` + variables. + + See also the :variable:`CTEST_PRESETS_FILE` variable. ``RETURN_VALUE `` Store in the ```` variable the return value of the native diff --git a/Help/command/ctest_test.rst b/Help/command/ctest_test.rst index ed44aa452c..e5a174cdbf 100644 --- a/Help/command/ctest_test.rst +++ b/Help/command/ctest_test.rst @@ -201,13 +201,22 @@ The options are: the :preset:`filter.include.name ` setting from the chosen preset. + This option also applies to the :command:`ctest_memcheck` command. + See also the :variable:`CTEST_TEST_PRESET` and + :variable:`CTEST_PRESET` variables. + ``PRESETS_FILE `` .. versionadded:: 4.4 Specify a :manual:`presets ` file to use instead of the default ``CMakePresets.json`` in the source directory. A relative path is interpreted relative to the source directory. - Has no effect unless ``PRESET`` is also specified. + Has no effect unless a preset is selected via the ``PRESET`` argument + or the :variable:`CTEST_TEST_PRESET` or :variable:`CTEST_PRESET` + variables. + + This option also applies to the :command:`ctest_memcheck` command. + See also the :variable:`CTEST_PRESETS_FILE` variable. ``QUIET`` .. versionadded:: 3.3 diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst index 9b9208f638..cf17060117 100644 --- a/Help/manual/cmake-variables.7.rst +++ b/Help/manual/cmake-variables.7.rst @@ -738,12 +738,14 @@ Variables for CTest /variable/CTEST_BINARY_DIRECTORY /variable/CTEST_BUILD_COMMAND /variable/CTEST_BUILD_NAME + /variable/CTEST_BUILD_PRESET /variable/CTEST_BZR_COMMAND /variable/CTEST_BZR_UPDATE_OPTIONS /variable/CTEST_CHANGE_ID /variable/CTEST_CHECKOUT_COMMAND /variable/CTEST_CONFIGURATION_TYPE /variable/CTEST_CONFIGURE_COMMAND + /variable/CTEST_CONFIGURE_PRESET /variable/CTEST_COVERAGE_COMMAND /variable/CTEST_COVERAGE_EXTRA_FLAGS /variable/CTEST_CUSTOM_COVERAGE_EXCLUDE @@ -792,6 +794,8 @@ Variables for CTest /variable/CTEST_P4_COMMAND /variable/CTEST_P4_OPTIONS /variable/CTEST_P4_UPDATE_OPTIONS + /variable/CTEST_PRESET + /variable/CTEST_PRESETS_FILE /variable/CTEST_RESOURCE_SPEC_FILE /variable/CTEST_RUN_CURRENT_SCRIPT /variable/CTEST_SCRIPT_DIRECTORY @@ -803,6 +807,7 @@ Variables for CTest /variable/CTEST_SVN_OPTIONS /variable/CTEST_SVN_UPDATE_OPTIONS /variable/CTEST_TEST_LOAD + /variable/CTEST_TEST_PRESET /variable/CTEST_TEST_TIMEOUT /variable/CTEST_TEST_COVERAGE_TOOL /variable/CTEST_TLS_VERIFY diff --git a/Help/release/dev/ctest-script-preset-arg.rst b/Help/release/dev/ctest-script-preset-arg.rst index 7732677dae..5d0530f49c 100644 --- a/Help/release/dev/ctest-script-preset-arg.rst +++ b/Help/release/dev/ctest-script-preset-arg.rst @@ -1,7 +1,14 @@ ctest-script-preset-arg ------------------------ -* The :command:`ctest_configure`, :command:`ctest_build`, +* The :command:`ctest_configure`, :command:`ctest_build`, :command:`ctest_test`, and :command:`ctest_memcheck` commands gained ``PRESET`` and ``PRESETS_FILE`` arguments to support using :manual:`presets ` for their :ref:`Dashboard Client` steps. + +* The :variable:`CTEST_CONFIGURE_PRESET`, :variable:`CTEST_BUILD_PRESET`, + :variable:`CTEST_TEST_PRESET`, :variable:`CTEST_PRESET`, and + :variable:`CTEST_PRESETS_FILE` variables may be used to specify preset + arguments to the above commands in a :ref:`Dashboard Client` script or + on the :program:`ctest` command line via the + :ref:`-D ` option. diff --git a/Help/variable/CTEST_BUILD_PRESET.rst b/Help/variable/CTEST_BUILD_PRESET.rst new file mode 100644 index 0000000000..8803b3fb94 --- /dev/null +++ b/Help/variable/CTEST_BUILD_PRESET.rst @@ -0,0 +1,10 @@ +CTEST_BUILD_PRESET +------------------ + +.. versionadded:: 4.4 + +Specify the ``PRESET`` option for the :command:`ctest_build` command +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ref:`-D ` option. + +See also :variable:`CTEST_PRESET` and :variable:`CTEST_PRESETS_FILE`. diff --git a/Help/variable/CTEST_CONFIGURE_PRESET.rst b/Help/variable/CTEST_CONFIGURE_PRESET.rst new file mode 100644 index 0000000000..58028bc31e --- /dev/null +++ b/Help/variable/CTEST_CONFIGURE_PRESET.rst @@ -0,0 +1,10 @@ +CTEST_CONFIGURE_PRESET +---------------------- + +.. versionadded:: 4.4 + +Specify the ``PRESET`` option for the :command:`ctest_configure` command +in a :manual:`ctest(1)` :ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ref:`-D ` option. + +See also :variable:`CTEST_PRESET` and :variable:`CTEST_PRESETS_FILE`. diff --git a/Help/variable/CTEST_PRESET.rst b/Help/variable/CTEST_PRESET.rst new file mode 100644 index 0000000000..a08ea5594e --- /dev/null +++ b/Help/variable/CTEST_PRESET.rst @@ -0,0 +1,22 @@ +CTEST_PRESET +------------ + +.. versionadded:: 4.4 + +Specify a preset name to use for all relevant :manual:`ctest(1)` +:ref:`Dashboard Client` steps in a dashboard script, +or on the :program:`ctest` command line via the :ref:`-D ` option. + +This variable sets the ``PRESET`` option for :command:`ctest_configure`, +:command:`ctest_build`, :command:`ctest_test`, and :command:`ctest_memcheck` +when those commands are not given an explicit ``PRESET`` argument and the +corresponding command-specific variable +(:variable:`CTEST_CONFIGURE_PRESET`, :variable:`CTEST_BUILD_PRESET`, or +:variable:`CTEST_TEST_PRESET`) is also unset. + +For :command:`ctest_configure`, an error is raised if no configure preset +with this name exists. For :command:`ctest_build`, :command:`ctest_test`, +and :command:`ctest_memcheck`, a warning is emitted and the variable is +ignored if no preset of the matching type exists with this name. + +See also :variable:`CTEST_PRESETS_FILE`. diff --git a/Help/variable/CTEST_PRESETS_FILE.rst b/Help/variable/CTEST_PRESETS_FILE.rst new file mode 100644 index 0000000000..3345d7e56c --- /dev/null +++ b/Help/variable/CTEST_PRESETS_FILE.rst @@ -0,0 +1,16 @@ +CTEST_PRESETS_FILE +------------------ + +.. versionadded:: 4.4 + +Specify the ``PRESETS_FILE`` option for all relevant :manual:`ctest(1)` +:ref:`Dashboard Client` step commands in a dashboard script, +or on the :program:`ctest` command line via the :ref:`-D ` option. + +This variable sets the ``PRESETS_FILE`` option for +:command:`ctest_configure`, :command:`ctest_build`, :command:`ctest_test`, +and :command:`ctest_memcheck` when those commands are not given an explicit +``PRESETS_FILE`` argument. + +See also :variable:`CTEST_PRESET`, :variable:`CTEST_CONFIGURE_PRESET`, +:variable:`CTEST_BUILD_PRESET`, and :variable:`CTEST_TEST_PRESET`. diff --git a/Help/variable/CTEST_TEST_PRESET.rst b/Help/variable/CTEST_TEST_PRESET.rst new file mode 100644 index 0000000000..cbbfcf5fe2 --- /dev/null +++ b/Help/variable/CTEST_TEST_PRESET.rst @@ -0,0 +1,11 @@ +CTEST_TEST_PRESET +----------------- + +.. versionadded:: 4.4 + +Specify the ``PRESET`` option for the :command:`ctest_test` and +:command:`ctest_memcheck` commands in a :manual:`ctest(1)` +:ref:`Dashboard Client` script, +or on the :program:`ctest` command line via the :ref:`-D ` option. + +See also :variable:`CTEST_PRESET` and :variable:`CTEST_PRESETS_FILE`. diff --git a/Source/CTest/cmCTestBuildCommand.cxx b/Source/CTest/cmCTestBuildCommand.cxx index 1000d6ed27..70ffdbab81 100644 --- a/Source/CTest/cmCTestBuildCommand.cxx +++ b/Source/CTest/cmCTestBuildCommand.cxx @@ -73,18 +73,54 @@ std::unique_ptr cmCTestBuildCommand::InitializeHandler( ? args.Target : mf.GetSafeDefinition("CTEST_BUILD_TARGET"); + // Preset name is set according to the following priority order: + // 1) The PRESET option to ctest_build() + // 2) CTEST_BUILD_PRESET script variable + // 3) CTEST_PRESET script variable (a warning is emitted if no build preset + // exists with this name) + std::string const sourceDirectory = + mf.GetSafeDefinition("CTEST_SOURCE_DIRECTORY"); + + // Presets file is set according to the following priority order: + // 1) The PRESETS_FILE option to ctest_build() + // 2) CTEST_PRESETS_FILE script variable + std::string const rawPresetsFile = !args.PresetsFile.empty() + ? args.PresetsFile + : mf.GetSafeDefinition("CTEST_PRESETS_FILE"); + + std::string const presetsFile = rawPresetsFile.empty() + ? "" + : cmSystemTools::CollapseFullPath(rawPresetsFile, sourceDirectory); + + std::string effectivePreset = !args.Preset.empty() ? args.Preset + : cmNonempty(mf.GetDefinition("CTEST_BUILD_PRESET")) + ? *mf.GetDefinition("CTEST_BUILD_PRESET") + : ""; + if (effectivePreset.empty()) { + cmValue v = mf.GetDefinition("CTEST_PRESET"); + if (cmNonempty(v)) { + std::string presetError; + auto presetCheck = + BuildPresetExists(*v, sourceDirectory, presetsFile, presetError); + if (presetCheck == PresetCheckResult::ReadError) { + status.SetError(cmStrCat('\n', presetError)); + return nullptr; + } + if (presetCheck == PresetCheckResult::Found) { + effectivePreset = *v; + } else { + cmCTestLog(this->CTest, WARNING, + "No build preset named \"" + << *v << "\" found, ignoring CTEST_PRESET." << std::endl); + } + } + } + cmValue ctestBuildCommand = mf.GetDefinition("CTEST_BUILD_COMMAND"); if (cmNonempty(ctestBuildCommand)) { this->CTest->SetCTestConfiguration("MakeCommand", *ctestBuildCommand, args.Quiet); - } else if (!args.Preset.empty()) { - std::string const sourceDirectory = - mf.GetSafeDefinition("CTEST_SOURCE_DIRECTORY"); - - std::string const presetsFile = args.PresetsFile.empty() - ? "" - : cmSystemTools::CollapseFullPath(args.PresetsFile, sourceDirectory); - + } else if (!effectivePreset.empty()) { cmCMakePresetsGraph presetsGraph; if (!presetsGraph.ReadProjectPresets(sourceDirectory, presetsFile)) { status.SetError(cmStrCat("\n Could not read presets from \"", @@ -94,7 +130,7 @@ std::unique_ptr cmCTestBuildCommand::InitializeHandler( } auto resolveResult = - presetsGraph.ResolvePreset(args.Preset, presetsGraph.BuildPresets); + presetsGraph.ResolvePreset(effectivePreset, presetsGraph.BuildPresets); auto resolveError = cmCMakePresetsGraph::FormatPresetError( resolveResult.StatusCode, resolveResult.ErrorPresetName, sourceDirectory); @@ -106,7 +142,7 @@ std::unique_ptr cmCTestBuildCommand::InitializeHandler( std::string buildCommand = cmStrCat('"', cmSystemTools::GetCMakeCommand(), '"'); buildCommand += " --build . --preset \""; - buildCommand += args.Preset; + buildCommand += effectivePreset; buildCommand += "\""; if (!presetsFile.empty()) { diff --git a/Source/CTest/cmCTestConfigureCommand.cxx b/Source/CTest/cmCTestConfigureCommand.cxx index 1f76abc88f..ac24e90744 100644 --- a/Source/CTest/cmCTestConfigureCommand.cxx +++ b/Source/CTest/cmCTestConfigureCommand.cxx @@ -191,14 +191,30 @@ bool cmCTestConfigureCommand::ExecuteConfigure(ConfigureArguments const& args, return false; } - std::string const presetsFile = args.PresetsFile.empty() + // Preset name is set according to the following priority order: + // 1) The PRESET option to ctest_configure() + // 2) CTEST_CONFIGURE_PRESET script variable + // 3) CTEST_PRESET script variable (error if no such configure preset exists) + std::string const presetName = !args.Preset.empty() ? args.Preset + : cmNonempty(mf.GetDefinition("CTEST_CONFIGURE_PRESET")) + ? *mf.GetDefinition("CTEST_CONFIGURE_PRESET") + : mf.GetSafeDefinition("CTEST_PRESET"); + + // Presets file is set according to the following priority order: + // 1) The PRESETS_FILE option to ctest_configure() + // 2) CTEST_PRESETS_FILE script variable + std::string const rawPresetsFile = !args.PresetsFile.empty() + ? args.PresetsFile + : mf.GetSafeDefinition("CTEST_PRESETS_FILE"); + + std::string const presetsFile = rawPresetsFile.empty() ? "" - : cmSystemTools::CollapseFullPath(args.PresetsFile, sourceDirectory); + : cmSystemTools::CollapseFullPath(rawPresetsFile, sourceDirectory); std::string configureCommand = mf.GetDefinition("CTEST_CONFIGURE_COMMAND"); if (configureCommand.empty() && !ConstructConfigureCommand(status, mf, sourceDirectory, buildDirectory, - args.Options, args.Preset, presetsFile, + args.Options, presetName, presetsFile, configureCommand)) { return false; } diff --git a/Source/CTest/cmCTestHandlerCommand.cxx b/Source/CTest/cmCTestHandlerCommand.cxx index b8e6a03d23..a85386ea75 100644 --- a/Source/CTest/cmCTestHandlerCommand.cxx +++ b/Source/CTest/cmCTestHandlerCommand.cxx @@ -8,9 +8,11 @@ #include +#include "cmCMakePresetsGraph.h" #include "cmCTest.h" #include "cmCTestGenericHandler.h" #include "cmExecutionStatus.h" +#include "cmJSONState.h" #include "cmMakefile.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" @@ -216,3 +218,55 @@ void cmCTestHandlerCommand::ProcessAdditionalValues(cmCTestGenericHandler*, cmExecutionStatus&) const { } + +namespace { +enum class PresetType +{ + Build, + Test, +}; + +cmCTestHandlerCommand::PresetCheckResult PresetExistsInGraph( + cmCMakePresetsGraph& graph, std::string const& name, + std::string const& sourceDir, std::string const& presetsFile, + PresetType presetType, std::string& errorMessage) +{ + using Result = cmCTestHandlerCommand::PresetCheckResult; + if (!graph.ReadProjectPresets(sourceDir, presetsFile)) { + errorMessage = cmStrCat("Could not read presets from \"", sourceDir, + "\":\n ", graph.parseState.GetErrorMessage()); + return Result::ReadError; + } + using Status = cmCMakePresetsGraph::PresetResolveStatus; + if (presetType == PresetType::Build) { + auto result = graph.ResolvePreset(name, graph.BuildPresets); + return result.StatusCode == Status::Success ? Result::Found + : Result::NotFound; + } + auto result = graph.ResolvePreset(name, graph.TestPresets); + return result.StatusCode == Status::Success ? Result::Found + : Result::NotFound; +} +} + +cmCTestHandlerCommand::PresetCheckResult +cmCTestHandlerCommand::BuildPresetExists(std::string const& name, + std::string const& sourceDir, + std::string const& presetsFile, + std::string& errorMessage) +{ + cmCMakePresetsGraph graph; + return PresetExistsInGraph(graph, name, sourceDir, presetsFile, + PresetType::Build, errorMessage); +} + +cmCTestHandlerCommand::PresetCheckResult +cmCTestHandlerCommand::TestPresetExists(std::string const& name, + std::string const& sourceDir, + std::string const& presetsFile, + std::string& errorMessage) +{ + cmCMakePresetsGraph graph; + return PresetExistsInGraph(graph, name, sourceDir, presetsFile, + PresetType::Test, errorMessage); +} diff --git a/Source/CTest/cmCTestHandlerCommand.h b/Source/CTest/cmCTestHandlerCommand.h index ef7bc5de92..4b5111be03 100644 --- a/Source/CTest/cmCTestHandlerCommand.h +++ b/Source/CTest/cmCTestHandlerCommand.h @@ -24,6 +24,13 @@ class cmCTestHandlerCommand : public cmCTestCommand public: using cmCTestCommand::cmCTestCommand; + enum class PresetCheckResult + { + Found, + NotFound, + ReadError, + }; + protected: struct BasicArguments : ArgumentParser::ParseResult { @@ -78,6 +85,15 @@ protected: bool ExecuteHandlerCommand(HandlerArguments& args, cmExecutionStatus& status) const; + static PresetCheckResult BuildPresetExists(std::string const& name, + std::string const& sourceDir, + std::string const& presetsFile, + std::string& errorMessage); + static PresetCheckResult TestPresetExists(std::string const& name, + std::string const& sourceDir, + std::string const& presetsFile, + std::string& errorMessage); + private: bool InvokeImpl(BasicArguments& args, std::vector const& unparsed, diff --git a/Source/CTest/cmCTestTestCommand.cxx b/Source/CTest/cmCTestTestCommand.cxx index 41df2b5d9e..3e8e319a38 100644 --- a/Source/CTest/cmCTestTestCommand.cxx +++ b/Source/CTest/cmCTestTestCommand.cxx @@ -34,16 +34,52 @@ std::unique_ptr cmCTestTestCommand::InitializeHandler( cmMakefile& mf = status.GetMakefile(); auto& args = static_cast(arguments); + std::string const sourceDirectory = + mf.GetSafeDefinition("CTEST_SOURCE_DIRECTORY"); + + // Presets file is set according to the following priority order: + // 1) The PRESETS_FILE option to ctest_test() + // 2) CTEST_PRESETS_FILE script variable + std::string const rawPresetsFile = !args.PresetsFile.empty() + ? args.PresetsFile + : mf.GetSafeDefinition("CTEST_PRESETS_FILE"); + + std::string const presetsFile = rawPresetsFile.empty() + ? "" + : cmSystemTools::CollapseFullPath(rawPresetsFile, sourceDirectory); + + // Preset name is set according to the following priority order: + // 1) The PRESET option to ctest_test() + // 2) CTEST_TEST_PRESET script variable + // 3) CTEST_PRESET script variable (a warning is emitted if no test preset + // exists with this name) + std::string effectivePreset = !args.Preset.empty() ? args.Preset + : cmNonempty(mf.GetDefinition("CTEST_TEST_PRESET")) + ? *mf.GetDefinition("CTEST_TEST_PRESET") + : ""; + if (effectivePreset.empty()) { + cmValue v = mf.GetDefinition("CTEST_PRESET"); + if (cmNonempty(v)) { + std::string presetError; + auto presetCheck = + TestPresetExists(*v, sourceDirectory, presetsFile, presetError); + if (presetCheck == PresetCheckResult::ReadError) { + status.SetError(cmStrCat('\n', presetError)); + return nullptr; + } + if (presetCheck == PresetCheckResult::Found) { + effectivePreset = *v; + } else { + cmCTestLog(this->CTest, WARNING, + "No test preset named \"" + << *v << "\" found, ignoring CTEST_PRESET." << std::endl); + } + } + } + std::unique_ptr presetsGraph; TestPreset const* expandedPreset = nullptr; - if (!args.Preset.empty()) { - std::string const sourceDirectory = - mf.GetSafeDefinition("CTEST_SOURCE_DIRECTORY"); - - std::string const presetsFile = args.PresetsFile.empty() - ? "" - : cmSystemTools::CollapseFullPath(args.PresetsFile, sourceDirectory); - + if (!effectivePreset.empty()) { presetsGraph = cm::make_unique(); if (!presetsGraph->ReadProjectPresets(sourceDirectory, presetsFile)) { status.SetError(cmStrCat("\n Could not read presets from \"", @@ -53,7 +89,7 @@ std::unique_ptr cmCTestTestCommand::InitializeHandler( } auto resolveResult = - presetsGraph->ResolvePreset(args.Preset, presetsGraph->TestPresets); + presetsGraph->ResolvePreset(effectivePreset, presetsGraph->TestPresets); auto resolveError = cmCMakePresetsGraph::FormatPresetError( resolveResult.StatusCode, resolveResult.ErrorPresetName, sourceDirectory); diff --git a/Tests/RunCMake/ctest_build/BuildPresetFromFileVar-check.cmake b/Tests/RunCMake/ctest_build/BuildPresetFromFileVar-check.cmake new file mode 100644 index 0000000000..7ca59c0465 --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildPresetFromFileVar-check.cmake @@ -0,0 +1,14 @@ +file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml") +if(build_xml_file) + file(READ "${build_xml_file}" build_xml) + if(NOT build_xml MATCHES "--preset.*my-build-preset") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --preset argument") + endif() + if(NOT build_xml MATCHES "--presets-file") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --presets-file argument") + endif() +else() + set(RunCMake_TEST_FAILED "Build.xml not found") +endif() diff --git a/Tests/RunCMake/ctest_build/BuildPresetGenericVar-check.cmake b/Tests/RunCMake/ctest_build/BuildPresetGenericVar-check.cmake new file mode 100644 index 0000000000..7dd0228a84 --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildPresetGenericVar-check.cmake @@ -0,0 +1,10 @@ +file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml") +if(build_xml_file) + file(READ "${build_xml_file}" build_xml) + if(NOT build_xml MATCHES "--preset.*my-build-preset") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Build.xml not found") +endif() diff --git a/Tests/RunCMake/ctest_build/BuildPresetVar-check.cmake b/Tests/RunCMake/ctest_build/BuildPresetVar-check.cmake new file mode 100644 index 0000000000..7dd0228a84 --- /dev/null +++ b/Tests/RunCMake/ctest_build/BuildPresetVar-check.cmake @@ -0,0 +1,10 @@ +file(GLOB build_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Build.xml") +if(build_xml_file) + file(READ "${build_xml_file}" build_xml) + if(NOT build_xml MATCHES "--preset.*my-build-preset") + set(RunCMake_TEST_FAILED + "Build.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Build.xml not found") +endif() diff --git a/Tests/RunCMake/ctest_build/CMakePresets.json.in b/Tests/RunCMake/ctest_build/CMakePresets.json.in index a676de1f33..fca5b7e675 100644 --- a/Tests/RunCMake/ctest_build/CMakePresets.json.in +++ b/Tests/RunCMake/ctest_build/CMakePresets.json.in @@ -10,6 +10,10 @@ "name": "my-configure-preset", "generator": "@RunCMake_GENERATOR@", "binaryDir": "${sourceDir}/../BuildPreset-build" + }, + { + "name": "my-build-preset", + "inherits": "my-configure-preset" } ], "buildPresets": [ diff --git a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake index e88f9018ba..97a1ce9dc8 100644 --- a/Tests/RunCMake/ctest_build/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_build/RunCMakeTest.cmake @@ -80,6 +80,26 @@ configure_file( run_ctest_build(BuildPreset PRESET my-build-preset) unset(RunCMake_TEST_SOURCE_DIR) +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPresetVar") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" + @ONLY) +set(CASE_TEST_PREFIX_CODE [[set(CTEST_BUILD_PRESET "my-build-preset")]]) +run_ctest(BuildPresetVar) +unset(CASE_TEST_PREFIX_CODE) +unset(RunCMake_TEST_SOURCE_DIR) + +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPresetGenericVar") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" + @ONLY) +set(CASE_TEST_PREFIX_CODE [[set(CTEST_PRESET "my-build-preset")]]) +run_ctest(BuildPresetGenericVar) +unset(CASE_TEST_PREFIX_CODE) +unset(RunCMake_TEST_SOURCE_DIR) + set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPresetFromFile") set(custom_presets_file "${RunCMake_BINARY_DIR}/BuildPresetFromFile/custom-presets.json") @@ -93,6 +113,21 @@ run_ctest_build(BuildPresetFromFile unset(RunCMake_TEST_SOURCE_DIR) unset(custom_presets_file) +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/BuildPresetFromFileVar") +set(custom_presets_file + "${RunCMake_BINARY_DIR}/BuildPresetFromFileVar/custom-presets.json") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${custom_presets_file}" + @ONLY) +set(CASE_TEST_PREFIX_CODE +"set(CTEST_BUILD_PRESET \"my-build-preset\") +set(CTEST_PRESETS_FILE \"${custom_presets_file}\")") +run_ctest(BuildPresetFromFileVar) +unset(CASE_TEST_PREFIX_CODE) +unset(RunCMake_TEST_SOURCE_DIR) +unset(custom_presets_file) + run_ctest_build(BuildPresetBadFile PRESET my-build-preset PRESETS_FILE /nonexistent/path/presets.json) diff --git a/Tests/RunCMake/ctest_configure/ConfigurePresetFromFileVar-check.cmake b/Tests/RunCMake/ctest_configure/ConfigurePresetFromFileVar-check.cmake new file mode 100644 index 0000000000..68c0d27ebb --- /dev/null +++ b/Tests/RunCMake/ctest_configure/ConfigurePresetFromFileVar-check.cmake @@ -0,0 +1,29 @@ +if(IS_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}/build") + set(RunCMake_TEST_FAILED + "CTEST_BINARY_DIRECTORY did not override buildDir from preset") +endif() + +file(GLOB configure_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml") +if(configure_xml_file) + file(READ "${configure_xml_file}" configure_xml) + if(NOT configure_xml MATCHES "\"--preset\" \"my-preset\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --preset argument") + endif() + if(NOT configure_xml MATCHES "\"--presets-file\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --presets-file argument") + endif() +else() + set(RunCMake_TEST_FAILED "Configure.xml not found") +endif() + +set(cmakecache_file "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") +if(EXISTS "${cmakecache_file}") + file(READ "${cmakecache_file}" cmakecache_txt) + if(NOT cmakecache_txt MATCHES "MY_CUSTOM_VAR:STRING=this-gets-set") + set(RunCMake_TEST_FAILED "CMakeCache.txt does not contain MY_CUSTOM_VAR") + endif() +else() + set(RunCMake_TEST_FAILED "CMakeCache.txt not found") +endif() diff --git a/Tests/RunCMake/ctest_configure/ConfigurePresetGenericVar-check.cmake b/Tests/RunCMake/ctest_configure/ConfigurePresetGenericVar-check.cmake new file mode 100644 index 0000000000..b89b32bc9e --- /dev/null +++ b/Tests/RunCMake/ctest_configure/ConfigurePresetGenericVar-check.cmake @@ -0,0 +1,25 @@ +if(IS_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}/build") + set(RunCMake_TEST_FAILED + "CTEST_BINARY_DIRECTORY did not override buildDir from preset") +endif() + +file(GLOB configure_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml") +if(configure_xml_file) + file(READ "${configure_xml_file}" configure_xml) + if(NOT configure_xml MATCHES "\"--preset\" \"my-preset\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Configure.xml not found") +endif() + +set(cmakecache_file "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") +if(EXISTS "${cmakecache_file}") + file(READ "${cmakecache_file}" cmakecache_txt) + if(NOT cmakecache_txt MATCHES "MY_CUSTOM_VAR:STRING=this-gets-set") + set(RunCMake_TEST_FAILED "CMakeCache.txt does not contain MY_CUSTOM_VAR") + endif() +else() + set(RunCMake_TEST_FAILED "CMakeCache.txt not found") +endif() diff --git a/Tests/RunCMake/ctest_configure/ConfigurePresetVar-check.cmake b/Tests/RunCMake/ctest_configure/ConfigurePresetVar-check.cmake new file mode 100644 index 0000000000..b89b32bc9e --- /dev/null +++ b/Tests/RunCMake/ctest_configure/ConfigurePresetVar-check.cmake @@ -0,0 +1,25 @@ +if(IS_DIRECTORY "${RunCMake_TEST_SOURCE_DIR}/build") + set(RunCMake_TEST_FAILED + "CTEST_BINARY_DIRECTORY did not override buildDir from preset") +endif() + +file(GLOB configure_xml_file "${RunCMake_TEST_BINARY_DIR}/Testing/*/Configure.xml") +if(configure_xml_file) + file(READ "${configure_xml_file}" configure_xml) + if(NOT configure_xml MATCHES "\"--preset\" \"my-preset\"") + set(RunCMake_TEST_FAILED + "Configure.xml does not contain the expected --preset argument") + endif() +else() + set(RunCMake_TEST_FAILED "Configure.xml not found") +endif() + +set(cmakecache_file "${RunCMake_TEST_BINARY_DIR}/CMakeCache.txt") +if(EXISTS "${cmakecache_file}") + file(READ "${cmakecache_file}" cmakecache_txt) + if(NOT cmakecache_txt MATCHES "MY_CUSTOM_VAR:STRING=this-gets-set") + set(RunCMake_TEST_FAILED "CMakeCache.txt does not contain MY_CUSTOM_VAR") + endif() +else() + set(RunCMake_TEST_FAILED "CMakeCache.txt not found") +endif() diff --git a/Tests/RunCMake/ctest_configure/RunCMakeTest.cmake b/Tests/RunCMake/ctest_configure/RunCMakeTest.cmake index 6b71abcba9..a98b64c6d0 100644 --- a/Tests/RunCMake/ctest_configure/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_configure/RunCMakeTest.cmake @@ -16,6 +16,24 @@ configure_file( @ONLY) run_ctest_configure(ConfigurePreset PRESET my-preset) +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/ConfigurePresetVar") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" + @ONLY) +set(CASE_TEST_PREFIX_CODE [[set(CTEST_CONFIGURE_PRESET "my-preset")]]) +run_ctest(ConfigurePresetVar) +unset(CASE_TEST_PREFIX_CODE) + +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/ConfigurePresetGenericVar") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" + @ONLY) +set(CASE_TEST_PREFIX_CODE [[set(CTEST_PRESET "my-preset")]]) +run_ctest(ConfigurePresetGenericVar) +unset(CASE_TEST_PREFIX_CODE) + set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/ConfigurePresetFromFile") set(custom_presets_file "${RunCMake_BINARY_DIR}/ConfigurePresetFromFile/custom-presets.json") configure_file( @@ -24,3 +42,17 @@ configure_file( @ONLY) run_ctest_configure(ConfigurePresetFromFile PRESET my-preset PRESETS_FILE "${custom_presets_file}") unset(custom_presets_file) + +set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/ConfigurePresetFromFileVar") +set(custom_presets_file "${RunCMake_BINARY_DIR}/ConfigurePresetFromFileVar/custom-presets.json") +configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${custom_presets_file}" + @ONLY) +set(CASE_TEST_PREFIX_CODE +"set(CTEST_CONFIGURE_PRESET \"my-preset\") +set(CTEST_PRESETS_FILE \"${custom_presets_file}\")") +run_ctest(ConfigurePresetFromFileVar) +unset(CASE_TEST_PREFIX_CODE) +unset(custom_presets_file) +unset(RunCMake_TEST_SOURCE_DIR) diff --git a/Tests/RunCMake/ctest_configure/test.cmake.in b/Tests/RunCMake/ctest_configure/test.cmake.in index fa456f3f98..2d23360f1b 100644 --- a/Tests/RunCMake/ctest_configure/test.cmake.in +++ b/Tests/RunCMake/ctest_configure/test.cmake.in @@ -1,4 +1,5 @@ cmake_minimum_required(VERSION 3.10) +@CASE_TEST_PREFIX_CODE@ set(CTEST_SITE "test-site") set(CTEST_BUILD_NAME "test-build-name") diff --git a/Tests/RunCMake/ctest_test/CMakePresets.json.in b/Tests/RunCMake/ctest_test/CMakePresets.json.in index 3d0d3d0861..a97394852f 100644 --- a/Tests/RunCMake/ctest_test/CMakePresets.json.in +++ b/Tests/RunCMake/ctest_test/CMakePresets.json.in @@ -10,6 +10,10 @@ "name": "my-configure-preset", "generator": "@RunCMake_GENERATOR@", "binaryDir": "${sourceDir}/build" + }, + { + "name": "my-include-preset", + "inherits": "my-configure-preset" } ], "testPresets": [ diff --git a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake index 3dc3f82230..ea399768b6 100644 --- a/Tests/RunCMake/ctest_test/RunCMakeTest.cmake +++ b/Tests/RunCMake/ctest_test/RunCMakeTest.cmake @@ -24,6 +24,8 @@ endforeach() TestPresetExclude TestPresetInclude TestPresetOverride + TestPresetVar + TestPresetGenericVar ) configure_file( "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" @@ -35,6 +37,14 @@ endforeach() run_ctest_test(TestPresetInclude PRESET my-include-preset) run_ctest_test(TestPresetOverride PRESET my-include-preset INCLUDE test2) + set(CASE_TEST_PREFIX_CODE [[set(CTEST_TEST_PRESET "my-include-preset")]]) + run_ctest(TestPresetVar) + unset(CASE_TEST_PREFIX_CODE) + + set(CASE_TEST_PREFIX_CODE [[set(CTEST_PRESET "my-include-preset")]]) + run_ctest(TestPresetGenericVar) + unset(CASE_TEST_PREFIX_CODE) + set(custom_presets_file "${RunCMake_BINARY_DIR}/TestPresetFileInclude/custom-presets.json") configure_file( @@ -47,6 +57,19 @@ endforeach() unset(CASE_CTEST_TEST_RAW_ARGS) unset(custom_presets_file) + set(custom_presets_file + "${RunCMake_BINARY_DIR}/TestPresetFromFileVar/custom-presets.json") + configure_file( + "${RunCMake_SOURCE_DIR}/CMakePresets.json.in" + "${custom_presets_file}" + @ONLY) + set(CASE_TEST_PREFIX_CODE +"set(CTEST_TEST_PRESET \"my-include-preset\") +set(CTEST_PRESETS_FILE \"${custom_presets_file}\")") + run_ctest(TestPresetFromFileVar) + unset(CASE_TEST_PREFIX_CODE) + unset(custom_presets_file) + set(CASE_CTEST_TEST_RAW_ARGS "PRESET my-include-preset PRESETS_FILE /nonexistent/path/presets.json") run_ctest(TestPresetBadFile) diff --git a/Tests/RunCMake/ctest_test/TestPresetFromFileVar-stdout.txt b/Tests/RunCMake/ctest_test/TestPresetFromFileVar-stdout.txt new file mode 100644 index 0000000000..a4bf544cfd --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetFromFileVar-stdout.txt @@ -0,0 +1,4 @@ + Start [0-9]+: test1 +1/1 Test #[0-9]+: test1 \.+ Passed +[0-9\.]+ sec ++ +100% tests passed out of 1 diff --git a/Tests/RunCMake/ctest_test/TestPresetGenericVar-stderr.txt b/Tests/RunCMake/ctest_test/TestPresetGenericVar-stderr.txt new file mode 100644 index 0000000000..e00bb592f6 --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetGenericVar-stderr.txt @@ -0,0 +1 @@ +No build preset named "my-include-preset" found, ignoring CTEST_PRESET\. diff --git a/Tests/RunCMake/ctest_test/TestPresetGenericVar-stdout.txt b/Tests/RunCMake/ctest_test/TestPresetGenericVar-stdout.txt new file mode 100644 index 0000000000..a4bf544cfd --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetGenericVar-stdout.txt @@ -0,0 +1,4 @@ + Start [0-9]+: test1 +1/1 Test #[0-9]+: test1 \.+ Passed +[0-9\.]+ sec ++ +100% tests passed out of 1 diff --git a/Tests/RunCMake/ctest_test/TestPresetVar-stdout.txt b/Tests/RunCMake/ctest_test/TestPresetVar-stdout.txt new file mode 100644 index 0000000000..a4bf544cfd --- /dev/null +++ b/Tests/RunCMake/ctest_test/TestPresetVar-stdout.txt @@ -0,0 +1,4 @@ + Start [0-9]+: test1 +1/1 Test #[0-9]+: test1 \.+ Passed +[0-9\.]+ sec ++ +100% tests passed out of 1