mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-27 04:09:36 +03:00
Introduce a mechanism for "immediate" evaluation of preset macros as
they are defined in the current JSON file, rather than deferring until
after the preset graph is merged. This allows `${fileDir}` to always
be expanded with the path to the directory containing the file which
contains the macro, instead of the path being dependent on the preset
in which the macro is used (which could be defined and inherited from
elsewhere).
Bump to presets version 12 to provide backwards compatibility with the
old behavior.
Fixes: #26982
Relates: #27329, #27639
840 lines
28 KiB
C++
840 lines
28 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <functional>
|
|
#include <map>
|
|
#include <string>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <cm/memory>
|
|
#include <cm/optional>
|
|
#include <cmext/string_view>
|
|
|
|
#include <cm3p/json/value.h>
|
|
|
|
#include "cmCMakePresetsErrors.h"
|
|
#include "cmCMakePresetsGraph.h"
|
|
#include "cmCMakePresetsGraphInternal.h"
|
|
#include "cmJSONHelpers.h"
|
|
#include "cmJSONState.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmVersion.h"
|
|
|
|
namespace {
|
|
using CacheVariable = cmCMakePresetsGraph::CacheVariable;
|
|
using ConfigurePreset = cmCMakePresetsGraph::ConfigurePreset;
|
|
using BuildPreset = cmCMakePresetsGraph::BuildPreset;
|
|
using TestPreset = cmCMakePresetsGraph::TestPreset;
|
|
using PackagePreset = cmCMakePresetsGraph::PackagePreset;
|
|
using WorkflowPreset = cmCMakePresetsGraph::WorkflowPreset;
|
|
using ArchToolsetStrategy = cmCMakePresetsGraph::ArchToolsetStrategy;
|
|
using JSONHelperBuilder = cmJSONHelperBuilder;
|
|
using ExpandMacroResult = cmCMakePresetsGraphInternal::ExpandMacroResult;
|
|
using MacroExpander = cmCMakePresetsGraphInternal::MacroExpander;
|
|
using MacroExpanderVector = cmCMakePresetsGraphInternal::MacroExpanderVector;
|
|
using cmCMakePresetsGraphInternal::BaseMacroExpander;
|
|
using cmCMakePresetsGraphInternal::ExpandMacros;
|
|
using cmCMakePresetsGraphInternal::ExpandImmediateMacros;
|
|
|
|
constexpr int MIN_VERSION = 1;
|
|
constexpr int MAX_VERSION = 12;
|
|
|
|
struct CMakeVersion
|
|
{
|
|
unsigned int Major = 0;
|
|
unsigned int Minor = 0;
|
|
unsigned int Patch = 0;
|
|
};
|
|
|
|
struct RootPresets
|
|
{
|
|
CMakeVersion CMakeMinimumRequired;
|
|
std::vector<ConfigurePreset> ConfigurePresets;
|
|
std::vector<BuildPreset> BuildPresets;
|
|
std::vector<TestPreset> TestPresets;
|
|
std::vector<PackagePreset> PackagePresets;
|
|
std::vector<WorkflowPreset> WorkflowPresets;
|
|
std::vector<std::string> Include;
|
|
};
|
|
|
|
std::unique_ptr<cmCMakePresetsGraphInternal::NotCondition> InvertCondition(
|
|
std::unique_ptr<cmCMakePresetsGraph::Condition> condition)
|
|
{
|
|
auto retval = cm::make_unique<cmCMakePresetsGraphInternal::NotCondition>();
|
|
retval->SubCondition = std::move(condition);
|
|
return retval;
|
|
}
|
|
|
|
auto const ConditionStringHelper = JSONHelperBuilder::String();
|
|
|
|
auto const ConditionBoolHelper = JSONHelperBuilder::Bool();
|
|
|
|
auto const ConditionStringListHelper = JSONHelperBuilder::Vector<std::string>(
|
|
cmCMakePresetsErrors::INVALID_CONDITION, ConditionStringHelper);
|
|
|
|
auto const ConstConditionHelper =
|
|
JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::ConstCondition>(
|
|
cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
|
|
.Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
|
|
.Bind("value"_s, &cmCMakePresetsGraphInternal::ConstCondition::Value,
|
|
ConditionBoolHelper, true);
|
|
|
|
auto const EqualsConditionHelper =
|
|
JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::EqualsCondition>(
|
|
cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
|
|
.Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
|
|
.Bind("lhs"_s, &cmCMakePresetsGraphInternal::EqualsCondition::Lhs,
|
|
ConditionStringHelper, true)
|
|
.Bind("rhs"_s, &cmCMakePresetsGraphInternal::EqualsCondition::Rhs,
|
|
ConditionStringHelper, true);
|
|
|
|
auto const InListConditionHelper =
|
|
JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::InListCondition>(
|
|
cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
|
|
.Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
|
|
.Bind("string"_s, &cmCMakePresetsGraphInternal::InListCondition::String,
|
|
ConditionStringHelper, true)
|
|
.Bind("list"_s, &cmCMakePresetsGraphInternal::InListCondition::List,
|
|
ConditionStringListHelper, true);
|
|
|
|
auto const MatchesConditionHelper =
|
|
JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::MatchesCondition>(
|
|
cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
|
|
.Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
|
|
.Bind("string"_s, &cmCMakePresetsGraphInternal::MatchesCondition::String,
|
|
ConditionStringHelper, true)
|
|
.Bind("regex"_s, &cmCMakePresetsGraphInternal::MatchesCondition::Regex,
|
|
ConditionStringHelper, true);
|
|
|
|
bool SubConditionHelper(std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
|
|
Json::Value const* value, cmJSONState* state);
|
|
|
|
auto const ListConditionVectorHelper =
|
|
JSONHelperBuilder::Vector<std::unique_ptr<cmCMakePresetsGraph::Condition>>(
|
|
cmCMakePresetsErrors::INVALID_CONDITION, SubConditionHelper);
|
|
auto const AnyAllOfConditionHelper =
|
|
JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::AnyAllOfCondition>(
|
|
cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
|
|
.Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
|
|
.Bind("conditions"_s,
|
|
&cmCMakePresetsGraphInternal::AnyAllOfCondition::Conditions,
|
|
ListConditionVectorHelper);
|
|
|
|
auto const NotConditionHelper =
|
|
JSONHelperBuilder::Object<cmCMakePresetsGraphInternal::NotCondition>(
|
|
cmCMakePresetsErrors::INVALID_CONDITION_OBJECT, false)
|
|
.Bind<std::string>("type"_s, nullptr, ConditionStringHelper, true)
|
|
.Bind("condition"_s,
|
|
&cmCMakePresetsGraphInternal::NotCondition::SubCondition,
|
|
SubConditionHelper);
|
|
|
|
bool ConditionHelper(std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
|
|
Json::Value const* value, cmJSONState* state)
|
|
{
|
|
if (!value) {
|
|
out.reset();
|
|
return true;
|
|
}
|
|
|
|
if (value->isBool()) {
|
|
auto c = cm::make_unique<cmCMakePresetsGraphInternal::ConstCondition>();
|
|
c->Value = value->asBool();
|
|
out = std::move(c);
|
|
return true;
|
|
}
|
|
|
|
if (value->isNull()) {
|
|
out = cm::make_unique<cmCMakePresetsGraphInternal::NullCondition>();
|
|
return true;
|
|
}
|
|
|
|
if (value->isObject()) {
|
|
if (!value->isMember("type")) {
|
|
cmCMakePresetsErrors::INVALID_CONDITION(value, state);
|
|
return false;
|
|
}
|
|
|
|
if (!(*value)["type"].isString()) {
|
|
cmCMakePresetsErrors::INVALID_CONDITION(value, state);
|
|
return false;
|
|
}
|
|
auto type = (*value)["type"].asString();
|
|
|
|
if (type == "const") {
|
|
auto c = cm::make_unique<cmCMakePresetsGraphInternal::ConstCondition>();
|
|
CHECK_OK(ConstConditionHelper(*c, value, state));
|
|
out = std::move(c);
|
|
return true;
|
|
}
|
|
|
|
if (type == "equals" || type == "notEquals") {
|
|
auto c = cm::make_unique<cmCMakePresetsGraphInternal::EqualsCondition>();
|
|
CHECK_OK(EqualsConditionHelper(*c, value, state));
|
|
out = std::move(c);
|
|
if (type == "notEquals") {
|
|
out = InvertCondition(std::move(out));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (type == "inList" || type == "notInList") {
|
|
auto c = cm::make_unique<cmCMakePresetsGraphInternal::InListCondition>();
|
|
CHECK_OK(InListConditionHelper(*c, value, state));
|
|
out = std::move(c);
|
|
if (type == "notInList") {
|
|
out = InvertCondition(std::move(out));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (type == "matches" || type == "notMatches") {
|
|
auto c =
|
|
cm::make_unique<cmCMakePresetsGraphInternal::MatchesCondition>();
|
|
CHECK_OK(MatchesConditionHelper(*c, value, state));
|
|
out = std::move(c);
|
|
if (type == "notMatches") {
|
|
out = InvertCondition(std::move(out));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (type == "anyOf" || type == "allOf") {
|
|
auto c =
|
|
cm::make_unique<cmCMakePresetsGraphInternal::AnyAllOfCondition>();
|
|
c->StopValue = (type == "anyOf");
|
|
CHECK_OK(AnyAllOfConditionHelper(*c, value, state));
|
|
out = std::move(c);
|
|
return true;
|
|
}
|
|
|
|
if (type == "not") {
|
|
auto c = cm::make_unique<cmCMakePresetsGraphInternal::NotCondition>();
|
|
CHECK_OK(NotConditionHelper(*c, value, state));
|
|
out = std::move(c);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
cmCMakePresetsErrors::INVALID_CONDITION(value, state);
|
|
return false;
|
|
}
|
|
|
|
bool SubConditionHelper(std::unique_ptr<cmCMakePresetsGraph::Condition>& out,
|
|
Json::Value const* value, cmJSONState* state)
|
|
{
|
|
std::unique_ptr<cmCMakePresetsGraph::Condition> ptr;
|
|
auto result = ConditionHelper(ptr, value, state);
|
|
if (ptr && ptr->IsNull()) {
|
|
cmCMakePresetsErrors::INVALID_CONDITION(value, state);
|
|
return false;
|
|
}
|
|
out = std::move(ptr);
|
|
return result;
|
|
}
|
|
|
|
bool EnvironmentHelper(cm::optional<std::string>& out,
|
|
Json::Value const* value, cmJSONState* state)
|
|
{
|
|
if (!value || value->isNull()) {
|
|
out = cm::nullopt;
|
|
return true;
|
|
}
|
|
if (value->isString()) {
|
|
out = value->asString();
|
|
return true;
|
|
}
|
|
cmCMakePresetsErrors::INVALID_PRESET(value, state);
|
|
return false;
|
|
}
|
|
|
|
auto const VersionIntHelper =
|
|
JSONHelperBuilder::Int(cmCMakePresetsErrors::INVALID_VERSION);
|
|
|
|
auto const VersionHelper = JSONHelperBuilder::Required<int>(
|
|
cmCMakePresetsErrors::NO_VERSION, VersionIntHelper);
|
|
|
|
auto const VersionRangeHelper = JSONHelperBuilder::Checked<int>(
|
|
cmCMakePresetsErrors::UNRECOGNIZED_VERSION_RANGE(MIN_VERSION, MAX_VERSION),
|
|
VersionHelper,
|
|
[](int const v) -> bool { return v >= MIN_VERSION && v <= MAX_VERSION; });
|
|
|
|
auto const RootVersionHelper =
|
|
JSONHelperBuilder::Object<int>(cmCMakePresetsErrors::INVALID_ROOT_OBJECT)
|
|
.Bind("version"_s, VersionRangeHelper, false);
|
|
|
|
auto const CMakeVersionUIntHelper =
|
|
JSONHelperBuilder::UInt(cmCMakePresetsErrors::INVALID_VERSION);
|
|
|
|
auto const CMakeVersionHelper =
|
|
JSONHelperBuilder::Object<CMakeVersion>(JsonErrors::INVALID_NAMED_OBJECT_KEY,
|
|
false)
|
|
.Bind("major"_s, &CMakeVersion::Major, CMakeVersionUIntHelper, false)
|
|
.Bind("minor"_s, &CMakeVersion::Minor, CMakeVersionUIntHelper, false)
|
|
.Bind("patch"_s, &CMakeVersion::Patch, CMakeVersionUIntHelper, false);
|
|
|
|
auto const IncludeHelper =
|
|
JSONHelperBuilder::String(cmCMakePresetsErrors::INVALID_INCLUDE);
|
|
|
|
auto const IncludeVectorHelper = JSONHelperBuilder::Vector<std::string>(
|
|
cmCMakePresetsErrors::INVALID_INCLUDE, IncludeHelper);
|
|
|
|
auto const RootPresetsHelper =
|
|
JSONHelperBuilder::Object<RootPresets>(
|
|
cmCMakePresetsErrors::INVALID_ROOT_OBJECT, false)
|
|
.Bind<int>("version"_s, nullptr, VersionHelper)
|
|
.Bind("configurePresets"_s, &RootPresets::ConfigurePresets,
|
|
cmCMakePresetsGraphInternal::ConfigurePresetsHelper, false)
|
|
.Bind("buildPresets"_s, &RootPresets::BuildPresets,
|
|
cmCMakePresetsGraphInternal::BuildPresetsHelper, false)
|
|
.Bind("testPresets"_s, &RootPresets::TestPresets,
|
|
cmCMakePresetsGraphInternal::TestPresetsHelper, false)
|
|
.Bind("packagePresets"_s, &RootPresets::PackagePresets,
|
|
cmCMakePresetsGraphInternal::PackagePresetsHelper, false)
|
|
.Bind("workflowPresets"_s, &RootPresets::WorkflowPresets,
|
|
cmCMakePresetsGraphInternal::WorkflowPresetsHelper, false)
|
|
.Bind("cmakeMinimumRequired"_s, &RootPresets::CMakeMinimumRequired,
|
|
CMakeVersionHelper, false)
|
|
.Bind("include"_s, &RootPresets::Include, IncludeVectorHelper, false)
|
|
.Bind<std::nullptr_t>("vendor"_s, nullptr,
|
|
cmCMakePresetsGraphInternal::VendorHelper(
|
|
cmCMakePresetsErrors::INVALID_ROOT),
|
|
false)
|
|
.Bind<std::nullptr_t>("$schema"_s, nullptr,
|
|
cmCMakePresetsGraphInternal::SchemaHelper(), false);
|
|
|
|
class EnvironmentMacroExpander : public MacroExpander
|
|
{
|
|
public:
|
|
ExpandMacroResult operator()(std::string const& macroNamespace,
|
|
std::string const& macroName,
|
|
std::string& macroOut,
|
|
int /*version*/) const override
|
|
{
|
|
if (macroNamespace == "penv") {
|
|
if (macroName.empty()) {
|
|
return ExpandMacroResult::Error;
|
|
}
|
|
if (cm::optional<std::string> value =
|
|
cmSystemTools::GetEnvVar(macroName)) {
|
|
macroOut += *value;
|
|
}
|
|
return ExpandMacroResult::Ok;
|
|
}
|
|
|
|
return ExpandMacroResult::Ignore;
|
|
}
|
|
};
|
|
}
|
|
|
|
namespace cmCMakePresetsGraphInternal {
|
|
bool PresetStringHelper(std::string& out, Json::Value const* value,
|
|
cmJSONState* state)
|
|
{
|
|
static auto const helper = JSONHelperBuilder::String();
|
|
return helper(out, value, state);
|
|
}
|
|
|
|
bool PresetNameHelper(std::string& out, Json::Value const* value,
|
|
cmJSONState* state)
|
|
{
|
|
if (!value || !value->isString() || value->asString().empty()) {
|
|
cmCMakePresetsErrors::INVALID_PRESET_NAME(value, state);
|
|
return false;
|
|
}
|
|
out = value->asString();
|
|
return true;
|
|
}
|
|
|
|
bool PresetVectorStringHelper(std::vector<std::string>& out,
|
|
Json::Value const* value, cmJSONState* state)
|
|
{
|
|
static auto const helper = JSONHelperBuilder::Vector<std::string>(
|
|
cmCMakePresetsErrors::INVALID_PRESET,
|
|
cmCMakePresetsGraphInternal::PresetStringHelper);
|
|
return helper(out, value, state);
|
|
}
|
|
|
|
bool PresetBoolHelper(bool& out, Json::Value const* value, cmJSONState* state)
|
|
{
|
|
static auto const helper = JSONHelperBuilder::Bool();
|
|
return helper(out, value, state);
|
|
}
|
|
|
|
bool PresetOptionalBoolHelper(cm::optional<bool>& out,
|
|
Json::Value const* value, cmJSONState* state)
|
|
{
|
|
static auto const helper =
|
|
JSONHelperBuilder::Optional<bool>(PresetBoolHelper);
|
|
return helper(out, value, state);
|
|
}
|
|
|
|
bool PresetIntHelper(int& out, Json::Value const* value, cmJSONState* state)
|
|
{
|
|
static auto const helper = JSONHelperBuilder::Int();
|
|
return helper(out, value, state);
|
|
}
|
|
|
|
bool PresetOptionalIntHelper(cm::optional<int>& out, Json::Value const* value,
|
|
cmJSONState* state)
|
|
{
|
|
static auto const helper = JSONHelperBuilder::Optional<int>(PresetIntHelper);
|
|
return helper(out, value, state);
|
|
}
|
|
|
|
bool PresetUIntHelper(unsigned int& out, Json::Value const* value,
|
|
cmJSONState* state)
|
|
{
|
|
static auto const helper = JSONHelperBuilder::UInt();
|
|
return helper(out, value, state);
|
|
}
|
|
|
|
bool PresetOptionalUIntHelper(cm::optional<unsigned int>& out,
|
|
Json::Value const* value, cmJSONState* state)
|
|
{
|
|
static auto const helper =
|
|
JSONHelperBuilder::Optional<unsigned int>(PresetUIntHelper);
|
|
return helper(out, value, state);
|
|
}
|
|
|
|
bool PresetVectorIntHelper(std::vector<int>& out, Json::Value const* value,
|
|
cmJSONState* state)
|
|
{
|
|
static auto const helper = JSONHelperBuilder::Vector<int>(
|
|
cmCMakePresetsErrors::INVALID_PRESET, PresetIntHelper);
|
|
return helper(out, value, state);
|
|
}
|
|
|
|
cmJSONHelper<std::nullptr_t> VendorHelper(ErrorGenerator const& error)
|
|
{
|
|
return [error](std::nullptr_t& /*out*/, Json::Value const* value,
|
|
cmJSONState* state) -> bool {
|
|
if (!value) {
|
|
return true;
|
|
}
|
|
|
|
if (!value->isObject()) {
|
|
error(value, state);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
}
|
|
|
|
bool PresetConditionHelper(
|
|
std::shared_ptr<cmCMakePresetsGraph::Condition>& out,
|
|
Json::Value const* value, cmJSONState* state)
|
|
{
|
|
std::unique_ptr<cmCMakePresetsGraph::Condition> ptr;
|
|
auto result = ConditionHelper(ptr, value, state);
|
|
out = std::move(ptr);
|
|
return result;
|
|
}
|
|
|
|
bool PresetVectorOneOrMoreStringHelper(std::vector<std::string>& out,
|
|
Json::Value const* value,
|
|
cmJSONState* state)
|
|
{
|
|
out.clear();
|
|
if (!value) {
|
|
return true;
|
|
}
|
|
|
|
if (value->isString()) {
|
|
out.push_back(value->asString());
|
|
return true;
|
|
}
|
|
|
|
return PresetVectorStringHelper(out, value, state);
|
|
}
|
|
|
|
bool EnvironmentMapHelper(
|
|
std::map<std::string, cm::optional<std::string>>& out,
|
|
Json::Value const* value, cmJSONState* state)
|
|
{
|
|
static auto const helper = JSONHelperBuilder::Map<cm::optional<std::string>>(
|
|
cmCMakePresetsErrors::INVALID_PRESET, EnvironmentHelper);
|
|
|
|
return helper(out, value, state);
|
|
}
|
|
|
|
cmJSONHelper<std::nullptr_t> SchemaHelper()
|
|
{
|
|
return [](std::nullptr_t&, Json::Value const*, cmJSONState*) -> bool {
|
|
return true;
|
|
};
|
|
}
|
|
}
|
|
|
|
bool cmCMakePresetsGraph::ReadJSONFile(std::string const& filename,
|
|
RootType rootType,
|
|
ReadReason readReason,
|
|
std::vector<File*>& inProgressFiles,
|
|
File*& file, std::string& errMsg)
|
|
{
|
|
bool result;
|
|
|
|
for (auto const& f : this->Files) {
|
|
if (cmSystemTools::SameFile(filename, f->Filename)) {
|
|
file = f.get();
|
|
auto fileIt =
|
|
std::find(inProgressFiles.begin(), inProgressFiles.end(), file);
|
|
if (fileIt != inProgressFiles.end()) {
|
|
cmCMakePresetsErrors::CYCLIC_INCLUDE(filename, &this->parseState);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
Json::Value root;
|
|
this->parseState = cmJSONState(filename, &root);
|
|
if (!this->parseState.errors.empty()) {
|
|
return false;
|
|
}
|
|
|
|
int v = 0;
|
|
if ((result = RootVersionHelper(v, &root, &parseState)) != true) {
|
|
return result;
|
|
}
|
|
|
|
// Support for build and test presets added in version 2.
|
|
if (v < 2) {
|
|
if (root.isMember("buildPresets")) {
|
|
cmCMakePresetsErrors::BUILD_TEST_PRESETS_UNSUPPORTED(
|
|
&root["buildPresets"], &this->parseState);
|
|
return false;
|
|
}
|
|
if (root.isMember("testPresets")) {
|
|
cmCMakePresetsErrors::BUILD_TEST_PRESETS_UNSUPPORTED(
|
|
&root["testPresets"], &this->parseState);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Support for package presets added in version 6.
|
|
if (v < 6 && root.isMember("packagePresets")) {
|
|
cmCMakePresetsErrors::PACKAGE_PRESETS_UNSUPPORTED(&root["packagePresets"],
|
|
&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for workflow presets added in version 6.
|
|
if (v < 6 && root.isMember("workflowPresets")) {
|
|
cmCMakePresetsErrors::WORKFLOW_PRESETS_UNSUPPORTED(
|
|
&root["workflowPresets"], &this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for include added in version 4.
|
|
if (v < 4 && root.isMember("include")) {
|
|
cmCMakePresetsErrors::INCLUDE_UNSUPPORTED(&root["include"],
|
|
&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for $schema added in version 8.
|
|
if (v < 8 && root.isMember("$schema")) {
|
|
cmCMakePresetsErrors::SCHEMA_UNSUPPORTED(&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for $comment added in version 10.
|
|
this->parseState.allowComments = (v >= 10);
|
|
|
|
RootPresets presets;
|
|
if ((result = RootPresetsHelper(presets, &root, &parseState)) != true) {
|
|
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) {
|
|
ErrorGenerator error = cmCMakePresetsErrors::UNRECOGNIZED_CMAKE_VERSION(
|
|
"major", currentMajor, required.Major);
|
|
error(&root["cmakeMinimumRequired"]["major"], &this->parseState);
|
|
return false;
|
|
}
|
|
if (required.Major == currentMajor) {
|
|
if (required.Minor > currentMinor) {
|
|
ErrorGenerator error = cmCMakePresetsErrors::UNRECOGNIZED_CMAKE_VERSION(
|
|
"minor", currentMinor, required.Minor);
|
|
error(&root["cmakeMinimumRequired"]["minor"], &this->parseState);
|
|
return false;
|
|
}
|
|
if (required.Minor == currentMinor && required.Patch > currentPatch) {
|
|
ErrorGenerator error = cmCMakePresetsErrors::UNRECOGNIZED_CMAKE_VERSION(
|
|
"patch", currentPatch, required.Patch);
|
|
error(&root["cmakeMinimumRequired"]["patch"], &this->parseState);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
auto filePtr = cm::make_unique<File>();
|
|
file = filePtr.get();
|
|
this->Files.emplace_back(std::move(filePtr));
|
|
inProgressFiles.emplace_back(file);
|
|
file->Filename = filename;
|
|
file->Version = v;
|
|
file->ReachableFiles.insert(file);
|
|
|
|
for (auto& preset : presets.ConfigurePresets) {
|
|
preset.OriginFile = file;
|
|
if (preset.Name.empty()) {
|
|
// No error, already handled by PresetNameHelper
|
|
return false;
|
|
}
|
|
|
|
if (!ExpandImmediateMacros<ConfigurePreset>(preset)) {
|
|
cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name,
|
|
&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
PresetPair<ConfigurePreset> presetPair;
|
|
presetPair.Unexpanded = preset;
|
|
presetPair.Expanded = cm::nullopt;
|
|
if (!this->ConfigurePresets.emplace(preset.Name, presetPair).second) {
|
|
cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for installDir presets added in version 3.
|
|
if (v < 3 && !preset.InstallDir.empty()) {
|
|
cmCMakePresetsErrors::INSTALL_PREFIX_UNSUPPORTED(&root["installDir"],
|
|
&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for conditions added in version 3.
|
|
if (v < 3 && preset.ConditionEvaluator) {
|
|
cmCMakePresetsErrors::CONDITION_UNSUPPORTED(&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for toolchainFile presets added in version 3.
|
|
if (v < 3 && !preset.ToolchainFile.empty()) {
|
|
cmCMakePresetsErrors::TOOLCHAIN_FILE_UNSUPPORTED(&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for trace presets added in version 7.
|
|
if (v < 7 &&
|
|
(preset.TraceMode.has_value() || preset.TraceFormat.has_value() ||
|
|
!preset.TraceRedirect.empty() || !preset.TraceSource.empty())) {
|
|
cmCMakePresetsErrors::TRACE_UNSUPPORTED(&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for graphviz argument added in version 10.
|
|
if (v < 10 && !preset.GraphVizFile.empty()) {
|
|
cmCMakePresetsErrors::GRAPHVIZ_FILE_UNSUPPORTED(&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
this->ConfigurePresetOrder.push_back(preset.Name);
|
|
}
|
|
|
|
for (auto& preset : presets.BuildPresets) {
|
|
preset.OriginFile = file;
|
|
if (preset.Name.empty()) {
|
|
// No error, already handled by PresetNameHelper
|
|
return false;
|
|
}
|
|
|
|
if (!ExpandImmediateMacros<BuildPreset>(preset)) {
|
|
cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name,
|
|
&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
PresetPair<BuildPreset> presetPair;
|
|
presetPair.Unexpanded = preset;
|
|
presetPair.Expanded = cm::nullopt;
|
|
if (!this->BuildPresets.emplace(preset.Name, presetPair).second) {
|
|
cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for conditions added in version 3.
|
|
if (v < 3 && preset.ConditionEvaluator) {
|
|
cmCMakePresetsErrors::CONDITION_UNSUPPORTED(&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
this->BuildPresetOrder.push_back(preset.Name);
|
|
}
|
|
|
|
for (auto& preset : presets.TestPresets) {
|
|
preset.OriginFile = file;
|
|
if (preset.Name.empty()) {
|
|
// No error, already handled by PresetNameHelper
|
|
return false;
|
|
}
|
|
|
|
if (!ExpandImmediateMacros<TestPreset>(preset)) {
|
|
cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name,
|
|
&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
PresetPair<TestPreset> presetPair;
|
|
presetPair.Unexpanded = preset;
|
|
presetPair.Expanded = cm::nullopt;
|
|
if (!this->TestPresets.emplace(preset.Name, presetPair).second) {
|
|
cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for conditions added in version 3.
|
|
if (v < 3 && preset.ConditionEvaluator) {
|
|
cmCMakePresetsErrors::CONDITION_UNSUPPORTED(&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for TestOutputTruncation added in version 5.
|
|
if (v < 5 && preset.Output && preset.Output->TestOutputTruncation) {
|
|
cmCMakePresetsErrors::TEST_OUTPUT_TRUNCATION_UNSUPPORTED(
|
|
&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for outputJUnitFile added in version 6.
|
|
if (v < 6 && preset.Output && !preset.Output->OutputJUnitFile.empty()) {
|
|
cmCMakePresetsErrors::CTEST_JUNIT_UNSUPPORTED(&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for processor-count-based jobs added in version 11.
|
|
if (v < 11 && preset.Execution && preset.Execution->Jobs.has_value() &&
|
|
!preset.Execution->Jobs->has_value()) {
|
|
cmCMakePresetsErrors::JOBS_PROC_UNSUPPORTED(&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
this->TestPresetOrder.push_back(preset.Name);
|
|
}
|
|
|
|
for (auto& preset : presets.PackagePresets) {
|
|
preset.OriginFile = file;
|
|
if (preset.Name.empty()) {
|
|
// No error, already handled by PresetNameHelper
|
|
return false;
|
|
}
|
|
|
|
if (!ExpandImmediateMacros<PackagePreset>(preset)) {
|
|
cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name,
|
|
&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
PresetPair<PackagePreset> presetPair;
|
|
presetPair.Unexpanded = preset;
|
|
presetPair.Expanded = cm::nullopt;
|
|
if (!this->PackagePresets.emplace(preset.Name, presetPair).second) {
|
|
cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for conditions added in version 3, but this requires version 5
|
|
// already, so no action needed.
|
|
|
|
this->PackagePresetOrder.push_back(preset.Name);
|
|
}
|
|
|
|
for (auto& preset : presets.WorkflowPresets) {
|
|
preset.OriginFile = file;
|
|
if (preset.Name.empty()) {
|
|
// No error, already handled by PresetNameHelper
|
|
return false;
|
|
}
|
|
|
|
if (!ExpandImmediateMacros<WorkflowPreset>(preset)) {
|
|
cmCMakePresetsErrors::INVALID_MACRO_EXPANSION(preset.Name,
|
|
&this->parseState);
|
|
return false;
|
|
}
|
|
|
|
PresetPair<WorkflowPreset> presetPair;
|
|
presetPair.Unexpanded = preset;
|
|
presetPair.Expanded = cm::nullopt;
|
|
if (!this->WorkflowPresets.emplace(preset.Name, presetPair).second) {
|
|
cmCMakePresetsErrors::DUPLICATE_PRESETS(preset.Name, &this->parseState);
|
|
return false;
|
|
}
|
|
|
|
// Support for conditions added in version 3, but this requires version 6
|
|
// already, so no action needed.
|
|
|
|
this->WorkflowPresetOrder.push_back(preset.Name);
|
|
}
|
|
|
|
auto const includeFile = [this, &inProgressFiles,
|
|
file](std::string const& include,
|
|
RootType rootType2, ReadReason readReason2,
|
|
std::string& FailureMessage) -> bool {
|
|
bool r;
|
|
File* includedFile;
|
|
if ((r =
|
|
this->ReadJSONFile(include, rootType2, readReason2, inProgressFiles,
|
|
includedFile, FailureMessage)) != true) {
|
|
return r;
|
|
}
|
|
|
|
file->ReachableFiles.insert(includedFile->ReachableFiles.begin(),
|
|
includedFile->ReachableFiles.end());
|
|
return true;
|
|
};
|
|
|
|
MacroExpanderVector macroExpanders{};
|
|
|
|
if (v >= 9) {
|
|
macroExpanders.push_back(
|
|
cm::make_unique<BaseMacroExpander>(*this, filename));
|
|
}
|
|
macroExpanders.push_back(cm::make_unique<EnvironmentMacroExpander>());
|
|
|
|
for (Json::ArrayIndex i = 0; i < presets.Include.size(); ++i) {
|
|
auto include = presets.Include[i];
|
|
|
|
// Support for macro expansion in includes added in version 7
|
|
if (v >= 7) {
|
|
if (ExpandMacros(include, macroExpanders, v) != ExpandMacroResult::Ok) {
|
|
cmCMakePresetsErrors::INVALID_INCLUDE(&root["include"][i],
|
|
&this->parseState);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!cmSystemTools::FileIsFullPath(include)) {
|
|
auto directory = cmSystemTools::GetFilenamePath(filename);
|
|
include = cmStrCat(directory, '/', include);
|
|
}
|
|
|
|
if ((result = includeFile(include, rootType, ReadReason::Included,
|
|
errMsg)) != true) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (rootType == RootType::User && readReason == ReadReason::Root) {
|
|
auto cmakePresetsFilename = GetFilename(this->SourceDir);
|
|
if (cmSystemTools::FileExists(cmakePresetsFilename)) {
|
|
if ((result = includeFile(cmakePresetsFilename, RootType::Project,
|
|
ReadReason::Root, errMsg)) != true) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
inProgressFiles.pop_back();
|
|
return true;
|
|
}
|