Pārlūkot izejas kodu

Merge topic 'cmake-presets-condition'

8bc5c8961e CMakePresets.json: Add the ability to conditionally disable presets
ce6ea7c927 Refactor: Move some common code into separate file
ebbd475e54 Refactor: Move cmCMakePresetsFile::ReadJSON into a separate file

Acked-by: Kitware Robot <[email protected]>
Merge-request: !5919
Brad King 4 gadi atpakaļ
vecāks
revīzija
928f4bda30
45 mainītis faili ar 2235 papildinājumiem un 818 dzēšanām
  1. 96 0
      Help/manual/cmake-presets.7.rst
  2. 12 1
      Help/manual/presets/example.json
  3. 348 8
      Help/manual/presets/schema.json
  4. 4 0
      Help/release/dev/cmake-presets-condition.rst
  5. 2 0
      Source/CMakeLists.txt
  6. 1 1
      Source/QtDialog/QCMake.cxx
  7. 107 805
      Source/cmCMakePresetsFile.cxx
  8. 10 0
      Source/cmCMakePresetsFile.h
  9. 102 0
      Source/cmCMakePresetsFileInternal.h
  10. 1002 0
      Source/cmCMakePresetsFileReadJSON.cxx
  11. 7 0
      Source/cmCTest.cxx
  12. 1 1
      Source/cmJSONHelpers.h
  13. 13 0
      Source/cmake.cxx
  14. 1 0
      Tests/RunCMake/CMakePresets/ConditionFuture-result.txt
  15. 2 0
      Tests/RunCMake/CMakePresets/ConditionFuture-stderr.txt
  16. 11 0
      Tests/RunCMake/CMakePresets/ConditionFuture.json.in
  17. 349 0
      Tests/RunCMake/CMakePresets/Conditions.json.in
  18. 22 0
      Tests/RunCMake/CMakePresets/ListConditions-stdout.txt
  19. 8 0
      Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
  20. 1 0
      Tests/RunCMake/CMakePresets/SimpleFalse-result.txt
  21. 1 0
      Tests/RunCMake/CMakePresets/SimpleFalse-stderr.txt
  22. 0 0
      Tests/RunCMake/CMakePresets/SimpleTrue.cmake
  23. 1 0
      Tests/RunCMake/CMakePresets/SubConditionNull-result.txt
  24. 2 0
      Tests/RunCMake/CMakePresets/SubConditionNull-stderr.txt
  25. 14 0
      Tests/RunCMake/CMakePresets/SubConditionNull.json.in
  26. 1 0
      Tests/RunCMake/CMakePresetsBuild/Condition-build-disabled-result.txt
  27. 2 0
      Tests/RunCMake/CMakePresetsBuild/Condition-build-disabled-stderr.txt
  28. 0 0
      Tests/RunCMake/CMakePresetsBuild/Condition.cmake
  29. 22 0
      Tests/RunCMake/CMakePresetsBuild/Condition.json.in
  30. 1 0
      Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-result.txt
  31. 2 0
      Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-stderr.txt
  32. 17 0
      Tests/RunCMake/CMakePresetsBuild/ConditionFuture.json.in
  33. 2 1
      Tests/RunCMake/CMakePresetsBuild/ListPresets-build-x-stdout.txt
  34. 11 1
      Tests/RunCMake/CMakePresetsBuild/ListPresets.json.in
  35. 2 0
      Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake
  36. 22 0
      Tests/RunCMake/CMakePresetsTest/Condition.json.in
  37. 1 0
      Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-result.txt
  38. 2 0
      Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-stderr.txt
  39. 17 0
      Tests/RunCMake/CMakePresetsTest/ConditionFuture.json.in
  40. 3 0
      Tests/RunCMake/CMakePresetsTest/ConditionListPresets-test-x-stdout.txt
  41. 1 0
      Tests/RunCMake/CMakePresetsTest/ConditionRunTests-test-disabled-result.txt
  42. 2 0
      Tests/RunCMake/CMakePresetsTest/ConditionRunTests-test-disabled-stderr.txt
  43. 2 0
      Tests/RunCMake/CMakePresetsTest/ConditionRunTests.cmake
  44. 7 0
      Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake
  45. 1 0
      Utilities/IWYU/mapping.imp

+ 96 - 0
Help/manual/cmake-presets.7.rst

@@ -119,6 +119,11 @@ that may contain the following fields:
   This field can also be a string, which is equivalent to an array
   This field can also be a string, which is equivalent to an array
   containing one string.
   containing one string.
 
 
+``condition``
+
+  An optional `Condition`_ object. This is allowed in preset files specifying
+  version ``3`` or above.
+
 ``vendor``
 ``vendor``
 
 
   An optional map containing vendor-specific information. CMake does not
   An optional map containing vendor-specific information. CMake does not
@@ -345,6 +350,11 @@ that may contain the following fields:
   This field can also be a string, which is equivalent to an array
   This field can also be a string, which is equivalent to an array
   containing one string.
   containing one string.
 
 
+``condition``
+
+  An optional `Condition`_ object. This is allowed in preset files specifying
+  version ``3`` or above.
+
 ``vendor``
 ``vendor``
 
 
   An optional map containing vendor-specific information. CMake does not
   An optional map containing vendor-specific information. CMake does not
@@ -464,6 +474,11 @@ that may contain the following fields:
   This field can also be a string, which is equivalent to an array
   This field can also be a string, which is equivalent to an array
   containing one string.
   containing one string.
 
 
+``condition``
+
+  An optional `Condition`_ object. This is allowed in preset files specifying
+  version ``3`` or above.
+
 ``vendor``
 ``vendor``
 
 
   An optional map containing vendor-specific information. CMake does not
   An optional map containing vendor-specific information. CMake does not
@@ -789,6 +804,87 @@ that may contain the following fields:
 
 
       Equivalent to passing ``--no-tests=ignore`` on the command line.
       Equivalent to passing ``--no-tests=ignore`` on the command line.
 
 
+Condition
+^^^^^^^^^
+
+The ``condition`` field of a preset, allowed in preset files specifying version
+``3`` or above, is used to determine whether or not the preset is enabled. For
+example, this can be used to disable a preset on platforms other than Windows.
+``condition`` may be either a boolean, ``null``, or an object. If it is a
+boolean, the boolean indicates whether the preset is enabled or disabled. If it
+is ``null``, the preset is enabled, but the ``null`` condition is not inherited
+by any presets that may inherit from the preset. Sub-conditions (for example in
+a ``not``, ``anyOf``, or ``allOf`` condition) may not be ``null``. If it is an
+object, it has the following fields:
+
+``type``
+
+  A required string with one of the following values:
+
+  ``"const"``
+
+    Indicates that the condition is constant. This is equivalent to using a
+    boolean in place of the object. The condition object will have the
+    following additional fields:
+
+    ``value``
+
+      A required boolean which provides a constant value for the condition's
+      evaluation.
+
+  ``"equals"``
+
+  ``"notEquals"``
+
+    Indicates that the condition compares two strings to see if they are equal
+    (or not equal). The condition object will have the following additional
+    fields:
+
+    ``lhs``
+
+      First string to compare. This field supports macro expansion.
+
+    ``rhs``
+
+      Second string to compare. This field supports macro expansion.
+
+  ``"inList"``
+
+  ``"notInList"``
+
+    Indicates that the condition searches for a string in a list of strings.
+    The condition object will have the following additional fields:
+
+    ``string``
+
+      A required string to search for. This field supports macro expansion.
+
+    ``list``
+
+      A required list of strings to search. This field supports macro
+      expansion, and uses short-circuit evaluation.
+
+  ``"anyOf"``
+
+  ``"allOf"``
+
+    Indicates that the condition is an aggregation of zero or more nested
+    conditions. The condition object will have the following additional fields:
+
+    ``conditions``
+
+      A required array of condition objects. These conditions use short-circuit
+      evaluation.
+
+  ``"not"``
+
+    Indicates that the condition is an inversion of another condition. The
+    condition object will have the following additional fields:
+
+    ``condition``
+
+      A required condition object.
+
 Macro Expansion
 Macro Expansion
 ^^^^^^^^^^^^^^^
 ^^^^^^^^^^^^^^^
 
 

+ 12 - 1
Help/manual/presets/example.json

@@ -1,5 +1,5 @@
 {
 {
-  "version": 2,
+  "version": 3,
   "cmakeMinimumRequired": {
   "cmakeMinimumRequired": {
     "major": 3,
     "major": 3,
     "minor": 20,
     "minor": 20,
@@ -35,6 +35,17 @@
       "displayName": "Ninja Multi-Config",
       "displayName": "Ninja Multi-Config",
       "description": "Default build using Ninja Multi-Config generator",
       "description": "Default build using Ninja Multi-Config generator",
       "generator": "Ninja Multi-Config"
       "generator": "Ninja Multi-Config"
+    },
+    {
+      "name": "windows-only",
+      "inherits": "default",
+      "displayName": "Windows-only configuration",
+      "description": "This build is only available on Windows",
+      "condition": {
+        "type": "equals",
+        "lhs": "${hostSystemName}",
+        "rhs": "Windows"
+      }
     }
     }
   ],
   ],
   "buildPresets": [
   "buildPresets": [

+ 348 - 8
Help/manual/presets/schema.json

@@ -24,8 +24,8 @@
         "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
         "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
         "vendor": { "$ref": "#/definitions/vendor" },
         "vendor": { "$ref": "#/definitions/vendor" },
         "configurePresets": { "$ref": "#/definitions/configurePresetsV1"},
         "configurePresets": { "$ref": "#/definitions/configurePresetsV1"},
-        "buildPresets": { "$ref": "#/definitions/buildPresets"},
-        "testPresets": { "$ref": "#/definitions/testPresets"}
+        "buildPresets": { "$ref": "#/definitions/buildPresetsV2"},
+        "testPresets": { "$ref": "#/definitions/testPresetsV2"}
       },
       },
       "additionalProperties": false
       "additionalProperties": false
     },
     },
@@ -38,8 +38,8 @@
         "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
         "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
         "vendor": { "$ref": "#/definitions/vendor" },
         "vendor": { "$ref": "#/definitions/vendor" },
         "configurePresets": { "$ref": "#/definitions/configurePresetsV3"},
         "configurePresets": { "$ref": "#/definitions/configurePresetsV3"},
-        "buildPresets": { "$ref": "#/definitions/buildPresets"},
-        "testPresets": { "$ref": "#/definitions/testPresets"}
+        "buildPresets": { "$ref": "#/definitions/buildPresetsV3"},
+        "testPresets": { "$ref": "#/definitions/testPresetsV3"}
       },
       },
       "additionalProperties": false
       "additionalProperties": false
     }
     }
@@ -82,7 +82,8 @@
           "installDir": {
           "installDir": {
             "type": "string",
             "type": "string",
             "description": "An optional string representing the path to the output binary directory. This field supports macro expansion. If a relative path is specified, it is calculated relative to the source directory. If binaryDir is not specified, it must be inherited from the inherits preset (unless this preset is hidden)."
             "description": "An optional string representing the path to the output binary directory. This field supports macro expansion. If a relative path is specified, it is calculated relative to the source directory. If binaryDir is not specified, it must be inherited from the inherits preset (unless this preset is hidden)."
-          }
+          },
+          "condition": { "$ref": "#/definitions/topCondition" }
         }
         }
       }
       }
     },
     },
@@ -358,7 +359,8 @@
           "environment": {},
           "environment": {},
           "warnings": {},
           "warnings": {},
           "errors": {},
           "errors": {},
-          "debug": {}
+          "debug": {},
+          "condition": {}
         },
         },
         "required": [
         "required": [
           "name"
           "name"
@@ -397,7 +399,17 @@
         "additionalProperties": false
         "additionalProperties": false
       }
       }
     },
     },
-    "buildPresets": {
+    "buildPresetsItemsV3": {
+      "type": "array",
+      "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 2 and higher.",
+      "items": {
+        "type": "object",
+        "properties": {
+          "condition": { "$ref": "#/definitions/topCondition" }
+        }
+      }
+    },
+    "buildPresetsItemsV2": {
       "type": "array",
       "type": "array",
       "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 2 and higher.",
       "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 2 and higher.",
       "items": {
       "items": {
@@ -513,13 +525,86 @@
             }
             }
           }
           }
         },
         },
+        "required": [
+          "name"
+        ]
+      }
+    },
+    "buildPresetsV3": {
+      "type": "array",
+      "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 2 and higher.",
+      "allOf": [
+        { "$ref": "#/definitions/buildPresetsItemsV3" },
+        { "$ref": "#/definitions/buildPresetsItemsV2" }
+      ],
+      "items": {
+        "type": "object",
+        "properties": {
+          "name": {},
+          "hidden": {},
+          "inherits": {},
+          "configurePreset": {},
+          "vendor": {},
+          "displayName": {},
+          "description": {},
+          "inheritConfigureEnvironment": {},
+          "environment": {},
+          "jobs": {},
+          "targets": {},
+          "configuration": {},
+          "cleanFirst": {},
+          "verbose": {},
+          "nativeToolOptions": {},
+          "condition": {}
+        },
         "required": [
         "required": [
           "name"
           "name"
         ],
         ],
         "additionalProperties": false
         "additionalProperties": false
       }
       }
     },
     },
-    "testPresets": {
+    "buildPresetsV2": {
+      "type": "array",
+      "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 2 and higher.",
+      "allOf": [
+        { "$ref": "#/definitions/buildPresetsItemsV2" }
+      ],
+      "items": {
+        "type": "object",
+        "properties": {
+          "name": {},
+          "hidden": {},
+          "inherits": {},
+          "configurePreset": {},
+          "vendor": {},
+          "displayName": {},
+          "description": {},
+          "inheritConfigureEnvironment": {},
+          "environment": {},
+          "jobs": {},
+          "targets": {},
+          "configuration": {},
+          "cleanFirst": {},
+          "verbose": {},
+          "nativeToolOptions": {}
+        },
+        "required": [
+          "name"
+        ],
+        "additionalProperties": false
+      }
+    },
+    "testPresetsItemsV3": {
+      "type": "array",
+      "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 2 and higher.",
+      "items": {
+        "type": "object",
+        "properties": {
+          "condition": { "$ref": "#/definitions/topCondition" }
+        }
+      }
+    },
+    "testPresetsItemsV2": {
       "type": "array",
       "type": "array",
       "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 2 and higher.",
       "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 2 and higher.",
       "items": {
       "items": {
@@ -829,11 +914,266 @@
             "additionalProperties": false
             "additionalProperties": false
           }
           }
         },
         },
+        "required": [
+          "name"
+        ]
+      }
+    },
+    "testPresetsV3": {
+      "type": "array",
+      "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 2 and higher.",
+      "allOf": [
+        { "$ref": "#/definitions/testPresetsItemsV2" },
+        { "$ref": "#/definitions/testPresetsItemsV3" }
+      ],
+      "items": {
+        "type": "object",
+        "properties": {
+          "name": {},
+          "hidden": {},
+          "inherits": {},
+          "configurePreset": {},
+          "vendor": {},
+          "displayName": {},
+          "description": {},
+          "inheritConfigureEnvironment": {},
+          "environment": {},
+          "configuration": {},
+          "overwriteConfigurationFile": {},
+          "output": {},
+          "filter": {},
+          "execution": {},
+          "condition": {}
+        },
         "required": [
         "required": [
           "name"
           "name"
         ],
         ],
         "additionalProperties": false
         "additionalProperties": false
       }
       }
