presets: Allow jobs to match ctest -j with no value

Allow an empty string for the "jobs" field of test presets to mirror
the `ctest --parallel` option without a number specified, which
corresponds to setting the parallelism to the maximum of the number of
processors or 2.

Issue: #27070
This commit is contained in:
Tyler Yankee
2026-01-29 16:25:08 -05:00
parent 71ff5a9d6f
commit 23615b7ca4
16 changed files with 108 additions and 13 deletions
+7 -3
View File
@@ -902,7 +902,9 @@ that may contain the following fields:
:option:`--parallel <ctest --parallel>` on the command line. If the value
is ``0``, it is equivalent to unbounded parallelism.
In preset files specifying version ``11`` or above, this field does not accept
In preset files specifying version ``11`` or above, this field can also be
a string, in which case it must be empty, and is equivalent to passing
``--parallel`` with ``<jobs>`` omitted; additionally, it does not accept
negative values.
``resourceSpecFile``
@@ -1465,8 +1467,10 @@ they were added and a summary of the new features and changes is given below.
* Changes to `Test Presets <Test Preset_>`_
* The `jobs <CMakePresets test jobs_>`_ field no longer accepts negative
values.
* The `jobs <CMakePresets test jobs_>`_ field now accepts an empty string
representing :option:`--parallel <ctest --parallel>` with ``<jobs>``
omitted. In addition, when an integer is specified, it must not be
negative.
Schema
======
+13 -3
View File
@@ -1006,9 +1006,19 @@
}
},
"testPresetsExecutionJobsV11": {
"type": "integer",
"description": "An optional integer. Equivalent to passing --parallel on the command line.",
"minimum": 0
"oneOf": [
{
"type": "integer",
"description": "An optional integer. Equivalent to passing --parallel on the command line.",
"minimum": 0
},
{
"type": "string",
"description": "An optional string. Equivalent to passing --parallel on the command line with the number of jobs omitted.",
"minLength": 0,
"maxLength": 0
}
]
},
"testPresetsExecutionJobsV2": {
"type": "integer",
+6
View File
@@ -183,6 +183,12 @@ void GRAPHVIZ_FILE_UNSUPPORTED(cmJSONState* state)
"File version must be 10 or higher for graphviz preset support");
}
void JOBS_PROC_UNSUPPORTED(cmJSONState* state)
{
state->AddError("File version must be 11 or higher for "
"processor-count-based jobs preset support");
}
void CYCLIC_INCLUDE(std::string const& file, cmJSONState* state)
{
state->AddError(cmStrCat("Cyclic include among preset files: ", file));
+2
View File
@@ -72,6 +72,8 @@ void TOOLCHAIN_FILE_UNSUPPORTED(cmJSONState* state);
void GRAPHVIZ_FILE_UNSUPPORTED(cmJSONState* state);
void JOBS_PROC_UNSUPPORTED(cmJSONState* state);
void CYCLIC_INCLUDE(std::string const& file, cmJSONState* state);
void TEST_OUTPUT_TRUNCATION_UNSUPPORTED(cmJSONState* state);
+1 -1
View File
@@ -289,7 +289,7 @@ public:
cm::optional<bool> StopOnFailure;
cm::optional<bool> EnableFailover;
cm::optional<unsigned int> Jobs;
cm::optional<cm::optional<unsigned int>> Jobs;
std::string ResourceSpecFile;
cm::optional<int> TestLoad;
cm::optional<ShowOnlyEnum> ShowOnly;
+7
View File
@@ -693,6 +693,13 @@ bool cmCMakePresetsGraph::ReadJSONFile(std::string const& filename,
return false;
}
// Support for processor-count-based jobs added in version 11.
if (v < 11 && preset.Execution && preset.Execution->Jobs.has_value() &&
!preset.Execution->Jobs->has_value()) {
cmCMakePresetsErrors::JOBS_PROC_UNSUPPORTED(&this->parseState);
return false;
}
this->TestPresetOrder.push_back(preset.Name);
}
@@ -301,6 +301,32 @@ auto const TestPresetOptionalExecutionNoTestsActionHelper =
JSONHelperBuilder::Optional<TestPreset::ExecutionOptions::NoTestsActionEnum>(
TestPresetExecutionNoTestsActionHelper);
bool TestPresetExecutionJobsHelper(cm::optional<unsigned int>& out,
Json::Value const* value,
cmJSONState* state)
{
if (value->isString()) {
if (!value->asString().empty()) {
cmCMakePresetsErrors::INVALID_PRESET(value, state);
return false;
}
out.reset();
return true;
}
if (value->isUInt()) {
out.emplace(value->asUInt());
return true;
}
cmCMakePresetsErrors::INVALID_PRESET(value, state);
return false;
}
auto const TestPresetOptionalExecutionJobsHelper =
JSONHelperBuilder::Optional<cm::optional<unsigned int>>(
TestPresetExecutionJobsHelper);
auto const TestPresetExecutionHelper =
JSONHelperBuilder::Optional<TestPreset::ExecutionOptions>(
JSONHelperBuilder::Object<TestPreset::ExecutionOptions>()
@@ -309,7 +335,7 @@ auto const TestPresetExecutionHelper =
.Bind("enableFailover"_s, &TestPreset::ExecutionOptions::EnableFailover,
cmCMakePresetsGraphInternal::PresetOptionalBoolHelper, false)
.Bind("jobs"_s, &TestPreset::ExecutionOptions::Jobs,
cmCMakePresetsGraphInternal::PresetOptionalUIntHelper, false)
TestPresetOptionalExecutionJobsHelper, false)
.Bind("resourceSpecFile"_s,
&TestPreset::ExecutionOptions::ResourceSpecFile,
cmCMakePresetsGraphInternal::PresetStringHelper, false)
+6 -2
View File
@@ -1710,8 +1710,12 @@ bool cmCTest::SetArgsFromPreset(std::string const& presetName,
expandedPreset->Execution->EnableFailover.value_or(false);
if (expandedPreset->Execution->Jobs) {
unsigned int jobs = *expandedPreset->Execution->Jobs;
this->SetParallelLevel(static_cast<size_t>(jobs));
cm::optional<unsigned int> jobs = *expandedPreset->Execution->Jobs;
if (jobs.has_value()) {
this->SetParallelLevel(static_cast<size_t>(jobs.value()));
} else {
this->SetParallelLevel(cm::nullopt);
}
this->Impl->ParallelLevelSetInCli = true;
}
@@ -0,0 +1,4 @@
Test project [^
]*/Tests/RunCMake/CMakePresetsTest/Good/build/default
.*
100% tests passed, 0 tests failed out of 5
+8 -1
View File
@@ -1,5 +1,5 @@
{
"version": 6,
"version": 11,
"configurePresets": [
{
"name": "default",
@@ -160,6 +160,13 @@
}
}
},
{
"name": "jobsProc",
"inherits": "minimal",
"execution": {
"jobs": ""
}
},
{
"name": "showOnly",
"inherits": "minimal",
@@ -1,5 +1,5 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresetsTest/InvalidJobs:
CMakePresets\.json:15: "jobs" expected an unsigned integer, got: -10
CMakePresets\.json:15: Invalid preset
"jobs": -10
\^$
@@ -0,0 +1,3 @@
^CMake Error: Could not read presets from [^
]*/Tests/RunCMake/CMakePresetsTest/JobsProcUnsupported:
File version must be 11 or higher for processor-count-based jobs preset support$
@@ -0,0 +1,19 @@
{
"version": 10,
"configurePresets": [
{
"name": "default",
"generator": "@RunCMake_GENERATOR@",
"binaryDir": "${sourceDir}/build"
}
],
"testPresets": [
{
"name": "default",
"configurePreset": "default",
"execution": {
"jobs": ""
}
}
]
}
@@ -9,4 +9,5 @@ Available test presets:
"exclude"
"index"
"indexFile"
"jobsProc"
"showOnly"
@@ -82,7 +82,7 @@ set(CMakePresetsTest_ASSETS "Good-indexFile.txt")
set(GoodTestPresets
"minimal;defaults;noEnvironment;withEnvironment"
"config-debug;config-release"
"exclude;index;indexFile;showOnly;outputLog;outputJUnit")
"exclude;index;indexFile;jobsProc;showOnly;outputLog;outputJUnit")
run_cmake_test_presets(Good
"default"
""
@@ -112,6 +112,7 @@ run_cmake_test_presets(ConditionFuture "" "" "x")
run_cmake_test_presets(TestOutputTruncationUnsupported "" "" "x")
run_cmake_test_presets(OutputJUnitUnsupported "" "" "x")
run_cmake_test_presets(InvalidJobs "" "" "x")
run_cmake_test_presets(JobsProcUnsupported "" "" "x")
set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
run_cmake_test_presets(ConfigurePresetUnreachable "" "" "x")
set(CMakePresetsTest_NO_CONFIGURE 0)