Browse Source

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
Tyler Yankee 2 months ago
parent
commit
23615b7ca4

+ 7 - 3
Help/manual/cmake-presets.7.rst

@@ -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
Help/manual/presets/schema.json

@@ -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 - 0
Source/cmCMakePresetsErrors.cxx

@@ -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 - 0
Source/cmCMakePresetsErrors.h

@@ -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
Source/cmCMakePresetsGraph.h

@@ -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 - 0
Source/cmCMakePresetsGraphReadJSON.cxx

@@ -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);
   }
 

+ 27 - 1
Source/cmCMakePresetsGraphReadJSONTestPresets.cxx

@@ -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
Source/cmCTest.cxx

@@ -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;
     }
 

+ 4 - 0
Tests/RunCMake/CMakePresetsTest/Good-test-jobsProc-stdout.txt

@@ -0,0 +1,4 @@
+Test project [^
+]*/Tests/RunCMake/CMakePresetsTest/Good/build/default
+.*
+100% tests passed, 0 tests failed out of 5

+ 8 - 1
Tests/RunCMake/CMakePresetsTest/Good.json.in

@@ -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 - 1
Tests/RunCMake/CMakePresetsTest/InvalidJobs-test-x-stderr.txt

@@ -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
                         \^$

+ 1 - 0
Tests/RunCMake/CMakePresetsTest/JobsProcUnsupported-test-x-result.txt

@@ -0,0 +1 @@
+1

+ 3 - 0
Tests/RunCMake/CMakePresetsTest/JobsProcUnsupported-test-x-stderr.txt

@@ -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$

+ 19 - 0
Tests/RunCMake/CMakePresetsTest/JobsProcUnsupported.json.in

@@ -0,0 +1,19 @@
+{
+  "version": 10,
+  "configurePresets": [
+    {
+      "name": "default",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ],
+  "testPresets": [
+    {
+      "name": "default",
+      "configurePreset": "default",
+      "execution": {
+        "jobs": ""
+      }
+    }
+  ]
+}

+ 1 - 0
Tests/RunCMake/CMakePresetsTest/ListPresets-test-x-stdout.txt

@@ -9,4 +9,5 @@ Available test presets:
   "exclude"
   "index"
   "indexFile"
+  "jobsProc"
   "showOnly"

+ 2 - 1
Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake

@@ -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)