+    },
+    "testPresetsV2": {
+      "type": "array",
+      "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 2 and higher.",
+      "allOf": [
+        { "$ref": "#/definitions/testPresetsItemsV2" }
+      ],
+      "items": {
+        "type": "object",
+        "properties": {
+          "name": {},
+          "hidden": {},
+          "inherits": {},
+          "configurePreset": {},
+          "vendor": {},
+          "displayName": {},
+          "description": {},
+          "inheritConfigureEnvironment": {},
+          "environment": {},
+          "configuration": {},
+          "overwriteConfigurationFile": {},
+          "output": {},
+          "filter": {},
+          "execution": {}
+        },
+        "required": [
+          "name"
+        ],
+        "additionalProperties": false
+      }
+    },
+    "condition": {
+      "anyOf": [
+        {
+          "type": "boolean",
+          "description": "A boolean which provides a constant value for the condition's evaluation."
+        },
+        {
+          "type": "object",
+          "properties": {
+            "type": {
+              "type": "string",
+              "description": "A required string specifying the type of the condition.",
+              "const": "const"
+            },
+            "value": {
+              "type": "boolean",
+              "description": "A required boolean which provides a constant value for the condition's evaluation."
+            }
+          },
+          "required": [
+            "type",
+            "value"
+          ],
+          "additionalProperties": false
+        },
+        {
+          "type": "object",
+          "properties": {
+            "type": {
+              "type": "string",
+              "description": "A required string specifying the type of the condition.",
+              "const": "equals"
+            },
+            "lhs": {
+              "type": "string",
+              "description": "First string to compare. This field supports macro expansion."
+            },
+            "rhs": {
+              "type": "string",
+              "description": "Second string to compare. This field supports macro expansion."
+            }
+          },
+          "required": [
+            "type",
+            "lhs",
+            "rhs"
+          ],
+          "additionalProperties": false
+        },
+        {
+          "type": "object",
+          "properties": {
+            "type": {
+              "type": "string",
+              "description": "A required string specifying the type of the condition.",
+              "const": "notEquals"
+            },
+            "lhs": {
+              "type": "string",
+              "description": "First string to compare. This field supports macro expansion."
+            },
+            "rhs": {
+              "type": "string",
+              "description": "Second string to compare. This field supports macro expansion."
+            }
+          },
+          "required": [
+            "type",
+            "lhs",
+            "rhs"
+          ],
+          "additionalProperties": false
+        },
+        {
+          "type": "object",
+          "properties": {
+            "type": {
+              "type": "string",
+              "description": "A required string specifying the type of the condition.",
+              "const": "inList"
+            },
+            "string": {
+              "type": "string",
+              "description": "A required string to search for. This field supports macro expansion."
+            },
+            "list": {
+              "type": "array",
+              "description": "A required list of strings to search. This field supports macro expansion, and uses short-circuit evaluation.",
+              "items": {
+                "type": "string"
+              }
+            }
+          },
+          "required": [
+            "type",
+            "string",
+            "list"
+          ],
+          "additionalProperties": false
+        },
+        {
+          "type": "object",
+          "properties": {
+            "type": {
+              "type": "string",
+              "description": "A required string specifying the type of the condition.",
+              "const": "notInList"
+            },
+            "string": {
+              "type": "string",
+              "description": "A required string to search for. This field supports macro expansion."
+            },
+            "list": {
+              "type": "array",
+              "description": "A required list of strings to search. This field supports macro expansion, and uses short-circuit evaluation.",
+              "items": {
+                "type": "string"
+              }
+            }
+          },
+          "required": [
+            "type",
+            "string",
+            "list"
+          ],
+          "additionalProperties": false
+        },
+        {
+          "type": "object",
+          "properties": {
+            "type": {
+              "type": "string",
+              "description": "A required string specifying the type of the condition.",
+              "const": "anyOf"
+            },
+            "conditions": {
+              "type": "array",
+              "description": "A required array of condition objects. These conditions use short-circuit evaluation.",
+              "items": { "$ref": "#/definitions/condition" }
+            }
+          },
+          "required": [
+            "type",
+            "conditions"
+          ],
+          "additionalProperties": false
+        },
+        {
+          "type": "object",
+          "properties": {
+            "type": {
+              "type": "string",
+              "description": "A required string specifying the type of the condition.",
+              "const": "allOf"
+            },
+            "conditions": {
+              "type": "array",
+              "description": "A required array of condition objects. These conditions use short-circuit evaluation.",
+              "items": { "$ref": "#/definitions/condition" }
+            }
+          },
+          "required": [
+            "type",
+            "conditions"
+          ],
+          "additionalProperties": false
+        },
+        {
+          "type": "object",
+          "properties": {
+            "type": {
+              "type": "string",
+              "description": "A required string specifying the type of the condition.",
+              "const": "not"
+            },
+            "condition": { "$ref": "#/definitions/condition" }
+          },
+          "required": [
+            "type",
+            "condition"
+          ],
+          "additionalProperties": false
+        }
+      ]
+    },
+    "topCondition": {
+      "anyOf": [
+        { "$ref": "#/definitions/condition" },
+        {
+          "type": "null",
+          "description": "Null indicates that the condition always evaluates to true and is not inherited."
+        }
+      ]
     }
     }
   }
   }
 }
 }

+ 4 - 0
Help/release/dev/cmake-presets-condition.rst

@@ -0,0 +1,4 @@
+cmake-presets-condition
+-----------------------
+
+* :manual:`cmake-presets(7)` now support conditional enabling of presets.

+ 2 - 0
Source/CMakeLists.txt

