mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-10-04 04:03:22 +03:00
presets: Evaluate fileDir macro ahead of schedule
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
This commit is contained in:
1 parent
e9abf0c7c8
commit
3c314349d5
16 files changed
+254
-19
No files matched your search
@@ -1257,12 +1257,14 @@ Macro Expansion
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
As mentioned above, some fields support macro expansion. Macros are
|
||||
recognized in the form ``$<macro-namespace>{<macro-name>}``. All macros are
|
||||
evaluated in the context of the preset being used, even if the macro is in a
|
||||
field that was inherited from another preset. For example, if the ``Base``
|
||||
preset sets variable ``PRESET_NAME`` to ``${presetName}``, and the
|
||||
``Derived`` preset inherits from ``Base``, ``PRESET_NAME`` will be set to
|
||||
``Derived``.
|
||||
recognized in the form ``$<macro-namespace>{<macro-name>}``.
|
||||
|
||||
In general, macros are evaluated in the context of the preset being used, even
|
||||
if the macro is in a field that was inherited from another preset. For example,
|
||||
if the ``Base`` preset sets variable ``PRESET_NAME`` to ``${presetName}``, and
|
||||
the ``Derived`` preset inherits from ``Base``, ``PRESET_NAME`` will be set to
|
||||
``Derived``. The ``${fileDir}`` macro as of preset version ``12`` is an
|
||||
exception to this rule.
|
||||
|
||||
It is an error to not put a closing brace at the end of a macro name. For
|
||||
example, ``${sourceDir`` is invalid. A dollar sign (``$``) followed by
|
||||
@@ -1304,9 +1306,38 @@ Recognized macros include:
|
||||
.. _`CMakePresets fileDir`:
|
||||
|
||||
``${fileDir}``
|
||||
Path to the directory containing the preset file which contains the macro.
|
||||
Path to the directory containing the preset file which defines the preset
|
||||
being used.
|
||||
|
||||
This is allowed in preset files specifying version ``4`` or above.
|
||||
|
||||
In preset files specifying version ``12`` or above, this *always* expands to
|
||||
the directory of the current preset file containing the macro, regardless
|
||||
of the preset being used.
|
||||
|
||||
For example, consider the following scenario.
|
||||
|
||||
* ``/path/to/CMakePresets.json`` includes
|
||||
``/path/to/subdir/CMakePresets.json``.
|
||||
* ``/path/to/subdir/CMakePresets.json`` defines preset ``Base``, which
|
||||
sets variable ``MY_DIR`` to ``${fileDir}``.
|
||||
* ``/path/to/CMakePresets.json`` defines preset ``Derived``, and
|
||||
``Derived`` inherits from ``Base``.
|
||||
|
||||
Under preset versions 4-11, ``MY_DIR`` will be set to ``/path/to/`` when
|
||||
using the ``Base`` preset, and ``/path/to/subdir/`` when using the
|
||||
``Derived`` preset.
|
||||
When ``/path/to/subdir/CMakePresets.json`` specifies version ``12`` or
|
||||
above, ``MY_DIR`` will always be set to ``/path/to/subdir/``, regardless of
|
||||
the preset being used.
|
||||
|
||||
.. note::
|
||||
|
||||
Since the ``${fileDir}`` macro in version 12 is expanded in the context of
|
||||
the current preset file, it is the version of the current file, rather than
|
||||
the version of the root file containing the preset being used, which
|
||||
enables this alternative behavior.
|
||||
|
||||
``${dollar}``
|
||||
A literal dollar sign (``$``).
|
||||
|
||||
@@ -1472,6 +1503,16 @@ they were added and a summary of the new features and changes is given below.
|
||||
representing :option:`--parallel <ctest --parallel>` with ``<jobs>``
|
||||
omitted.
|
||||
|
||||
``12``
|
||||
.. versionadded:: 4.4
|
||||
|
||||
* Changes to `Macro Expansion`_
|
||||
|
||||
* The `${fileDir} <CMakePresets fileDir_>`_ macro now always expands to
|
||||
the directory of preset file containing the ``${fileDir}`` macro,
|
||||
regardless of whether it is inherited by another preset in a different
|
||||
directory.
|
||||
|
||||
Schema
|
||||
======
|
||||
|
||||
|
||||
@@ -180,6 +180,25 @@
|
||||
"include": { "$ref": "#/definitions/include" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"$schema": { "$ref": "#/definitions/$schema" },
|
||||
"$comment": { "$ref": "#/definitions/$comment" },
|
||||
"version": {
|
||||
"const": 12,
|
||||
"description": "A required integer representing the version of the JSON schema."
|
||||
},
|
||||
"cmakeMinimumRequired": { "$ref": "#/definitions/cmakeMinimumRequiredV10" },
|
||||
"vendor": { "$ref": "#/definitions/vendor" },
|
||||
"configurePresets": { "$ref": "#/definitions/configurePresetsV10" },
|
||||
"buildPresets": { "$ref": "#/definitions/buildPresetsV11" },
|
||||
"testPresets": { "$ref": "#/definitions/testPresetsV11" },
|
||||
"packagePresets": { "$ref": "#/definitions/packagePresetsV10" },
|
||||
"workflowPresets": { "$ref": "#/definitions/workflowPresetsV10" },
|
||||
"include": { "$ref": "#/definitions/include" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
],
|
||||
"required": [
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
presets-fileDir
|
||||
---------------
|
||||
|
||||
* :manual:`cmake-presets(7)` files now support schema version 12. The
|
||||
:ref:`${fileDir} <CMakePresets fileDir>` macro now always expands to the
|
||||
directory of the preset file containing the ``${fileDir}`` macro, regardless
|
||||
of whether it is inherited by another preset in a different directory.
|
||||
@@ -27,6 +27,8 @@
|
||||
case ExpandMacroResult::Ignore: \
|
||||
out.reset(); \
|
||||
return true; \
|
||||
case ExpandMacroResult::Defer: \
|
||||
CM_FALLTHROUGH; \
|
||||
case ExpandMacroResult::Ok: \
|
||||
break; \
|
||||
} \
|
||||
@@ -54,6 +56,9 @@ using BaseMacroExpander = cmCMakePresetsGraphInternal::BaseMacroExpander;
|
||||
template <typename T>
|
||||
using PresetMacroExpander =
|
||||
cmCMakePresetsGraphInternal::PresetMacroExpander<T>;
|
||||
template <typename T>
|
||||
using ImmediateMacroExpander =
|
||||
cmCMakePresetsGraphInternal::ImmediateMacroExpander<T>;
|
||||
using cmCMakePresetsGraphInternal::ExpandMacros;
|
||||
|
||||
void InheritString(std::string& child, std::string const& parent)
|
||||
@@ -453,6 +458,8 @@ bool ExpandMacros(cmCMakePresetsGraph* graph, T const& preset,
|
||||
case ExpandMacroResult::Ignore:
|
||||
out.reset();
|
||||
return true;
|
||||
case ExpandMacroResult::Defer:
|
||||
CM_FALLTHROUGH;
|
||||
case ExpandMacroResult::Ok:
|
||||
break;
|
||||
}
|
||||
@@ -498,6 +505,30 @@ ExpandMacroResult VisitEnv(std::string& value, CycleStatus& status,
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool cmCMakePresetsGraphInternal::ExpandImmediateMacros(T& preset)
|
||||
{
|
||||
MacroExpanderVector macroExpanders{};
|
||||
macroExpanders.push_back(cm::make_unique<ImmediateMacroExpander<T>>(preset));
|
||||
cm::optional<T> out = preset;
|
||||
bool result = CheckExpandMacros(preset, out, macroExpanders);
|
||||
if (out.has_value()) {
|
||||
preset = out.value();
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
template bool cmCMakePresetsGraphInternal::ExpandImmediateMacros<
|
||||
ConfigurePreset>(ConfigurePreset&);
|
||||
template bool cmCMakePresetsGraphInternal::ExpandImmediateMacros<BuildPreset>(
|
||||
BuildPreset&);
|
||||
template bool cmCMakePresetsGraphInternal::ExpandImmediateMacros<TestPreset>(
|
||||
TestPreset&);
|
||||
template bool cmCMakePresetsGraphInternal::ExpandImmediateMacros<
|
||||
PackagePreset>(PackagePreset&);
|
||||
template bool cmCMakePresetsGraphInternal::ExpandImmediateMacros<
|
||||
WorkflowPreset>(WorkflowPreset&);
|
||||
|
||||
ExpandMacroResult cmCMakePresetsGraphInternal::ExpandMacros(
|
||||
std::string& out, MacroExpanderVector const& macroExpanders, int version)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@ enum class ExpandMacroResult
|
||||
{
|
||||
Ok,
|
||||
Ignore,
|
||||
Defer,
|
||||
Error,
|
||||
};
|
||||
|
||||
@@ -125,6 +126,49 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class ImmediateMacroExpander : public MacroExpander
|
||||
{
|
||||
T const& Preset;
|
||||
|
||||
public:
|
||||
ImmediateMacroExpander(T const& preset)
|
||||
: Preset(preset)
|
||||
{
|
||||
}
|
||||
ExpandMacroResult operator()(std::string const& macroNamespace,
|
||||
std::string const& macroName,
|
||||
std::string& macroOut,
|
||||
int version) const override
|
||||
{
|
||||
if (macroNamespace.empty()) {
|
||||
if (macroName == "fileDir") {
|
||||
if (version < 12) {
|
||||
return ExpandMacroResult::Defer;
|
||||
}
|
||||
macroOut +=
|
||||
cmSystemTools::GetParentDirectory(Preset.OriginFile->Filename);
|
||||
return ExpandMacroResult::Ok;
|
||||
}
|
||||
}
|
||||
return ExpandMacroResult::Defer;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
bool ExpandImmediateMacros(T& preset);
|
||||
|
||||
extern template bool ExpandImmediateMacros<
|
||||
cmCMakePresetsGraph::ConfigurePreset>(cmCMakePresetsGraph::ConfigurePreset&);
|
||||
extern template bool ExpandImmediateMacros<cmCMakePresetsGraph::BuildPreset>(
|
||||
cmCMakePresetsGraph::BuildPreset&);
|
||||
extern template bool ExpandImmediateMacros<cmCMakePresetsGraph::TestPreset>(
|
||||
cmCMakePresetsGraph::TestPreset&);
|
||||
extern template bool ExpandImmediateMacros<cmCMakePresetsGraph::PackagePreset>(
|
||||
cmCMakePresetsGraph::PackagePreset&);
|
||||
extern template bool ExpandImmediateMacros<
|
||||
cmCMakePresetsGraph::WorkflowPreset>(cmCMakePresetsGraph::WorkflowPreset&);
|
||||
|
||||
class NullCondition : public cmCMakePresetsGraph::Condition
|
||||
{
|
||||
bool Evaluate(MacroExpanderVector const& /*expanders*/, int /*version*/,
|
||||
|
||||
@@ -38,9 +38,10 @@ 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 = 11;
|
||||
constexpr int MAX_VERSION = 12;
|
||||
|
||||
struct CMakeVersion
|
||||
{
|
||||
@@ -591,6 +592,12 @@ bool cmCMakePresetsGraph::ReadJSONFile(std::string const& filename,
|
||||
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;
|
||||
@@ -642,6 +649,12 @@ bool cmCMakePresetsGraph::ReadJSONFile(std::string const& filename,
|
||||
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;
|
||||
@@ -666,6 +679,12 @@ bool cmCMakePresetsGraph::ReadJSONFile(std::string const& filename,
|
||||
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;
|
||||
@@ -710,6 +729,12 @@ bool cmCMakePresetsGraph::ReadJSONFile(std::string const& filename,
|
||||
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;
|
||||
@@ -731,6 +756,12 @@ bool cmCMakePresetsGraph::ReadJSONFile(std::string const& filename,
|
||||
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;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/TestVariable.cmake)
|
||||
|
||||
test_variable(TEST_FILE_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}/subdir")
|
||||
@@ -1,6 +1,15 @@
|
||||
{
|
||||
"version": 4,
|
||||
"include": [
|
||||
"subdir/FileDir.json"
|
||||
"@FileDir_include_subdir@"
|
||||
],
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "FileDirExt",
|
||||
"inherits": "FileDir",
|
||||
"cacheVariables": {
|
||||
"PRESET": "FileDirExt"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/TestVariable.cmake)
|
||||
|
||||
if(NOT "${PRESET}" STREQUAL "FileDirInclude")
|
||||
test_variable(CMAKE_BINARY_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}/subdir/build")
|
||||
test_variable(TEST_FILE_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}/subdir")
|
||||
endif()
|
||||
@@ -0,0 +1,9 @@
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/TestVariable.cmake)
|
||||
|
||||
if (PRESET STREQUAL "FileDir")
|
||||
test_variable(CMAKE_BINARY_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}/subdir/build")
|
||||
test_variable(TEST_FILE_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}/subdir")
|
||||
elseif (PRESET STREQUAL "FileDirExt")
|
||||
test_variable(CMAKE_BINARY_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}/build")
|
||||
test_variable(TEST_FILE_DIR "" "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
endif()
|
||||
@@ -1,5 +1,5 @@
|
||||
^CMake Error: Could not read presets from [^
|
||||
]*/Tests/RunCMake/CMakePresets/HighVersion:
|
||||
CMakePresets\.json:2: Unrecognized "version" 1000: must be >=1 and <=11
|
||||
CMakePresets\.json:2: Unrecognized "version" 1000: must be >=1 and <=12
|
||||
"version": 1000,
|
||||
\^$
|
||||
@@ -1,5 +1,5 @@
|
||||
^CMake Error: Could not read presets from [^
|
||||
]*/Tests/RunCMake/CMakePresets/LowVersion:
|
||||
CMakePresets\.json:2: Unrecognized "version" 0: must be >=1 and <=11
|
||||
CMakePresets\.json:2: Unrecognized "version" 0: must be >=1 and <=12
|
||||
"version": 0,
|
||||
\^
|
||||
@@ -377,10 +377,20 @@ run_cmake_presets(HostSystemNameFuture)
|
||||
|
||||
# Test ${fileDir} macro
|
||||
set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/FileDir.json.in")
|
||||
set(FileDir_include_subdir "subdir/FileDirv4.json")
|
||||
set(CMakePresets_EXTRA_FILES
|
||||
"${RunCMake_SOURCE_DIR}/subdir/FileDir.json.in"
|
||||
)
|
||||
run_cmake_presets(FileDir)
|
||||
"${RunCMake_SOURCE_DIR}/${FileDir_include_subdir}.in"
|
||||
)
|
||||
run_cmake_presets(FileDirv4 --preset FileDir)
|
||||
run_cmake_presets(FileDirv4 --preset FileDirExt)
|
||||
set(FileDir_include_subdir "subdir/FileDirv12.json")
|
||||
set(CMakePresets_EXTRA_FILES
|
||||
"${RunCMake_SOURCE_DIR}/${FileDir_include_subdir}.in"
|
||||
"${RunCMake_SOURCE_DIR}/subdir/FileDirInclude.json.in"
|
||||
)
|
||||
run_cmake_presets(FileDirv12 --preset FileDir)
|
||||
run_cmake_presets(FileDirv12 --preset FileDirExt)
|
||||
run_cmake_presets(FileDirv12 --preset FileDirInclude)
|
||||
unset(CMakePresets_EXTRA_FILES)
|
||||
set(CMakePresets_FILE "${RunCMake_SOURCE_DIR}/FileDirFuture.json.in")
|
||||
run_cmake_presets(FileDirFuture)
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": 9,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "FileDirInclude",
|
||||
"generator": "@RunCMake_GENERATOR@",
|
||||
"binaryDir": "${fileDir}/build",
|
||||
"cacheVariables": {
|
||||
"PRESET": "FileDirInclude"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"version": 12,
|
||||
"include": [
|
||||
"${fileDir}/FileDirInclude.json"
|
||||
],
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "FileDir",
|
||||
"generator": "@RunCMake_GENERATOR@",
|
||||
"binaryDir": "${fileDir}/build",
|
||||
"cacheVariables": {
|
||||
"TEST_FILE_DIR": "${fileDir}",
|
||||
"PRESET": "FileDir"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+3
-2
@@ -4,9 +4,10 @@
|
||||
{
|
||||
"name": "FileDir",
|
||||
"generator": "@RunCMake_GENERATOR@",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"binaryDir": "${fileDir}/build",
|
||||
"cacheVariables": {
|
||||
"TEST_FILE_DIR": "${fileDir}"
|
||||
"TEST_FILE_DIR": "${fileDir}",
|
||||
"PRESET": "FileDir"
|
||||
}
|
||||
}
|
||||
]
|
||||
Reference in new issue
Block a user