From ebbd475e54e0ce66dbb1b07a129a11b05cdf3dbb Mon Sep 17 00:00:00 2001
From: Kyle Edwards <kyle.edwards@kitware.com>
Date: Fri, 19 Mar 2021 10:25:50 -0400
Subject: Refactor: Move cmCMakePresetsFile::ReadJSON into a separate file

Some compilers complain about translation units that are too large.
In order to prepare for upcoming additions to cmCMakePresetsFile,
split ReadJSON into a separate file.
---
 Source/CMakeLists.txt                 |   1 +
 Source/cmCMakePresetsFile.cxx         | 787 ---------------------------------
 Source/cmCMakePresetsFileReadJSON.cxx | 803 ++++++++++++++++++++++++++++++++++
 3 files changed, 804 insertions(+), 787 deletions(-)
 create mode 100644 Source/cmCMakePresetsFileReadJSON.cxx

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index d782890..8b11a53 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -198,6 +198,7 @@ set(SRCS
   cmCMakePath.cxx
   cmCMakePresetsFile.cxx
   cmCMakePresetsFile.h
+  cmCMakePresetsFileReadJSON.cxx
   cmCommandArgumentParserHelper.cxx
   cmCommonTargetGenerator.cxx
   cmCommonTargetGenerator.h
diff --git a/Source/cmCMakePresetsFile.cxx b/Source/cmCMakePresetsFile.cxx
index c73ca9a..1d95953 100644
--- a/Source/cmCMakePresetsFile.cxx
+++ b/Source/cmCMakePresetsFile.cxx
@@ -9,18 +9,8 @@
 #include <iterator>
 #include <utility>
 
-#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 "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmVersion.h"
 
 #define CHECK_OK(expr)                                                        \
   {                                                                           \
@@ -51,675 +41,9 @@ enum class CycleStatus
 };
 
 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;
-};
-
-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);
 
 void InheritString(std::string& child, const std::string& parent)
 {
@@ -1557,117 +881,6 @@ const char* cmCMakePresetsFile::ResultToString(ReadFileResult result)
   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()
 {
   this->ConfigurePresets.clear();
diff --git a/Source/cmCMakePresetsFileReadJSON.cxx b/Source/cmCMakePresetsFileReadJSON.cxx
new file mode 100644
index 0000000..b71b176
--- /dev/null
+++ b/Source/cmCMakePresetsFileReadJSON.cxx
@@ -0,0 +1,803 @@
+/* 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/optional>
+#include <cmext/string_view>
+
+#include <cm3p/json/reader.h>
+#include <cm3p/json/value.h>
+
+#include "cmsys/FStream.hxx"
+
+#include "cmCMakePresetsFile.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;
+};
+
+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);
+}
+
+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;
+}
-- 
cgit v0.12


From ce6ea7c927b9f80f219a7783ce725bea4c24091f Mon Sep 17 00:00:00 2001
From: Kyle Edwards <kyle.edwards@kitware.com>
Date: Fri, 19 Mar 2021 10:40:03 -0400
Subject: Refactor: Move some common code into separate file

This code is going to be needed by both cmCMakePresetsFile and
cmCMakePresetsFileReadJSON when the upcoming condition types are
created. Move it into a header file.
---
 Source/CMakeLists.txt               |  1 +
 Source/cmCMakePresetsFile.cxx       | 20 +++-----------------
 Source/cmCMakePresetsFileInternal.h | 22 ++++++++++++++++++++++
 3 files changed, 26 insertions(+), 17 deletions(-)
 create mode 100644 Source/cmCMakePresetsFileInternal.h

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 8b11a53..938745c 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -198,6 +198,7 @@ set(SRCS
   cmCMakePath.cxx
   cmCMakePresetsFile.cxx
   cmCMakePresetsFile.h
+  cmCMakePresetsFileInternal.h
   cmCMakePresetsFileReadJSON.cxx
   cmCommandArgumentParserHelper.cxx
   cmCommonTargetGenerator.cxx
diff --git a/Source/cmCMakePresetsFile.cxx b/Source/cmCMakePresetsFile.cxx
index 1d95953..c0b0981 100644
--- a/Source/cmCMakePresetsFile.cxx
+++ b/Source/cmCMakePresetsFile.cxx
@@ -9,16 +9,10 @@
 #include <iterator>
 #include <utility>
 
+#include "cmCMakePresetsFileInternal.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 
-#define CHECK_OK(expr)                                                        \
-  {                                                                           \
-    auto _result = expr;                                                      \
-    if (_result != ReadFileResult::READ_OK)                                   \
-      return _result;                                                         \
-  }
-
 #define CHECK_EXPAND(out, field, expanders, version)                          \
   {                                                                           \
     switch (ExpandMacros(field, expanders, version)) {                        \
@@ -44,6 +38,8 @@ using ReadFileResult = cmCMakePresetsFile::ReadFileResult;
 using ConfigurePreset = cmCMakePresetsFile::ConfigurePreset;
 using BuildPreset = cmCMakePresetsFile::BuildPreset;
 using TestPreset = cmCMakePresetsFile::TestPreset;
+using ExpandMacroResult = cmCMakePresetsFileInternal::ExpandMacroResult;
+using MacroExpander = cmCMakePresetsFileInternal::MacroExpander;
 
 void InheritString(std::string& child, const std::string& parent)
 {
@@ -166,16 +162,6 @@ bool IsValidMacroNamespace(const std::string& str)
     [&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,
                            const std::vector<MacroExpander>& macroExpanders,
                            int version);
diff --git a/Source/cmCMakePresetsFileInternal.h b/Source/cmCMakePresetsFileInternal.h
new file mode 100644
index 0000000..f05b8ce
--- /dev/null
+++ b/Source/cmCMakePresetsFileInternal.h
@@ -0,0 +1,22 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#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)>;
+}
-- 
cgit v0.12


From 8bc5c8961e552a15091b7f4d2c205ce90b8f764f Mon Sep 17 00:00:00 2001
From: Kyle Edwards <kyle.edwards@kitware.com>
Date: Wed, 10 Mar 2021 14:59:14 -0500
Subject: CMakePresets.json: Add the ability to conditionally disable presets

---
 Help/manual/cmake-presets.7.rst                    |  96 ++++++
 Help/manual/presets/example.json                   |  13 +-
 Help/manual/presets/schema.json                    | 356 ++++++++++++++++++++-
 Help/release/dev/cmake-presets-condition.rst       |   4 +
 Source/QtDialog/QCMake.cxx                         |   2 +-
 Source/cmCMakePresetsFile.cxx                      | 109 ++++++-
 Source/cmCMakePresetsFile.h                        |  10 +
 Source/cmCMakePresetsFileInternal.h                |  80 +++++
 Source/cmCMakePresetsFileReadJSON.cxx              | 205 +++++++++++-
 Source/cmCTest.cxx                                 |   7 +
 Source/cmJSONHelpers.h                             |   2 +-
 Source/cmake.cxx                                   |  13 +
 .../CMakePresets/ConditionFuture-result.txt        |   1 +
 .../CMakePresets/ConditionFuture-stderr.txt        |   2 +
 .../RunCMake/CMakePresets/ConditionFuture.json.in  |  11 +
 Tests/RunCMake/CMakePresets/Conditions.json.in     | 349 ++++++++++++++++++++
 .../CMakePresets/ListConditions-stdout.txt         |  22 ++
 Tests/RunCMake/CMakePresets/RunCMakeTest.cmake     |   8 +
 Tests/RunCMake/CMakePresets/SimpleFalse-result.txt |   1 +
 Tests/RunCMake/CMakePresets/SimpleFalse-stderr.txt |   1 +
 Tests/RunCMake/CMakePresets/SimpleTrue.cmake       |   0
 .../CMakePresets/SubConditionNull-result.txt       |   1 +
 .../CMakePresets/SubConditionNull-stderr.txt       |   2 +
 .../RunCMake/CMakePresets/SubConditionNull.json.in |  14 +
 .../Condition-build-disabled-result.txt            |   1 +
 .../Condition-build-disabled-stderr.txt            |   2 +
 Tests/RunCMake/CMakePresetsBuild/Condition.cmake   |   0
 Tests/RunCMake/CMakePresetsBuild/Condition.json.in |  22 ++
 ...onditionFuture-build-conditionFuture-result.txt |   1 +
 ...onditionFuture-build-conditionFuture-stderr.txt |   2 +
 .../CMakePresetsBuild/ConditionFuture.json.in      |  17 +
 .../ListPresets-build-x-stdout.txt                 |   3 +-
 .../RunCMake/CMakePresetsBuild/ListPresets.json.in |  12 +-
 .../RunCMake/CMakePresetsBuild/RunCMakeTest.cmake  |   2 +
 Tests/RunCMake/CMakePresetsTest/Condition.json.in  |  22 ++
 .../ConditionFuture-test-x-result.txt              |   1 +
 .../ConditionFuture-test-x-stderr.txt              |   2 +
 .../CMakePresetsTest/ConditionFuture.json.in       |  17 +
 .../ConditionListPresets-test-x-stdout.txt         |   3 +
 .../ConditionRunTests-test-disabled-result.txt     |   1 +
 .../ConditionRunTests-test-disabled-stderr.txt     |   2 +
 .../CMakePresetsTest/ConditionRunTests.cmake       |   2 +
 Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake |   7 +
 Utilities/IWYU/mapping.imp                         |   1 +
 44 files changed, 1410 insertions(+), 19 deletions(-)
 create mode 100644 Help/release/dev/cmake-presets-condition.rst
 create mode 100644 Tests/RunCMake/CMakePresets/ConditionFuture-result.txt
 create mode 100644 Tests/RunCMake/CMakePresets/ConditionFuture-stderr.txt
 create mode 100644 Tests/RunCMake/CMakePresets/ConditionFuture.json.in
 create mode 100644 Tests/RunCMake/CMakePresets/Conditions.json.in
 create mode 100644 Tests/RunCMake/CMakePresets/ListConditions-stdout.txt
 create mode 100644 Tests/RunCMake/CMakePresets/SimpleFalse-result.txt
 create mode 100644 Tests/RunCMake/CMakePresets/SimpleFalse-stderr.txt
 create mode 100644 Tests/RunCMake/CMakePresets/SimpleTrue.cmake
 create mode 100644 Tests/RunCMake/CMakePresets/SubConditionNull-result.txt
 create mode 100644 Tests/RunCMake/CMakePresets/SubConditionNull-stderr.txt
 create mode 100644 Tests/RunCMake/CMakePresets/SubConditionNull.json.in
 create mode 100644 Tests/RunCMake/CMakePresetsBuild/Condition-build-disabled-result.txt
 create mode 100644 Tests/RunCMake/CMakePresetsBuild/Condition-build-disabled-stderr.txt
 create mode 100644 Tests/RunCMake/CMakePresetsBuild/Condition.cmake
 create mode 100644 Tests/RunCMake/CMakePresetsBuild/Condition.json.in
 create mode 100644 Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-result.txt
 create mode 100644 Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-stderr.txt
 create mode 100644 Tests/RunCMake/CMakePresetsBuild/ConditionFuture.json.in
 create mode 100644 Tests/RunCMake/CMakePresetsTest/Condition.json.in
 create mode 100644 Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-result.txt
 create mode 100644 Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-stderr.txt
 create mode 100644 Tests/RunCMake/CMakePresetsTest/ConditionFuture.json.in
 create mode 100644 Tests/RunCMake/CMakePresetsTest/ConditionListPresets-test-x-stdout.txt
 create mode 100644 Tests/RunCMake/CMakePresetsTest/ConditionRunTests-test-disabled-result.txt
 create mode 100644 Tests/RunCMake/CMakePresetsTest/ConditionRunTests-test-disabled-stderr.txt
 create mode 100644 Tests/RunCMake/CMakePresetsTest/ConditionRunTests.cmake

diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst
index 5273236..cc72603 100644
--- a/Help/manual/cmake-presets.7.rst
+++ b/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
   containing one string.
 
+``condition``
+
+  An optional `Condition`_ object. This is allowed in preset files specifying
+  version ``3`` or above.
+
 ``vendor``
 
   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
   containing one string.
 
+``condition``
+
+  An optional `Condition`_ object. This is allowed in preset files specifying
+  version ``3`` or above.
+
 ``vendor``
 
   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
   containing one string.
 
+``condition``
+
+  An optional `Condition`_ object. This is allowed in preset files specifying
+  version ``3`` or above.
+
 ``vendor``
 
   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.
 
+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
 ^^^^^^^^^^^^^^^
 
diff --git a/Help/manual/presets/example.json b/Help/manual/presets/example.json
index dfc2910..346f342 100644
--- a/Help/manual/presets/example.json
+++ b/Help/manual/presets/example.json
@@ -1,5 +1,5 @@
 {
-  "version": 2,
+  "version": 3,
   "cmakeMinimumRequired": {
     "major": 3,
     "minor": 20,
@@ -35,6 +35,17 @@
       "displayName": "Ninja Multi-Config",
       "description": "Default build using Ninja Multi-Config generator",
       "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": [
diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json
index a5025bb..dbcead5 100644
--- a/Help/manual/presets/schema.json
+++ b/Help/manual/presets/schema.json
@@ -24,8 +24,8 @@
         "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
         "vendor": { "$ref": "#/definitions/vendor" },
         "configurePresets": { "$ref": "#/definitions/configurePresetsV1"},
-        "buildPresets": { "$ref": "#/definitions/buildPresets"},
-        "testPresets": { "$ref": "#/definitions/testPresets"}
+        "buildPresets": { "$ref": "#/definitions/buildPresetsV2"},
+        "testPresets": { "$ref": "#/definitions/testPresetsV2"}
       },
       "additionalProperties": false
     },
@@ -38,8 +38,8 @@
         "cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequired"},
         "vendor": { "$ref": "#/definitions/vendor" },
         "configurePresets": { "$ref": "#/definitions/configurePresetsV3"},
-        "buildPresets": { "$ref": "#/definitions/buildPresets"},
-        "testPresets": { "$ref": "#/definitions/testPresets"}
+        "buildPresets": { "$ref": "#/definitions/buildPresetsV3"},
+        "testPresets": { "$ref": "#/definitions/testPresetsV3"}
       },
       "additionalProperties": false
     }
@@ -82,7 +82,8 @@
           "installDir": {
             "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)."
-          }
+          },
+          "condition": { "$ref": "#/definitions/topCondition" }
         }
       }
     },
@@ -358,7 +359,8 @@
           "environment": {},
           "warnings": {},
           "errors": {},
-          "debug": {}
+          "debug": {},
+          "condition": {}
         },
         "required": [
           "name"
@@ -397,7 +399,17 @@
         "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",
       "description": "An optional array of build preset objects. Used to specify arguments to cmake --build. Available in version 2 and higher.",
       "items": {
@@ -515,11 +527,84 @@
         },
         "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": [
+          "name"
         ],
         "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",
       "description": "An optional array of test preset objects. Used to specify arguments to ctest. Available in version 2 and higher.",
       "items": {
@@ -831,9 +916,264 @@
         },
         "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": [
+          "name"
         ],
         "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."
+        }
+      ]
     }
   }
 }
diff --git a/Help/release/dev/cmake-presets-condition.rst b/Help/release/dev/cmake-presets-condition.rst
new file mode 100644
index 0000000..aa01bc1
--- /dev/null
+++ b/Help/release/dev/cmake-presets-condition.rst
@@ -0,0 +1,4 @@
+cmake-presets-condition
+-----------------------
+
+* :manual:`cmake-presets(7)` now support conditional enabling of presets.
diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx
index f593f83..7d037e3 100644
--- a/Source/QtDialog/QCMake.cxx
+++ b/Source/QtDialog/QCMake.cxx
@@ -557,7 +557,7 @@ void QCMake::loadPresets()
     preset.toolset = std::move(QString::fromLocal8Bit(p.Toolset.data()));
     preset.setToolset = !p.ToolsetStrategy ||
       p.ToolsetStrategy == cmCMakePresetsFile::ArchToolsetStrategy::Set;
-    preset.enabled = it.Expanded &&
+    preset.enabled = it.Expanded && it.Expanded->ConditionResult &&
       std::find_if(this->AvailableGenerators.begin(),
                    this->AvailableGenerators.end(),
                    [&p](const cmake::GeneratorInfo& g) {
diff --git a/Source/cmCMakePresetsFile.cxx b/Source/cmCMakePresetsFile.cxx
index c0b0981..fbe9fe5 100644
--- a/Source/cmCMakePresetsFile.cxx
+++ b/Source/cmCMakePresetsFile.cxx
@@ -9,6 +9,8 @@
 #include <iterator>
 #include <utility>
 
+#include <cm/string_view>
+
 #include "cmCMakePresetsFileInternal.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
@@ -114,6 +116,14 @@ ReadFileResult VisitPreset(
     for (auto const& v : parentPreset.Environment) {
       preset.Environment.insert(v);
     }
+
+    if (!preset.ConditionEvaluator) {
+      preset.ConditionEvaluator = parentPreset.ConditionEvaluator;
+    }
+  }
+
+  if (preset.ConditionEvaluator && preset.ConditionEvaluator->IsNull()) {
+    preset.ConditionEvaluator.reset();
   }
 
   CHECK_OK(preset.VisitPresetAfterInherit())
@@ -382,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);
 }
 
@@ -505,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::ConfigurePreset::VisitPresetInherit(
   const cmCMakePresetsFile::Preset& parentPreset)
@@ -862,6 +959,10 @@ const char* cmCMakePresetsFile::ResultToString(ReadFileResult result)
     case ReadFileResult::INSTALL_PREFIX_UNSUPPORTED:
       return "File version must be 3 or higher for installDir preset "
              "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";
@@ -918,7 +1019,7 @@ void cmCMakePresetsFile::PrintConfigurePresetList(
   for (auto const& p : this->ConfigurePresetOrder) {
     auto const& preset = this->ConfigurePresets.at(p);
     if (!preset.Unexpanded.Hidden && preset.Expanded &&
-        filter(preset.Unexpanded)) {
+        preset.Expanded->ConditionResult && filter(preset.Unexpanded)) {
       presets.push_back(
         static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded));
     }
@@ -935,7 +1036,8 @@ void cmCMakePresetsFile::PrintBuildPresetList() const
   std::vector<const cmCMakePresetsFile::Preset*> presets;
   for (auto const& p : this->BuildPresetOrder) {
     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(
         static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded));
     }
@@ -952,7 +1054,8 @@ void cmCMakePresetsFile::PrintTestPresetList() const
   std::vector<const cmCMakePresetsFile::Preset*> presets;
   for (auto const& p : this->TestPresetOrder) {
     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(
         static_cast<const cmCMakePresetsFile::Preset*>(&preset.Unexpanded));
     }
diff --git a/Source/cmCMakePresetsFile.h b/Source/cmCMakePresetsFile.h
index 02e6a32..0999d5a 100644
--- a/Source/cmCMakePresetsFile.h
+++ b/Source/cmCMakePresetsFile.h
@@ -2,8 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #pragma once
 
+#include "cmConfigure.h" // IWYU pragma: keep
+
 #include <functional>
 #include <map>
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
@@ -34,6 +37,8 @@ public:
     BUILD_TEST_PRESETS_UNSUPPORTED,
     INVALID_CONFIGURE_PRESET,
     INSTALL_PREFIX_UNSUPPORTED,
+    INVALID_CONDITION,
+    CONDITION_UNSUPPORTED,
   };
 
   enum class ArchToolsetStrategy
@@ -49,6 +54,8 @@ public:
     std::string Value;
   };
 
+  class Condition;
+
   class Preset
   {
   public:
@@ -71,6 +78,9 @@ public:
     std::string DisplayName;
     std::string Description;
 
+    std::shared_ptr<Condition> ConditionEvaluator;
+    bool ConditionResult = true;
+
     std::map<std::string, cm::optional<std::string>> Environment;
 
     virtual ReadFileResult VisitPresetInherit(const Preset& parent) = 0;
diff --git a/Source/cmCMakePresetsFileInternal.h b/Source/cmCMakePresetsFileInternal.h
index f05b8ce..ffb6ce9 100644
--- a/Source/cmCMakePresetsFileInternal.h
+++ b/Source/cmCMakePresetsFileInternal.h
@@ -1,5 +1,7 @@
 /* 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)                                                        \
@@ -20,3 +22,81 @@ enum class ExpandMacroResult
 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;
+};
+}
diff --git a/Source/cmCMakePresetsFileReadJSON.cxx b/Source/cmCMakePresetsFileReadJSON.cxx
index b71b176..e26e7b4 100644
--- a/Source/cmCMakePresetsFileReadJSON.cxx
+++ b/Source/cmCMakePresetsFileReadJSON.cxx
@@ -6,6 +6,7 @@
 #include <utility>
 #include <vector>
 
+#include <cm/memory>
 #include <cm/optional>
 #include <cmext/string_view>
 
@@ -15,6 +16,7 @@
 #include "cmsys/FStream.hxx"
 
 #include "cmCMakePresetsFile.h"
+#include "cmCMakePresetsFileInternal.h"
 #include "cmJSONHelpers.h"
 #include "cmVersion.h"
 
@@ -44,6 +46,180 @@ struct RootPresets
   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*/,
@@ -306,7 +482,9 @@ auto const ConfigurePresetHelper =
           false)
     .Bind("warnings"_s, PresetWarningsHelper, false)
     .Bind("errors"_s, PresetErrorsHelper, false)
-    .Bind("debug"_s, PresetDebugHelper, false);
+    .Bind("debug"_s, PresetDebugHelper, false)
+    .Bind("condition"_s, &ConfigurePreset::ConditionEvaluator,
+          PresetConditionHelper, false);
 
 auto const BuildPresetHelper =
   cmJSONObjectHelper<BuildPreset, ReadFileResult>(
@@ -335,7 +513,9 @@ auto const BuildPresetHelper =
           false)
     .Bind("verbose"_s, &BuildPreset::Verbose, PresetOptionalBoolHelper, false)
     .Bind("nativeToolOptions"_s, &BuildPreset::NativeToolOptions,
-          PresetVectorStringHelper, false);
+          PresetVectorStringHelper, false)
+    .Bind("condition"_s, &BuildPreset::ConditionEvaluator,
+          PresetConditionHelper, false);
 
 ReadFileResult TestPresetOutputVerbosityHelper(
   TestPreset::OutputOptions::VerbosityEnum& out, const Json::Value* value)