@@ -198,6 +198,8 @@ set(SRCS
   cmCMakePath.cxx
   cmCMakePath.cxx
   cmCMakePresetsFile.cxx
   cmCMakePresetsFile.cxx
   cmCMakePresetsFile.h
   cmCMakePresetsFile.h
+  cmCMakePresetsFileInternal.h
+  cmCMakePresetsFileReadJSON.cxx
   cmCommandArgumentParserHelper.cxx
   cmCommandArgumentParserHelper.cxx
   cmCommonTargetGenerator.cxx
   cmCommonTargetGenerator.cxx
   cmCommonTargetGenerator.h
   cmCommonTargetGenerator.h

+ 1 - 1
Source/QtDialog/QCMake.cxx

@@ -557,7 +557,7 @@ void QCMake::loadPresets()
     preset.toolset = std::move(QString::fromLocal8Bit(p.Toolset.data()));
     preset.toolset = std::move(QString::fromLocal8Bit(p.Toolset.data()));
     preset.setToolset = !p.ToolsetStrategy ||
     preset.setToolset = !p.ToolsetStrategy ||
       p.ToolsetStrategy == cmCMakePresetsFile::ArchToolsetStrategy::Set;
       p.ToolsetStrategy == cmCMakePresetsFile::ArchToolsetStrategy::Set;
-    preset.enabled = it.Expanded &&
+    preset.enabled = it.Expanded && it.Expanded->ConditionResult &&
       std::find_if(this->AvailableGenerators.begin(),
       std::find_if(this->AvailableGenerators.begin(),
                    this->AvailableGenerators.end(),
                    this->AvailableGenerators.end(),
                    [&p](const cmake::GeneratorInfo& g) {
                    [&p](const cmake::GeneratorInfo& g) {

+ 107 - 805
Source/cmCMakePresetsFile.cxx

@@ -10,24 +10,10 @@
 #include <utility>
 #include <utility>
 
 
 #include <cm/string_view>
 #include <cm/string_view>
-#include <cmext/string_view>
 
 
-#include <cm3p/json/reader.h>
-#include <cm3p/json/value.h>
-
-#include "cmsys/FStream.hxx"
-
-#include "cmJSONHelpers.h"
+#include "cmCMakePresetsFileInternal.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
-#include "cmVersion.h"
-
-#define CHECK_OK(expr)                                                        \
-  {                                                                           \
-    auto _result = expr;                                                      \
-    if (_result != ReadFileResult::READ_OK)                                   \
-      return _result;                                                         \
-  }
 
 
 #define CHECK_EXPAND(out, field, expanders, version)                          \
 #define CHECK_EXPAND(out, field, expanders, version)                          \
   {                                                                           \
   {                                                                           \
@@ -51,675 +37,11 @@ enum class CycleStatus
 };
 };
 
 
 using ReadFileResult = cmCMakePresetsFile::ReadFileResult;
 using ReadFileResult = cmCMakePresetsFile::ReadFileResult;
-using CacheVariable = cmCMakePresetsFile::CacheVariable;
 using ConfigurePreset = cmCMakePresetsFile::ConfigurePreset;
 using ConfigurePreset = cmCMakePresetsFile::ConfigurePreset;
 using BuildPreset = cmCMakePresetsFile::BuildPreset;
 using BuildPreset = cmCMakePresetsFile::BuildPreset;
 using TestPreset = cmCMakePresetsFile::TestPreset;
 using TestPreset = cmCMakePresetsFile::TestPreset;
-using ArchToolsetStrategy = cmCMakePresetsFile::ArchToolsetStrategy;
-
-constexpr int MIN_VERSION = 1;
-constexpr int MAX_VERSION = 3;
-
-struct CMakeVersion
-{
-  unsigned int Major = 0;
-  unsigned int Minor = 0;
-  unsigned int Patch = 0;
-};
-
-struct RootPresets
-{
-  CMakeVersion CMakeMinimumRequired;
-  std::vector<cmCMakePresetsFile::ConfigurePreset> ConfigurePresets;
-  std::vector<cmCMakePresetsFile::BuildPreset> BuildPresets;
-  std::vector<cmCMakePresetsFile::TestPreset> TestPresets;
-};
-
-cmJSONHelper<std::nullptr_t, ReadFileResult> VendorHelper(ReadFileResult error)
-{
-  return [error](std::nullptr_t& /*out*/,
-                 const Json::Value* value) -> ReadFileResult {
-    if (!value) {
-      return ReadFileResult::READ_OK;
-    }
-
-    if (!value->isObject()) {
-      return error;
-    }
-
-    return ReadFileResult::READ_OK;
-  };
-}
-
-auto const VersionIntHelper = cmJSONIntHelper<ReadFileResult>(
-  ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
-
-auto const VersionHelper = cmJSONRequiredHelper<int, ReadFileResult>(
-  ReadFileResult::NO_VERSION, VersionIntHelper);
-
-auto const RootVersionHelper =
-  cmJSONObjectHelper<int, ReadFileResult>(ReadFileResult::READ_OK,
-                                          ReadFileResult::INVALID_ROOT)
-    .Bind("version"_s, VersionHelper, false);
-
-auto const VariableStringHelper = cmJSONStringHelper<ReadFileResult>(
-  ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE);
-
-ReadFileResult VariableValueHelper(std::string& out, const Json::Value* value)
-{
-  if (!value) {
-    out.clear();
-    return ReadFileResult::READ_OK;
-  }
-
-  if (value->isBool()) {
-    out = value->asBool() ? "TRUE" : "FALSE";
-    return ReadFileResult::READ_OK;
-  }
-
-  return VariableStringHelper(out, value);
-}
-
-auto const VariableObjectHelper =
-  cmJSONObjectHelper<CacheVariable, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE, false)
-    .Bind("type"_s, &CacheVariable::Type, VariableStringHelper, false)
-    .Bind("value"_s, &CacheVariable::Value, VariableValueHelper);
-
-ReadFileResult VariableHelper(cm::optional<CacheVariable>& out,
-                              const Json::Value* value)
-{
-  if (value->isBool()) {
-    out = CacheVariable{
-      /*Type=*/"BOOL",
-      /*Value=*/value->asBool() ? "TRUE" : "FALSE",
-    };
-    return ReadFileResult::READ_OK;
-  }
-  if (value->isString()) {
-    out = CacheVariable{
-      /*Type=*/"",
-      /*Value=*/value->asString(),
-    };
-    return ReadFileResult::READ_OK;
-  }
-  if (value->isObject()) {
-    out.emplace();
-    return VariableObjectHelper(*out, value);
-  }
-  if (value->isNull()) {
-    out = cm::nullopt;
-    return ReadFileResult::READ_OK;
-  }
-  return ReadFileResult::INVALID_VARIABLE;
-}
-
-auto const VariablesHelper =
-  cmJSONMapHelper<cm::optional<CacheVariable>, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, VariableHelper);
-
-auto const PresetStringHelper = cmJSONStringHelper<ReadFileResult>(
-  ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
-
-ReadFileResult EnvironmentHelper(cm::optional<std::string>& out,
-                                 const Json::Value* value)
-{
-  if (!value || value->isNull()) {
-    out = cm::nullopt;
-    return ReadFileResult::READ_OK;
-  }
-  if (value->isString()) {
-    out = value->asString();
-    return ReadFileResult::READ_OK;
-  }
-  return ReadFileResult::INVALID_PRESET;
-}
-
-auto const EnvironmentMapHelper =
-  cmJSONMapHelper<cm::optional<std::string>, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
-    EnvironmentHelper);
-
-auto const PresetVectorStringHelper =
-  cmJSONVectorHelper<std::string, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
-    PresetStringHelper);
-
-ReadFileResult PresetInheritsHelper(std::vector<std::string>& out,
-                                    const Json::Value* value)
-{
-  out.clear();
-  if (!value) {
-    return ReadFileResult::READ_OK;
-  }
-
-  if (value->isString()) {
-    out.push_back(value->asString());
-    return ReadFileResult::READ_OK;
-  }
-
-  return PresetVectorStringHelper(out, value);
-}
-
-auto const PresetBoolHelper = cmJSONBoolHelper<ReadFileResult>(
-  ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
-
-auto const PresetOptionalBoolHelper =
-  cmJSONOptionalHelper<bool, ReadFileResult>(ReadFileResult::READ_OK,
-                                             PresetBoolHelper);
-
-auto const PresetIntHelper = cmJSONIntHelper<ReadFileResult>(
-  ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
-
-auto const PresetOptionalIntHelper = cmJSONOptionalHelper<int, ReadFileResult>(
-  ReadFileResult::READ_OK, PresetIntHelper);
-
-auto const PresetVectorIntHelper = cmJSONVectorHelper<int, ReadFileResult>(
-  ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, PresetIntHelper);
-
-auto const PresetWarningsHelper =
-  cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
-    .Bind("dev"_s, &ConfigurePreset::WarnDev, PresetOptionalBoolHelper, false)
-    .Bind("deprecated"_s, &ConfigurePreset::WarnDeprecated,
-          PresetOptionalBoolHelper, false)
-    .Bind("uninitialized"_s, &ConfigurePreset::WarnUninitialized,
-          PresetOptionalBoolHelper, false)
-    .Bind("unusedCli"_s, &ConfigurePreset::WarnUnusedCli,
-          PresetOptionalBoolHelper, false)
-    .Bind("systemVars"_s, &ConfigurePreset::WarnSystemVars,
-          PresetOptionalBoolHelper, false);
-
-auto const PresetErrorsHelper =
-  cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
-    .Bind("dev"_s, &ConfigurePreset::ErrorDev, PresetOptionalBoolHelper, false)
-    .Bind("deprecated"_s, &ConfigurePreset::ErrorDeprecated,
-          PresetOptionalBoolHelper, false);
-
-auto const PresetDebugHelper =
-  cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
-    .Bind("output"_s, &ConfigurePreset::DebugOutput, PresetOptionalBoolHelper,
-          false)
-    .Bind("tryCompile"_s, &ConfigurePreset::DebugTryCompile,
-          PresetOptionalBoolHelper, false)
-    .Bind("find"_s, &ConfigurePreset::DebugFind, PresetOptionalBoolHelper,
-          false);
-
-ReadFileResult ArchToolsetStrategyHelper(
-  cm::optional<ArchToolsetStrategy>& out, const Json::Value* value)
-{
-  if (!value) {
-    out = cm::nullopt;
-    return ReadFileResult::READ_OK;
-  }
-
-  if (!value->isString()) {
-    return ReadFileResult::INVALID_PRESET;
-  }
-
-  if (value->asString() == "set") {
-    out = ArchToolsetStrategy::Set;
-    return ReadFileResult::READ_OK;
-  }
-
-  if (value->asString() == "external") {
-    out = ArchToolsetStrategy::External;
-    return ReadFileResult::READ_OK;
-  }
-
-  return ReadFileResult::INVALID_PRESET;
-}
-
-std::function<ReadFileResult(ConfigurePreset&, const Json::Value*)>
-ArchToolsetHelper(
-  std::string ConfigurePreset::*valueField,
-  cm::optional<ArchToolsetStrategy> ConfigurePreset::*strategyField)
-{
-  auto const objectHelper =
-    cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
-      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
-      .Bind("value", valueField, PresetStringHelper, false)
-      .Bind("strategy", strategyField, ArchToolsetStrategyHelper, false);
-  return [valueField, strategyField, objectHelper](
-           ConfigurePreset& out, const Json::Value* value) -> ReadFileResult {
-    if (!value) {
-      (out.*valueField).clear();
-      out.*strategyField = cm::nullopt;
-      return ReadFileResult::READ_OK;
-    }
-
-    if (value->isString()) {
-      out.*valueField = value->asString();
-      out.*strategyField = cm::nullopt;
-      return ReadFileResult::READ_OK;
-    }
-
-    if (value->isObject()) {
-      return objectHelper(out, value);
-    }
-
-    return ReadFileResult::INVALID_PRESET;
-  };
-}
-
-auto const ArchitectureHelper = ArchToolsetHelper(
-  &ConfigurePreset::Architecture, &ConfigurePreset::ArchitectureStrategy);
-auto const ToolsetHelper = ArchToolsetHelper(
-  &ConfigurePreset::Toolset, &ConfigurePreset::ToolsetStrategy);
-
-auto const ConfigurePresetHelper =
-  cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
-    .Bind("name"_s, &ConfigurePreset::Name, PresetStringHelper)
-    .Bind("inherits"_s, &ConfigurePreset::Inherits, PresetInheritsHelper,
-          false)
-    .Bind("hidden"_s, &ConfigurePreset::Hidden, PresetBoolHelper, false)
-    .Bind<std::nullptr_t>("vendor"_s, nullptr,
-                          VendorHelper(ReadFileResult::INVALID_PRESET), false)
-    .Bind("displayName"_s, &ConfigurePreset::DisplayName, PresetStringHelper,
-          false)
-    .Bind("description"_s, &ConfigurePreset::Description, PresetStringHelper,
-          false)
-    .Bind("generator"_s, &ConfigurePreset::Generator, PresetStringHelper,
-          false)
-    .Bind("architecture"_s, ArchitectureHelper, false)
-    .Bind("toolset"_s, ToolsetHelper, false)
-    .Bind("binaryDir"_s, &ConfigurePreset::BinaryDir, PresetStringHelper,
-          false)
-    .Bind("installDir"_s, &ConfigurePreset::InstallDir, PresetStringHelper,
-          false)
-    .Bind<std::string>("cmakeExecutable"_s, nullptr, PresetStringHelper, false)
-    .Bind("cacheVariables"_s, &ConfigurePreset::CacheVariables,
-          VariablesHelper, false)
-    .Bind("environment"_s, &ConfigurePreset::Environment, EnvironmentMapHelper,
-          false)
-    .Bind("warnings"_s, PresetWarningsHelper, false)
-    .Bind("errors"_s, PresetErrorsHelper, false)
-    .Bind("debug"_s, PresetDebugHelper, false);
-
-auto const BuildPresetHelper =
-  cmJSONObjectHelper<BuildPreset, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
-    .Bind("name"_s, &BuildPreset::Name, PresetStringHelper)
-    .Bind("inherits"_s, &BuildPreset::Inherits, PresetInheritsHelper, false)
-    .Bind("hidden"_s, &BuildPreset::Hidden, PresetBoolHelper, false)
-    .Bind<std::nullptr_t>("vendor"_s, nullptr,
-                          VendorHelper(ReadFileResult::INVALID_PRESET), false)
-    .Bind("displayName"_s, &BuildPreset::DisplayName, PresetStringHelper,
-          false)
-    .Bind("description"_s, &BuildPreset::Description, PresetStringHelper,
-          false)
-    .Bind("environment"_s, &BuildPreset::Environment, EnvironmentMapHelper,
-          false)
-    .Bind("configurePreset"_s, &BuildPreset::ConfigurePreset,
-          PresetStringHelper, false)
-    .Bind("inheritConfigureEnvironment"_s,
-          &BuildPreset::InheritConfigureEnvironment, PresetOptionalBoolHelper,
-          false)
-    .Bind("jobs"_s, &BuildPreset::Jobs, PresetOptionalIntHelper, false)
-    .Bind("targets"_s, &BuildPreset::Targets, PresetVectorStringHelper, false)
-    .Bind("configuration"_s, &BuildPreset::Configuration, PresetStringHelper,
-          false)
-    .Bind("cleanFirst"_s, &BuildPreset::CleanFirst, PresetOptionalBoolHelper,
-          false)
-    .Bind("verbose"_s, &BuildPreset::Verbose, PresetOptionalBoolHelper, false)
-    .Bind("nativeToolOptions"_s, &BuildPreset::NativeToolOptions,
-          PresetVectorStringHelper, false);
-
-ReadFileResult TestPresetOutputVerbosityHelper(
-  TestPreset::OutputOptions::VerbosityEnum& out, const Json::Value* value)
-{
-  if (!value) {
-    out = TestPreset::OutputOptions::VerbosityEnum::Default;
-    return ReadFileResult::READ_OK;
-  }
-
-  if (!value->isString()) {
-    return ReadFileResult::INVALID_PRESET;
-  }
-
-  if (value->asString() == "default") {
-    out = TestPreset::OutputOptions::VerbosityEnum::Default;
-    return ReadFileResult::READ_OK;
-  }
-
-  if (value->asString() == "verbose") {
-    out = TestPreset::OutputOptions::VerbosityEnum::Verbose;
-    return ReadFileResult::READ_OK;
-  }
-
-  if (value->asString() == "extra") {
-    out = TestPreset::OutputOptions::VerbosityEnum::Extra;
-    return ReadFileResult::READ_OK;
-  }
-
-  return ReadFileResult::INVALID_PRESET;
-}
-
-auto const TestPresetOptionalOutputVerbosityHelper =
-  cmJSONOptionalHelper<TestPreset::OutputOptions::VerbosityEnum,
-                       ReadFileResult>(ReadFileResult::READ_OK,
-                                       TestPresetOutputVerbosityHelper);
-
-auto const TestPresetOptionalOutputHelper =
-  cmJSONOptionalHelper<TestPreset::OutputOptions, ReadFileResult>(
-    ReadFileResult::READ_OK,
-    cmJSONObjectHelper<TestPreset::OutputOptions, ReadFileResult>(
-      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
-      .Bind("shortProgress"_s, &TestPreset::OutputOptions::ShortProgress,
-            PresetOptionalBoolHelper, false)
-      .Bind("verbosity"_s, &TestPreset::OutputOptions::Verbosity,
-            TestPresetOptionalOutputVerbosityHelper, false)
-      .Bind("debug"_s, &TestPreset::OutputOptions::Debug,
-            PresetOptionalBoolHelper, false)
-      .Bind("outputOnFailure"_s, &TestPreset::OutputOptions::OutputOnFailure,
-            PresetOptionalBoolHelper, false)
-      .Bind("quiet"_s, &TestPreset::OutputOptions::Quiet,
-            PresetOptionalBoolHelper, false)
-      .Bind("outputLogFile"_s, &TestPreset::OutputOptions::OutputLogFile,
-            PresetStringHelper, false)
-      .Bind("labelSummary"_s, &TestPreset::OutputOptions::LabelSummary,
-            PresetOptionalBoolHelper, false)
-      .Bind("subprojectSummary"_s,
-            &TestPreset::OutputOptions::SubprojectSummary,
-            PresetOptionalBoolHelper, false)
-      .Bind("maxPassedTestOutputSize"_s,
-            &TestPreset::OutputOptions::MaxPassedTestOutputSize,
-            PresetOptionalIntHelper, false)
-      .Bind("maxFailedTestOutputSize"_s,
-            &TestPreset::OutputOptions::MaxFailedTestOutputSize,
-            PresetOptionalIntHelper, false)
-      .Bind("maxTestNameWidth"_s, &TestPreset::OutputOptions::MaxTestNameWidth,
-            PresetOptionalIntHelper, false));
-
-auto const TestPresetOptionalFilterIncludeIndexObjectHelper =
-  cmJSONOptionalHelper<TestPreset::IncludeOptions::IndexOptions,
-                       ReadFileResult>(
-    ReadFileResult::READ_OK,
-    cmJSONObjectHelper<TestPreset::IncludeOptions::IndexOptions,
-                       ReadFileResult>(ReadFileResult::READ_OK,
-                                       ReadFileResult::INVALID_PRESET)
-      .Bind("start"_s, &TestPreset::IncludeOptions::IndexOptions::Start,
-            PresetOptionalIntHelper, false)
-      .Bind("end"_s, &TestPreset::IncludeOptions::IndexOptions::End,
-            PresetOptionalIntHelper, false)
-      .Bind("stride"_s, &TestPreset::IncludeOptions::IndexOptions::Stride,
-            PresetOptionalIntHelper, false)
-      .Bind("specificTests"_s,
-            &TestPreset::IncludeOptions::IndexOptions::SpecificTests,
-            PresetVectorIntHelper, false));
-
-ReadFileResult TestPresetOptionalFilterIncludeIndexHelper(
-  cm::optional<TestPreset::IncludeOptions::IndexOptions>& out,
-  const Json::Value* value)
-{
-  if (!value) {
-    out = cm::nullopt;
-    return ReadFileResult::READ_OK;
-  }
-
-  if (value->isString()) {
-    out.emplace();
-    out->IndexFile = value->asString();
-    return ReadFileResult::READ_OK;
-  }
-
-  if (value->isObject()) {
-    return TestPresetOptionalFilterIncludeIndexObjectHelper(out, value);
-  }
-
-  return ReadFileResult::INVALID_PRESET;
-}
-
-auto const TestPresetOptionalFilterIncludeHelper =
-  cmJSONOptionalHelper<TestPreset::IncludeOptions, ReadFileResult>(
-    ReadFileResult::READ_OK,
-    cmJSONObjectHelper<TestPreset::IncludeOptions, ReadFileResult>(
-      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
-      .Bind("name"_s, &TestPreset::IncludeOptions::Name, PresetStringHelper,
-            false)
-      .Bind("label"_s, &TestPreset::IncludeOptions::Label, PresetStringHelper,
-            false)
-      .Bind("index"_s, &TestPreset::IncludeOptions::Index,
-            TestPresetOptionalFilterIncludeIndexHelper, false)
-      .Bind("useUnion"_s, &TestPreset::IncludeOptions::UseUnion,
-            PresetOptionalBoolHelper, false));
-
-auto const TestPresetOptionalFilterExcludeFixturesHelper =
-  cmJSONOptionalHelper<TestPreset::ExcludeOptions::FixturesOptions,
-                       ReadFileResult>(
-    ReadFileResult::READ_OK,
-    cmJSONObjectHelper<TestPreset::ExcludeOptions::FixturesOptions,
-                       ReadFileResult>(ReadFileResult::READ_OK,
-                                       ReadFileResult::INVALID_PRESET)
-      .Bind("any"_s, &TestPreset::ExcludeOptions::FixturesOptions::Any,
-            PresetStringHelper, false)
-      .Bind("setup"_s, &TestPreset::ExcludeOptions::FixturesOptions::Setup,
-            PresetStringHelper, false)
-      .Bind("cleanup"_s, &TestPreset::ExcludeOptions::FixturesOptions::Cleanup,
-            PresetStringHelper, false));
-
-auto const TestPresetOptionalFilterExcludeHelper =
-  cmJSONOptionalHelper<TestPreset::ExcludeOptions, ReadFileResult>(
-    ReadFileResult::READ_OK,
-    cmJSONObjectHelper<TestPreset::ExcludeOptions, ReadFileResult>(
-      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
-      .Bind("name"_s, &TestPreset::ExcludeOptions::Name, PresetStringHelper,
-            false)
-      .Bind("label"_s, &TestPreset::ExcludeOptions::Label, PresetStringHelper,
-            false)
-      .Bind("fixtures"_s, &TestPreset::ExcludeOptions::Fixtures,
-            TestPresetOptionalFilterExcludeFixturesHelper, false));
-
-ReadFileResult TestPresetExecutionShowOnlyHelper(
-  TestPreset::ExecutionOptions::ShowOnlyEnum& out, const Json::Value* value)
-{
-  if (!value || !value->isString()) {
-    return ReadFileResult::INVALID_PRESET;
-  }
-
-  if (value->asString() == "human") {
-    out = TestPreset::ExecutionOptions::ShowOnlyEnum::Human;
-    return ReadFileResult::READ_OK;
-  }
-
-  if (value->asString() == "json-v1") {
-    out = TestPreset::ExecutionOptions::ShowOnlyEnum::JsonV1;
-    return ReadFileResult::READ_OK;
-  }
-
-  return ReadFileResult::INVALID_PRESET;
-}
-
-auto const TestPresetOptionalExecutionShowOnlyHelper =
-  cmJSONOptionalHelper<TestPreset::ExecutionOptions::ShowOnlyEnum,
-                       ReadFileResult>(ReadFileResult::READ_OK,
-                                       TestPresetExecutionShowOnlyHelper);
-
-ReadFileResult TestPresetExecutionModeHelper(
-  TestPreset::ExecutionOptions::RepeatOptions::ModeEnum& out,
-  const Json::Value* value)
-{
-  if (!value) {
-    return ReadFileResult::READ_OK;
-  }
-
-  if (!value->isString()) {
-    return ReadFileResult::INVALID_PRESET;
-  }
-
-  if (value->asString() == "until-fail") {
-    out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::UntilFail;
-    return ReadFileResult::READ_OK;
-  }
-
-  if (value->asString() == "until-pass") {
-    out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::UntilPass;
-    return ReadFileResult::READ_OK;
-  }
-
-  if (value->asString() == "after-timeout") {
-    out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::AfterTimeout;
-    return ReadFileResult::READ_OK;
-  }
-
-  return ReadFileResult::INVALID_PRESET;
-}
-
-auto const TestPresetOptionalExecutionRepeatHelper =
-  cmJSONOptionalHelper<TestPreset::ExecutionOptions::RepeatOptions,
-                       ReadFileResult>(
-    ReadFileResult::READ_OK,
-    cmJSONObjectHelper<TestPreset::ExecutionOptions::RepeatOptions,
-                       ReadFileResult>(ReadFileResult::READ_OK,
-                                       ReadFileResult::INVALID_PRESET)
-      .Bind("mode"_s, &TestPreset::ExecutionOptions::RepeatOptions::Mode,
-            TestPresetExecutionModeHelper, true)
-      .Bind("count"_s, &TestPreset::ExecutionOptions::RepeatOptions::Count,
-            PresetIntHelper, true));
-
-ReadFileResult TestPresetExecutionNoTestsActionHelper(
-  TestPreset::ExecutionOptions::NoTestsActionEnum& out,
-  const Json::Value* value)
-{
-  if (!value) {
-    out = TestPreset::ExecutionOptions::NoTestsActionEnum::Default;
-    return ReadFileResult::READ_OK;
-  }
-
-  if (!value->isString()) {
-    return ReadFileResult::INVALID_PRESET;
-  }
-
-  if (value->asString() == "default") {
-    out = TestPreset::ExecutionOptions::NoTestsActionEnum::Default;
-    return ReadFileResult::READ_OK;
-  }
-
-  if (value->asString() == "error") {
-    out = TestPreset::ExecutionOptions::NoTestsActionEnum::Error;
-    return ReadFileResult::READ_OK;
-  }
-
-  if (value->asString() == "ignore") {
-    out = TestPreset::ExecutionOptions::NoTestsActionEnum::Ignore;
-    return ReadFileResult::READ_OK;
-  }
-
-  return ReadFileResult::INVALID_PRESET;
-}
-
-auto const TestPresetOptionalExecutionNoTestsActionHelper =
-  cmJSONOptionalHelper<TestPreset::ExecutionOptions::NoTestsActionEnum,
-                       ReadFileResult>(ReadFileResult::READ_OK,
-                                       TestPresetExecutionNoTestsActionHelper);
-
-auto const TestPresetExecutionHelper =
-  cmJSONOptionalHelper<TestPreset::ExecutionOptions, ReadFileResult>(
-    ReadFileResult::READ_OK,
-    cmJSONObjectHelper<TestPreset::ExecutionOptions, ReadFileResult>(
-      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
-      .Bind("stopOnFailure"_s, &TestPreset::ExecutionOptions::StopOnFailure,
-            PresetOptionalBoolHelper, false)
-      .Bind("enableFailover"_s, &TestPreset::ExecutionOptions::EnableFailover,
-            PresetOptionalBoolHelper, false)
-      .Bind("jobs"_s, &TestPreset::ExecutionOptions::Jobs,
-            PresetOptionalIntHelper, false)
-      .Bind("resourceSpecFile"_s,
-            &TestPreset::ExecutionOptions::ResourceSpecFile,
-            PresetStringHelper, false)
-      .Bind("testLoad"_s, &TestPreset::ExecutionOptions::TestLoad,
-            PresetOptionalIntHelper, false)
-      .Bind("showOnly"_s, &TestPreset::ExecutionOptions::ShowOnly,
-            TestPresetOptionalExecutionShowOnlyHelper, false)
-      .Bind("repeat"_s, &TestPreset::ExecutionOptions::Repeat,
-            TestPresetOptionalExecutionRepeatHelper, false)
-      .Bind("interactiveDebugging"_s,
-            &TestPreset::ExecutionOptions::InteractiveDebugging,
-            PresetOptionalBoolHelper, false)
-      .Bind("scheduleRandom"_s, &TestPreset::ExecutionOptions::ScheduleRandom,
-            PresetOptionalBoolHelper, false)
-      .Bind("timeout"_s, &TestPreset::ExecutionOptions::Timeout,
-            PresetOptionalIntHelper, false)
-      .Bind("noTestsAction"_s, &TestPreset::ExecutionOptions::NoTestsAction,
-            TestPresetOptionalExecutionNoTestsActionHelper, false));
-
-auto const TestPresetFilterHelper =
-  cmJSONOptionalHelper<TestPreset::FilterOptions, ReadFileResult>(
-    ReadFileResult::READ_OK,
-    cmJSONObjectHelper<TestPreset::FilterOptions, ReadFileResult>(
-      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
-      .Bind("include"_s, &TestPreset::FilterOptions::Include,
-            TestPresetOptionalFilterIncludeHelper, false)
-      .Bind("exclude"_s, &TestPreset::FilterOptions::Exclude,
-            TestPresetOptionalFilterExcludeHelper, false));
-
-auto const TestPresetHelper =
-  cmJSONObjectHelper<TestPreset, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
-    .Bind("name"_s, &TestPreset::Name, PresetStringHelper)
-    .Bind("inherits"_s, &TestPreset::Inherits, PresetInheritsHelper, false)
-    .Bind("hidden"_s, &TestPreset::Hidden, PresetBoolHelper, false)
-    .Bind<std::nullptr_t>("vendor"_s, nullptr,
-                          VendorHelper(ReadFileResult::INVALID_PRESET), false)
-    .Bind("displayName"_s, &TestPreset::DisplayName, PresetStringHelper, false)
-    .Bind("description"_s, &TestPreset::Description, PresetStringHelper, false)
-    .Bind("environment"_s, &TestPreset::Environment, EnvironmentMapHelper,
-          false)
-    .Bind("configurePreset"_s, &TestPreset::ConfigurePreset,
-          PresetStringHelper, false)
-    .Bind("inheritConfigureEnvironment"_s,
-          &TestPreset::InheritConfigureEnvironment, PresetOptionalBoolHelper,
-          false)
-    .Bind("configuration"_s, &TestPreset::Configuration, PresetStringHelper,
-          false)
-    .Bind("overwriteConfigurationFile"_s,
-          &TestPreset::OverwriteConfigurationFile, PresetVectorStringHelper,
-          false)
-    .Bind("output"_s, &TestPreset::Output, TestPresetOptionalOutputHelper,
-          false)
-    .Bind("filter"_s, &TestPreset::Filter, TestPresetFilterHelper, false)
-    .Bind("execution"_s, &TestPreset::Execution, TestPresetExecutionHelper,
-          false);
-
-auto const ConfigurePresetsHelper =
-  cmJSONVectorHelper<ConfigurePreset, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
-    ConfigurePresetHelper);
-
-auto const BuildPresetsHelper =
-  cmJSONVectorHelper<BuildPreset, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
-    BuildPresetHelper);
-
-auto const TestPresetsHelper = cmJSONVectorHelper<TestPreset, ReadFileResult>(
-  ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS, TestPresetHelper);
-
-auto const CMakeVersionUIntHelper = cmJSONUIntHelper<ReadFileResult>(
-  ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
-
-auto const CMakeVersionHelper =
-  cmJSONObjectHelper<CMakeVersion, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_CMAKE_VERSION, false)
-    .Bind("major"_s, &CMakeVersion::Major, CMakeVersionUIntHelper, false)
-    .Bind("minor"_s, &CMakeVersion::Minor, CMakeVersionUIntHelper, false)
-    .Bind("patch"_s, &CMakeVersion::Patch, CMakeVersionUIntHelper, false);
-
-auto const RootPresetsHelper =
-  cmJSONObjectHelper<RootPresets, ReadFileResult>(
-    ReadFileResult::READ_OK, ReadFileResult::INVALID_ROOT, false)
-    .Bind<int>("version"_s, nullptr, VersionHelper)
-    .Bind("configurePresets"_s, &RootPresets::ConfigurePresets,
-          ConfigurePresetsHelper, false)
-    .Bind("buildPresets"_s, &RootPresets::BuildPresets, BuildPresetsHelper,
-          false)
-    .Bind("testPresets"_s, &RootPresets::TestPresets, TestPresetsHelper, false)
-    .Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired,
-          CMakeVersionHelper, false)
-    .Bind<std::nullptr_t>("vendor"_s, nullptr,
-                          VendorHelper(ReadFileResult::INVALID_ROOT), false);
+using ExpandMacroResult = cmCMakePresetsFileInternal::ExpandMacroResult;
+using MacroExpander = cmCMakePresetsFileInternal::MacroExpander;
 
 
 void InheritString(std::string& child, const std::string& parent)
 void InheritString(std::string& child, const std::string& parent)
 {
 {
@@ -794,6 +116,14 @@ ReadFileResult VisitPreset(
     for (auto const& v : parentPreset.Environment) {
     for (auto const& v : parentPreset.Environment) {
       preset.Environment.insert(v);
       preset.Environment.insert(v);
     }
     }
+
+    if (!preset.ConditionEvaluator) {
+      preset.ConditionEvaluator = parentPreset.ConditionEvaluator;
+    }
+  }
+
+  if (preset.ConditionEvaluator && preset.ConditionEvaluator->IsNull()) {
+    preset.ConditionEvaluator.reset();
   }
   }
 
 
   CHECK_OK(preset.VisitPresetAfterInherit())
   CHECK_OK(preset.VisitPresetAfterInherit())
@@ -842,16 +172,6 @@ bool IsValidMacroNamespace(const std::string& str)
     [&str](const char* prefix) -> bool { return str == prefix; });
     [&str](const char* prefix) -> bool { return str == prefix; });
 }
 }
 
 
-enum class ExpandMacroResult
-{
-  Ok,
-  Ignore,
-  Error,
-};
-
-using MacroExpander = std::function<ExpandMacroResult(
-  const std::string&, const std::string&, std::string&, int version)>;
-
 ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status,
 ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status,
                            const std::vector<MacroExpander>& macroExpanders,
                            const std::vector<MacroExpander>& macroExpanders,
                            int version);
                            int version);
@@ -1072,6 +392,19 @@ bool ExpandMacros(const cmCMakePresetsFile& file, const T& preset,
     }
     }
   }
   }
 
 
