1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-15 03:48:02 +08:00

set/unset commands: add CACHE{variable} syntax support

Add the support of CACHE{variable} syntax to enable:
* better consistency with other commands which use this syntax
* more flexibility regarding cache variable options
This commit is contained in:
Marc Chevrier
2025-08-04 17:06:19 +02:00
parent 185a4e6c5d
commit 8af64914d6
16 changed files with 276 additions and 29 deletions

View File

@@ -44,15 +44,16 @@ Set Cache Entry
^^^^^^^^^^^^^^^
.. signature::
set(<variable> <value>... CACHE <type> <docstring> [FORCE])
set(CACHE{<variable>} [TYPE <type>] [HELP <helpstring>...] [FORCE]
VALUE [<value>...])
:target: CACHE
Sets the given cache ``<variable>`` (cache entry). Since cache entries
are meant to provide user-settable values this does not overwrite
existing cache entries by default. Use the ``FORCE`` option to
overwrite existing entries.
.. versionadded:: 4.2
The ``<type>`` must be specified as one of:
Sets the given cache ``<variable>`` (cache entry). The options are:
``TYPE <type>``
Specify the type of the cache entry. The ``<type>`` must be one of:
``BOOL``
Boolean ``ON/OFF`` value.
@@ -77,9 +78,26 @@ Set Cache Entry
They may be used to store variables persistently across runs.
Use of this type implies ``FORCE``.
The ``<docstring>`` must be specified as a line of text
providing a quick summary of the option
for presentation to :manual:`cmake-gui(1)` users.
If ``TYPE`` is not specified, if the cache variable already exist and its
type is not ``UNINITIALIZED``, the type previously specified will be kept
otherwise, ``STRING`` will be used.
``HELP <helpstring>...``
The ``<helpstring>`` must be specified as a line of text providing a quick
summary of the option for presentation to :manual:`cmake-gui(1)` users. If
more than one string is given, they are concatenated into a single string
with no separator between them.
If ``HELP`` is not specified, an empty string will be used.
``FORCE``
Since cache entries are meant to provide user-settable values this does not
overwrite existing cache entries by default. Use the ``FORCE`` option to
overwrite existing entries.
``VALUE <value>...``
List of values to be set to the cache ``<variable>``. This argument must be
always the last one.
If the cache entry does not exist prior to the call or the ``FORCE``
option is given then the cache entry will be set to the given value.
@@ -101,6 +119,13 @@ Set Cache Entry
then the ``set`` command will treat the path as relative to the
current working directory and convert it to an absolute path.
.. signature::
set(<variable> <value>... CACHE <type> <docstring> [FORCE])
:target: CACHE_legacy
This signature is supported for compatibility purpose. Use preferably the
other one.
Set Environment Variable
^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -3,37 +3,54 @@ unset
Unset a variable, cache variable, or environment variable.
Unset Normal Variable or Cache Entry
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Unset Normal Variable
^^^^^^^^^^^^^^^^^^^^^
.. code-block:: cmake
.. signature::
unset(<variable> [PARENT_SCOPE])
:target: normal
unset(<variable> [CACHE | PARENT_SCOPE])
Removes a normal variable from the current scope, causing it
to become undefined.
Removes a normal variable from the current scope, causing it
to become undefined. If ``CACHE`` is present, then a cache variable
is removed instead of a normal variable.
If ``PARENT_SCOPE`` is present then the variable is removed from the scope
above the current scope. See the same option in the :command:`set` command
for further details.
If ``PARENT_SCOPE`` is present then the variable is removed from the scope
above the current scope. See the same option in the :command:`set` command
for further details.
.. include:: include/UNSET_NOTE.rst
Unset Cache Entry
^^^^^^^^^^^^^^^^^
.. signature::
unset(CACHE{<variable>})
:target: CACHE
.. versionadded:: 4.2
Removes ``<variable>`` from the cache, causing it to become undefined.
.. signature::
unset(<variable> CACHE)
:target: CACHE_legacy
This signature is supported for compatibility purpose. Use preferably the
other one.
Unset Environment Variable
^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: cmake
.. signature::
unset(ENV{<variable>})
:target: ENV
Removes ``<variable>`` from the currently available
:manual:`Environment Variables <cmake-env-variables(7)>`.
Subsequent calls of ``$ENV{<variable>}`` will return the empty string.
Removes ``<variable>`` from the currently available
:manual:`Environment Variables <cmake-env-variables(7)>`.
Subsequent calls of ``$ENV{<variable>}`` will return the empty string.
This command affects only the current CMake process, not the process
from which CMake was called, nor the system environment at large,
nor the environment of subsequent build or test processes.
This command affects only the current CMake process, not the process
from which CMake was called, nor the system environment at large,
nor the environment of subsequent build or test processes.
See Also
^^^^^^^^

View File

@@ -0,0 +1,5 @@
set-unset-CACHE
---------------
* The :command:`set` and :command:`unset` commands gain the support of the
``CACHE{<variable>}`` syntax to handle cache entries.

