diff options
48 files changed, 761 insertions, 269 deletions
diff --git a/.gitlab/ci/configure_fedora31_common.cmake b/.gitlab/ci/configure_fedora31_common.cmake index dc068d5..c343833 100644 --- a/.gitlab/ci/configure_fedora31_common.cmake +++ b/.gitlab/ci/configure_fedora31_common.cmake @@ -1,4 +1,5 @@ set(BUILD_CursesDialog ON CACHE BOOL "") set(BUILD_QtDialog ON CACHE BOOL "") +set(CMake_TEST_JSON_SCHEMA ON CACHE BOOL "") include("${CMAKE_CURRENT_LIST_DIR}/configure_common.cmake") diff --git a/.gitlab/ci/docker/fedora31/install_deps.sh b/.gitlab/ci/docker/fedora31/install_deps.sh index 8de213a..fa57f61 100755 --- a/.gitlab/ci/docker/fedora31/install_deps.sh +++ b/.gitlab/ci/docker/fedora31/install_deps.sh @@ -65,6 +65,7 @@ dnf install --setopt=install_weak_deps=False -y \ pypy3 pypy3-devel \ python2 python2-devel python2-numpy \ python3 python3-devel python3-numpy \ + python3-jsmin python3-jsonschema \ ruby rubygems ruby-devel \ SDL-devel \ sqlite-devel \ diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml index 217442f..315552b 100644 --- a/.gitlab/os-linux.yml +++ b/.gitlab/os-linux.yml @@ -49,7 +49,7 @@ ### Fedora .fedora31: - image: "kitware/cmake:ci-fedora31-x86_64-2020-10-03" + image: "kitware/cmake:ci-fedora31-x86_64-2020-10-20" variables: GIT_CLONE_PATH: "$CI_BUILDS_DIR/cmake ci/long file name for testing purposes" diff --git a/Help/guide/ide-integration/index.rst b/Help/guide/ide-integration/index.rst index 46ad4ef..addf932 100644 --- a/Help/guide/ide-integration/index.rst +++ b/Help/guide/ide-integration/index.rst @@ -69,6 +69,9 @@ While reading, parsing, and evaluating the contents of ``CMakePresets.json`` is straightforward, it is not trivial. In addition to the documentation, IDE vendors may also wish to refer to the CMake source code and test cases for a better understanding of how to implement the format. +:download:`This file </manual/presets/schema.json>` provides a machine-readable +JSON schema for the ``CMakePresets.json`` format that IDE vendors may find +useful for validation and providing editing assistance. Configuring =========== diff --git a/Help/manual/cmake-presets.7.rst b/Help/manual/cmake-presets.7.rst index dd6337d..7040ad5 100644 --- a/Help/manual/cmake-presets.7.rst +++ b/Help/manual/cmake-presets.7.rst @@ -176,9 +176,10 @@ Format ``cacheVariables`` An optional map of cache variables. The key is the variable name (which - may not be an empty string), and the value is either ``null``, a string - representing the value of the variable (which supports macro expansion), - or an object with the following fields: + may not be an empty string), and the value is either ``null``, a boolean + (which is equivalent to a value of ``"TRUE"`` or ``"FALSE"`` and a type + of ``BOOL``), a string representing the value of the variable (which + supports macro expansion), or an object with the following fields: ``type`` @@ -186,7 +187,8 @@ Format ``value`` - A required string representing the value of the variable. This field + A required string or boolean representing the value of the variable. + A boolean is equivalent to ``"TRUE"`` or ``"FALSE"``. This field supports macro expansion. Cache variables are inherited through the ``inherits`` field, and the @@ -353,3 +355,9 @@ Format ``<macro-name>`` with a very short (preferably <= 4 characters) vendor identifier prefix, followed by a ``.``, followed by the macro name. For example, the Example IDE could have ``$vendor{xide.ideInstallDir}``. + +Schema +====== + +:download:`This file </manual/presets/schema.json>` provides a machine-readable +JSON schema for the ``CMakePresets.json`` format. diff --git a/Help/manual/presets/schema.json b/Help/manual/presets/schema.json new file mode 100644 index 0000000..ba4568f --- /dev/null +++ b/Help/manual/presets/schema.json @@ -0,0 +1,254 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "description": "The presets specify the generator and the build directory, and optionally a list of variables and other arguments to pass to CMake.", + "properties": { + "version": { + "type": "integer", + "description": "A required integer representing the version of the JSON schema. Currently, the only supported version is 1.", + "minimum": 1, + "maximum": 1 + }, + "cmakeMinimumRequired": { + "type": "object", + "description": "An optional object representing the minimum version of CMake needed to build this project.", + "properties": { + "major": { + "type": "integer", + "description": "An optional integer representing the major version." + }, + "minor": { + "type": "integer", + "description": "An optional integer representing the minor version." + }, + "patch": { + "type": "integer", + "description": "An optional integer representing the patch version." + } + }, + "additionalProperties": false + }, + "vendor": { + "type": "object", + "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, the keys should be a vendor-specific domain name followed by a /-separated path. For example, the Example IDE 1.0 could use example.com/ExampleIDE/1.0. The value of each field can be anything desired by the vendor, though will typically be a map.", + "properties": {} + }, + "configurePresets": { + "type": "array", + "description": "An optional array of configure preset objects.", + "items": { + "type": "object", + "description": "A configure preset object.", + "properties": { + "name": { + "type": "string", + "description": "A required string representing the machine-friendly name of the preset. This identifier is used in the --preset argument. There must not be two presets in the union of CMakePresets.json and CMakeUserPresets.json in the same directory with the same name.", + "minLength": 1 + }, + "hidden": { + "type": "boolean", + "description": "An optional boolean specifying whether or not a preset should be hidden. If a preset is hidden, it cannot be used in the --preset= argument, will not show up in the CMake GUI, and does not have to have a valid generator or binaryDir, even from inheritance. hidden presets are intended to be used as a base for other presets to inherit via the inherits field." + }, + "inherits": { + "anyOf": [ + { + "type": "string", + "description": "An optional string representing the name of the preset to inherit from.", + "minLength": 1 + }, + { + "type": "array", + "description": "An optional array of strings representing the names of presets to inherit from. The preset will inherit all of the fields from the inherits presets by default (except name, hidden, inherits, description, and longDescription), but can override them as desired. If multiple inherits presets provide conflicting values for the same field, the earlier preset in the inherits list will be preferred. Presets in CMakePresets.json may not inherit from presets in CMakeUserPresets.json.", + "items": { + "type": "string", + "description": "An optional string representing the name of the preset to inherit from.", + "minLength": 1 + } + } + ] + }, + "vendor": { + "type": "object", + "description": "An optional map containing vendor-specific information. CMake does not interpret the contents of this field except to verify that it is a map if it does exist. However, it should follow the same conventions as the root-level vendor field. If vendors use their own per-preset vendor field, they should implement inheritance in a sensible manner when appropriate.", + "properties": {} + }, + "displayName": { + "type": "string", + "description": "An optional string with a human-friendly name of the preset." + }, + "description": { + "type": "string", + "description": "An optional string with a human-friendly description of the preset." + }, + "generator": { + "type": "string", + "description": "An optional string representing the generator to use for the preset. If generator is not specified, it must be inherited from the inherits preset (unless this preset is hidden). Note that for Visual Studio generators, unlike in the command line -G argument, you cannot include the platform name in the generator name. Use the architecture field instead." + }, + "architecture": { + "type": "string", + "description": "An optional string representing the platform name to use for Visual Studio generators." + }, + "toolset": { + "type": "string", + "description": "An optional string representing the toolset name to use for Visual Studio generators." + }, + "cmakeGeneratorConfig": { + "type": "string", + "description": "An optional string telling CMake how to handle the architecture and toolset fields. Valid values are: \"default\": Set the platform and toolset using the architecture and toolset fields respectively. On non-Visual Studio generators, this will result in an error if architecture or toolset are set. \"ignore\": Do not set the platform or toolset at all, even on Visual Studio generators. This is useful if, for example, a preset uses the Ninja generator, and an IDE knows how to set up the Visual C++ environment from the architecture and toolset fields. In that case, CMake will ignore architecture and toolset, but the IDE can use them to set up the environment before invoking CMake.", + "enum": [ + "default", + "ignore" + ] + }, + "binaryDir": { + "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)." + }, + "cmakeExecutable": { + "type": "string", + "description": "An optional string representing the path to the CMake executable to use for this preset. This is reserved for use by IDEs, and is not used by CMake itself. IDEs that use this field should expand any macros in it." + }, + "cacheVariables": { + "type": "object", + "description": "An optional map of cache variables. The key is the variable name (which must not be an empty string). Cache variables are inherited through the inherits field, and the preset's variables will be the union of its own cacheVariables and the cacheVariables from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied.", + "properties": {}, + "additionalProperties": { + "anyOf": [ + { + "type": "null", + "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." + }, + { + "type": "boolean", + "description": "A boolean representing the value of the variable. Equivalent to \"TRUE\" or \"FALSE\"." + }, + { + "type": "string", + "description": "A string representing the value of the variable (which supports macro expansion)." + }, + { + "type": "object", + "description": "An object representing the type and value of the variable.", + "properties": { + "type": { + "type": "string", + "description": "An optional string representing the type of the variable. It should be BOOL, FILEPATH, PATH, STRING, or INTERNAL." + }, + "value": { + "anyOf": [ + { + "type": "boolean", + "description": "A required boolean representing the value of the variable. Equivalent to \"TRUE\" or \"FALSE\"." + }, + { + "type": "string", + "description": "A required string representing the value of the variable. This field supports macro expansion." + } + ] + } + }, + "required": [ + "value" + ], + "additionalProperties": false + } + ] + }, + "propertyNames": { + "pattern": "^.+$" + } + }, + "environment": { + "type": "object", + "description": "An optional map of environment variables. The key is the variable name (which must not be an empty string). Each variable is set regardless of whether or not a value was given to it by the process's environment. This field supports macro expansion, and environment variables in this map may reference each other, and may be listed in any order, as long as such references do not cause a cycle (for example,if ENV_1 is $env{ENV_2}, ENV_2 may not be $env{ENV_1}.) Environment variables are inherited through the inherits field, and the preset's environment will be the union of its own environment and the environment from all its parents. If multiple presets in this union define the same variable, the standard rules of inherits are applied. Setting a variable to null causes it to not be set, even if a value was inherited from another preset.", + "properties": {}, + "additionalProperties": { + "anyOf": [ + { + "type": "null", + "description": "Setting a variable to null causes it to not be set, even if a value was inherited from another preset." + }, + { + "type": "string", + "description": "A string representing the value of the variable." + } + ] + }, + "propertyNames": { + "pattern": "^.+$" + } + }, + "warnings": { + "type": "object", + "description": "An optional object specifying warnings.", + "properties": { + "dev": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Wdev or -Wno-dev on the command line. This may not be set to false if errors.dev is set to true." + }, + "deprecated": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Wdeprecated or -Wno-deprecated on the command line. This may not be set to false if errors.deprecated is set to true." + }, + "uninitialized": { + "type": "boolean", + "description": "An optional boolean. Setting this to true is equivalent to passing --warn-uninitialized on the command line." + }, + "unusedCli": { + "type": "boolean", + "description": "An optional boolean. Setting this to false is equivalent to passing --no-warn-unused-cli on the command line." + }, + "systemVars": { + "type": "boolean", + "description": "An optional boolean. Setting this to true is equivalent to passing --check-system-vars on the command line." + } + }, + "additionalProperties": false + }, + "errors": { + "type": "object", + "description": "An optional object specifying errors.", + "properties": { + "dev": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Werror=dev or -Wno-error=dev on the command line. This may not be set to true if warnings.dev is set to false." + }, + "deprecated": { + "type": "boolean", + "description": "An optional boolean. Equivalent to passing -Werror=deprecated or -Wno-error=deprecated on the command line. This may not be set to true if warnings.deprecated is set to false." + } + }, + "additionalProperties": false + }, + "debug": { + "type": "object", + "description": "An optional object specifying debug options.", + "properties": { + "output": { + "type": "boolean", + "description": "An optional boolean. Setting this to true is equivalent to passing --debug-output on the command line." + }, + "tryCompile": { + "type": "boolean", + "description": "An optional boolean. Setting this to true is equivalent to passing --debug-trycompile on the command line." + }, + "find": { + "type": "boolean", + "description": "An optional boolean. Setting this to true is equivalent to passing --debug-find on the command line." + } + }, + "additionalProperties": false + } + }, + "required": [ + "name" + ], + "additionalProperties": false + } + } + }, + "required": [ + "version" + ], + "additionalProperties": false +} diff --git a/Source/CMakeVersion.cmake b/Source/CMakeVersion.cmake index 41c84af..67d986a 100644 --- a/Source/CMakeVersion.cmake +++ b/Source/CMakeVersion.cmake @@ -1,7 +1,7 @@ # CMake version number components. set(CMake_VERSION_MAJOR 3) set(CMake_VERSION_MINOR 19) -set(CMake_VERSION_PATCH 20201020) +set(CMake_VERSION_PATCH 20201022) #set(CMake_VERSION_RC 0) set(CMake_VERSION_IS_DIRTY 0) diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx index 9017a63..3789e93 100644 --- a/Source/QtDialog/QCMake.cxx +++ b/Source/QtDialog/QCMake.cxx @@ -157,8 +157,8 @@ void QCMake::setPreset(const QString& name, bool setBinary) if (!name.isNull()) { std::string presetName(name.toLocal8Bit()); - auto const& preset = this->CMakePresetsFile.Presets[presetName]; - auto expandedPreset = this->CMakePresetsFile.ExpandMacros(preset); + auto const& expandedPreset = + this->CMakePresetsFile.Presets[presetName].Expanded; if (expandedPreset) { if (setBinary) { QString binaryDir = @@ -420,8 +420,7 @@ QCMakePropertyList QCMake::properties() const if (!this->PresetName.isNull()) { std::string presetName(this->PresetName.toLocal8Bit()); - auto p = this->CMakePresetsFile.ExpandMacros( - this->CMakePresetsFile.Presets.at(presetName)); + auto const& p = this->CMakePresetsFile.Presets.at(presetName).Expanded; if (p) { for (auto const& v : p->CacheVariables) { if (!v.second) { @@ -537,7 +536,8 @@ void QCMake::loadPresets() QVector<QCMakePreset> presets; for (auto const& name : this->CMakePresetsFile.PresetOrder) { - auto const& p = this->CMakePresetsFile.Presets[name]; + auto const& it = this->CMakePresetsFile.Presets[name]; + auto const& p = it.Unexpanded; if (p.Hidden) { continue; } @@ -554,12 +554,12 @@ void QCMake::loadPresets() preset.toolset = std::move(QString::fromLocal8Bit(p.Toolset.data())); preset.setGenConfig = !p.GeneratorConfig || p.GeneratorConfig == cmCMakePresetsFile::CMakeGeneratorConfig::Default; - preset.enabled = std::find_if(this->AvailableGenerators.begin(), - this->AvailableGenerators.end(), - [&p](const cmake::GeneratorInfo& g) { - return g.name == p.Generator; - }) != this->AvailableGenerators.end() && - this->CMakePresetsFile.ExpandMacros(p); + preset.enabled = it.Expanded && + std::find_if(this->AvailableGenerators.begin(), + this->AvailableGenerators.end(), + [&p](const cmake::GeneratorInfo& g) { + return g.name == p.Generator; + }) != this->AvailableGenerators.end(); presets.push_back(preset); } emit this->presetsChanged(presets); diff --git a/Source/cmCMakePresetsFile.cxx b/Source/cmCMakePresetsFile.cxx index 90a0faa..b3bb6df 100644 --- a/Source/cmCMakePresetsFile.cxx +++ b/Source/cmCMakePresetsFile.cxx @@ -29,6 +29,7 @@ enum class CycleStatus using ReadFileResult = cmCMakePresetsFile::ReadFileResult; using CacheVariable = cmCMakePresetsFile::CacheVariable; using UnexpandedPreset = cmCMakePresetsFile::UnexpandedPreset; +using ExpandedPreset = cmCMakePresetsFile::ExpandedPreset; using CMakeGeneratorConfig = cmCMakePresetsFile::CMakeGeneratorConfig; constexpr int MIN_VERSION = 1; @@ -77,15 +78,37 @@ auto const RootVersionHelper = 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, VariableStringHelper); + .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=*/"", @@ -290,9 +313,9 @@ void InheritOptionalBool(cm::optional<bool>& child, * that each preset has the required fields, either directly or through * inheritance. */ -ReadFileResult VisitPreset(std::map<std::string, UnexpandedPreset>& presets, - UnexpandedPreset& preset, - std::map<std::string, CycleStatus> cycleStatus) +ReadFileResult VisitPreset( + std::map<std::string, cmCMakePresetsFile::PresetPair>& presets, + UnexpandedPreset& preset, std::map<std::string, CycleStatus> cycleStatus) { switch (cycleStatus[preset.Name]) { case CycleStatus::InProgress: @@ -318,35 +341,38 @@ ReadFileResult VisitPreset(std::map<std::string, UnexpandedPreset>& presets, return ReadFileResult::INVALID_PRESET; } - if (!preset.User && parent->second.User) { + if (!preset.User && parent->second.Unexpanded.User) { return ReadFileResult::USER_PRESET_INHERITANCE; } - auto result = VisitPreset(presets, parent->second, cycleStatus); + auto result = VisitPreset(presets, parent->second.Unexpanded, cycleStatus); if (result != ReadFileResult::READ_OK) { return result; } - InheritString(preset.Generator, parent->second.Generator); - InheritString(preset.Architecture, parent->second.Architecture); - InheritString(preset.Toolset, parent->second.Toolset); + InheritString(preset.Generator, parent->second.Unexpanded.Generator); + InheritString(preset.Architecture, parent->second.Unexpanded.Architecture); + InheritString(preset.Toolset, parent->second.Unexpanded.Toolset); if (!preset.GeneratorConfig) { - preset.GeneratorConfig = parent->second.GeneratorConfig; + preset.GeneratorConfig = parent->second.Unexpanded.GeneratorConfig; } - InheritString(preset.BinaryDir, parent->second.BinaryDir); - InheritOptionalBool(preset.WarnDev, parent->second.WarnDev); - InheritOptionalBool(preset.ErrorDev, parent->second.ErrorDev); - InheritOptionalBool(preset.WarnDeprecated, parent->second.WarnDeprecated); + InheritString(preset.BinaryDir, parent->second.Unexpanded.BinaryDir); + InheritOptionalBool(preset.WarnDev, parent->second.Unexpanded.WarnDev); + InheritOptionalBool(preset.ErrorDev, parent->second.Unexpanded.ErrorDev); + InheritOptionalBool(preset.WarnDeprecated, + parent->second.Unexpanded.WarnDeprecated); InheritOptionalBool(preset.ErrorDeprecated, - parent->second.ErrorDeprecated); + parent->second.Unexpanded.ErrorDeprecated); InheritOptionalBool(preset.WarnUninitialized, - parent->second.WarnUninitialized); - InheritOptionalBool(preset.WarnUnusedCli, parent->second.WarnUnusedCli); - InheritOptionalBool(preset.WarnSystemVars, parent->second.WarnSystemVars); - for (auto const& v : parent->second.CacheVariables) { + parent->second.Unexpanded.WarnUninitialized); + InheritOptionalBool(preset.WarnUnusedCli, + parent->second.Unexpanded.WarnUnusedCli); + InheritOptionalBool(preset.WarnSystemVars, + parent->second.Unexpanded.WarnSystemVars); + for (auto const& v : parent->second.Unexpanded.CacheVariables) { preset.CacheVariables.insert(v); } - for (auto const& v : parent->second.Environment) { + for (auto const& v : parent->second.Unexpanded.Environment) { preset.Environment.insert(v); } } @@ -371,7 +397,7 @@ ReadFileResult VisitPreset(std::map<std::string, UnexpandedPreset>& presets, } ReadFileResult ComputePresetInheritance( - std::map<std::string, UnexpandedPreset>& presets) + std::map<std::string, cmCMakePresetsFile::PresetPair>& presets) { std::map<std::string, CycleStatus> cycleStatus; for (auto const& it : presets) { @@ -379,7 +405,7 @@ ReadFileResult ComputePresetInheritance( } for (auto& it : presets) { - auto result = VisitPreset(presets, it.second, cycleStatus); + auto result = VisitPreset(presets, it.second.Unexpanded, cycleStatus); if (result != ReadFileResult::READ_OK) { return result; } @@ -415,44 +441,111 @@ bool IsValidMacroNamespace(const std::string& str) return false; } -bool VisitEnv(const cmCMakePresetsFile& file, - cmCMakePresetsFile::ExpandedPreset& preset, - std::map<std::string, CycleStatus>& envCycles, - std::string& value, CycleStatus& status); +enum class ExpandMacroResult +{ + Ok, + Ignore, + Error, +}; + +ExpandMacroResult VisitEnv(const cmCMakePresetsFile& file, + cmCMakePresetsFile::ExpandedPreset& preset, + std::map<std::string, CycleStatus>& envCycles, + std::string& value, CycleStatus& status); +ExpandMacroResult ExpandMacros(const cmCMakePresetsFile& file, + cmCMakePresetsFile::ExpandedPreset& preset, + std::map<std::string, CycleStatus>& envCycles, + std::string& out); +ExpandMacroResult ExpandMacro(const cmCMakePresetsFile& file, + cmCMakePresetsFile::ExpandedPreset& preset, + std::map<std::string, CycleStatus>& envCycles, + std::string& out, + const std::string& macroNamespace, + const std::string& macroName); + bool ExpandMacros(const cmCMakePresetsFile& file, - cmCMakePresetsFile::ExpandedPreset& preset, - std::map<std::string, CycleStatus>& envCycles, - std::string& out); -bool ExpandMacro(const cmCMakePresetsFile& file, - cmCMakePresetsFile::ExpandedPreset& preset, - std::map<std::string, CycleStatus>& envCycles, - std::string& out, const std::string& macroNamespace, - const std::string& macroName); - -bool VisitEnv(const cmCMakePresetsFile& file, - cmCMakePresetsFile::ExpandedPreset& preset, - std::map<std::string, CycleStatus>& envCycles, - std::string& value, CycleStatus& status) + const UnexpandedPreset& preset, + cm::optional<ExpandedPreset>& out) +{ + out = preset; + + std::map<std::string, CycleStatus> envCycles; + for (auto const& v : out->Environment) { + envCycles[v.first] = CycleStatus::Unvisited; + } + + for (auto& v : out->Environment) { + if (v.second) { + switch (VisitEnv(file, *out, envCycles, *v.second, envCycles[v.first])) { + case ExpandMacroResult::Error: + return false; + case ExpandMacroResult::Ignore: + out.reset(); + return true; + case ExpandMacroResult::Ok: + break; + } + } + } + + std::string binaryDir = preset.BinaryDir; + switch (ExpandMacros(file, *out, envCycles, binaryDir)) { + case ExpandMacroResult::Error: + return false; + case ExpandMacroResult::Ignore: + out.reset(); + return true; + case ExpandMacroResult::Ok: + break; + } + if (!cmSystemTools::FileIsFullPath(binaryDir)) { + binaryDir = cmStrCat(file.SourceDir, '/', binaryDir); + } + out->BinaryDir = cmSystemTools::CollapseFullPath(binaryDir); + cmSystemTools::ConvertToUnixSlashes(out->BinaryDir); + + for (auto& variable : out->CacheVariables) { + if (variable.second) { + switch (ExpandMacros(file, *out, envCycles, variable.second->Value)) { + case ExpandMacroResult::Error: + return false; + case ExpandMacroResult::Ignore: + out.reset(); + return true; + case ExpandMacroResult::Ok: + break; + } + } + } + + return true; +} + +ExpandMacroResult VisitEnv(const cmCMakePresetsFile& file, + cmCMakePresetsFile::ExpandedPreset& preset, + std::map<std::string, CycleStatus>& envCycles, + std::string& value, CycleStatus& status) { if (status == CycleStatus::Verified) { - return true; + return ExpandMacroResult::Ok; } if (status == CycleStatus::InProgress) { - return false; + return ExpandMacroResult::Error; } status = CycleStatus::InProgress; - if (!ExpandMacros(file, preset, envCycles, value)) { - return false; + auto e = ExpandMacros(file, preset, envCycles, value); + if (e != ExpandMacroResult::Ok) { + return e; } status = CycleStatus::Verified; - return true; + return ExpandMacroResult::Ok; } -bool ExpandMacros(const cmCMakePresetsFile& file, - cmCMakePresetsFile::ExpandedPreset& preset, - std::map<std::string, CycleStatus>& envCycles, - std::string& out) +ExpandMacroResult ExpandMacros(const cmCMakePresetsFile& file, + cmCMakePresetsFile::ExpandedPreset& preset, + std::map<std::string, CycleStatus>& envCycles, + std::string& out) { std::string result; std::string macroNamespace; @@ -499,9 +592,10 @@ bool ExpandMacros(const cmCMakePresetsFile& file, case State::MacroName: if (c == '}') { - if (!ExpandMacro(file, preset, envCycles, result, macroNamespace, - macroName)) { - return false; + auto e = ExpandMacro(file, preset, envCycles, result, macroNamespace, + macroName); + if (e != ExpandMacroResult::Ok) { + return e; } macroNamespace.clear(); macroName.clear(); @@ -521,67 +615,72 @@ bool ExpandMacros(const cmCMakePresetsFile& file, result += macroNamespace; break; case State::MacroName: - return false; + return ExpandMacroResult::Error; } out = std::move(result); - return true; + return ExpandMacroResult::Ok; } -bool ExpandMacro(const cmCMakePresetsFile& file, - cmCMakePresetsFile::ExpandedPreset& preset, - std::map<std::string, CycleStatus>& envCycles, - std::string& out, const std::string& macroNamespace, - const std::string& macroName) +ExpandMacroResult ExpandMacro(const cmCMakePresetsFile& file, + cmCMakePresetsFile::ExpandedPreset& preset, + std::map<std::string, CycleStatus>& envCycles, + std::string& out, + const std::string& macroNamespace, + const std::string& macroName) { if (macroNamespace.empty()) { if (macroName == "sourceDir") { out += file.SourceDir; - return true; + return ExpandMacroResult::Ok; } if (macroName == "sourceParentDir") { out += cmSystemTools::GetParentDirectory(file.SourceDir); - return true; + return ExpandMacroResult::Ok; } if (macroName == "presetName") { out += preset.Name; - return true; + return ExpandMacroResult::Ok; } if (macroName == "generator") { out += preset.Generator; - return true; + return ExpandMacroResult::Ok; } if (macroName == "dollar") { out += '$'; - return true; + return ExpandMacroResult::Ok; } } if (macroNamespace == "env" && !macroName.empty()) { auto v = preset.Environment.find(macroName); if (v != preset.Environment.end() && v->second) { - if (!VisitEnv(file, preset, envCycles, *v->second, - envCycles[macroName])) { - return false; + auto e = + VisitEnv(file, preset, envCycles, *v->second, envCycles[macroName]); + if (e != ExpandMacroResult::Ok) { + return e; } out += *v->second; - return true; + return ExpandMacroResult::Ok; } } if (macroNamespace == "env" || macroNamespace == "penv") { if (macroName.empty()) { - return false; + return ExpandMacroResult::Error; } const char* value = std::getenv(macroName.c_str()); if (value) { out += value; } - return true; + return ExpandMacroResult::Ok; } - // "vendor" falls through to here - return false; + if (macroNamespace == "vendor") { + return ExpandMacroResult::Ignore; + } + + return ExpandMacroResult::Error; } } @@ -604,7 +703,7 @@ cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadProjectPresets( this->PresetOrder.clear(); std::vector<std::string> presetOrder; - std::map<std::string, UnexpandedPreset> presetMap; + std::map<std::string, PresetPair> presetMap; std::string filename = GetUserFilename(this->SourceDir); if (cmSystemTools::FileExists(filename)) { @@ -634,6 +733,12 @@ cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadProjectPresets( return result; } + for (auto& it : presetMap) { + if (!ExpandMacros(*this, it.second.Unexpanded, it.second.Expanded)) { + return ReadFileResult::INVALID_MACRO_EXPANSION; + } + } + this->PresetOrder = std::move(presetOrder); this->Presets = std::move(presetMap); return ReadFileResult::READ_OK; @@ -672,51 +777,16 @@ const char* cmCMakePresetsFile::ResultToString(ReadFileResult result) return "Cyclic preset inheritance"; case ReadFileResult::USER_PRESET_INHERITANCE: return "Project preset inherits from user preset"; - default: - return "Unknown error"; - } -} - -cm::optional<cmCMakePresetsFile::ExpandedPreset> -cmCMakePresetsFile::ExpandMacros(const UnexpandedPreset& preset) const -{ - ExpandedPreset retval{ preset }; - - std::map<std::string, CycleStatus> envCycles; - for (auto const& v : retval.Environment) { - envCycles[v.first] = CycleStatus::Unvisited; - } - - for (auto& v : retval.Environment) { - if (v.second && - !VisitEnv(*this, retval, envCycles, *v.second, envCycles[v.first])) { - return cm::nullopt; - } - } - - std::string binaryDir = preset.BinaryDir; - if (!::ExpandMacros(*this, retval, envCycles, binaryDir)) { - return cm::nullopt; - } - if (!cmSystemTools::FileIsFullPath(binaryDir)) { - binaryDir = cmStrCat(this->SourceDir, '/', binaryDir); - } - retval.BinaryDir = cmSystemTools::CollapseFullPath(binaryDir); - cmSystemTools::ConvertToUnixSlashes(retval.BinaryDir); - - for (auto& variable : retval.CacheVariables) { - if (variable.second && - !::ExpandMacros(*this, retval, envCycles, variable.second->Value)) { - return cm::nullopt; - } + case ReadFileResult::INVALID_MACRO_EXPANSION: + return "Invalid macro expansion"; } - return cm::make_optional(retval); + return "Unknown error"; } cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile( const std::string& filename, std::vector<std::string>& presetOrder, - std::map<std::string, UnexpandedPreset>& presetMap, bool user) + std::map<std::string, PresetPair>& presetMap, bool user) { cmsys::ifstream fin(filename.c_str()); if (!fin) { @@ -763,7 +833,7 @@ cmCMakePresetsFile::ReadFileResult cmCMakePresetsFile::ReadJSONFile( if (preset.Name.empty()) { return ReadFileResult::INVALID_PRESET; } - if (!presetMap.insert({ preset.Name, preset }).second) { + if (!presetMap.insert({ preset.Name, { preset, cm::nullopt } }).second) { return ReadFileResult::DUPLICATE_PRESETS; } presetOrder.push_back(preset.Name); diff --git a/Source/cmCMakePresetsFile.h b/Source/cmCMakePresetsFile.h index 70ec4c5..87797d7 100644 --- a/Source/cmCMakePresetsFile.h +++ b/Source/cmCMakePresetsFile.h @@ -102,8 +102,15 @@ public: } }; + class PresetPair + { + public: + UnexpandedPreset Unexpanded; + cm::optional<ExpandedPreset> Expanded; + }; + std::string SourceDir; - std::map<std::string, UnexpandedPreset> Presets; + std::map<std::string, PresetPair> Presets; std::vector<std::string> PresetOrder; enum class ReadFileResult @@ -123,6 +130,7 @@ public: DUPLICATE_PRESETS, CYCLIC_PRESET_INHERITANCE, USER_PRESET_INHERITANCE, + INVALID_MACRO_EXPANSION, }; static std::string GetFilename(const std::string& sourceDir); @@ -131,11 +139,9 @@ public: bool allowNoFiles = false); static const char* ResultToString(ReadFileResult result); - cm::optional<ExpandedPreset> ExpandMacros( - const UnexpandedPreset& preset) const; - private: - ReadFileResult ReadJSONFile( - const std::string& filename, std::vector<std::string>& presetOrder, - std::map<std::string, UnexpandedPreset>& presetMap, bool user); + ReadFileResult ReadJSONFile(const std::string& filename, + std::vector<std::string>& presetOrder, + std::map<std::string, PresetPair>& presetMap, + bool user); }; diff --git a/Source/cmFindCommon.cxx b/Source/cmFindCommon.cxx index dee91d7..7952336 100644 --- a/Source/cmFindCommon.cxx +++ b/Source/cmFindCommon.cxx @@ -182,7 +182,7 @@ void cmFindCommon::SelectDefaultSearchModes() { this->NoCMakeSystemPath, "CMAKE_FIND_USE_CMAKE_SYSTEM_PATH" } } }; - for (auto& path : search_paths) { + for (auto const& path : search_paths) { cmProp def = this->Makefile->GetDefinition(path.second); if (def) { path.first = !cmIsOn(*def); diff --git a/Source/cmGeneratorExpressionEvaluator.h b/Source/cmGeneratorExpressionEvaluator.h index 3e7737e..af2afd6 100644 --- a/Source/cmGeneratorExpressionEvaluator.h +++ b/Source/cmGeneratorExpressionEvaluator.h @@ -60,7 +60,7 @@ struct TextContent : public cmGeneratorExpressionEvaluator void Extend(size_t length) { this->Length += length; } - size_t GetLength() { return this->Length; } + size_t GetLength() const { return this->Length; } private: const char* Content; diff --git a/Source/cmGeneratorExpressionNode.cxx b/Source/cmGeneratorExpressionNode.cxx index 4ca7405..e40316e 100644 --- a/Source/cmGeneratorExpressionNode.cxx +++ b/Source/cmGeneratorExpressionNode.cxx @@ -677,7 +677,7 @@ struct CompilerIdNode : public cmGeneratorExpressionNode } static cmsys::RegularExpression compilerIdValidator("^[A-Za-z0-9_]*$"); - for (auto& param : parameters) { + for (auto const& param : parameters) { if (!compilerIdValidator.find(param)) { reportError(context, content->GetOriginalExpression(), @@ -805,7 +805,7 @@ struct PlatformIdNode : public cmGeneratorExpressionNode return parameters.front().empty() ? "1" : "0"; } - for (auto& param : parameters) { + for (auto const& param : parameters) { if (param == platformId) { return "1"; } @@ -901,7 +901,7 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode return std::string(); } context->HadContextSensitiveCondition = true; - for (auto& param : parameters) { + for (auto const& param : parameters) { if (context->Config.empty()) { if (param.empty()) { return "1"; @@ -927,7 +927,7 @@ static const struct ConfigurationTestNode : public cmGeneratorExpressionNode if (cmProp mapValue = context->CurrentTarget->GetProperty(mapProp)) { cmExpandList(cmSystemTools::UpperCase(*mapValue), mappedConfigs); - for (auto& param : parameters) { + for (auto const& param : parameters) { if (cm::contains(mappedConfigs, cmSystemTools::UpperCase(param))) { return "1"; } @@ -995,7 +995,7 @@ static const struct CompileLanguageNode : public cmGeneratorExpressionNode return context->Language; } - for (auto& param : parameters) { + for (auto const& param : parameters) { if (context->Language == param) { return "1"; } @@ -1101,7 +1101,7 @@ static const struct LinkLanguageNode : public cmGeneratorExpressionNode return context->Language; } - for (auto& param : parameters) { + for (auto const& param : parameters) { if (context->Language == param) { return "1"; } @@ -1129,7 +1129,7 @@ struct LinkerId } static cmsys::RegularExpression linkerIdValidator("^[A-Za-z0-9_]*$"); - for (auto& param : parameters) { + for (auto const& param : parameters) { if (!linkerIdValidator.find(param)) { reportError(context, content->GetOriginalExpression(), "Expression syntax not recognized."); diff --git a/Source/cmGeneratorTarget.cxx b/Source/cmGeneratorTarget.cxx index 3b47550..1ed82fd 100644 --- a/Source/cmGeneratorTarget.cxx +++ b/Source/cmGeneratorTarget.cxx @@ -254,7 +254,7 @@ EvaluatedTargetPropertyEntries EvaluateTargetPropertyEntries( { EvaluatedTargetPropertyEntries out; out.Entries.reserve(in.size()); - for (auto& entry : in) { + for (auto const& entry : in) { out.Entries.emplace_back(EvaluateTargetPropertyEntry( thisTarget, config, lang, dagChecker, *entry)); } @@ -332,7 +332,7 @@ cmGeneratorTarget::~cmGeneratorTarget() = default; const std::string& cmGeneratorTarget::GetSourcesProperty() const { std::vector<std::string> values; - for (auto& se : this->SourceEntries) { + for (auto const& se : this->SourceEntries) { values.push_back(se->GetInput()); } static std::string value; @@ -2504,7 +2504,7 @@ public: } } - bool GetHadLinkLanguageSensitiveCondition() + bool GetHadLinkLanguageSensitiveCondition() const { return HadLinkLanguageSensitiveCondition; } diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 1197db6..52f1d52 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -2601,7 +2601,7 @@ void cmGlobalGenerator::AddGlobalTarget_Test( } void cmGlobalGenerator::AddGlobalTarget_EditCache( - std::vector<GlobalTargetInfo>& targets) + std::vector<GlobalTargetInfo>& targets) const { const char* editCacheTargetName = this->GetEditCacheTargetName(); if (!editCacheTargetName) { @@ -2635,7 +2635,7 @@ void cmGlobalGenerator::AddGlobalTarget_EditCache( } void cmGlobalGenerator::AddGlobalTarget_RebuildCache( - std::vector<GlobalTargetInfo>& targets) + std::vector<GlobalTargetInfo>& targets) const { const char* rebuildCacheTargetName = this->GetRebuildCacheTargetName(); if (!rebuildCacheTargetName) { @@ -2758,7 +2758,7 @@ void cmGlobalGenerator::AddGlobalTarget_Install( } } -std::string cmGlobalGenerator::GetPredefinedTargetsFolder() +std::string cmGlobalGenerator::GetPredefinedTargetsFolder() const { cmProp prop = this->GetCMakeInstance()->GetState()->GetGlobalProperty( "PREDEFINED_TARGETS_FOLDER"); diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index de5eba7..afafba9 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -574,8 +574,9 @@ protected: void AddGlobalTarget_Package(std::vector<GlobalTargetInfo>& targets); void AddGlobalTarget_PackageSource(std::vector<GlobalTargetInfo>& targets); void AddGlobalTarget_Test(std::vector<GlobalTargetInfo>& targets); - void AddGlobalTarget_EditCache(std::vector<GlobalTargetInfo>& targets); - void AddGlobalTarget_RebuildCache(std::vector<GlobalTargetInfo>& targets); + void AddGlobalTarget_EditCache(std::vector<GlobalTargetInfo>& targets) const; + void AddGlobalTarget_RebuildCache( + std::vector<GlobalTargetInfo>& targets) const; void AddGlobalTarget_Install(std::vector<GlobalTargetInfo>& targets); cmTarget CreateGlobalTarget(GlobalTargetInfo const& gti, cmMakefile* mf); @@ -601,7 +602,7 @@ protected: cmGeneratorTarget* FindGeneratorTargetImpl(std::string const& name) const; - std::string GetPredefinedTargetsFolder(); + std::string GetPredefinedTargetsFolder() const; private: using TargetMap = std::unordered_map<std::string, cmTarget*>; diff --git a/Source/cmGlobalGhsMultiGenerator.cxx b/Source/cmGlobalGhsMultiGenerator.cxx index c08c9cf..33bf830 100644 --- a/Source/cmGlobalGhsMultiGenerator.cxx +++ b/Source/cmGlobalGhsMultiGenerator.cxx @@ -704,7 +704,7 @@ bool cmGlobalGhsMultiGenerator::ComputeTargetBuildOrder( std::set<cmGeneratorTarget const*> temp; std::set<cmGeneratorTarget const*> perm; - for (auto ti : tgt) { + for (auto const ti : tgt) { bool r = VisitTarget(temp, perm, build, ti); if (r) { return r; @@ -726,7 +726,7 @@ bool cmGlobalGhsMultiGenerator::VisitTarget( * in the same order */ OrderedTargetDependSet sortedTargets(this->GetTargetDirectDepends(ti), ""); - for (auto& di : sortedTargets) { + for (auto const& di : sortedTargets) { if (this->VisitTarget(temp, perm, order, di)) { return true; } diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx index 9e25a54..9b27bc5 100644 --- a/Source/cmGlobalNinjaGenerator.cxx +++ b/Source/cmGlobalNinjaGenerator.cxx @@ -1057,7 +1057,7 @@ void cmGlobalNinjaGenerator::CloseCompileCommandsStream() } } -void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os) +void cmGlobalNinjaGenerator::WriteDisclaimer(std::ostream& os) const { os << "# CMAKE generated file: DO NOT EDIT!\n" << "# Generated by \"" << this->GetName() << "\"" @@ -1088,7 +1088,7 @@ std::string cmGlobalNinjaGenerator::OrderDependsTargetForTarget( void cmGlobalNinjaGenerator::AppendTargetOutputs( cmGeneratorTarget const* target, cmNinjaDeps& outputs, - const std::string& config, cmNinjaTargetDepends depends) + const std::string& config, cmNinjaTargetDepends depends) const { // for frameworks, we want the real name, not smple name // frameworks always appear versioned, and the build.ninja diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h index 8d6ebdc..94abb40 100644 --- a/Source/cmGlobalNinjaGenerator.h +++ b/Source/cmGlobalNinjaGenerator.h @@ -252,7 +252,7 @@ public: : GG(gg) { } - std::string operator()(std::string const& path) + std::string operator()(std::string const& path) const { return this->GG->ConvertToNinjaPath(path); } @@ -323,7 +323,7 @@ public: void AppendTargetOutputs(cmGeneratorTarget const* target, cmNinjaDeps& outputs, const std::string& config, - cmNinjaTargetDepends depends); + cmNinjaTargetDepends depends) const; void AppendTargetDepends(cmGeneratorTarget const* target, cmNinjaDeps& outputs, const std::string& config, const std::string& fileConfig, @@ -463,7 +463,7 @@ private: void CleanMetaData(); /// Write the common disclaimer text at the top of each build file. - void WriteDisclaimer(std::ostream& os); + void WriteDisclaimer(std::ostream& os) const; void WriteAssumedSourceDependencies(); diff --git a/Source/cmLocalGenerator.cxx b/Source/cmLocalGenerator.cxx index b7ad78a..47e10e0 100644 --- a/Source/cmLocalGenerator.cxx +++ b/Source/cmLocalGenerator.cxx @@ -279,7 +279,7 @@ static void MoveSystemIncludesToEnd(std::vector<BT<std::string>>& includeDirs, }); } -void cmLocalGenerator::TraceDependencies() +void cmLocalGenerator::TraceDependencies() const { // Generate the rule files for each target. const auto& targets = this->GetGeneratorTargets(); @@ -3210,7 +3210,7 @@ std::string cmLocalGenerator::GetProjectName() const } std::string cmLocalGenerator::ConstructComment( - cmCustomCommandGenerator const& ccg, const char* default_comment) + cmCustomCommandGenerator const& ccg, const char* default_comment) const { // Check for a comment provided with the command. if (ccg.GetComment()) { diff --git a/Source/cmLocalGenerator.h b/Source/cmLocalGenerator.h index 162e70f..b8bd3bc 100644 --- a/Source/cmLocalGenerator.h +++ b/Source/cmLocalGenerator.h @@ -77,7 +77,7 @@ public: /** * Calls TraceVSDependencies() on all targets of this generator. */ - void TraceDependencies(); + void TraceDependencies() const; virtual void AddHelperCommands() {} @@ -451,7 +451,7 @@ public: const std::string& fname); /** Construct a comment for a custom command. */ std::string ConstructComment(cmCustomCommandGenerator const& ccg, - const char* default_comment = ""); + const char* default_comment = "") const; // Compute object file names. std::string GetObjectFileNameWithoutTarget( const cmSourceFile& source, std::string const& dir_max, diff --git a/Source/cmLocalUnixMakefileGenerator3.h b/Source/cmLocalUnixMakefileGenerator3.h index 8286d67..5edca2a 100644 --- a/Source/cmLocalUnixMakefileGenerator3.h +++ b/Source/cmLocalUnixMakefileGenerator3.h @@ -178,11 +178,11 @@ public: /** Get whether to create rules to generate preprocessed and assembly sources. This could be converted to a variable lookup later. */ - bool GetCreatePreprocessedSourceRules() + bool GetCreatePreprocessedSourceRules() const { return !this->SkipPreprocessedSourceRules; } - bool GetCreateAssemblySourceRules() + bool GetCreateAssemblySourceRules() const { return !this->SkipAssemblySourceRules; } diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 22594bd..6d1868c 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -2029,7 +2029,7 @@ void cmMakefile::RemoveDefinition(const std::string& name) #endif } -void cmMakefile::RemoveCacheDefinition(const std::string& name) +void cmMakefile::RemoveCacheDefinition(const std::string& name) const { this->GetState()->RemoveCacheEntry(name); } @@ -2839,7 +2839,7 @@ void cmMakefile::SetRecursionDepth(int recursionDepth) this->RecursionDepth = recursionDepth; } -std::string cmMakefile::NewDeferId() +std::string cmMakefile::NewDeferId() const { return this->GetGlobalGenerator()->NewDeferId(); } @@ -4039,7 +4039,7 @@ cmTest* cmMakefile::GetTest(const std::string& testName) const } void cmMakefile::GetTests(const std::string& config, - std::vector<cmTest*>& tests) + std::vector<cmTest*>& tests) const { for (const auto& generator : this->GetTestGenerators()) { if (generator->TestsForConfig(config)) { @@ -4389,7 +4389,7 @@ cmPolicies::PolicyStatus cmMakefile::GetPolicyStatus(cmPolicies::PolicyID id, return this->StateSnapshot.GetPolicy(id, parent_scope); } -bool cmMakefile::PolicyOptionalWarningEnabled(std::string const& var) +bool cmMakefile::PolicyOptionalWarningEnabled(std::string const& var) const { // Check for an explicit CMAKE_POLICY_WARNING_CMP<NNNN> setting. if (cmProp val = this->GetDefinition(var)) { @@ -4494,7 +4494,7 @@ bool cmMakefile::HasCMP0054AlreadyBeenReported( return !this->CMP0054ReportedIds.insert(context).second; } -void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm) +void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm) const { /* Record the setting of every policy. */ using PolicyID = cmPolicies::PolicyID; diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index ecc6aab..26ed737 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -304,7 +304,7 @@ public: */ void RemoveDefinition(const std::string& name); //! Remove a definition from the cache. - void RemoveCacheDefinition(const std::string& name); + void RemoveCacheDefinition(const std::string& name) const; /** * Specify the name of the project for this build. @@ -345,7 +345,7 @@ public: bool parent_scope = false) const; bool SetPolicyVersion(std::string const& version_min, std::string const& version_max); - void RecordPolicies(cmPolicies::PolicyMap& pm); + void RecordPolicies(cmPolicies::PolicyMap& pm) const; //@} /** Helper class to push and pop policies automatically. */ @@ -738,7 +738,7 @@ public: /** * Get all tests that run under the given configuration. */ - void GetTests(const std::string& config, std::vector<cmTest*>& tests); + void GetTests(const std::string& config, std::vector<cmTest*>& tests) const; /** * Return a location of a file in cmake or custom modules directory @@ -885,7 +885,7 @@ public: return this->SystemIncludeDirectories; } - bool PolicyOptionalWarningEnabled(std::string const& var); + bool PolicyOptionalWarningEnabled(std::string const& var) const; void PushLoopBlock(); void PopLoopBlock(); @@ -926,7 +926,7 @@ public: int GetRecursionDepth() const; void SetRecursionDepth(int recursionDepth); - std::string NewDeferId(); + std::string NewDeferId() const; bool DeferCall(std::string id, std::string fileName, cmListFileFunction lff); bool DeferCancelCall(std::string const& id); cm::optional<std::string> DeferGetCallIds() const; diff --git a/Source/cmQtAutoGen.h b/Source/cmQtAutoGen.h index cf90417..2db1b84 100644 --- a/Source/cmQtAutoGen.h +++ b/Source/cmQtAutoGen.h @@ -29,13 +29,13 @@ public: { } - bool operator>(IntegerVersion const version) + bool operator>(IntegerVersion const version) const { return (this->Major > version.Major) || ((this->Major == version.Major) && (this->Minor > version.Minor)); } - bool operator>=(IntegerVersion const version) + bool operator>=(IntegerVersion const version) const { return (this->Major > version.Major) || ((this->Major == version.Major) && (this->Minor >= version.Minor)); diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index e076d1e..4eebec6 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -213,7 +213,7 @@ public: bool CheckImportedLibName(std::string const& prop, std::string const& value) const; - std::string ProcessSourceItemCMP0049(const std::string& s); + std::string ProcessSourceItemCMP0049(const std::string& s) const; }; namespace { @@ -740,7 +740,8 @@ void cmTarget::AddSources(std::vector<std::string> const& srcs) } } -std::string cmTargetInternals::ProcessSourceItemCMP0049(const std::string& s) +std::string cmTargetInternals::ProcessSourceItemCMP0049( + const std::string& s) const { std::string src = s; @@ -791,7 +792,7 @@ struct CreateLocation { } - cmSourceFileLocation operator()(const std::string& filename) + cmSourceFileLocation operator()(const std::string& filename) const { return cmSourceFileLocation(this->Makefile, filename); } @@ -857,7 +858,7 @@ cmSourceFile* cmTarget::AddSource(const std::string& src, bool before) cmSourceFileLocationKind::Known); } -void cmTarget::ClearDependencyInformation(cmMakefile& mf) +void cmTarget::ClearDependencyInformation(cmMakefile& mf) const { std::string depname = cmStrCat(this->GetName(), "_LIB_DEPENDS"); mf.RemoveCacheDefinition(depname); diff --git a/Source/cmTarget.h b/Source/cmTarget.h index d8f66bc..c2a4d86 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -114,7 +114,7 @@ public: LinkLibraryVectorType const& GetOriginalLinkLibraries() const; //! Clear the dependency information recorded for this target, if any. - void ClearDependencyInformation(cmMakefile& mf); + void ClearDependencyInformation(cmMakefile& mf) const; void AddLinkLibrary(cmMakefile& mf, std::string const& lib, cmTargetLinkLibraryType llt); diff --git a/Source/cmVisualStudio10TargetGenerator.cxx b/Source/cmVisualStudio10TargetGenerator.cxx index 0becee2..1ca7de8 100644 --- a/Source/cmVisualStudio10TargetGenerator.cxx +++ b/Source/cmVisualStudio10TargetGenerator.cxx @@ -2402,27 +2402,28 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( configDefines += *ccdefs; } - // Add precompile headers compile options. - std::string customAndPchOptions = options; + // We have pch state in the following situation: + // 1. We have SKIP_PRECOMPILE_HEADERS == true + // 2. We are creating the pre-compiled header + // 3. We are a different language than the linker language AND pch is + // enabled const std::string pchSource = this->GeneratorTarget->GetPchSource(config, lang); - if (!pchSource.empty() && !sf.GetProperty("SKIP_PRECOMPILE_HEADERS")) { - std::string pchOptions; - if (sf.GetFullPath() == pchSource) { - pchOptions = - this->GeneratorTarget->GetPchCreateCompileOptions(config, lang); - } else { - pchOptions = - this->GeneratorTarget->GetPchUseCompileOptions(config, lang); - } - customAndPchOptions = cmStrCat(customAndPchOptions, ';', pchOptions); - } + const bool skipPCH = + pchSource.empty() || sf.GetPropertyAsBool("SKIP_PRECOMPILE_HEADERS"); + const bool makePCH = (sf.GetFullPath() == pchSource); + const bool useSharedPCH = + !skipPCH && (lang == this->GeneratorTarget->GetLinkerLanguage(config)); + const bool useDifferentLangPCH = + !skipPCH && (lang != this->GeneratorTarget->GetLinkerLanguage(config)); + const bool needsPCHFlags = + (makePCH || useSharedPCH || useDifferentLangPCH); // if we have flags or defines for this config then // use them if (!flags.empty() || !options.empty() || !configDefines.empty() || - !includes.empty() || compileAs || noWinRT || - !customAndPchOptions.empty()) { + !includes.empty() || compileAs || noWinRT || !options.empty() || + needsPCHFlags) { cmGlobalVisualStudio10Generator* gg = this->GlobalGenerator; cmIDEFlagTable const* flagtable = nullptr; const std::string& srclang = source->GetLanguage(); @@ -2455,15 +2456,35 @@ void cmVisualStudio10TargetGenerator::OutputSourceSpecificFlags( } else { clOptions.Parse(flags); } - if (!customAndPchOptions.empty()) { + + if (needsPCHFlags) { + // Add precompile headers compile options. + std::string expandedOptions; + std::string pchOptions; + if (makePCH) { + pchOptions = + this->GeneratorTarget->GetPchCreateCompileOptions(config, lang); + } else if (useSharedPCH) { + std::string pchHeader = + this->GeneratorTarget->GetPchHeader(config, lang); + clOptions.AddFlag("ForcedIncludeFiles", pchHeader); + } else if (useDifferentLangPCH) { + pchOptions = + this->GeneratorTarget->GetPchUseCompileOptions(config, lang); + } + this->LocalGenerator->AppendCompileOptions(expandedOptions, + pchOptions); + clOptions.Parse(expandedOptions); + } + + if (!options.empty()) { std::string expandedOptions; if (configDependentOptions) { this->LocalGenerator->AppendCompileOptions( expandedOptions, - genexInterpreter.Evaluate(customAndPchOptions, "COMPILE_OPTIONS")); + genexInterpreter.Evaluate(options, "COMPILE_OPTIONS")); } else { - this->LocalGenerator->AppendCompileOptions(expandedOptions, - customAndPchOptions); + this->LocalGenerator->AppendCompileOptions(expandedOptions, options); } clOptions.Parse(expandedOptions); } @@ -2786,6 +2807,13 @@ bool cmVisualStudio10TargetGenerator::ComputeClOptions( this->GeneratorTarget->GetPchHeader(configName, linkLanguage); if (this->MSTools && vcxproj == this->ProjectType && pchHeader.empty()) { clOptions.AddFlag("PrecompiledHeader", "NotUsing"); + } else if (this->MSTools && vcxproj == this->ProjectType && + !pchHeader.empty()) { + clOptions.AddFlag("PrecompiledHeader", "Use"); + clOptions.AddFlag("PrecompiledHeaderFile", pchHeader); + std::string pchFile = + this->GeneratorTarget->GetPchFile(configName, linkLanguage); + clOptions.AddFlag("PrecompiledHeaderOutputFile", pchFile); } // Get preprocessor definitions for this directory. diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 720a567..7d044ec 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -1042,17 +1042,17 @@ void cmake::SetArgs(const std::vector<std::string>& args) this->PrintPresetList(settingsFile); return; } - if (preset->second.Hidden) { + if (preset->second.Unexpanded.Hidden) { cmSystemTools::Error(cmStrCat("Cannot use hidden preset in ", this->GetHomeDirectory(), ": \"", presetName, '"')); this->PrintPresetList(settingsFile); return; } - auto expandedPreset = settingsFile.ExpandMacros(preset->second); + auto const& expandedPreset = preset->second.Expanded; if (!expandedPreset) { cmSystemTools::Error(cmStrCat("Could not evaluate preset \"", - preset->second.Name, + preset->second.Unexpanded.Name, "\": Invalid macro expansion")); return; } @@ -1464,13 +1464,12 @@ void cmake::PrintPresetList(const cmCMakePresetsFile& file) const std::vector<cmCMakePresetsFile::UnexpandedPreset> presets; for (auto const& p : file.PresetOrder) { auto const& preset = file.Presets.at(p); - if (!preset.Hidden && + if (!preset.Unexpanded.Hidden && preset.Expanded && std::find_if(generators.begin(), generators.end(), [&preset](const GeneratorInfo& info) { - return info.name == preset.Generator; - }) != generators.end() && - file.ExpandMacros(preset)) { - presets.push_back(preset); + return info.name == preset.Unexpanded.Generator; + }) != generators.end()) { + presets.push_back(preset.Unexpanded); } } @@ -1938,7 +1937,7 @@ int cmake::ActualConfigure() } } - auto& mf = this->GlobalGenerator->GetMakefiles()[0]; + const auto& mf = this->GlobalGenerator->GetMakefiles()[0]; if (mf->IsOn("CTEST_USE_LAUNCHERS") && !this->State->GetGlobalProperty("RULE_LAUNCH_COMPILE")) { cmSystemTools::Error( @@ -2280,12 +2279,12 @@ cmProp cmake::GetCacheDefinition(const std::string& name) const return this->State->GetInitializedCacheValue(name); } -void cmake::AddScriptingCommands() +void cmake::AddScriptingCommands() const { GetScriptingCommands(this->GetState()); } -void cmake::AddProjectCommands() +void cmake::AddProjectCommands() const { GetProjectCommands(this->GetState()); } diff --git a/Source/cmake.h b/Source/cmake.h index 262d673..f70fd14 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -411,7 +411,7 @@ public: WorkingMode GetWorkingMode() { return this->CurrentWorkingMode; } //! Debug the try compile stuff by not deleting the files - bool GetDebugTryCompile() { return this->DebugTryCompile; } + bool GetDebugTryCompile() const { return this->DebugTryCompile; } void DebugTryCompileOn() { this->DebugTryCompile = true; } /** @@ -456,11 +456,11 @@ public: void SetShowLogContext(bool b) { this->LogContext = b; } //! Do we want debug output during the cmake run. - bool GetDebugOutput() { return this->DebugOutput; } + bool GetDebugOutput() const { return this->DebugOutput; } void SetDebugOutputOn(bool b) { this->DebugOutput = b; } //! Do we want debug output from the find commands during the cmake run. - bool GetDebugFindOutput() { return this->DebugFindOutput; } + bool GetDebugFindOutput() const { return this->DebugFindOutput; } void SetDebugFindOutputOn(bool b) { this->DebugFindOutput = b; } //! Do we want trace output during the cmake run. @@ -482,11 +482,11 @@ public: void SetTraceFile(std::string const& file); void PrintTraceFormatVersion(); - bool GetWarnUninitialized() { return this->WarnUninitialized; } + bool GetWarnUninitialized() const { return this->WarnUninitialized; } void SetWarnUninitialized(bool b) { this->WarnUninitialized = b; } - bool GetWarnUnusedCli() { return this->WarnUnusedCli; } + bool GetWarnUnusedCli() const { return this->WarnUnusedCli; } void SetWarnUnusedCli(bool b) { this->WarnUnusedCli = b; } - bool GetCheckSystemVars() { return this->CheckSystemVars; } + bool GetCheckSystemVars() const { return this->CheckSystemVars; } void SetCheckSystemVars(bool b) { this->CheckSystemVars = b; } void MarkCliAsUsed(const std::string& variable); @@ -591,8 +591,8 @@ protected: using RegisteredExtraGeneratorsVector = std::vector<cmExternalMakefileProjectGeneratorFactory*>; RegisteredExtraGeneratorsVector ExtraGenerators; - void AddScriptingCommands(); - void AddProjectCommands(); + void AddScriptingCommands() const; + void AddProjectCommands() const; void AddDefaultGenerators(); void AddDefaultExtraGenerators(); diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 6072711..38ab553 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -773,7 +773,7 @@ add_RunCMake_test(PrecompileHeaders -DCMAKE_C_COMPILER_ID=${CMAKE_C_COMPILER_ID} -DCMAKE_C_COMPILER_VERSION=${CMAKE_C_COMPILER_VERSION}) add_RunCMake_test("UnityBuild") -add_RunCMake_test(CMakePresets) +add_RunCMake_test(CMakePresets -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} -DCMake_TEST_JSON_SCHEMA=${CMake_TEST_JSON_SCHEMA}) add_RunCMake_test(TransformDepfile) if(WIN32) diff --git a/Tests/RunCMake/CMakePresets/CMakePresets.json.in b/Tests/RunCMake/CMakePresets/CMakePresets.json.in index 8bfc602..ea7df45 100644 --- a/Tests/RunCMake/CMakePresets/CMakePresets.json.in +++ b/Tests/RunCMake/CMakePresets/CMakePresets.json.in @@ -46,6 +46,22 @@ "type": "BOOL", "value": "OFF" }, + "TEST_BOOL_TRUE": true, + "TEST_BOOL_FALSE": false, + "TEST_TYPED_BOOL_TRUE": { + "type": "STRING", + "value": true + }, + "TEST_TYPED_BOOL_FALSE": { + "type": "STRING", + "value": false + }, + "TEST_UNTYPED_BOOL_TRUE": { + "value": true + }, + "TEST_UNTYPED_BOOL_FALSE": { + "value": false + }, "TEST_PRESET_NAME": { "type": "STRING", "value": "x${presetName}x" @@ -306,16 +322,6 @@ "binaryDir": "${sourceDir}/build" }, { - "name": "UnclosedMacro", - "generator": "@RunCMake_GENERATOR@", - "binaryDir": "${sourceDir" - }, - { - "name": "NoSuchMacro", - "generator": "@RunCMake_GENERATOR@", - "binaryDir": "${noexist}" - }, - { "name": "VendorMacro", "generator": "@RunCMake_GENERATOR@", "binaryDir": "$vendor{unknown.unknownMacro}" @@ -326,31 +332,6 @@ "binaryDir": "${sourceDir}/build" }, { - "name": "EnvCycle", - "generator": "@RunCMake_GENERATOR@", - "binaryDir": "${sourceDir}/build", - "environment": { - "ENV_1": "$env{ENV_2}", - "ENV_2": "$env{ENV_1}" - } - }, - { - "name": "EmptyEnv", - "generator": "@RunCMake_GENERATOR@", - "binaryDir": "${sourceDir}/build", - "cacheVariables": { - "MY_VAR": "$env{}" - } - }, - { - "name": "EmptyPenv", - "generator": "@RunCMake_GENERATOR@", - "binaryDir": "${sourceDir}/build", - "cacheVariables": { - "MY_VAR": "$penv{}" - } - }, - { "name": "UseHiddenPreset", "generator": "@RunCMake_GENERATOR@", "binaryDir": "${sourceDir}/build", diff --git a/Tests/RunCMake/CMakePresets/EmptyEnv-stderr.txt b/Tests/RunCMake/CMakePresets/EmptyEnv-stderr.txt index 9c9c025..723ac21 100644 --- a/Tests/RunCMake/CMakePresets/EmptyEnv-stderr.txt +++ b/Tests/RunCMake/CMakePresets/EmptyEnv-stderr.txt @@ -1 +1,2 @@ -^CMake Error: Could not evaluate preset "EmptyEnv": Invalid macro expansion$ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/EmptyEnv: Invalid macro expansion$ diff --git a/Tests/RunCMake/CMakePresets/EmptyEnv.json.in b/Tests/RunCMake/CMakePresets/EmptyEnv.json.in new file mode 100644 index 0000000..ef0d575 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/EmptyEnv.json.in @@ -0,0 +1,13 @@ +{ + "version": 1, + "configurePresets": [ + { + "name": "EmptyEnv", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "MY_VAR": "$env{}" + } + } + ] +} diff --git a/Tests/RunCMake/CMakePresets/EmptyPenv-stderr.txt b/Tests/RunCMake/CMakePresets/EmptyPenv-stderr.txt index 395c7b4..880cee6 100644 --- a/Tests/RunCMake/CMakePresets/EmptyPenv-stderr.txt +++ b/Tests/RunCMake/CMakePresets/EmptyPenv-stderr.txt @@ -1 +1,2 @@ -^CMake Error: Could not evaluate preset "EmptyPenv": Invalid macro expansion$ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/EmptyPenv: Invalid macro expansion$ diff --git a/Tests/RunCMake/CMakePresets/EmptyPenv.json.in b/Tests/RunCMake/CMakePresets/EmptyPenv.json.in new file mode 100644 index 0000000..9081fe5 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/EmptyPenv.json.in @@ -0,0 +1,13 @@ +{ + "version": 1, + "configurePresets": [ + { + "name": "EmptyPenv", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "MY_VAR": "$penv{}" + } + } + ] +} diff --git a/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt b/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt index c8568f1..1d22b87 100644 --- a/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt +++ b/Tests/RunCMake/CMakePresets/EnvCycle-stderr.txt @@ -1 +1,2 @@ -^CMake Error: Could not evaluate preset "EnvCycle": Invalid macro expansion$ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/EnvCycle: Invalid macro expansion$ diff --git a/Tests/RunCMake/CMakePresets/EnvCycle.json.in b/Tests/RunCMake/CMakePresets/EnvCycle.json.in new file mode 100644 index 0000000..25a1349 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/EnvCycle.json.in @@ -0,0 +1,14 @@ +{ + "version": 1, + "configurePresets": [ + { + "name": "EnvCycle", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir}/build", + "environment": { + "ENV_1": "$env{ENV_2}", + "ENV_2": "$env{ENV_1}" + } + } + ] +} diff --git a/Tests/RunCMake/CMakePresets/Good-stdout.txt b/Tests/RunCMake/CMakePresets/Good-stdout.txt index ce6189e..7adf1ca 100644 --- a/Tests/RunCMake/CMakePresets/Good-stdout.txt +++ b/Tests/RunCMake/CMakePresets/Good-stdout.txt @@ -1,5 +1,7 @@ Preset CMake variables: + TEST_BOOL_FALSE:BOOL="FALSE" + TEST_BOOL_TRUE:BOOL="TRUE" TEST_DOLLAR="\$" TEST_D_ENV_REF="xEnvironment variablex" TEST_D_ENV_REF_P="" @@ -32,6 +34,10 @@ Preset CMake variables: TEST_TRAILING_DOLLAR="a \$" TEST_TRAILING_UNKNOWN_NAMESPACE="\$unknown{namespace" TEST_TRUE:BOOL="TRUE" + TEST_TYPED_BOOL_FALSE:STRING="FALSE" + TEST_TYPED_BOOL_TRUE:STRING="TRUE" + TEST_UNTYPED_BOOL_FALSE="FALSE" + TEST_UNTYPED_BOOL_TRUE="TRUE" Preset environment variables: diff --git a/Tests/RunCMake/CMakePresets/Good.cmake b/Tests/RunCMake/CMakePresets/Good.cmake index d8e3e2d..55b85da 100644 --- a/Tests/RunCMake/CMakePresets/Good.cmake +++ b/Tests/RunCMake/CMakePresets/Good.cmake @@ -8,6 +8,12 @@ test_variable(TEST_SOURCE_PARENT_DIR "PATH" "${_parent_dir}") test_variable(TEST_SOURCE_LIST "FILEPATH" "${CMAKE_SOURCE_DIR}/CMakeLists.txt") test_variable(TEST_TRUE "BOOL" "TRUE") test_variable(TEST_OFF "BOOL" "OFF") +test_variable(TEST_BOOL_TRUE "BOOL" "TRUE") +test_variable(TEST_BOOL_FALSE "BOOL" "FALSE") +test_variable(TEST_TYPED_BOOL_TRUE "STRING" "TRUE") +test_variable(TEST_TYPED_BOOL_FALSE "STRING" "FALSE") +test_variable(TEST_UNTYPED_BOOL_TRUE "UNINITIALIZED" "TRUE") +test_variable(TEST_UNTYPED_BOOL_FALSE "UNINITIALIZED" "FALSE") test_variable(TEST_PRESET_NAME "STRING" "xGoodx") test_variable(TEST_GENERATOR "UNINITIALIZED" "x${CMAKE_GENERATOR}x") test_variable(TEST_DOLLAR "UNINITIALIZED" "$") diff --git a/Tests/RunCMake/CMakePresets/ListPresets.json.in b/Tests/RunCMake/CMakePresets/ListPresets.json.in index c7646c6..2ef3797 100644 --- a/Tests/RunCMake/CMakePresets/ListPresets.json.in +++ b/Tests/RunCMake/CMakePresets/ListPresets.json.in @@ -26,7 +26,7 @@ { "name": "invalid-macro", "generator": "@RunCMake_GENERATOR@", - "binaryDir": "${noexist}" + "binaryDir": "$vendor{noexist}" }, { "name": "ListPresetsHidden", diff --git a/Tests/RunCMake/CMakePresets/NoSuchMacro-stderr.txt b/Tests/RunCMake/CMakePresets/NoSuchMacro-stderr.txt index 08fa87b..7dafe62 100644 --- a/Tests/RunCMake/CMakePresets/NoSuchMacro-stderr.txt +++ b/Tests/RunCMake/CMakePresets/NoSuchMacro-stderr.txt @@ -1 +1,2 @@ -^CMake Error: Could not evaluate preset "NoSuchMacro": Invalid macro expansion$ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/NoSuchMacro: Invalid macro expansion$ diff --git a/Tests/RunCMake/CMakePresets/NoSuchMacro.json.in b/Tests/RunCMake/CMakePresets/NoSuchMacro.json.in new file mode 100644 index 0000000..94d0b76 --- /dev/null +++ b/Tests/RunCMake/CMakePresets/NoSuchMacro.json.in @@ -0,0 +1,10 @@ +{ + "version": 1, + "configurePresets": [ + { + "name": "NoSuchMacro", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${noexist}" + } + ] +} diff --git a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake index 18ea093..dddf05f 100644 --- a/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake +++ b/Tests/RunCMake/CMakePresets/RunCMakeTest.cmake @@ -7,6 +7,24 @@ if(RunCMake_GENERATOR MATCHES "^(Visual Studio [0-9]+ [0-9]+) ") set(RunCMake_GENERATOR "${CMAKE_MATCH_1}") endif() +set(RunCMake-check-file check.cmake) + +function(validate_schema file expected_result) + execute_process( + COMMAND "${PYTHON_EXECUTABLE}" "${RunCMake_SOURCE_DIR}/validate_schema.py" "${file}" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _output + ERROR_VARIABLE _error + ) + if(NOT _result STREQUAL expected_result) + string(REPLACE "\n" "\n" _output_p "${_output}") + string(REPLACE "\n" "\n" _error_p "${_error}") + string(APPEND RunCMake_TEST_FAILED "Expected result of validating ${file}: ${expected_result}\nActual result: ${_result}\nOutput:\n${_output_p}\nError:\n${_error_p}") + endif() + + set(RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}" PARENT_SCOPE) +endfunction() + function(run_cmake_presets name) set(RunCMake_TEST_SOURCE_DIR "${RunCMake_BINARY_DIR}/${name}") set(_source_arg "${RunCMake_TEST_SOURCE_DIR}") @@ -57,6 +75,7 @@ function(run_cmake_presets name) endfunction() # Test CMakePresets.json errors +set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) run_cmake_presets(NoCMakePresets) run_cmake_presets(JSONParseError) run_cmake_presets(InvalidRoot) @@ -65,15 +84,21 @@ run_cmake_presets(InvalidVersion) run_cmake_presets(LowVersion) run_cmake_presets(HighVersion) run_cmake_presets(InvalidVendor) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) run_cmake_presets(NoPresets) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) run_cmake_presets(InvalidPresets) run_cmake_presets(PresetNotObject) run_cmake_presets(NoPresetName) run_cmake_presets(InvalidPresetName) run_cmake_presets(EmptyPresetName) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) run_cmake_presets(NoPresetGenerator) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) run_cmake_presets(InvalidPresetGenerator) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) run_cmake_presets(NoPresetBinaryDir) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) run_cmake_presets(InvalidPresetBinaryDir) run_cmake_presets(InvalidVariables) run_cmake_presets(VariableNotObject) @@ -83,6 +108,7 @@ run_cmake_presets(ExtraRootField) run_cmake_presets(ExtraPresetField) run_cmake_presets(ExtraVariableField) run_cmake_presets(InvalidPresetVendor) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) run_cmake_presets(DuplicatePresets) run_cmake_presets(CyclicInheritance0) run_cmake_presets(CyclicInheritance1) @@ -90,13 +116,22 @@ run_cmake_presets(CyclicInheritance2) run_cmake_presets(InvalidInheritance) run_cmake_presets(ErrorNoWarningDev) run_cmake_presets(ErrorNoWarningDeprecated) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) run_cmake_presets(InvalidCMakeGeneratorConfig) run_cmake_presets(UnknownCMakeGeneratorConfig) run_cmake_presets(EmptyCacheKey) run_cmake_presets(EmptyEnvKey) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) +run_cmake_presets(UnclosedMacro) +run_cmake_presets(NoSuchMacro) +run_cmake_presets(EnvCycle) +run_cmake_presets(EmptyEnv) +run_cmake_presets(EmptyPenv) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 1) # Test cmakeMinimumRequired field run_cmake_presets(MinimumRequiredInvalid) +set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) run_cmake_presets(MinimumRequiredEmpty) run_cmake_presets(MinimumRequiredMajor) run_cmake_presets(MinimumRequiredMinor) @@ -142,13 +177,8 @@ run_cmake_presets(GoodInheritanceMultiSecond) run_cmake_presets(GoodInheritanceMacro) # Test bad preset arguments -run_cmake_presets(UnclosedMacro) -run_cmake_presets(NoSuchMacro) run_cmake_presets(VendorMacro) run_cmake_presets(InvalidGenerator) -run_cmake_presets(EnvCycle) -run_cmake_presets(EmptyEnv) -run_cmake_presets(EmptyPenv) # Test Visual Studio-specific stuff if(RunCMake_GENERATOR MATCHES "^Visual Studio ") diff --git a/Tests/RunCMake/CMakePresets/UnclosedMacro-stderr.txt b/Tests/RunCMake/CMakePresets/UnclosedMacro-stderr.txt index 248510d..f9481f0 100644 --- a/Tests/RunCMake/CMakePresets/UnclosedMacro-stderr.txt +++ b/Tests/RunCMake/CMakePresets/UnclosedMacro-stderr.txt @@ -1 +1,2 @@ -^CMake Error: Could not evaluate preset "UnclosedMacro": Invalid macro expansion$ +^CMake Error: Could not read presets from [^ +]*/Tests/RunCMake/CMakePresets/UnclosedMacro: Invalid macro expansion$ diff --git a/Tests/RunCMake/CMakePresets/UnclosedMacro.json.in b/Tests/RunCMake/CMakePresets/UnclosedMacro.json.in new file mode 100644 index 0000000..ad6cf7d --- /dev/null +++ b/Tests/RunCMake/CMakePresets/UnclosedMacro.json.in @@ -0,0 +1,10 @@ +{ + "version": 1, + "configurePresets": [ + { + "name": "UnclosedMacro", + "generator": "@RunCMake_GENERATOR@", + "binaryDir": "${sourceDir" + } + ] +} diff --git a/Tests/RunCMake/CMakePresets/check.cmake b/Tests/RunCMake/CMakePresets/check.cmake new file mode 100644 index 0000000..bf43c7e --- /dev/null +++ b/Tests/RunCMake/CMakePresets/check.cmake @@ -0,0 +1,15 @@ +if(PYTHON_EXECUTABLE AND CMake_TEST_JSON_SCHEMA) + if(NOT CMakePresets_SCHEMA_EXPECTED_RESULT) + set(CMakePresets_SCHEMA_EXPECTED_RESULT 0) + endif() + if(EXISTS "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json") + validate_schema("${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json" "${CMakePresets_SCHEMA_EXPECTED_RESULT}") + endif() + + if(NOT CMakeUserPresets_SCHEMA_EXPECTED_RESULT) + set(CMakeUserPresets_SCHEMA_EXPECTED_RESULT 0) + endif() + if(EXISTS "${RunCMake_TEST_SOURCE_DIR}/CMakeUserPresets.json") + validate_schema("${RunCMake_TEST_SOURCE_DIR}/CMakeUserPresets.json" "${CMakeUserPresets_SCHEMA_EXPECTED_RESULT}") + endif() +endif() diff --git a/Tests/RunCMake/CMakePresets/validate_schema.py b/Tests/RunCMake/CMakePresets/validate_schema.py new file mode 100644 index 0000000..c9f84ee --- /dev/null +++ b/Tests/RunCMake/CMakePresets/validate_schema.py @@ -0,0 +1,17 @@ +import jsmin +import json +import jsonschema +import os.path +import sys + + +with open(sys.argv[1], "rb") as f: + contents = json.loads(jsmin.jsmin(f.read().decode("utf-8-sig"))) + +schema_file = os.path.join( + os.path.dirname(__file__), + "..", "..", "..", "Help", "manual", "presets", "schema.json") +with open(schema_file) as f: + schema = json.load(f) + +jsonschema.validate(contents, schema) |