+  if (preset.ConditionEvaluator) {
+    cm::optional<bool> result;
+    if (!preset.ConditionEvaluator->Evaluate(
+          macroExpanders, file.GetVersion(preset), result)) {
+      return false;
+    }
+    if (!result) {
+      out.reset();
+      return true;
+    }
+    out->ConditionResult = *result;
+  }
+
   return ExpandMacros(file, preset, out, macroExpanders);
   return ExpandMacros(file, preset, out, macroExpanders);
 }
 }
 
 
@@ -1195,6 +528,80 @@ ExpandMacroResult ExpandMacro(std::string& out,
 }
 }
 }
 }
 
 
+bool cmCMakePresetsFileInternal::EqualsCondition::Evaluate(
+  const std::vector<MacroExpander>& expanders, int version,
+  cm::optional<bool>& out) const
+{
+  std::string lhs = this->Lhs;
+  CHECK_EXPAND(out, lhs, expanders, version);
+
+  std::string rhs = this->Rhs;
+  CHECK_EXPAND(out, rhs, expanders, version);
+
+  out = (lhs == rhs);
+  return true;
+}
+
+bool cmCMakePresetsFileInternal::InListCondition::Evaluate(
+  const std::vector<MacroExpander>& expanders, int version,
+  cm::optional<bool>& out) const
+{
+  std::string str = this->String;
+  CHECK_EXPAND(out, str, expanders, version);
+
+  for (auto item : this->List) {
+    CHECK_EXPAND(out, item, expanders, version);
+    if (str == item) {
+      out = true;
+      return true;
+    }
+  }
+
+  out = false;
+  return true;
+}
+
+bool cmCMakePresetsFileInternal::AnyAllOfCondition::Evaluate(
+  const std::vector<MacroExpander>& expanders, int version,
+  cm::optional<bool>& out) const
+{
+  for (auto const& condition : this->Conditions) {
+    cm::optional<bool> result;
+    if (!condition->Evaluate(expanders, version, result)) {
+      out.reset();
+      return false;
+    }
+
+    if (!result) {
+      out.reset();
+      return true;
+    }
+
+    if (result == this->StopValue) {
+      out = result;
+      return true;
+    }
+  }
+
+  out = !this->StopValue;
+  return true;
+}
+
+bool cmCMakePresetsFileInternal::NotCondition::Evaluate(
+  const std::vector<MacroExpander>& expanders, int version,
+  cm::optional<bool>& out) const
+{
+  out.reset();
+  if (!this->SubCondition->Evaluate(expanders, version, out)) {
+    out.reset();
+    return false;
+  }
+  if (out) {
+    *out = !*out;
+  }
+  return true;
+}
+
 cmCMakePresetsFile::ReadFileResult
 cmCMakePresetsFile::ReadFileResult
 cmCMakePresetsFile::ConfigurePreset::VisitPresetInherit(
 cmCMakePresetsFile::ConfigurePreset::VisitPresetInherit(
   const cmCMakePresetsFile::Preset& parentPreset)
   const cmCMakePresetsFile::Preset& parentPreset)
@@ -1552,122 +959,15 @@ const char* cmCMakePresetsFile::ResultToString(ReadFileResult result)
     case ReadFileResult::INSTALL_PREFIX_UNSUPPORTED:
     case ReadFileResult::INSTALL_PREFIX_UNSUPPORTED:
       return "File version must be 3 or higher for installDir preset "
       return "File version must be 3 or higher for installDir preset "
              "support.";
              "support.";
+    case ReadFileResult::INVALID_CONDITION:
+      return "Invalid preset condition";
+    case ReadFileResult::CONDITION_UNSUPPORTED:
+      return "File version must be 3 or higher for condition support";
   }
   }
 
 
   return "Unknown error";
   return "Unknown error";
 }
 }
 
 
-cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile(
-  const std::string& filename, bool user)
-{
-  cmsys::ifstream fin(filename.c_str());
-  if (!fin) {
-    return ReadFileResult::FILE_NOT_FOUND;
-  }
-  // If there's a BOM, toss it.
-  cmsys::FStream::ReadBOM(fin);
-
-  Json::Value root;
-  Json::CharReaderBuilder builder;
-  Json::CharReaderBuilder::strictMode(&builder.settings_);
-  if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
-    return ReadFileResult::JSON_PARSE_ERROR;
-  }
-
-  int v = 0;
-  auto result = RootVersionHelper(v, &root);
-  if (result != ReadFileResult::READ_OK) {
-    return result;
-  }
-  if (v < MIN_VERSION || v > MAX_VERSION) {
-    return ReadFileResult::UNRECOGNIZED_VERSION;
-  }
-  if (user) {
-    this->UserVersion = v;
-  } else {
-    this->Version = v;
-  }
-
-  // Support for build and test presets added in version 2.
-  if (v < 2 &&
-      (root.isMember("buildPresets") || root.isMember("testPresets"))) {
-    return ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED;
-  }
-
-  RootPresets presets;
-  if ((result = RootPresetsHelper(presets, &root)) !=
-      ReadFileResult::READ_OK) {
-    return result;
-  }
-
-  unsigned int currentMajor = cmVersion::GetMajorVersion();
-  unsigned int currentMinor = cmVersion::GetMinorVersion();
-  unsigned int currentPatch = cmVersion::GetPatchVersion();
-  auto const& required = presets.CMakeMinimumRequired;
-  if (required.Major > currentMajor ||
-      (required.Major == currentMajor &&
-       (required.Minor > currentMinor ||
-        (required.Minor == currentMinor &&
-         (required.Patch > currentPatch))))) {
-    return ReadFileResult::UNRECOGNIZED_CMAKE_VERSION;
-  }
-
-  for (auto& preset : presets.ConfigurePresets) {
-    preset.User = user;
-    if (preset.Name.empty()) {
-      return ReadFileResult::INVALID_PRESET;
-    }
-
-    PresetPair<ConfigurePreset> presetPair;
-    presetPair.Unexpanded = preset;
-    presetPair.Expanded = cm::nullopt;
-    if (!this->ConfigurePresets
-           .emplace(std::make_pair(preset.Name, presetPair))
-           .second) {
-      return ReadFileResult::DUPLICATE_PRESETS;
-    }
-
-    // Support for installDir presets added in version 3.
-    if (v < 3 && !preset.InstallDir.empty()) {
-      return ReadFileResult::INSTALL_PREFIX_UNSUPPORTED;
-    }
-
-    this->ConfigurePresetOrder.push_back(preset.Name);
-  }
-
-  for (auto& preset : presets.BuildPresets) {
-    preset.User = user;
-    if (preset.Name.empty()) {
-      return ReadFileResult::INVALID_PRESET;
-    }
-
-    PresetPair<BuildPreset> presetPair;
-    presetPair.Unexpanded = preset;
-    presetPair.Expanded = cm::nullopt;
-    if (!this->BuildPresets.emplace(preset.Name, presetPair).second) {
-      return ReadFileResult::DUPLICATE_PRESETS;
-    }
-    this->BuildPresetOrder.push_back(preset.Name);
-  }
-
-  for (auto& preset : presets.TestPresets) {
-    preset.User = user;
-    if (preset.Name.empty()) {
-      return ReadFileResult::INVALID_PRESET;
-    }
-
-    PresetPair<TestPreset> presetPair;
-    presetPair.Unexpanded = preset;
-    presetPair.Expanded = cm::nullopt;
-    if (!this->TestPresets.emplace(preset.Name, presetPair).second) {
-      return ReadFileResult::DUPLICATE_PRESETS;
-    }
-    this->TestPresetOrder.push_back(preset.Name);
-  }
-
-  return ReadFileResult::READ_OK;
-}
-
 void cmCMakePresetsFile::ClearPresets()
 void cmCMakePresetsFile::ClearPresets()
 {
 {
   this->ConfigurePresets.clear();
   this->ConfigurePresets.clear();
@@ -1719,7 +1019,7 @@ void cmCMakePresetsFile::PrintConfigurePresetList(
   for (auto const& p : this->ConfigurePresetOrder) {
   for (auto const& p : this->ConfigurePresetOrder) {
     auto const& preset = this->ConfigurePresets.at(p);
     auto const& preset = this->ConfigurePresets.at(p);
     if (!preset.Unexpanded.Hidden && preset.Expanded &&
     if (!preset.Unexpanded.Hidden && preset.Expanded &&
-        filter(preset.Unexpanded)) {
+        preset.Expanded->ConditionResult && filter(preset.Unexpanded)) {
       presets.push_back(
       presets.push_back(
         static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded));
         static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded));
     }
     }
@@ -1736,7 +1036,8 @@ void cmCMakePresetsFile::PrintBuildPresetList() const
   std::vector<const cmCMakePresetsFile::Preset*> presets;
   std::vector<const cmCMakePresetsFile::Preset*> presets;
   for (auto const& p : this->BuildPresetOrder) {
   for (auto const& p : this->BuildPresetOrder) {
     auto const& preset = this->BuildPresets.at(p);
     auto const& preset = this->BuildPresets.at(p);
-    if (!preset.Unexpanded.Hidden && preset.Expanded) {
+    if (!preset.Unexpanded.Hidden && preset.Expanded &&
+        preset.Expanded->ConditionResult) {
       presets.push_back(
       presets.push_back(
         static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded));
         static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded));
     }
     }
@@ -1753,7 +1054,8 @@ void cmCMakePresetsFile::PrintTestPresetList() const
   std::vector<const cmCMakePresetsFile::Preset*> presets;
   std::vector<const cmCMakePresetsFile::Preset*> presets;
   for (auto const& p : this->TestPresetOrder) {
   for (auto const& p : this->TestPresetOrder) {
     auto const& preset = this->TestPresets.at(p);
     auto const& preset = this->TestPresets.at(p);
-    if (!preset.Unexpanded.Hidden && preset.Expanded) {
+    if (!preset.Unexpanded.Hidden && preset.Expanded &&
+        preset.Expanded->ConditionResult) {
       presets.push_back(
       presets.push_back(
         static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded));
         static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded));
     }
     }

+ 10 - 0
Source/cmCMakePresetsFile.h

@@ -2,8 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #pragma once
 #pragma once
 
 
+#include "cmConfigure.h" // IWYU pragma: keep
+
 #include <functional>
 #include <functional>
 #include <map>
 #include <map>
+#include <memory>
 #include <string>
 #include <string>
 #include <utility>
 #include <utility>
 #include <vector>
 #include <vector>
@@ -34,6 +37,8 @@ public:
     BUILD_TEST_PRESETS_UNSUPPORTED,
     BUILD_TEST_PRESETS_UNSUPPORTED,
     INVALID_CONFIGURE_PRESET,
     INVALID_CONFIGURE_PRESET,
     INSTALL_PREFIX_UNSUPPORTED,
     INSTALL_PREFIX_UNSUPPORTED,
+    INVALID_CONDITION,
+    CONDITION_UNSUPPORTED,
   };
   };
 
 
   enum class ArchToolsetStrategy
   enum class ArchToolsetStrategy
@@ -49,6 +54,8 @@ public:
     std::string Value;
     std::string Value;
   };
   };
 
 