View File

@@ -2,6 +2,14 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmSetCommand.h"
#include <algorithm>
#include <cm/optional>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmArgumentParser.h"
#include "cmArgumentParserTypes.h"
#include "cmExecutionStatus.h"
#include "cmList.h"
#include "cmMakefile.h"
@@ -22,8 +30,8 @@ bool cmSetCommand(std::vector<std::string> const& args,
return false;
}
// watch for ENV signatures
auto const& variable = args[0]; // VAR is always first
// watch for ENV{} signature
if (cmHasLiteralPrefix(variable, "ENV{") && variable.size() > 5) {
// what is the variable name
auto const& varName = variable.substr(4, variable.size() - 5);
@@ -58,6 +66,81 @@ bool cmSetCommand(std::vector<std::string> const& args,
return true;
}
// watch for CACHE{} signature
if (cmHasLiteralPrefix(variable, "CACHE{") && variable.size() > 7 &&
cmHasLiteralSuffix(variable, "}")) {
// what is the variable name
auto const& varName = variable.substr(6, variable.size() - 7);
// VALUE handling
auto valueArg = std::find(args.cbegin() + 1, args.cend(), "VALUE");
if (valueArg == args.cend()) {
status.SetError("Required argument 'VALUE' is missing.");
return false;
}
auto value = cmMakeRange(valueArg + 1, args.cend());
// Handle options
struct Arguments : public ArgumentParser::ParseResult
{
ArgumentParser::Continue validateTypeValue(cm::string_view type)
{
if (!cmState::StringToCacheEntryType(std::string{ type },
this->Type)) {
this->AddKeywordError("TYPE"_s,
cmStrCat("Invalid value: ", type, '.'));
}
return ArgumentParser::Continue::No;
}
cmStateEnums::CacheEntryType Type = cmStateEnums::UNINITIALIZED;
cm::optional<ArgumentParser::NonEmpty<std::vector<std::string>>> Help;
bool Force = false;
};
static auto const optionsParser =
cmArgumentParser<Arguments>{}
.Bind("TYPE"_s, &Arguments::validateTypeValue)
.Bind("HELP"_s, &Arguments::Help)
.Bind("FORCE"_s, &Arguments::Force);
std::vector<std::string> unrecognizedArguments;
auto parsedArgs = optionsParser.Parse(
cmMakeRange(args.cbegin() + 1, valueArg), &unrecognizedArguments);
if (!unrecognizedArguments.empty()) {
status.SetError(cmStrCat("Called with unsupported argument(s): ",
cmJoin(unrecognizedArguments, ", "_s), '.'));
return false;
}
if (parsedArgs.MaybeReportError(status.GetMakefile())) {
return false;
}
// see if this is already in the cache
cmState* state = status.GetMakefile().GetState();
cmValue existingValue = state->GetCacheEntryValue(varName);
cmStateEnums::CacheEntryType existingType =
state->GetCacheEntryType(varName);
if (parsedArgs.Type == cmStateEnums::UNINITIALIZED) {
parsedArgs.Type = existingType == cmStateEnums::UNINITIALIZED
? cmStateEnums::STRING
: existingType;
}
std::string help = parsedArgs.Help
? cmJoin(*parsedArgs.Help, "")
: *state->GetCacheEntryProperty(varName, "HELPSTRING");
if (existingValue && existingType != cmStateEnums::UNINITIALIZED) {
// if the set is trying to CACHE the value but the value
// is already in the cache and the type is not internal
// then leave now without setting any definitions in the cache
// or the makefile
if (parsedArgs.Type != cmStateEnums::INTERNAL && !parsedArgs.Force) {
return true;
}
}
status.GetMakefile().AddCacheDefinition(
varName,
valueArg == args.cend() ? *existingValue : cmList::to_string(value),
help, parsedArgs.Type, parsedArgs.Force);
return true;
}
// SET (VAR) // Removes the definition of VAR.
if (args.size() == 1) {
status.GetMakefile().RemoveDefinition(variable);

View File

@@ -28,6 +28,14 @@ bool cmUnsetCommand(std::vector<std::string> const& args,
#endif
return true;
}
// unset(CACHE{VAR})
if (cmHasLiteralPrefix(variable, "CACHE{") && variable.size() > 7 &&
cmHasLiteralSuffix(variable, "}")) {
// get the variable name
auto const& varName = variable.substr(6, variable.size() - 7);
status.GetMakefile().RemoveCacheDefinition(varName);
return true;
}
// unset(VAR)
if (args.size() == 1) {
status.GetMakefile().RemoveDefinition(variable);

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,11 @@
CMake Error at CacheMissingArguments.cmake:[0-9]+ \(set\):
Error after keyword "HELP":
missing required value
Error after keyword "TYPE":
missing required value
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
set(CACHE{VAR} TYPE HELP VALUE)

View File

@@ -0,0 +1,73 @@
set(CACHE{VAR1} VALUE v1 v2)
if(NOT DEFINED CACHE{VAR1})
message(SEND_ERROR "CACHE{VAR1} not defined")
endif()
get_property(type CACHE VAR1 PROPERTY TYPE)
if(NOT type STREQUAL "STRING")
message(SEND_ERROR "CACHE{VAR1} TYPE should be \"STRING\", not \"${type}\"")
endif()
set(CACHE{VAR1} VALUE v3 v4)
if(NOT "$CACHE{VAR1}" STREQUAL "v1;v2")
message(SEND_ERROR "CACHE{VAR1} value should be \"v1;v2\", not \"$CACHE{VAR1}\"")
endif()
set(CACHE{VAR1} FORCE VALUE v3 v4)
if(NOT "$CACHE{VAR1}" STREQUAL "v3;v4")
message(SEND_ERROR "CACHE{VAR1} value should be \"v3;v4\", not \"$CACHE{VAR1}\"")
endif()
set(CACHE{VAR1} FORCE VALUE)
if(NOT DEFINED CACHE{VAR1})
message(SEND_ERROR "CACHE{VAR1} not defined")
endif()
if(NOT "$CACHE{VAR1}" STREQUAL "")
message(SEND_ERROR "CACHE{VAR1} value should be \"\", not \"$CACHE{VAR1}\"")
endif()
unset(CACHE{VAR1})
if(DEFINED CACHE{VAR1})
message(SEND_ERROR "CACHE{VAR1} defined")
endif()
set(CACHE{VAR2} TYPE PATH HELP "help1 " "help2" VALUE v1 v2)
if(NOT DEFINED CACHE{VAR2})
message(SEND_ERROR "CACHE{VAR2} not defined")
endif()
get_property(type CACHE VAR2 PROPERTY TYPE)
if(NOT type STREQUAL "PATH")
message(SEND_ERROR "CACHE{VAR2} TYPE should be \"PATH\", not \"${type}\"")
endif()
get_property(help CACHE VAR2 PROPERTY HELPSTRING)
if(NOT help STREQUAL "help1 help2")
message(SEND_ERROR "CACHE{VAR2} HELP should be \"help1 help2\", not \"${help}\"")
endif()
if(NOT "$CACHE{VAR2}" STREQUAL "v1;v2")
message(SEND_ERROR "CACHE{VAR2} value should be \"v1;v2\", not \"$CACHE{VAR2}\"")
endif()
set(CACHE{VAR2} FORCE VALUE v3 v4)
if(NOT "$CACHE{VAR2}" STREQUAL "v3;v4")
message(SEND_ERROR "CACHE{VAR2} value should be \"v3;v4\", not \"$CACHE{VAR2}\"")
endif()
get_property(type CACHE VAR2 PROPERTY TYPE)
if(NOT type STREQUAL "PATH")
message(SEND_ERROR "CACHE{VAR2} TYPE should be \"PATH\", not \"${type}\"")
endif()
get_property(help CACHE VAR2 PROPERTY HELPSTRING)
if(NOT help STREQUAL "help1 help2")
message(SEND_ERROR "CACHE{VAR2} HELP should be \"help1 help2\", not \"${help}\"")
endif()
unset(CACHE{VAR2})
if(DEFINED CACHE{VAR2})
message(SEND_ERROR "CACHE{VAR2} defined")
endif()
set(CACHE{VAR3} TYPE INTERNAL HELP "help" VALUE v1 v2)
if(NOT DEFINED CACHE{VAR3})
message(SEND_ERROR "CACHE{VAR3} not defined")
endif()
set(CACHE{VAR3} VALUE v3 v4)
if(NOT "$CACHE{VAR3}" STREQUAL "v3;v4")
message(SEND_ERROR "CACHE{VAR3} value should be \"v3;v4\", not \"$CACHE{VAR3}\"")
endif()
get_property(type CACHE VAR3 PROPERTY TYPE)
if(NOT type STREQUAL "INTERNAL")
message(SEND_ERROR "CACHE{VAR3} TYPE should be \"INTERNAL\", not \"${type}\"")
endif()

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,4 @@
CMake Error at CacheUnknownArguments.cmake:[0-9]+ \(set\):
set Called with unsupported argument\(s\): FOO.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
set(CACHE{VAR} TYPE STRING FOO HELP "help" VALUE)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,6 @@
CMake Error at CacheWrongTYPE.cmake:[0-9]+ \(set\):
Error after keyword "TYPE":
Invalid value: FOO.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)

View File

@@ -0,0 +1,2 @@
set(CACHE{VAR} TYPE FOO VALUE)

View File

@@ -6,3 +6,9 @@ run_cmake(ParentPulling)
run_cmake(ParentPullingRecursive)
run_cmake(UnknownCacheType)
run_cmake(ExtraEnvValue)
# set(CACHE{}) syntax
run_cmake(CacheUnknownArguments)
run_cmake(CacheMissingArguments)
run_cmake(CacheWrongTYPE)
run_cmake(CacheSetUnset)