Issue
From the CMake set()
command's documentation:
If is INTERNAL, the cache variable is marked as internal, and will not be shown to the user in tools like cmake-gui. This is intended for values that should be persisted in the cache, but which users should not normally change. INTERNAL implies FORCE.
By "INTERNAL implies FORCE", does this mean that FORCE
is applied, or does this read that any use of INTERNAL
should also apply FORCE
?
Said another way- are the following equivalent?
set(foo "value" CACHE INTERNAL "description" FORCE)
set(foo "value" CACHE INTERNAL "description")
Solution
Does cmake set INTERNAL apply FORCE?
Yes in most cases.
FORCE
isn't currently applied implicitly with INTERNAL
during the initial configuration of a generated buildsystem if the cache variable is specified via commandline with no type (granted- this is a strange edge-case- one has to ask why a user would think to set something that's supposed be internal via commandline at all).
From just trying it:
cmake_minimum_required(VERSION 3.25)
project(hello)
# uncomment for first test
# set(foo OFF CACHE BOOL "set to OFF")
# set(bar OFF CACHE BOOL "set to OFF")
set(foo ON CACHE INTERNAL "set to OFF on commandline")
set(bar ON CACHE INTERNAL "set to OFF on commandline" FORCE)
message("foo: $CACHE{foo}")
message("bar: $CACHE{bar}")
If you set the cache entries prior via set()
:
$ cmake -S . -B build --fresh
<...>
foo: ON
bar: ON
<...>
If you set the cache entries prior via commandline with types specified:
$ cmake -S . -B build --fresh -Dfoo:BOOL=OFF -Dbar:BOOL=OFF
<...>
foo: ON
bar: ON
<...>
If you set the cache entries prior via commandline with types unspecified:
$ cmake -S . -B build --fresh -Dfoo=OFF -Dbar=OFF
<...>
foo: OFF
bar: ON
<...>
If I'm reading correctly, this little kink has been mentioned on the Kitware issue tracker for CMake: set: CACHE INTERNAL type initialization drops value (#22734). See Brad King (one of the Maintainers)'s reply here:
The problem is in the code here that is used to initialize the type of a cache entry that was defined without one. It accounts for
FORCE
meaning that the old value won't be preserved, but not for typeINTERNAL
meaning "force" implicitly. This patch fixes it:diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 23b97ed789..db7d048e01 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -1911,7 +1911,7 @@ void cmMakefile::AddCacheDefinition(const std::string& name, const char* value, cmStateEnums::UNINITIALIZED)) { // if this is not a force, then use the value from the cache // if it is a force, then use the value being passed in - if (!force) { + if (!force && type != cmStateEnums::INTERNAL) { value = existingValue->c_str(); } if (type == cmStateEnums::PATH || type == cmStateEnums::FILEPATH) {
Further investigation is needed to determine other possible effects of that change and whether it needs a policy. This probably hasn't been noticed often because the
INTERNAL
type is not typically meant to be set publicly. As a workaround, one can pass the type explicitly on the command line:-Dvar:INTERNAL=value
. That skips the problematic code path.
Answered By - starball Answer Checked By - David Goodson (WPSolving Volunteer)