+  class Condition;
+
   class Preset
   class Preset
   {
   {
   public:
   public:
@@ -71,6 +78,9 @@ public:
     std::string DisplayName;
     std::string DisplayName;
     std::string Description;
     std::string Description;
 
 
+    std::shared_ptr<Condition> ConditionEvaluator;
+    bool ConditionResult = true;
+
     std::map<std::string, cm::optional<std::string>> Environment;
     std::map<std::string, cm::optional<std::string>> Environment;
 
 
     virtual ReadFileResult VisitPresetInherit(const Preset& parent) = 0;
     virtual ReadFileResult VisitPresetInherit(const Preset& parent) = 0;

+ 102 - 0
Source/cmCMakePresetsFileInternal.h

@@ -0,0 +1,102 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include <memory>
+
+#include "cmCMakePresetsFile.h"
+
+#define CHECK_OK(expr)                                                        \
+  {                                                                           \
+    auto _result = expr;                                                      \
+    if (_result != ReadFileResult::READ_OK)                                   \
+      return _result;                                                         \
+  }
+
+namespace cmCMakePresetsFileInternal {
+enum class ExpandMacroResult
+{
+  Ok,
+  Ignore,
+  Error,
+};
+
+using MacroExpander = std::function<ExpandMacroResult(
+  const std::string&, const std::string&, std::string&, int version)>;
+}
+
+class cmCMakePresetsFile::Condition
+{
+public:
+  virtual ~Condition() = default;
+
+  virtual bool Evaluate(
+    const std::vector<cmCMakePresetsFileInternal::MacroExpander>& expanders,
+    int version, cm::optional<bool>& out) const = 0;
+  virtual bool IsNull() const { return false; }
+};
+
+namespace cmCMakePresetsFileInternal {
+
+class NullCondition : public cmCMakePresetsFile::Condition
+{
+  bool Evaluate(const std::vector<MacroExpander>& /*expanders*/,
+                int /*version*/, cm::optional<bool>& out) const override
+  {
+    out = true;
+    return true;
+  }
+
+  bool IsNull() const override { return true; }
+};
+
+class ConstCondition : public cmCMakePresetsFile::Condition
+{
+public:
+  bool Evaluate(const std::vector<MacroExpander>& /*expanders*/,
+                int /*version*/, cm::optional<bool>& out) const override
+  {
+    out = this->Value;
+    return true;
+  }
+
+  bool Value;
+};
+
+class EqualsCondition : public cmCMakePresetsFile::Condition
+{
+public:
+  bool Evaluate(const std::vector<MacroExpander>& expanders, int version,
+                cm::optional<bool>& out) const override;
+
+  std::string Lhs;
+  std::string Rhs;
+};
+
+class InListCondition : public cmCMakePresetsFile::Condition
+{
+public:
+  bool Evaluate(const std::vector<MacroExpander>& expanders, int version,
+                cm::optional<bool>& out) const override;
+
+  std::string String;
+  std::vector<std::string> List;
+};
+
+class AnyAllOfCondition : public cmCMakePresetsFile::Condition
+{
+public:
+  bool Evaluate(const std::vector<MacroExpander>& expanders, int version,
+                cm::optional<bool>& out) const override;
+
+  std::vector<std::unique_ptr<Condition>> Conditions;
+  bool StopValue;
+};
+
+class NotCondition : public cmCMakePresetsFile::Condition
+{
+public:
+  bool Evaluate(const std::vector<MacroExpander>& expanders, int version,
+                cm::optional<bool>& out) const override;
+
+  std::unique_ptr<Condition> SubCondition;
+};
+}

+ 1002 - 0
Source/cmCMakePresetsFileReadJSON.cxx

@@ -0,0 +1,1002 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include <functional>
+#include <map>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <cm/memory>
+#include <cm/optional>
+#include <cmext/string_view>
+
+#include <cm3p/json/reader.h>
+#include <cm3p/json/value.h>
+
+#include "cmsys/FStream.hxx"
+
+#include "cmCMakePresetsFile.h"
+#include "cmCMakePresetsFileInternal.h"
+#include "cmJSONHelpers.h"
+#include "cmVersion.h"
+
+namespace {
+using ReadFileResult = cmCMakePresetsFile::ReadFileResult;
+using CacheVariable = cmCMakePresetsFile::CacheVariable;
+using ConfigurePreset = cmCMakePresetsFile::ConfigurePreset;
+using BuildPreset = cmCMakePresetsFile::BuildPreset;
+using TestPreset = cmCMakePresetsFile::TestPreset;
+using ArchToolsetStrategy = cmCMakePresetsFile::ArchToolsetStrategy;
+
+constexpr int MIN_VERSION = 1;
+constexpr int MAX_VERSION = 3;
+
+struct CMakeVersion
+{
+  unsigned int Major = 0;
+  unsigned int Minor = 0;
+  unsigned int Patch = 0;
+};
+
+struct RootPresets
+{
+  CMakeVersion CMakeMinimumRequired;
+  std::vector<cmCMakePresetsFile::ConfigurePreset> ConfigurePresets;
+  std::vector<cmCMakePresetsFile::BuildPreset> BuildPresets;
+  std::vector<cmCMakePresetsFile::TestPreset> TestPresets;
+};
+
+std::unique_ptr<cmCMakePresetsFileInternal::NotCondition> InvertCondition(
+  std::unique_ptr<cmCMakePresetsFile::Condition> condition)
+{
+  auto retval = cm::make_unique<cmCMakePresetsFileInternal::NotCondition>();
+  retval->SubCondition = std::move(condition);
+  return retval;
+}
+
+auto const ConditionStringHelper = cmJSONStringHelper<ReadFileResult>(
+  ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION);
+
+auto const ConditionBoolHelper = cmJSONBoolHelper<ReadFileResult>(
+  ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION);
+
+auto const ConditionStringListHelper =
+  cmJSONVectorHelper<std::string, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION,
+    ConditionStringHelper);
+
+auto const ConstConditionHelper =
+  cmJSONObjectHelper<cmCMakePresetsFileInternal::ConstCondition,
+                     ReadFileResult>(ReadFileResult::READ_OK,
+                                     ReadFileResult::INVALID_CONDITION, false)
+    .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
+    .Bind("value"_s, &cmCMakePresetsFileInternal::ConstCondition::Value,
+          ConditionBoolHelper, true);
+
+auto const EqualsConditionHelper =
+  cmJSONObjectHelper<cmCMakePresetsFileInternal::EqualsCondition,
+                     ReadFileResult>(ReadFileResult::READ_OK,
+                                     ReadFileResult::INVALID_CONDITION, false)
+    .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
+    .Bind("lhs"_s, &cmCMakePresetsFileInternal::EqualsCondition::Lhs,
+          ConditionStringHelper, true)
+    .Bind("rhs"_s, &cmCMakePresetsFileInternal::EqualsCondition::Rhs,
+          ConditionStringHelper, true);
+
+auto const InListConditionHelper =
+  cmJSONObjectHelper<cmCMakePresetsFileInternal::InListCondition,
+                     ReadFileResult>(ReadFileResult::READ_OK,
+                                     ReadFileResult::INVALID_CONDITION, false)
+    .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
+    .Bind("string"_s, &cmCMakePresetsFileInternal::InListCondition::String,
+          ConditionStringHelper, true)
+    .Bind("list"_s, &cmCMakePresetsFileInternal::InListCondition::List,
+          ConditionStringListHelper, true);
+
+ReadFileResult SubConditionHelper(
+  std::unique_ptr<cmCMakePresetsFile::Condition>& out,
+  const Json::Value* value);
+
+auto const ListConditionVectorHelper =
+  cmJSONVectorHelper<std::unique_ptr<cmCMakePresetsFile::Condition>,
+                     ReadFileResult>(ReadFileResult::READ_OK,
+                                     ReadFileResult::INVALID_CONDITION,
+                                     SubConditionHelper);
+auto const AnyAllOfConditionHelper =
+  cmJSONObjectHelper<cmCMakePresetsFileInternal::AnyAllOfCondition,
+                     ReadFileResult>(ReadFileResult::READ_OK,
+                                     ReadFileResult::INVALID_CONDITION, false)
+    .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
+    .Bind("conditions"_s,
+          &cmCMakePresetsFileInternal::AnyAllOfCondition::Conditions,
+          ListConditionVectorHelper);
+
+auto const NotConditionHelper =
+  cmJSONObjectHelper<cmCMakePresetsFileInternal::NotCondition, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_CONDITION, false)
+    .Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
+    .Bind("condition"_s,
+          &cmCMakePresetsFileInternal::NotCondition::SubCondition,
+          SubConditionHelper);
+
+ReadFileResult ConditionHelper(
+  std::unique_ptr<cmCMakePresetsFile::Condition>& out,
+  const Json::Value* value)
+{
+  if (!value) {
+    out.reset();
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->isBool()) {
+    auto c = cm::make_unique<cmCMakePresetsFileInternal::ConstCondition>();
+    c->Value = value->asBool();
+    out = std::move(c);
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->isNull()) {
+    out = cm::make_unique<cmCMakePresetsFileInternal::NullCondition>();
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->isObject()) {
+    if (!value->isMember("type")) {
+      return ReadFileResult::INVALID_CONDITION;
+    }
+
+    if (!(*value)["type"].isString()) {
+      return ReadFileResult::INVALID_CONDITION;
+    }
+    auto type = (*value)["type"].asString();
+
+    if (type == "const") {
+      auto c = cm::make_unique<cmCMakePresetsFileInternal::ConstCondition>();
+      CHECK_OK(ConstConditionHelper(*c, value));
+      out = std::move(c);
+      return ReadFileResult::READ_OK;
+    }
+
+    if (type == "equals" || type == "notEquals") {
+      auto c = cm::make_unique<cmCMakePresetsFileInternal::EqualsCondition>();
+      CHECK_OK(EqualsConditionHelper(*c, value));
+      out = std::move(c);
+      if (type == "notEquals") {
+        out = InvertCondition(std::move(out));
+      }
+      return ReadFileResult::READ_OK;
+    }
+
+    if (type == "inList" || type == "notInList") {
+      auto c = cm::make_unique<cmCMakePresetsFileInternal::InListCondition>();
+      CHECK_OK(InListConditionHelper(*c, value));
+      out = std::move(c);
+      if (type == "notInList") {
+        out = InvertCondition(std::move(out));
+      }
+      return ReadFileResult::READ_OK;
+    }
+
+    if (type == "anyOf" || type == "allOf") {
+      auto c =
+        cm::make_unique<cmCMakePresetsFileInternal::AnyAllOfCondition>();
+      c->StopValue = (type == "anyOf");
+      CHECK_OK(AnyAllOfConditionHelper(*c, value));
+      out = std::move(c);
+      return ReadFileResult::READ_OK;
+    }
+
+    if (type == "not") {
+      auto c = cm::make_unique<cmCMakePresetsFileInternal::NotCondition>();
+      CHECK_OK(NotConditionHelper(*c, value));
+      out = std::move(c);
+      return ReadFileResult::READ_OK;
+    }
+  }
+
+  return ReadFileResult::INVALID_CONDITION;
+}
+
+ReadFileResult PresetConditionHelper(
+  std::shared_ptr<cmCMakePresetsFile::Condition>& out,
+  const Json::Value* value)
+{
+  std::unique_ptr<cmCMakePresetsFile::Condition> ptr;
+  auto result = ConditionHelper(ptr, value);
+  out = std::move(ptr);
+  return result;
+}
+
+ReadFileResult SubConditionHelper(
+  std::unique_ptr<cmCMakePresetsFile::Condition>& out,
+  const Json::Value* value)
+{
+  std::unique_ptr<cmCMakePresetsFile::Condition> ptr;
+  auto result = ConditionHelper(ptr, value);
+  if (ptr && ptr->IsNull()) {
+    return ReadFileResult::INVALID_CONDITION;
+  }
+  out = std::move(ptr);
+  return result;
+}
+
+cmJSONHelper<std::nullptr_t, ReadFileResult> VendorHelper(ReadFileResult error)
+{
+  return [error](std::nullptr_t& /*out*/,
+                 const Json::Value* value) -> ReadFileResult {
+    if (!value) {
+      return ReadFileResult::READ_OK;
+    }
+
+    if (!value->isObject()) {
+      return error;
+    }
+
+    return ReadFileResult::READ_OK;
+  };
+}
+
+auto const VersionIntHelper = cmJSONIntHelper<ReadFileResult>(
+  ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
+
+auto const VersionHelper = cmJSONRequiredHelper<int, ReadFileResult>(
+  ReadFileResult::NO_VERSION, VersionIntHelper);
+
+auto const RootVersionHelper =
+  cmJSONObjectHelper<int, ReadFileResult>(ReadFileResult::READ_OK,
+                                          ReadFileResult::INVALID_ROOT)
+    .Bind("version"_s, VersionHelper, false);
+
+auto const VariableStringHelper = cmJSONStringHelper<ReadFileResult>(
+  ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE);
+
+ReadFileResult VariableValueHelper(std::string& out, const Json::Value* value)
+{
+  if (!value) {
+    out.clear();
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->isBool()) {
+    out = value->asBool() ? "TRUE" : "FALSE";
+    return ReadFileResult::READ_OK;
+  }
+
+  return VariableStringHelper(out, value);
+}
+
+auto const VariableObjectHelper =
+  cmJSONObjectHelper<CacheVariable, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_VARIABLE, false)
+    .Bind("type"_s, &CacheVariable::Type, VariableStringHelper, false)
+    .Bind("value"_s, &CacheVariable::Value, VariableValueHelper);
+
+ReadFileResult VariableHelper(cm::optional<CacheVariable>& out,
+                              const Json::Value* value)
+{
+  if (value->isBool()) {
+    out = CacheVariable{
+      /*Type=*/"BOOL",
+      /*Value=*/value->asBool() ? "TRUE" : "FALSE",
+    };
+    return ReadFileResult::READ_OK;
+  }
+  if (value->isString()) {
+    out = CacheVariable{
+      /*Type=*/"",
+      /*Value=*/value->asString(),
+    };
+    return ReadFileResult::READ_OK;
+  }
+  if (value->isObject()) {
+    out.emplace();
+    return VariableObjectHelper(*out, value);
+  }
+  if (value->isNull()) {
+    out = cm::nullopt;
+    return ReadFileResult::READ_OK;
+  }
+  return ReadFileResult::INVALID_VARIABLE;
+}
+
+auto const VariablesHelper =
+  cmJSONMapHelper<cm::optional<CacheVariable>, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, VariableHelper);
+
+auto const PresetStringHelper = cmJSONStringHelper<ReadFileResult>(
+  ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
+
+ReadFileResult EnvironmentHelper(cm::optional<std::string>& out,
+                                 const Json::Value* value)
+{
+  if (!value || value->isNull()) {
+    out = cm::nullopt;
+    return ReadFileResult::READ_OK;
+  }
+  if (value->isString()) {
+    out = value->asString();
+    return ReadFileResult::READ_OK;
+  }
+  return ReadFileResult::INVALID_PRESET;
+}
+
+auto const EnvironmentMapHelper =
+  cmJSONMapHelper<cm::optional<std::string>, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
+    EnvironmentHelper);
+
+auto const PresetVectorStringHelper =
+  cmJSONVectorHelper<std::string, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET,
+    PresetStringHelper);
+
+ReadFileResult PresetInheritsHelper(std::vector<std::string>& out,
+                                    const Json::Value* value)
+{
+  out.clear();
+  if (!value) {
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->isString()) {
+    out.push_back(value->asString());
+    return ReadFileResult::READ_OK;
+  }
+
+  return PresetVectorStringHelper(out, value);
+}
+
+auto const PresetBoolHelper = cmJSONBoolHelper<ReadFileResult>(
+  ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
+
+auto const PresetOptionalBoolHelper =
+  cmJSONOptionalHelper<bool, ReadFileResult>(ReadFileResult::READ_OK,
+                                             PresetBoolHelper);
+
+auto const PresetIntHelper = cmJSONIntHelper<ReadFileResult>(
+  ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET);
+
+auto const PresetOptionalIntHelper = cmJSONOptionalHelper<int, ReadFileResult>(
+  ReadFileResult::READ_OK, PresetIntHelper);
+
+auto const PresetVectorIntHelper = cmJSONVectorHelper<int, ReadFileResult>(
+  ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, PresetIntHelper);
+
+auto const PresetWarningsHelper =
+  cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+    .Bind("dev"_s, &ConfigurePreset::WarnDev, PresetOptionalBoolHelper, false)
+    .Bind("deprecated"_s, &ConfigurePreset::WarnDeprecated,
+          PresetOptionalBoolHelper, false)
+    .Bind("uninitialized"_s, &ConfigurePreset::WarnUninitialized,
+          PresetOptionalBoolHelper, false)
+    .Bind("unusedCli"_s, &ConfigurePreset::WarnUnusedCli,
+          PresetOptionalBoolHelper, false)
+    .Bind("systemVars"_s, &ConfigurePreset::WarnSystemVars,
+          PresetOptionalBoolHelper, false);
+
+auto const PresetErrorsHelper =
+  cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+    .Bind("dev"_s, &ConfigurePreset::ErrorDev, PresetOptionalBoolHelper, false)
+    .Bind("deprecated"_s, &ConfigurePreset::ErrorDeprecated,
+          PresetOptionalBoolHelper, false);
+
+auto const PresetDebugHelper =
+  cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+    .Bind("output"_s, &ConfigurePreset::DebugOutput, PresetOptionalBoolHelper,
+          false)
+    .Bind("tryCompile"_s, &ConfigurePreset::DebugTryCompile,
+          PresetOptionalBoolHelper, false)
+    .Bind("find"_s, &ConfigurePreset::DebugFind, PresetOptionalBoolHelper,
+          false);
+
+ReadFileResult ArchToolsetStrategyHelper(
+  cm::optional<ArchToolsetStrategy>& out, const Json::Value* value)
+{
+  if (!value) {
+    out = cm::nullopt;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (!value->isString()) {
+    return ReadFileResult::INVALID_PRESET;
+  }
+
+  if (value->asString() == "set") {
+    out = ArchToolsetStrategy::Set;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->asString() == "external") {
+    out = ArchToolsetStrategy::External;
+    return ReadFileResult::READ_OK;
+  }
+
+  return ReadFileResult::INVALID_PRESET;
+}
+
+std::function<ReadFileResult(ConfigurePreset&, const Json::Value*)>
+ArchToolsetHelper(
+  std::string ConfigurePreset::*valueField,
+  cm::optional<ArchToolsetStrategy> ConfigurePreset::*strategyField)
+{
+  auto const objectHelper =
+    cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
+      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+      .Bind("value", valueField, PresetStringHelper, false)
+      .Bind("strategy", strategyField, ArchToolsetStrategyHelper, false);
+  return [valueField, strategyField, objectHelper](
+           ConfigurePreset& out, const Json::Value* value) -> ReadFileResult {
+    if (!value) {
+      (out.*valueField).clear();
+      out.*strategyField = cm::nullopt;
+      return ReadFileResult::READ_OK;
+    }
+
+    if (value->isString()) {
+      out.*valueField = value->asString();
+      out.*strategyField = cm::nullopt;
+      return ReadFileResult::READ_OK;
+    }
+
+    if (value->isObject()) {
+      return objectHelper(out, value);
+    }
+
+    return ReadFileResult::INVALID_PRESET;
+  };
+}
+
+auto const ArchitectureHelper = ArchToolsetHelper(
+  &ConfigurePreset::Architecture, &ConfigurePreset::ArchitectureStrategy);
+auto const ToolsetHelper = ArchToolsetHelper(
+  &ConfigurePreset::Toolset, &ConfigurePreset::ToolsetStrategy);
+
+auto const ConfigurePresetHelper =
+  cmJSONObjectHelper<ConfigurePreset, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+    .Bind("name"_s, &ConfigurePreset::Name, PresetStringHelper)
+    .Bind("inherits"_s, &ConfigurePreset::Inherits, PresetInheritsHelper,
+          false)
+    .Bind("hidden"_s, &ConfigurePreset::Hidden, PresetBoolHelper, false)
+    .Bind<std::nullptr_t>("vendor"_s, nullptr,
+                          VendorHelper(ReadFileResult::INVALID_PRESET), false)
+    .Bind("displayName"_s, &ConfigurePreset::DisplayName, PresetStringHelper,
+          false)
+    .Bind("description"_s, &ConfigurePreset::Description, PresetStringHelper,
+          false)
+    .Bind("generator"_s, &ConfigurePreset::Generator, PresetStringHelper,
+          false)
+    .Bind("architecture"_s, ArchitectureHelper, false)
+    .Bind("toolset"_s, ToolsetHelper, false)
+    .Bind("binaryDir"_s, &ConfigurePreset::BinaryDir, PresetStringHelper,
+          false)
+    .Bind("installDir"_s, &ConfigurePreset::InstallDir, PresetStringHelper,
+          false)
+    .Bind<std::string>("cmakeExecutable"_s, nullptr, PresetStringHelper, false)
+    .Bind("cacheVariables"_s, &ConfigurePreset::CacheVariables,
+          VariablesHelper, false)
+    .Bind("environment"_s, &ConfigurePreset::Environment, EnvironmentMapHelper,
+          false)
+    .Bind("warnings"_s, PresetWarningsHelper, false)
+    .Bind("errors"_s, PresetErrorsHelper, false)
+    .Bind("debug"_s, PresetDebugHelper, false)
+    .Bind("condition"_s, &ConfigurePreset::ConditionEvaluator,
+          PresetConditionHelper, false);
+
+auto const BuildPresetHelper =
+  cmJSONObjectHelper<BuildPreset, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+    .Bind("name"_s, &BuildPreset::Name, PresetStringHelper)
+    .Bind("inherits"_s, &BuildPreset::Inherits, PresetInheritsHelper, false)
+    .Bind("hidden"_s, &BuildPreset::Hidden, PresetBoolHelper, false)
+    .Bind<std::nullptr_t>("vendor"_s, nullptr,
+                          VendorHelper(ReadFileResult::INVALID_PRESET), false)
+    .Bind("displayName"_s, &BuildPreset::DisplayName, PresetStringHelper,
+          false)
+    .Bind("description"_s, &BuildPreset::Description, PresetStringHelper,
+          false)
+    .Bind("environment"_s, &BuildPreset::Environment, EnvironmentMapHelper,
+          false)
+    .Bind("configurePreset"_s, &BuildPreset::ConfigurePreset,
+          PresetStringHelper, false)
+    .Bind("inheritConfigureEnvironment"_s,
+          &BuildPreset::InheritConfigureEnvironment, PresetOptionalBoolHelper,
+          false)
+    .Bind("jobs"_s, &BuildPreset::Jobs, PresetOptionalIntHelper, false)
+    .Bind("targets"_s, &BuildPreset::Targets, PresetVectorStringHelper, false)
+    .Bind("configuration"_s, &BuildPreset::Configuration, PresetStringHelper,
+          false)
+    .Bind("cleanFirst"_s, &BuildPreset::CleanFirst, PresetOptionalBoolHelper,
+          false)
+    .Bind("verbose"_s, &BuildPreset::Verbose, PresetOptionalBoolHelper, false)
+    .Bind("nativeToolOptions"_s, &BuildPreset::NativeToolOptions,
+          PresetVectorStringHelper, false)
+    .Bind("condition"_s, &BuildPreset::ConditionEvaluator,
+          PresetConditionHelper, false);
+
+ReadFileResult TestPresetOutputVerbosityHelper(
+  TestPreset::OutputOptions::VerbosityEnum& out, const Json::Value* value)
+{
+  if (!value) {
+    out = TestPreset::OutputOptions::VerbosityEnum::Default;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (!value->isString()) {
+    return ReadFileResult::INVALID_PRESET;
+  }
+
+  if (value->asString() == "default") {
+    out = TestPreset::OutputOptions::VerbosityEnum::Default;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->asString() == "verbose") {
+    out = TestPreset::OutputOptions::VerbosityEnum::Verbose;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->asString() == "extra") {
+    out = TestPreset::OutputOptions::VerbosityEnum::Extra;
+    return ReadFileResult::READ_OK;
+  }
+
+  return ReadFileResult::INVALID_PRESET;
+}
+
+auto const TestPresetOptionalOutputVerbosityHelper =
+  cmJSONOptionalHelper<TestPreset::OutputOptions::VerbosityEnum,
+                       ReadFileResult>(ReadFileResult::READ_OK,
+                                       TestPresetOutputVerbosityHelper);
+
+auto const TestPresetOptionalOutputHelper =
+  cmJSONOptionalHelper<TestPreset::OutputOptions, ReadFileResult>(
+    ReadFileResult::READ_OK,
+    cmJSONObjectHelper<TestPreset::OutputOptions, ReadFileResult>(
+      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+      .Bind("shortProgress"_s, &TestPreset::OutputOptions::ShortProgress,
+            PresetOptionalBoolHelper, false)
+      .Bind("verbosity"_s, &TestPreset::OutputOptions::Verbosity,
+            TestPresetOptionalOutputVerbosityHelper, false)
+      .Bind("debug"_s, &TestPreset::OutputOptions::Debug,
+            PresetOptionalBoolHelper, false)
+      .Bind("outputOnFailure"_s, &TestPreset::OutputOptions::OutputOnFailure,
+            PresetOptionalBoolHelper, false)
+      .Bind("quiet"_s, &TestPreset::OutputOptions::Quiet,
+            PresetOptionalBoolHelper, false)
+      .Bind("outputLogFile"_s, &TestPreset::OutputOptions::OutputLogFile,
+            PresetStringHelper, false)
+      .Bind("labelSummary"_s, &TestPreset::OutputOptions::LabelSummary,
+            PresetOptionalBoolHelper, false)
+      .Bind("subprojectSummary"_s,
+            &TestPreset::OutputOptions::SubprojectSummary,
+            PresetOptionalBoolHelper, false)
+      .Bind("maxPassedTestOutputSize"_s,
+            &TestPreset::OutputOptions::MaxPassedTestOutputSize,
+            PresetOptionalIntHelper, false)
+      .Bind("maxFailedTestOutputSize"_s,
+            &TestPreset::OutputOptions::MaxFailedTestOutputSize,
+            PresetOptionalIntHelper, false)
+      .Bind("maxTestNameWidth"_s, &TestPreset::OutputOptions::MaxTestNameWidth,
+            PresetOptionalIntHelper, false));
+
+auto const TestPresetOptionalFilterIncludeIndexObjectHelper =
+  cmJSONOptionalHelper<TestPreset::IncludeOptions::IndexOptions,
+                       ReadFileResult>(
+    ReadFileResult::READ_OK,
+    cmJSONObjectHelper<TestPreset::IncludeOptions::IndexOptions,
+                       ReadFileResult>(ReadFileResult::READ_OK,
+                                       ReadFileResult::INVALID_PRESET)
+      .Bind("start"_s, &TestPreset::IncludeOptions::IndexOptions::Start,
+            PresetOptionalIntHelper, false)
+      .Bind("end"_s, &TestPreset::IncludeOptions::IndexOptions::End,
+            PresetOptionalIntHelper, false)
+      .Bind("stride"_s, &TestPreset::IncludeOptions::IndexOptions::Stride,
+            PresetOptionalIntHelper, false)
+      .Bind("specificTests"_s,
+            &TestPreset::IncludeOptions::IndexOptions::SpecificTests,
+            PresetVectorIntHelper, false));
+
+ReadFileResult TestPresetOptionalFilterIncludeIndexHelper(
+  cm::optional<TestPreset::IncludeOptions::IndexOptions>& out,
+  const Json::Value* value)
+{
+  if (!value) {
+    out = cm::nullopt;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->isString()) {
+    out.emplace();
+    out->IndexFile = value->asString();
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->isObject()) {
+    return TestPresetOptionalFilterIncludeIndexObjectHelper(out, value);
+  }
+
+  return ReadFileResult::INVALID_PRESET;
+}
+
+auto const TestPresetOptionalFilterIncludeHelper =
+  cmJSONOptionalHelper<TestPreset::IncludeOptions, ReadFileResult>(
+    ReadFileResult::READ_OK,
+    cmJSONObjectHelper<TestPreset::IncludeOptions, ReadFileResult>(
+      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
+      .Bind("name"_s, &TestPreset::IncludeOptions::Name, PresetStringHelper,
+            false)
+      .Bind("label"_s, &TestPreset::IncludeOptions::Label, PresetStringHelper,
+            false)
+      .Bind("index"_s, &TestPreset::IncludeOptions::Index,
+            TestPresetOptionalFilterIncludeIndexHelper, false)
+      .Bind("useUnion"_s, &TestPreset::IncludeOptions::UseUnion,
+            PresetOptionalBoolHelper, false));
+
+auto const TestPresetOptionalFilterExcludeFixturesHelper =
+  cmJSONOptionalHelper<TestPreset::ExcludeOptions::FixturesOptions,
+                       ReadFileResult>(
+    ReadFileResult::READ_OK,
+    cmJSONObjectHelper<TestPreset::ExcludeOptions::FixturesOptions,
+                       ReadFileResult>(ReadFileResult::READ_OK,
+                                       ReadFileResult::INVALID_PRESET)
+      .Bind("any"_s, &TestPreset::ExcludeOptions::FixturesOptions::Any,
+            PresetStringHelper, false)
+      .Bind("setup"_s, &TestPreset::ExcludeOptions::FixturesOptions::Setup,
+            PresetStringHelper, false)
+      .Bind("cleanup"_s, &TestPreset::ExcludeOptions::FixturesOptions::Cleanup,
+            PresetStringHelper, false));
+
+auto const TestPresetOptionalFilterExcludeHelper =
+  cmJSONOptionalHelper<TestPreset::ExcludeOptions, ReadFileResult>(
+    ReadFileResult::READ_OK,
+    cmJSONObjectHelper<TestPreset::ExcludeOptions, ReadFileResult>(
+      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
+      .Bind("name"_s, &TestPreset::ExcludeOptions::Name, PresetStringHelper,
+            false)
+      .Bind("label"_s, &TestPreset::ExcludeOptions::Label, PresetStringHelper,
+            false)
+      .Bind("fixtures"_s, &TestPreset::ExcludeOptions::Fixtures,
+            TestPresetOptionalFilterExcludeFixturesHelper, false));
+
+ReadFileResult TestPresetExecutionShowOnlyHelper(
+  TestPreset::ExecutionOptions::ShowOnlyEnum& out, const Json::Value* value)
+{
+  if (!value || !value->isString()) {
+    return ReadFileResult::INVALID_PRESET;
+  }
+
+  if (value->asString() == "human") {
+    out = TestPreset::ExecutionOptions::ShowOnlyEnum::Human;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->asString() == "json-v1") {
+    out = TestPreset::ExecutionOptions::ShowOnlyEnum::JsonV1;
+    return ReadFileResult::READ_OK;
+  }
+
+  return ReadFileResult::INVALID_PRESET;
+}
+
+auto const TestPresetOptionalExecutionShowOnlyHelper =
+  cmJSONOptionalHelper<TestPreset::ExecutionOptions::ShowOnlyEnum,
+                       ReadFileResult>(ReadFileResult::READ_OK,
+                                       TestPresetExecutionShowOnlyHelper);
+
+ReadFileResult TestPresetExecutionModeHelper(
+  TestPreset::ExecutionOptions::RepeatOptions::ModeEnum& out,
+  const Json::Value* value)
+{
+  if (!value) {
+    return ReadFileResult::READ_OK;
+  }
+
+  if (!value->isString()) {
+    return ReadFileResult::INVALID_PRESET;
+  }
+
+  if (value->asString() == "until-fail") {
+    out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::UntilFail;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->asString() == "until-pass") {
+    out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::UntilPass;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->asString() == "after-timeout") {
+    out = TestPreset::ExecutionOptions::RepeatOptions::ModeEnum::AfterTimeout;
+    return ReadFileResult::READ_OK;
+  }
+
+  return ReadFileResult::INVALID_PRESET;
+}
+
+auto const TestPresetOptionalExecutionRepeatHelper =
+  cmJSONOptionalHelper<TestPreset::ExecutionOptions::RepeatOptions,
+                       ReadFileResult>(
+    ReadFileResult::READ_OK,
+    cmJSONObjectHelper<TestPreset::ExecutionOptions::RepeatOptions,
+                       ReadFileResult>(ReadFileResult::READ_OK,
+                                       ReadFileResult::INVALID_PRESET)
+      .Bind("mode"_s, &TestPreset::ExecutionOptions::RepeatOptions::Mode,
+            TestPresetExecutionModeHelper, true)
+      .Bind("count"_s, &TestPreset::ExecutionOptions::RepeatOptions::Count,
+            PresetIntHelper, true));
+
+ReadFileResult TestPresetExecutionNoTestsActionHelper(
+  TestPreset::ExecutionOptions::NoTestsActionEnum& out,
+  const Json::Value* value)
+{
+  if (!value) {
+    out = TestPreset::ExecutionOptions::NoTestsActionEnum::Default;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (!value->isString()) {
+    return ReadFileResult::INVALID_PRESET;
+  }
+
+  if (value->asString() == "default") {
+    out = TestPreset::ExecutionOptions::NoTestsActionEnum::Default;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->asString() == "error") {
+    out = TestPreset::ExecutionOptions::NoTestsActionEnum::Error;
+    return ReadFileResult::READ_OK;
+  }
+
+  if (value->asString() == "ignore") {
+    out = TestPreset::ExecutionOptions::NoTestsActionEnum::Ignore;
+    return ReadFileResult::READ_OK;
+  }
+
+  return ReadFileResult::INVALID_PRESET;
+}
+
+auto const TestPresetOptionalExecutionNoTestsActionHelper =
+  cmJSONOptionalHelper<TestPreset::ExecutionOptions::NoTestsActionEnum,
+                       ReadFileResult>(ReadFileResult::READ_OK,
+                                       TestPresetExecutionNoTestsActionHelper);
+
+auto const TestPresetExecutionHelper =
+  cmJSONOptionalHelper<TestPreset::ExecutionOptions, ReadFileResult>(
+    ReadFileResult::READ_OK,
+    cmJSONObjectHelper<TestPreset::ExecutionOptions, ReadFileResult>(
+      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
+      .Bind("stopOnFailure"_s, &TestPreset::ExecutionOptions::StopOnFailure,
+            PresetOptionalBoolHelper, false)
+      .Bind("enableFailover"_s, &TestPreset::ExecutionOptions::EnableFailover,
+            PresetOptionalBoolHelper, false)
+      .Bind("jobs"_s, &TestPreset::ExecutionOptions::Jobs,
+            PresetOptionalIntHelper, false)
+      .Bind("resourceSpecFile"_s,
+            &TestPreset::ExecutionOptions::ResourceSpecFile,
+            PresetStringHelper, false)
+      .Bind("testLoad"_s, &TestPreset::ExecutionOptions::TestLoad,
+            PresetOptionalIntHelper, false)
+      .Bind("showOnly"_s, &TestPreset::ExecutionOptions::ShowOnly,
+            TestPresetOptionalExecutionShowOnlyHelper, false)
+      .Bind("repeat"_s, &TestPreset::ExecutionOptions::Repeat,
+            TestPresetOptionalExecutionRepeatHelper, false)
+      .Bind("interactiveDebugging"_s,
+            &TestPreset::ExecutionOptions::InteractiveDebugging,
+            PresetOptionalBoolHelper, false)
+      .Bind("scheduleRandom"_s, &TestPreset::ExecutionOptions::ScheduleRandom,
+            PresetOptionalBoolHelper, false)
+      .Bind("timeout"_s, &TestPreset::ExecutionOptions::Timeout,
+            PresetOptionalIntHelper, false)
+      .Bind("noTestsAction"_s, &TestPreset::ExecutionOptions::NoTestsAction,
+            TestPresetOptionalExecutionNoTestsActionHelper, false));
+
+auto const TestPresetFilterHelper =
+  cmJSONOptionalHelper<TestPreset::FilterOptions, ReadFileResult>(
+    ReadFileResult::READ_OK,
+    cmJSONObjectHelper<TestPreset::FilterOptions, ReadFileResult>(
+      ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET)
+      .Bind("include"_s, &TestPreset::FilterOptions::Include,
+            TestPresetOptionalFilterIncludeHelper, false)
+      .Bind("exclude"_s, &TestPreset::FilterOptions::Exclude,
+            TestPresetOptionalFilterExcludeHelper, false));
+
+auto const TestPresetHelper =
+  cmJSONObjectHelper<TestPreset, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESET, false)
+    .Bind("name"_s, &TestPreset::Name, PresetStringHelper)
+    .Bind("inherits"_s, &TestPreset::Inherits, PresetInheritsHelper, false)
+    .Bind("hidden"_s, &TestPreset::Hidden, PresetBoolHelper, false)
+    .Bind<std::nullptr_t>("vendor"_s, nullptr,
+                          VendorHelper(ReadFileResult::INVALID_PRESET), false)
+    .Bind("displayName"_s, &TestPreset::DisplayName, PresetStringHelper, false)
+    .Bind("description"_s, &TestPreset::Description, PresetStringHelper, false)
+    .Bind("environment"_s, &TestPreset::Environment, EnvironmentMapHelper,
+          false)
+    .Bind("configurePreset"_s, &TestPreset::ConfigurePreset,
+          PresetStringHelper, false)
+    .Bind("inheritConfigureEnvironment"_s,
+          &TestPreset::InheritConfigureEnvironment, PresetOptionalBoolHelper,
+          false)
+    .Bind("configuration"_s, &TestPreset::Configuration, PresetStringHelper,
+          false)
+    .Bind("overwriteConfigurationFile"_s,
+          &TestPreset::OverwriteConfigurationFile, PresetVectorStringHelper,
+          false)
+    .Bind("output"_s, &TestPreset::Output, TestPresetOptionalOutputHelper,
+          false)
+    .Bind("filter"_s, &TestPreset::Filter, TestPresetFilterHelper, false)
+    .Bind("execution"_s, &TestPreset::Execution, TestPresetExecutionHelper,
+          false)
+    .Bind("condition"_s, &TestPreset::ConditionEvaluator,
+          PresetConditionHelper, false);
+
+auto const ConfigurePresetsHelper =
+  cmJSONVectorHelper<ConfigurePreset, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
+    ConfigurePresetHelper);
+
+auto const BuildPresetsHelper =
+  cmJSONVectorHelper<BuildPreset, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS,
+    BuildPresetHelper);
+
+auto const TestPresetsHelper = cmJSONVectorHelper<TestPreset, ReadFileResult>(
+  ReadFileResult::READ_OK, ReadFileResult::INVALID_PRESETS, TestPresetHelper);
+
+auto const CMakeVersionUIntHelper = cmJSONUIntHelper<ReadFileResult>(
+  ReadFileResult::READ_OK, ReadFileResult::INVALID_VERSION);
+
+auto const CMakeVersionHelper =
+  cmJSONObjectHelper<CMakeVersion, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_CMAKE_VERSION, false)
+    .Bind("major"_s, &CMakeVersion::Major, CMakeVersionUIntHelper, false)
+    .Bind("minor"_s, &CMakeVersion::Minor, CMakeVersionUIntHelper, false)
+    .Bind("patch"_s, &CMakeVersion::Patch, CMakeVersionUIntHelper, false);
+
+auto const RootPresetsHelper =
+  cmJSONObjectHelper<RootPresets, ReadFileResult>(
+    ReadFileResult::READ_OK, ReadFileResult::INVALID_ROOT, false)
+    .Bind<int>("version"_s, nullptr, VersionHelper)
+    .Bind("configurePresets"_s, &RootPresets::ConfigurePresets,
+          ConfigurePresetsHelper, false)
+    .Bind("buildPresets"_s, &RootPresets::BuildPresets, BuildPresetsHelper,
+          false)
+    .Bind("testPresets"_s, &RootPresets::TestPresets, TestPresetsHelper, false)
+    .Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired,
+          CMakeVersionHelper, false)
+    .Bind<std::nullptr_t>("vendor"_s, nullptr,
+                          VendorHelper(ReadFileResult::INVALID_ROOT), false);
+}
+
+cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile(
+  const std::string& filename, bool user)
+{
+  cmsys::ifstream fin(filename.c_str());
+  if (!fin) {
+    return ReadFileResult::FILE_NOT_FOUND;
+  }
+  // If there's a BOM, toss it.
+  cmsys::FStream::ReadBOM(fin);
+
+  Json::Value root;
+  Json::CharReaderBuilder builder;
+  Json::CharReaderBuilder::strictMode(&builder.settings_);
+  if (!Json::parseFromStream(builder, fin, &root, nullptr)) {
+    return ReadFileResult::JSON_PARSE_ERROR;
+  }
+
+  int v = 0;
+  auto result = RootVersionHelper(v, &root);
+  if (result != ReadFileResult::READ_OK) {
+    return result;
+  }
+  if (v < MIN_VERSION || v > MAX_VERSION) {
+    return ReadFileResult::UNRECOGNIZED_VERSION;
+  }
+  if (user) {
+    this->UserVersion = v;
+  } else {
+    this->Version = v;
+  }
+
+  // Support for build and test presets added in version 2.
+  if (v < 2 &&
+      (root.isMember("buildPresets") || root.isMember("testPresets"))) {
+    return ReadFileResult::BUILD_TEST_PRESETS_UNSUPPORTED;
+  }
+
+  RootPresets presets;
+  if ((result = RootPresetsHelper(presets, &root)) !=
+      ReadFileResult::READ_OK) {
+    return result;
+  }
+
+  unsigned int currentMajor = cmVersion::GetMajorVersion();
+  unsigned int currentMinor = cmVersion::GetMinorVersion();
+  unsigned int currentPatch = cmVersion::GetPatchVersion();
+  auto const& required = presets.CMakeMinimumRequired;
+  if (required.Major > currentMajor ||
+      (required.Major == currentMajor &&
+       (required.Minor > currentMinor ||
+        (required.Minor == currentMinor &&
+         (required.Patch > currentPatch))))) {
+    return ReadFileResult::UNRECOGNIZED_CMAKE_VERSION;
+  }
+
+  for (auto& preset : presets.ConfigurePresets) {
+    preset.User = user;
+    if (preset.Name.empty()) {
+      return ReadFileResult::INVALID_PRESET;
+    }
+
+    PresetPair<ConfigurePreset> presetPair;
+    presetPair.Unexpanded = preset;
+    presetPair.Expanded = cm::nullopt;
+    if (!this->ConfigurePresets
+           .emplace(std::make_pair(preset.Name, presetPair))
+           .second) {
+      return ReadFileResult::DUPLICATE_PRESETS;
+    }
+
+    // Support for installDir presets added in version 3.
+    if (v < 3 && !preset.InstallDir.empty()) {
+      return ReadFileResult::INSTALL_PREFIX_UNSUPPORTED;
+    }
+
+    // Support for conditions added in version 3.
+    if (v < 3 && preset.ConditionEvaluator) {
+      return ReadFileResult::CONDITION_UNSUPPORTED;
+    }
+
+    this->ConfigurePresetOrder.push_back(preset.Name);
+  }
+
+  for (auto& preset : presets.BuildPresets) {
+    preset.User = user;
+    if (preset.Name.empty()) {
+      return ReadFileResult::INVALID_PRESET;
+    }
+
+    PresetPair<BuildPreset> presetPair;
+    presetPair.Unexpanded = preset;
+    presetPair.Expanded = cm::nullopt;
+    if (!this->BuildPresets.emplace(preset.Name, presetPair).second) {
+      return ReadFileResult::DUPLICATE_PRESETS;
+    }
+
+    // Support for conditions added in version 3.
+    if (v < 3 && preset.ConditionEvaluator) {
+      return ReadFileResult::CONDITION_UNSUPPORTED;
+    }
+
+    this->BuildPresetOrder.push_back(preset.Name);
+  }
+
+  for (auto& preset : presets.TestPresets) {
+    preset.User = user;
+    if (preset.Name.empty()) {
+      return ReadFileResult::INVALID_PRESET;
+    }
+
+    PresetPair<TestPreset> presetPair;
+    presetPair.Unexpanded = preset;
+    presetPair.Expanded = cm::nullopt;
+    if (!this->TestPresets.emplace(preset.Name, presetPair).second) {
+      return ReadFileResult::DUPLICATE_PRESETS;
+    }
+
+    // Support for conditions added in version 3.
+    if (v < 3 && preset.ConditionEvaluator) {
+      return ReadFileResult::CONDITION_UNSUPPORTED;
+    }
+
+    this->TestPresetOrder.push_back(preset.Name);
+  }
+
+  return ReadFileResult::READ_OK;
+}

+ 7 - 0
Source/cmCTest.cxx

@@ -2310,6 +2310,13 @@ bool cmCTest::SetArgsFromPreset(const std::string& presetName,
     return false;
     return false;
   }
   }
 
 
+  if (!expandedPreset->ConditionResult) {
+    cmSystemTools::Error(cmStrCat("Cannot use disabled test preset in ",
+                                  workingDirectory, ": \"", presetName, '"'));
+    settingsFile.PrintTestPresetList();
+    return false;
+  }
+
   auto configurePresetPair =
   auto configurePresetPair =
     settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset);
     settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset);
   if (configurePresetPair == settingsFile.ConfigurePresets.end()) {
   if (configurePresetPair == settingsFile.ConfigurePresets.end()) {

+ 1 - 1
Source/cmJSONHelpers.h

@@ -239,7 +239,7 @@ cmJSONHelper<std::vector<T>, E> cmJSONVectorFilterHelper(E success, E fail,
       if (!filter(t)) {
       if (!filter(t)) {
         continue;
         continue;
       }
       }
-      out.push_back(t);
+      out.push_back(std::move(t));
     }
     }
     return success;
     return success;
   };
   };