@@ -651,7 +831,9 @@ auto const TestPresetHelper =
           false)
     .Bind("filter"_s, &TestPreset::Filter, TestPresetFilterHelper, false)
     .Bind("execution"_s, &TestPreset::Execution, TestPresetExecutionHelper,
-          false);
+          false)
+    .Bind("condition"_s, &TestPreset::ConditionEvaluator,
+          PresetConditionHelper, false);
 
 auto const ConfigurePresetsHelper =
   cmJSONVectorHelper<ConfigurePreset, ReadFileResult>(
@@ -766,6 +948,11 @@ cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile(
       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);
   }
 
@@ -781,6 +968,12 @@ cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile(
     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);
   }
 
@@ -796,6 +989,12 @@ cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile(
     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);
   }
 
diff --git a/Source/cmCTest.cxx b/Source/cmCTest.cxx
index 77b4441..4228d30 100644
--- a/Source/cmCTest.cxx
+++ b/Source/cmCTest.cxx
@@ -2310,6 +2310,13 @@ bool cmCTest::SetArgsFromPreset(const std::string& presetName,
     return false;
   }
 
+  if (!expandedPreset->ConditionResult) {
+    cmSystemTools::Error(cmStrCat("Cannot use disabled test preset in ",
+                                  workingDirectory, ": \"", presetName, '"'));
+    settingsFile.PrintTestPresetList();
+    return false;
+  }
+
   auto configurePresetPair =
     settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset);
   if (configurePresetPair == settingsFile.ConfigurePresets.end()) {
diff --git a/Source/cmJSONHelpers.h b/Source/cmJSONHelpers.h
index a63347d..6690aef 100644
--- a/Source/cmJSONHelpers.h
+++ b/Source/cmJSONHelpers.h
@@ -239,7 +239,7 @@ cmJSONHelper<std::vector<T>, E> cmJSONVectorFilterHelper(E success, E fail,
       if (!filter(t)) {
         continue;
       }
-      out.push_back(t);
+      out.push_back(std::move(t));
     }
     return success;
   };