+ 13 - 0
Source/cmake.cxx

@@ -1217,6 +1217,11 @@ void cmake::SetArgs(const std::vector<std::string>& args)
                                     "\": Invalid macro expansion"));
                                     "\": Invalid macro expansion"));
       return;
       return;
     }
     }
+    if (!expandedPreset->ConditionResult) {
+      cmSystemTools::Error(cmStrCat("Could not use disabled preset \"",
+                                    preset->second.Unexpanded.Name, "\""));
+      return;
+    }
 
 
     if (!this->State->IsCacheLoaded() && !haveBArg) {
     if (!this->State->IsCacheLoaded() && !haveBArg) {
       this->SetHomeOutputDirectory(expandedPreset->BinaryDir);
       this->SetHomeOutputDirectory(expandedPreset->BinaryDir);
@@ -3164,6 +3169,14 @@ int cmake::Build(int jobs, std::string dir, std::vector<std::string> targets,
       return 1;
       return 1;
     }
     }
 
 
+    if (!expandedPreset->ConditionResult) {
+      cmSystemTools::Error(cmStrCat("Cannot use disabled build preset in ",
+                                    this->GetHomeDirectory(), ": \"",
+                                    presetName, '"'));
+      settingsFile.PrintBuildPresetList();
+      return 1;
+    }
+
     auto configurePresetPair =
     auto configurePresetPair =
       settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset);
       settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset);
     if (configurePresetPair == settingsFile.ConfigurePresets.end()) {
     if (configurePresetPair == settingsFile.ConfigurePresets.end()) {

+ 1 - 0
Tests/RunCMake/CMakePresets/ConditionFuture-result.txt

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

+ 2 - 0
Tests/RunCMake/CMakePresets/ConditionFuture-stderr.txt

@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/ConditionFuture: File version must be 3 or higher for condition support$

+ 11 - 0
Tests/RunCMake/CMakePresets/ConditionFuture.json.in

@@ -0,0 +1,11 @@
+{
+  "version": 2,
+  "configurePresets": [
+    {
+      "name": "ConditionFuture",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build",
+      "condition": true
+    }
+  ]
+}

+ 349 - 0
Tests/RunCMake/CMakePresets/Conditions.json.in

@@ -0,0 +1,349 @@
+{
+  "version": 3,
+  "configurePresets": [
+    {
+      "name": "Base",
+      "hidden": true,
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    },
+    {
+      "name": "SimpleTrue",
+      "inherits": "Base",
+      "condition": true
+    },
+    {
+      "name": "SimpleFalse",
+      "inherits": "Base",
+      "condition": false
+    },
+    {
+      "name": "Null",
+      "inherits": "Base",
+      "condition": null
+    },
+    {
+      "name": "ConstTrue",
+      "inherits": "Base",
+      "condition": {
+        "type": "const",
+        "value": true
+      }
+    },
+    {
+      "name": "ConstFalse",
+      "inherits": "Base",
+      "condition": {
+        "type": "const",
+        "value": false
+      }
+    },
+    {
+      "name": "EqualsTrue",
+      "inherits": "Base",
+      "condition": {
+        "type": "equals",
+        "lhs": "abc",
+        "rhs": "abc"
+      }
+    },
+    {
+      "name": "EqualsFalse",
+      "inherits": "Base",
+      "condition": {
+        "type": "equals",
+        "lhs": "abc",
+        "rhs": "abcd"
+      }
+    },
+    {
+      "name": "EqualsMacroLeft",
+      "inherits": "Base",
+      "condition": {
+        "type": "equals",
+        "lhs": "${presetName}",
+        "rhs": "EqualsMacroLeft"
+      }
+    },
+    {
+      "name": "EqualsMacroRight",
+      "inherits": "Base",
+      "condition": {
+        "type": "equals",
+        "lhs": "EqualsMacroRight",
+        "rhs": "${presetName}"
+      }
+    },
+    {
+      "name": "NotEqualsTrue",
+      "inherits": "Base",
+      "condition": {
+        "type": "notEquals",
+        "lhs": "abc",
+        "rhs": "abcd"
+      }
+    },
+    {
+      "name": "NotEqualsFalse",
+      "inherits": "Base",
+      "condition": {
+        "type": "notEquals",
+        "lhs": "abc",
+        "rhs": "abc"
+      }
+    },
+    {
+      "name": "InListTrue",
+      "inherits": "Base",
+      "condition": {
+        "type": "inList",
+        "string": "b",
+        "list": [
+          "a",
+          "b",
+          "c"
+        ]
+      }
+    },
+    {
+      "name": "InListFalse",
+      "inherits": "Base",
+      "condition": {
+        "type": "inList",
+        "string": "d",
+        "list": [
+          "a",
+          "b",
+          "c"
+        ]
+      }
+    },
+    {
+      "name": "InListMacroString",
+      "inherits": "Base",
+      "condition": {
+        "type": "inList",
+        "string": "${presetName}",
+        "list": [
+          "InListMacroString",
+          "AnotherString"
+        ]
+      }
+    },
+    {
+      "name": "InListMacroList",
+      "inherits": "Base",
+      "condition": {
+        "type": "inList",
+        "string": "InListMacroList",
+        "list": [
+          "${presetName}",
+          "AnotherString"
+        ]
+      }
+    },
+    {
+      "name": "InListShortCircuit",
+      "inherits": "Base",
+      "condition": {
+        "type": "inList",
+        "string": "a",
+        "list": [
+          "a",
+          "${invalidMacro}"
+        ]
+      }
+    },
+    {
+      "name": "NotInListTrue",
+      "inherits": "Base",
+      "condition": {
+        "type": "notInList",
+        "string": "d",
+        "list": [
+          "a",
+          "b",
+          "c"
+        ]
+      }
+    },
+    {
+      "name": "NotInListFalse",
+      "inherits": "Base",
+      "condition": {
+        "type": "notInList",
+        "string": "a",
+        "list": [
+          "a",
+          "b",
+          "c"
+        ]
+      }
+    },
+    {
+      "name": "AnyOfTrue1",
+      "inherits": "Base",
+      "condition": {
+        "type": "anyOf",
+        "conditions": [
+          true,
+          false
+        ]
+      }
+    },
+    {
+      "name": "AnyOfTrue2",
+      "inherits": "Base",
+      "condition": {
+        "type": "anyOf",
+        "conditions": [
+          false,
+          true
+        ]
+      }
+    },
+    {
+      "name": "AnyOfFalse",
+      "inherits": "Base",
+      "condition": {
+        "type": "anyOf",
+        "conditions": [
+          false,
+          {
+            "type": "equals",
+            "lhs": "abc",
+            "rhs": "abcd"
+          }
+        ]
+      }
+    },
+    {
+      "name": "AnyOfShortCircuit",
+      "inherits": "Base",
+      "condition": {
+        "type": "anyOf",
+        "conditions": [
+          true,
+          {
+            "type": "equals",
+            "lhs": "${invalidMacro}",
+            "rhs": ""
+          }
+        ]
+      }
+    },
+    {
+      "name": "AnyOfEmpty",
+      "inherits": "Base",
+      "condition": {
+        "type": "anyOf",
+        "conditions": []
+      }
+    },
+    {
+      "name": "AllOfTrue",
+      "inherits": "Base",
+      "condition": {
+        "type": "allOf",
+        "conditions": [
+          true,
+          {
+            "type": "equals",
+            "lhs": "abc",
+            "rhs": "abc"
+          }
+        ]
+      }
+    },
+    {
+      "name": "AllOfFalse1",
+      "inherits": "Base",
+      "condition": {
+        "type": "allOf",
+        "conditions": [
+          false,
+          true
+        ]
+      }
+    },
+    {
+      "name": "AllOfFalse2",
+      "inherits": "Base",
+      "condition": {
+        "type": "allOf",
+        "conditions": [
+          true,
+          false
+        ]
+      }
+    },
+    {
+      "name": "AllOfShortCircuit",
+      "inherits": "Base",
+      "condition": {
+        "type": "allOf",
+        "conditions": [
+          false,
+          {
+            "type": "equals",
+            "lhs": "${invalidMacro}",
+            "rhs": ""
+          }
+        ]
+      }
+    },
+    {
+      "name": "AllOfEmpty",
+      "inherits": "Base",
+      "condition": {
+        "type": "allOf",
+        "conditions": []
+      }
+    },
+    {
+      "name": "NotTrue",
+      "inherits": "Base",
+      "condition": {
+        "type": "not",
+        "condition": true
+      }
+    },
+    {
+      "name": "NotFalse",
+      "inherits": "Base",
+      "condition": {
+        "type": "not",
+        "condition": false
+      }
+    },
+    {
+      "name": "InheritanceBase",
+      "inherits": "Base",
+      "hidden": true,
+      "condition": {
+        "type": "equals",
+        "lhs": "${presetName}",
+        "rhs": "InheritanceChildTrue"
+      }
+    },
+    {
+      "name": "InheritanceChildTrue",
+      "inherits": "InheritanceBase"
+    },
+    {
+      "name": "InheritanceChildFalse",
+      "inherits": "InheritanceBase"
+    },
+    {
+      "name": "InheritanceNull",
+      "inherits": "Null"
+    },
+    {
+      "name": "InheritanceNullFalse",
+      "inherits": [
+        "Null",
+        "SimpleFalse"
+      ]
+    }
+  ]
+}

+ 22 - 0
Tests/RunCMake/CMakePresets/ListConditions-stdout.txt

@@ -0,0 +1,22 @@
+Available configure presets:
+
+  "SimpleTrue"
+  "Null"
+  "ConstTrue"
+  "EqualsTrue"
+  "EqualsMacroLeft"
+  "EqualsMacroRight"
+  "NotEqualsTrue"
+  "InListTrue"
+  "InListMacroString"
+  "InListMacroList"
+  "InListShortCircuit"
+  "NotInListTrue"
+  "AnyOfTrue1"
+  "AnyOfTrue2"
+  "AnyOfShortCircuit"
+  "AllOfTrue"
+  "AllOfEmpty"
+  "NotFalse"
+  "InheritanceChildTrue"
+  "InheritanceNull"$

+ 8 - 0
Tests/RunCMake/CMakePresets/RunCMakeTest.cmake

@@ -118,6 +118,8 @@ run_cmake_presets(EnvCycle)
 run_cmake_presets(EmptyEnv)
 run_cmake_presets(EmptyEnv)
 run_cmake_presets(EmptyPenv)
 run_cmake_presets(EmptyPenv)
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
+run_cmake_presets(ConditionFuture)
+run_cmake_presets(SubConditionNull)
 
 
 # Test cmakeMinimumRequired field
 # Test cmakeMinimumRequired field
 run_cmake_presets(MinimumRequiredInvalid)
 run_cmake_presets(MinimumRequiredInvalid)
@@ -267,6 +269,12 @@ run_cmake_presets(HostSystemName)
 set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/HostSystemNameFuture.json.in")
 set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/HostSystemNameFuture.json.in")
 run_cmake_presets(HostSystemNameFuture)
 run_cmake_presets(HostSystemNameFuture)
 
 
+# Test conditions
+set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/Conditions.json.in")
+run_cmake_presets(ListConditions --list-presets)
+run_cmake_presets(SimpleTrue)
+run_cmake_presets(SimpleFalse)
+
 # Test the example from the documentation
 # Test the example from the documentation
 file(READ "${RunCMake_SOURCE_DIR}/../../../Help/manual/presets/example.json" _example)
 file(READ "${RunCMake_SOURCE_DIR}/../../../Help/manual/presets/example.json" _example)
 string(REPLACE "\"generator\": \"Ninja\"" "\"generator\": \"@RunCMake_GENERATOR@\"" _example "${_example}")
 string(REPLACE "\"generator\": \"Ninja\"" "\"generator\": \"@RunCMake_GENERATOR@\"" _example "${_example}")

+ 1 - 0
Tests/RunCMake/CMakePresets/SimpleFalse-result.txt

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

+ 1 - 0
Tests/RunCMake/CMakePresets/SimpleFalse-stderr.txt

@@ -0,0 +1 @@
+^CMake Error: Could not use disabled preset "SimpleFalse"$

+ 0 - 0
Tests/RunCMake/CMakePresets/SimpleTrue.cmake


+ 1 - 0
Tests/RunCMake/CMakePresets/SubConditionNull-result.txt

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

+ 2 - 0
Tests/RunCMake/CMakePresets/SubConditionNull-stderr.txt

@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/SubConditionNull: Invalid preset condition$

+ 14 - 0
Tests/RunCMake/CMakePresets/SubConditionNull.json.in

@@ -0,0 +1,14 @@
+{
+  "version": 3,
+  "configurePresets": [
+    {
+      "name": "SubConditionNull",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build",
+      "condition": {
+        "type": "not",
+        "condition": null
+      }
+    }
+  ]
+}

+ 1 - 0
Tests/RunCMake/CMakePresetsBuild/Condition-build-disabled-result.txt

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

+ 2 - 0
Tests/RunCMake/CMakePresetsBuild/Condition-build-disabled-stderr.txt

@@ -0,0 +1,2 @@
+^CMake Error: Cannot use disabled build preset in [^
+]*/Tests/RunCMake/CMakePresetsBuild/Condition: "disabled"$

+ 0 - 0
Tests/RunCMake/CMakePresetsBuild/Condition.cmake


+ 22 - 0
Tests/RunCMake/CMakePresetsBuild/Condition.json.in

@@ -0,0 +1,22 @@
+{
+  "version": 3,
+  "configurePresets": [
+    {
+      "name": "default",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ],
+  "buildPresets": [
+    {
+      "name": "enabled",
+      "configurePreset": "default",
+      "condition": true
+    },
+    {
+      "name": "disabled",
+      "configurePreset": "default",
+      "condition": false
+    }
+  ]
+}

+ 1 - 0
Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-result.txt

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

+ 2 - 0
Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-stderr.txt

@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresetsBuild/ConditionFuture: File version must be 3 or higher for condition support$

+ 17 - 0
Tests/RunCMake/CMakePresetsBuild/ConditionFuture.json.in

@@ -0,0 +1,17 @@
+{
+  "version": 2,
+  "configurePresets": [
+    {
+      "name": "default",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ],
+  "buildPresets": [
+    {
+      "name": "conditionFuture",
+      "configurePreset": "default",
+      "condition": true
+    }
+  ]
+}

+ 2 - 1
Tests/RunCMake/CMakePresetsBuild/ListPresets-build-x-stdout.txt

@@ -1,5 +1,6 @@
-Available build presets:
+^Available build presets:
 
 
   "build-default" - build-default displayName
   "build-default" - build-default displayName
   "empty"
   "empty"
   "display"       - display displayName
   "display"       - display displayName
+  "true"$

+ 11 - 1
Tests/RunCMake/CMakePresetsBuild/ListPresets.json.in

@@ -1,5 +1,5 @@
 {
 {
-    "version": 2,
+    "version": 3,
     "configurePresets": [
     "configurePresets": [
         {
         {
             "name": "default",
             "name": "default",
@@ -26,6 +26,16 @@
         {
         {
             "name": "hidden",
             "name": "hidden",
             "hidden": true
             "hidden": true
+        },
+        {
+            "name": "true",
+            "inherits": "build-default",
+            "condition": true
+        },
+        {
+            "name": "false",
+            "inherits": "build-default",
+            "condition": false
         }
         }
     ]
     ]
 }
 }

+ 2 - 0
Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake

@@ -64,6 +64,7 @@ set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
 
 
 run_cmake_build_presets(Good "default;other" "build-other;withEnvironment;noEnvironment;macros;vendorObject")
 run_cmake_build_presets(Good "default;other" "build-other;withEnvironment;noEnvironment;macros;vendorObject")
 run_cmake_build_presets(InvalidConfigurePreset "default" "badConfigurePreset")
 run_cmake_build_presets(InvalidConfigurePreset "default" "badConfigurePreset")
+run_cmake_build_presets(Condition "default" "enabled;disabled")
 
 
 set(CMakePresetsBuild_BUILD_ONLY 1)
 set(CMakePresetsBuild_BUILD_ONLY 1)
 run_cmake_build_presets(ListPresets "x" "x" "--list-presets")
 run_cmake_build_presets(ListPresets "x" "x" "--list-presets")
@@ -72,5 +73,6 @@ run_cmake_build_presets(Invalid "x" "hidden;vendorMacro")
 
 
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
 run_cmake_build_presets(PresetsUnsupported "x" "x")
 run_cmake_build_presets(PresetsUnsupported "x" "x")
+run_cmake_build_presets(ConditionFuture "x" "conditionFuture")
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
 set(CMakePresetsBuild_BUILD_ONLY 0)
 set(CMakePresetsBuild_BUILD_ONLY 0)

+ 22 - 0
Tests/RunCMake/CMakePresetsTest/Condition.json.in

@@ -0,0 +1,22 @@
+{
+  "version": 3,
+  "configurePresets": [
+    {
+      "name": "default",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ],
+  "testPresets": [
+    {
+      "name": "enabled",
+      "configurePreset": "default",
+      "condition": true
+    },
+    {
+      "name": "disabled",
+      "configurePreset": "default",
+      "condition": false
+    }
+  ]
+}

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

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

+ 2 - 0
Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-stderr.txt

@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresetsTest/ConditionFuture: File version must be 3 or higher for condition support$

+ 17 - 0
Tests/RunCMake/CMakePresetsTest/ConditionFuture.json.in

@@ -0,0 +1,17 @@
+{
+  "version": 2,
+  "configurePresets": [
+    {
+      "name": "default",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build"
+    }
+  ],
+  "testPresets": [
+    {
+      "name": "conditionFuture",
+      "configurePreset": "default",
+      "condition": true
+    }
+  ]
+}

+ 3 - 0
Tests/RunCMake/CMakePresetsTest/ConditionListPresets-test-x-stdout.txt

@@ -0,0 +1,3 @@
+^Available test presets:
+
+  "enabled"$

+ 1 - 0
Tests/RunCMake/CMakePresetsTest/ConditionRunTests-test-disabled-result.txt

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

+ 2 - 0
Tests/RunCMake/CMakePresetsTest/ConditionRunTests-test-disabled-stderr.txt

@@ -0,0 +1,2 @@
+^CMake Error: Cannot use disabled test preset in [^
+]*/Tests/RunCMake/CMakePresetsTest/ConditionRunTests: "disabled"$

+ 2 - 0
Tests/RunCMake/CMakePresetsTest/ConditionRunTests.cmake

@@ -0,0 +1,2 @@
+enable_testing()
+add_test(true ${CMAKE_COMMAND} -E true)

+ 7 - 0
Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake

@@ -90,6 +90,12 @@ run_cmake_test_presets(InvalidConfigurePreset "default" "" "badConfigurePreset")
 set(CMakePresetsTest_NO_CONFIGURE 1)
 set(CMakePresetsTest_NO_CONFIGURE 1)
 set(CMakePresetsTest_FILE "${RunCMake_SOURCE_DIR}/Good.json.in")
 set(CMakePresetsTest_FILE "${RunCMake_SOURCE_DIR}/Good.json.in")
 run_cmake_test_presets(ListPresets "" "" "x" "--list-presets")
 run_cmake_test_presets(ListPresets "" "" "x" "--list-presets")
+
+set(CMakePresetsTest_FILE "${RunCMake_SOURCE_DIR}/Condition.json.in")
+run_cmake_test_presets(ConditionListPresets "" "" "x" "--list-presets")
+unset(CMakePresetsTest_NO_CONFIGURE)
+run_cmake_test_presets(ConditionRunTests "default" "" "enabled;disabled")
+set(CMakePresetsTest_NO_CONFIGURE 1)
 unset(CMakePresetsTest_FILE)
 unset(CMakePresetsTest_FILE)
 
 
 run_cmake_test_presets(NoConfigurePreset "" "" "noConfigurePreset")
 run_cmake_test_presets(NoConfigurePreset "" "" "noConfigurePreset")
@@ -98,6 +104,7 @@ run_cmake_test_presets(Invalid "" "" "hidden;vendorMacro")
 
 
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
 run_cmake_test_presets(PresetsUnsupported "" "" "x")
 run_cmake_test_presets(PresetsUnsupported "" "" "x")
+run_cmake_test_presets(ConditionFuture "" "" "x")
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
 set(CMakePresetsTest_NO_CONFIGURE 0)
 set(CMakePresetsTest_NO_CONFIGURE 0)
 
 

+ 1 - 0
Utilities/IWYU/mapping.imp

@@ -96,6 +96,7 @@
   { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<1, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<1, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<60, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<60, 1> > >::type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<1, 1000> > >::type", private, "\"cmConfigure.h\"", public ] },
   { symbol: [ "std::enable_if<true, std::chrono::duration<long, std::ratio<1, 1000> > >::type", private, "\"cmConfigure.h\"", public ] },
+  { symbol: [ "__gnu_cxx::__enable_if<true, bool>::__type", private, "\"cmConfigure.h\"", public ] },
 
 
   # Wrappers for 3rd-party libraries
   # Wrappers for 3rd-party libraries
   { include: [ "@<.*curl/curlver.h>", private, "<cm3p/curl/curl.h>", public ] },
   { include: [ "@<.*curl/curlver.h>", private, "<cm3p/curl/curl.h>", public ] },