diff --git a/Source/cmake.cxx b/Source/cmake.cxx
index b12eeee..61ce154 100644
--- a/Source/cmake.cxx
+++ b/Source/cmake.cxx
@@ -1217,6 +1217,11 @@ void cmake::SetArgs(const std::vector<std::string>& args)
                                     "\": Invalid macro expansion"));
       return;
     }
+    if (!expandedPreset->ConditionResult) {
+      cmSystemTools::Error(cmStrCat("Could not use disabled preset \"",
+                                    preset->second.Unexpanded.Name, "\""));
+      return;
+    }
 
     if (!this->State->IsCacheLoaded() && !haveBArg) {
       this->SetHomeOutputDirectory(expandedPreset->BinaryDir);
@@ -3164,6 +3169,14 @@ int cmake::Build(int jobs, std::string dir, std::vector<std::string> targets,
       return 1;
     }
 
+    if (!expandedPreset->ConditionResult) {
+      cmSystemTools::Error(cmStrCat("Cannot use disabled build preset in ",
+                                    this->GetHomeDirectory(), ": \"",
+                                    presetName, '"'));
+      settingsFile.PrintBuildPresetList();
+      return 1;
+    }
+
     auto configurePresetPair =
       settingsFile.ConfigurePresets.find(expandedPreset->ConfigurePreset);
     if (configurePresetPair == settingsFile.ConfigurePresets.end()) {
diff --git a/Tests/RunCMake/CMakePresets/ConditionFuture-result.txt b/Tests/RunCMake/CMakePresets/ConditionFuture-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/ConditionFuture-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/ConditionFuture-stderr.txt b/Tests/RunCMake/CMakePresets/ConditionFuture-stderr.txt
new file mode 100644
index 0000000..ea5f47f
--- /dev/null
+++ b/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$
diff --git a/Tests/RunCMake/CMakePresets/ConditionFuture.json.in b/Tests/RunCMake/CMakePresets/ConditionFuture.json.in
new file mode 100644
index 0000000..9d4798b
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/ConditionFuture.json.in
@@ -0,0 +1,11 @@
+{
+  "version": 2,
+  "configurePresets": [
+    {
+      "name": "ConditionFuture",
+      "generator": "@RunCMake_GENERATOR@",
+      "binaryDir": "${sourceDir}/build",
+      "condition": true
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/Conditions.json.in b/Tests/RunCMake/CMakePresets/Conditions.json.in
new file mode 100644
index 0000000..9a01e2f
--- /dev/null
+++ b/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"
+      ]
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresets/ListConditions-stdout.txt b/Tests/RunCMake/CMakePresets/ListConditions-stdout.txt
new file mode 100644
index 0000000..19f91d4
--- /dev/null
+++ b/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"$
diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
index 24ac0e9..22425b2 100644
--- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake
@@ -118,6 +118,8 @@ run_cmake_presets(EnvCycle)
 run_cmake_presets(EmptyEnv)
 run_cmake_presets(EmptyPenv)
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
+run_cmake_presets(ConditionFuture)
+run_cmake_presets(SubConditionNull)
 
 # Test cmakeMinimumRequired field
 run_cmake_presets(MinimumRequiredInvalid)
@@ -267,6 +269,12 @@ run_cmake_presets(HostSystemName)
 set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/HostSystemNameFuture.json.in")
 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
 file(READ "${RunCMake_SOURCE_DIR}/../../../Help/manual/presets/example.json" _example)
 string(REPLACE "\"generator\": \"Ninja\"" "\"generator\": \"@RunCMake_GENERATOR@\"" _example "${_example}")
diff --git a/Tests/RunCMake/CMakePresets/SimpleFalse-result.txt b/Tests/RunCMake/CMakePresets/SimpleFalse-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/SimpleFalse-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/SimpleFalse-stderr.txt b/Tests/RunCMake/CMakePresets/SimpleFalse-stderr.txt
new file mode 100644
index 0000000..6a9a7de
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/SimpleFalse-stderr.txt
@@ -0,0 +1 @@
+^CMake Error: Could not use disabled preset "SimpleFalse"$
diff --git a/Tests/RunCMake/CMakePresets/SimpleTrue.cmake b/Tests/RunCMake/CMakePresets/SimpleTrue.cmake
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/CMakePresets/SubConditionNull-result.txt b/Tests/RunCMake/CMakePresets/SubConditionNull-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/SubConditionNull-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresets/SubConditionNull-stderr.txt b/Tests/RunCMake/CMakePresets/SubConditionNull-stderr.txt
new file mode 100644
index 0000000..42b74d6
--- /dev/null
+++ b/Tests/RunCMake/CMakePresets/SubConditionNull-stderr.txt
@@ -0,0 +1,2 @@
+^CMake Error: Could not read presets from [^
+]*/Tests/RunCMake/CMakePresets/SubConditionNull: Invalid preset condition$
diff --git a/Tests/RunCMake/CMakePresets/SubConditionNull.json.in b/Tests/RunCMake/CMakePresets/SubConditionNull.json.in
new file mode 100644
index 0000000..eed3da6
--- /dev/null
+++ b/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
+      }
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsBuild/Condition-build-disabled-result.txt b/Tests/RunCMake/CMakePresetsBuild/Condition-build-disabled-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsBuild/Condition-build-disabled-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsBuild/Condition-build-disabled-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/Condition-build-disabled-stderr.txt
new file mode 100644
index 0000000..c35f5d7
--- /dev/null
+++ b/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"$
diff --git a/Tests/RunCMake/CMakePresetsBuild/Condition.cmake b/Tests/RunCMake/CMakePresetsBuild/Condition.cmake
new file mode 100644
index 0000000..e69de29
diff --git a/Tests/RunCMake/CMakePresetsBuild/Condition.json.in b/Tests/RunCMake/CMakePresetsBuild/Condition.json.in
new file mode 100644
index 0000000..aaee96a
--- /dev/null
+++ b/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
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-result.txt b/Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-stderr.txt b/Tests/RunCMake/CMakePresetsBuild/ConditionFuture-build-conditionFuture-stderr.txt
new file mode 100644
index 0000000..f08f4c1
--- /dev/null
+++ b/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$
diff --git a/Tests/RunCMake/CMakePresetsBuild/ConditionFuture.json.in b/Tests/RunCMake/CMakePresetsBuild/ConditionFuture.json.in
new file mode 100644
index 0000000..2f3f7d8
--- /dev/null
+++ b/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
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsBuild/ListPresets-build-x-stdout.txt b/Tests/RunCMake/CMakePresetsBuild/ListPresets-build-x-stdout.txt
index 4d30707..2d362d4 100644
--- a/Tests/RunCMake/CMakePresetsBuild/ListPresets-build-x-stdout.txt
+++ b/Tests/RunCMake/CMakePresetsBuild/ListPresets-build-x-stdout.txt
@@ -1,5 +1,6 @@
-Available build presets:
+^Available build presets:
 
   "build-default" - build-default displayName
   "empty"
   "display"       - display displayName
+  "true"$
diff --git a/Tests/RunCMake/CMakePresetsBuild/ListPresets.json.in b/Tests/RunCMake/CMakePresetsBuild/ListPresets.json.in
index 3f5e02c..26504d3 100644
--- a/Tests/RunCMake/CMakePresetsBuild/ListPresets.json.in
+++ b/Tests/RunCMake/CMakePresetsBuild/ListPresets.json.in
@@ -1,5 +1,5 @@
 {
-    "version": 2,
+    "version": 3,
     "configurePresets": [
         {
             "name": "default",
@@ -26,6 +26,16 @@
         {
             "name": "hidden",
             "hidden": true
+        },
+        {
+            "name": "true",
+            "inherits": "build-default",
+            "condition": true
+        },
+        {
+            "name": "false",
+            "inherits": "build-default",
+            "condition": false
         }
     ]
 }
diff --git a/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake
index 2559b12..afa22eb 100644
--- a/Tests/RunCMake/CMakePresetsBuild/RunCMakeTest.cmake
+++ b/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(InvalidConfigurePreset "default" "badConfigurePreset")
+run_cmake_build_presets(Condition "default" "enabled;disabled")
 
 set(CMakePresetsBuild_BUILD_ONLY 1)
 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)
 run_cmake_build_presets(PresetsUnsupported "x" "x")
+run_cmake_build_presets(ConditionFuture "x" "conditionFuture")
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
 set(CMakePresetsBuild_BUILD_ONLY 0)
diff --git a/Tests/RunCMake/CMakePresetsTest/Condition.json.in b/Tests/RunCMake/CMakePresetsTest/Condition.json.in
new file mode 100644
index 0000000..0baf176
--- /dev/null
+++ b/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
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-result.txt b/Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-stderr.txt b/Tests/RunCMake/CMakePresetsTest/ConditionFuture-test-x-stderr.txt
new file mode 100644
index 0000000..b814bbb
--- /dev/null
+++ b/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$
diff --git a/Tests/RunCMake/CMakePresetsTest/ConditionFuture.json.in b/Tests/RunCMake/CMakePresetsTest/ConditionFuture.json.in
new file mode 100644
index 0000000..4b9f33f
--- /dev/null
+++ b/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
+    }
+  ]
+}
diff --git a/Tests/RunCMake/CMakePresetsTest/ConditionListPresets-test-x-stdout.txt b/Tests/RunCMake/CMakePresetsTest/ConditionListPresets-test-x-stdout.txt
new file mode 100644
index 0000000..11918e5
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsTest/ConditionListPresets-test-x-stdout.txt
@@ -0,0 +1,3 @@
+^Available test presets:
+
+  "enabled"$
diff --git a/Tests/RunCMake/CMakePresetsTest/ConditionRunTests-test-disabled-result.txt b/Tests/RunCMake/CMakePresetsTest/ConditionRunTests-test-disabled-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsTest/ConditionRunTests-test-disabled-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/CMakePresetsTest/ConditionRunTests-test-disabled-stderr.txt b/Tests/RunCMake/CMakePresetsTest/ConditionRunTests-test-disabled-stderr.txt
new file mode 100644
index 0000000..5db3b77
--- /dev/null
+++ b/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"$
diff --git a/Tests/RunCMake/CMakePresetsTest/ConditionRunTests.cmake b/Tests/RunCMake/CMakePresetsTest/ConditionRunTests.cmake
new file mode 100644
index 0000000..b29161e
--- /dev/null
+++ b/Tests/RunCMake/CMakePresetsTest/ConditionRunTests.cmake
@@ -0,0 +1,2 @@
+enable_testing()
+add_test(true ${CMAKE_COMMAND} -E true)
diff --git a/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake
index c93dff3..70d25d4 100644
--- a/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake
+++ b/Tests/RunCMake/CMakePresetsTest/RunCMakeTest.cmake
@@ -90,6 +90,12 @@ run_cmake_test_presets(InvalidConfigurePreset "default" "" "badConfigurePreset")
 set(CMakePresetsTest_NO_CONFIGURE 1)
 set(CMakePresetsTest_FILE "${RunCMake_SOURCE_DIR}/Good.json.in")
 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)
 
 run_cmake_test_presets(NoConfigurePreset "" "" "noConfigurePreset")
@@ -98,6 +104,7 @@ run_cmake_test_presets(Invalid "" "" "hidden;vendorMacro")
 
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 1)
 run_cmake_test_presets(PresetsUnsupported "" "" "x")
+run_cmake_test_presets(ConditionFuture "" "" "x")
 set(CMakePresets_SCHEMA_EXPECTED_RESULT 0)
 set(CMakePresetsTest_NO_CONFIGURE 0)
 
diff --git a/Utilities/IWYU/mapping.imp b/Utilities/IWYU/mapping.imp
index c2aced5..f2aef3e 100644
--- a/Utilities/IWYU/mapping.imp
+++ b/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<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: [ "__gnu_cxx::__enable_if<true, bool>::__type", private, "\"cmConfigure.h\"", public ] },
 
   # Wrappers for 3rd-party libraries
   { include: [ "@<.*curl/curlver.h>", private, "<cm3p/curl/curl.h>", public ] },
-- 
cgit v0.12