cmake: Add cached environment id that triggers reconfigure when changed

The opaque `CMAKE_SYSTEM_ENVIRONMENT_ID` variable is cached during the
first invocation of `cmake` and is a user-defined hint about the state
of the environment. When the value changes, `cmake` either does nothing,
emits a warning (by default), or refreshes the cache as if by `--fresh`.
The action is controlled by the `CMAKE_SYSTEM_ENVIRONMENT_ACTION`
environment variable (which is not cached).

Implements: #28025
This commit is contained in:
Aiden Woodruff
2026-08-24 15:06:35 -04:00
parent cb89e8d15f
commit fc3c71d11f
12 changed files with 206 additions and 0 deletions
@@ -0,0 +1,24 @@
CMAKE_SYSTEM_ENVIRONMENT_ACTION
-------------------------------
.. versionadded:: 4.5
.. include:: include/ENV_VAR.rst
Specify the action taken by :manual:`cmake(1)` when the value of
:envvar:`CMAKE_SYSTEM_ENVIRONMENT_ID` in the environment does not match the
cached value. If not set, the default is ``WARN``.
The value may be one of:
``IGNORE``
No action is taken.
``WARN``
A warning is emitted to the user.
``REFRESH``
The cache is automatically refreshed, as if by :cmake-option:`--fresh`.
Take care when setting this option, as all cache variables not specified in
a configure preset or the current invocation of :manual:`cmake(1)` will be
lost.
@@ -0,0 +1,15 @@
CMAKE_SYSTEM_ENVIRONMENT_ID
---------------------------
.. versionadded:: 4.5
.. include:: include/ENV_VAR.rst
Externally-defined environment identifier that is cached. The value is not
interpreted by :manual:`cmake(1)` and is a hint about the state of the
environment during the first configure. When the value changes,
:manual:`cmake(1)` by default emits a warning to the user indicating that the
environment has changed and introspection results may be out of date.
The default warning behavior can be modified by setting
:envvar:`CMAKE_SYSTEM_ENVIRONMENT_ACTION`.
+2
View File
@@ -32,6 +32,8 @@ Environment Variables that Change Behavior
/envvar/CMAKE_PROGRAM_PATH
/envvar/CMAKE_TLS_VERIFY
/envvar/CMAKE_TLS_VERSION
/envvar/CMAKE_SYSTEM_ENVIRONMENT_ACTION
/envvar/CMAKE_SYSTEM_ENVIRONMENT_ID
/envvar/NO_COLOR
/envvar/SSL_CERT_DIR
/envvar/SSL_CERT_FILE
+5
View File
@@ -219,6 +219,11 @@ Options
from any previous run will be removed. The download, update, and patch
steps will therefore be forced to re-execute.
.. versionadded:: 4.5
See the :envvar:`CMAKE_SYSTEM_ENVIRONMENT_ID` and
:envvar:`CMAKE_SYSTEM_ENVIRONMENT_ACTION` environment variables for a
mechanism to hint to :program:`cmake` when ``--fresh`` may be necessary.
.. option:: -L[A][H]
List non-advanced cached variables.
+7
View File
@@ -0,0 +1,7 @@
CMAKE_SYSTEM_ENVIRONMENT_ID
---------------------------
* The :envvar:`CMAKE_SYSTEM_ENVIRONMENT_ID` environment variable was added to
hint to :manual:`cmake(1)` when :cmake-option:`--fresh` may be necessary,
along with :envvar:`CMAKE_SYSTEM_ENVIRONMENT_ACTION` to control what happens
when :envvar:`CMAKE_SYSTEM_ENVIRONMENT_ID` changes.
+80
View File
@@ -3033,6 +3033,68 @@ void cmake::InitializeInstrumentation()
#endif
}
int cmake::HandleDifferentSystemEnvironmentId(std::string envId,
std::string cachedId)
{
enum class Action
{
Ignore,
Warn,
Refresh,
} action = Action::Warn;
static std::string const actionEnvName = "CMAKE_SYSTEM_ENVIRONMENT_ACTION";
if (cmSystemTools::HasEnv(actionEnvName)) {
std::string actionEnv;
cmSystemTools::GetEnv(actionEnvName, actionEnv);
if (actionEnv == "IGNORE") {
action = Action::Ignore;
} else if (actionEnv == "WARN") {
action = Action::Warn;
} else if (actionEnv == "REFRESH") {
action = Action::Refresh;
} else {
this->IssueMessage(
MessageType::FATAL_ERROR,
cmStrCat("Unsupported ", actionEnvName, " '", actionEnv, '\''));
return -1;
}
}
switch (action) {
case Action::Ignore:
break;
case Action::Warn: {
std::string msg = cmStrCat(
"CMAKE_SYSTEM_ENVIRONMENT_ID: ", envId,
"\nDoes not match the previous value: ", cachedId,
"\nThe configure results are probably outdated. Consider running"
" cmake with --fresh, removing the CMakeCache.txt file and"
" CMakeFiles directory, or choosing a different binary"
" directory.");
this->IssueMessage(MessageType::WARNING, msg);
break;
}
case Action::Refresh: {
std::string msg =
cmStrCat("CMAKE_SYSTEM_ENVIRONMENT_ID: ", envId,
"\nDoes not match the previous value: ", cachedId,
"\nThe cache will be refreshed automatically.");
this->IssueMessage(MessageType::MESSAGE, msg);
this->DeleteCache(this->GetHomeOutputDirectory());
if (this->LoadCache() < 0) {
cmSystemTools::Error(
"Error executing cmake::LoadCache(). Aborting.\n");
return -1;
}
this->AddCacheEntry(
"CMAKE_SYSTEM_ENVIRONMENT_ID", envId,
"Opaque identifier for the current system environment",
cmStateEnums::INTERNAL);
break;
}
}
return 0;
}
// handle a command line invocation
int cmake::Run(std::vector<std::string> const& args, bool noconfigure)
{
@@ -3079,6 +3141,24 @@ int cmake::Run(std::vector<std::string> const& args, bool noconfigure)
cmSystemTools::Error("Error executing cmake::LoadCache(). Aborting.\n");
return -1;
}
std::string const idKey = "CMAKE_SYSTEM_ENVIRONMENT_ID";
cmValue cachedEnvId = this->State->GetInitializedCacheValue(idKey);
std::string sysEnvId;
cmSystemTools::GetEnv(idKey, sysEnvId);
if (cachedEnvId) {
if (sysEnvId != *cachedEnvId) {
if (this->HandleDifferentSystemEnvironmentId(sysEnvId, *cachedEnvId) <
0) {
// Failed to LoadCache()
return -1;
}
}
} else {
this->AddCacheEntry(
idKey, sysEnvId,
"Opaque identifier for the current system environment",
cmStateEnums::INTERNAL);
}
} else {
if (this->FreshCache) {
cmSystemTools::Error("--fresh allowed only when configuring a project");
+8
View File
@@ -712,6 +712,14 @@ protected:
void RunCheckForUnusedVariables();
int HandleDeleteCacheVariables(
std::map<std::string, std::string> const& var);
/**
* Warn about different CMAKE_SYSTEM_ENVIRONMENT_ID, ignore, or refresh the
* cache depending on CMAKE_SYSTEM_ENVIRONMENT_ACTION.
*
* @return 0 on success or -1 if LoadCache fails.
*/
int HandleDifferentSystemEnvironmentId(std::string envId,
std::string cachedId);
using RegisteredGeneratorsVector =
std::vector<std::unique_ptr<cmGlobalGeneratorFactory>>;
@@ -135,3 +135,52 @@ block()
"-DCMAKE_TOOLCHAIN_FILE=foo.cmake"
)
endblock()
unset(ENV{CMAKE_SYSTEM_ENVIRONMENT_ID})
unset(ENV{CMAKE_SYSTEM_ENVIRONMENT_ACTION})
block()
set(ENV{CMAKE_SYSTEM_ENVIRONMENT_ID} original)
run_cmake(SystemEnvironmentId)
set(RunCMake_TEST_NO_CLEAN 1)
# Unchanged environment id should not emit a warning.
set(RunCMake_TEST_VARIANT_DESCRIPTION Unchanged)
run_cmake(SystemEnvironmentId)
set(ENV{CMAKE_SYSTEM_ENVIRONMENT_ID} different)
# Changed environment id should emit a warning by default.
set(RunCMake_TEST_VARIANT_DESCRIPTION Changed)
set(RunCMake-stderr-file SystemEnvironmentIdChanged-stderr.txt)
run_cmake(SystemEnvironmentId)
# Changed environment id with IGNORE action should not emit a warning.
set(RunCMake_TEST_VARIANT_DESCRIPTION ChangedIgnore)
set(ENV{CMAKE_SYSTEM_ENVIRONMENT_ACTION} IGNORE)
unset(RunCMake-stderr-file)
run_cmake(SystemEnvironmentId)
# Changed environment id with WARN action should not emit a warning.
set(RunCMake_TEST_VARIANT_DESCRIPTION ChangedWarn)
set(ENV{CMAKE_SYSTEM_ENVIRONMENT_ACTION} WARN)
set(RunCMake-stderr-file SystemEnvironmentIdChanged-stderr.txt)
run_cmake(SystemEnvironmentId)
# Changed environment id with REFRESH action should refresh the cache.
set(RunCMake_TEST_VARIANT_DESCRIPTION ChangedRefresh)
set(ENV{CMAKE_SYSTEM_ENVIRONMENT_ACTION} REFRESH)
set(RunCMake-stderr-file SystemEnvironmentIdChangedRefresh-stderr.txt)
run_cmake(SystemEnvironmentId)
# Unrecognized CMAKE_SYSTEM_ENVIRONMENT_ACTION should error.
set(RunCMake_TEST_VARIANT_DESCRIPTION InvalidAction)
set(ENV{CMAKE_SYSTEM_ENVIRONMENT_ID} foo)
set(ENV{CMAKE_SYSTEM_ENVIRONMENT_ACTION} bar)
set(RunCMake_TEST_EXPECT_RESULT 1)
set(RunCMake-stderr-file SystemEnvironmentIdInvalidAction-stderr.txt)
run_cmake(SystemEnvironmentId)
endblock()
unset(ENV{CMAKE_SYSTEM_ENVIRONMENT_ACTION})
unset(ENV{CMAKE_SYSTEM_ENVIRONMENT_ID})
@@ -0,0 +1,8 @@
CMake Warning:
CMAKE_SYSTEM_ENVIRONMENT_ID: different
Does not match the previous value: original
The configure results are probably outdated\. ?Consider running cmake with
--fresh, removing the CMakeCache\.txt file and CMakeFiles directory, or
choosing a different binary directory\.
@@ -0,0 +1,6 @@
CMake Warning:
CMAKE_SYSTEM_ENVIRONMENT_ID: different
Does not match the previous value: original
The cache will be refreshed automatically\.
@@ -0,0 +1,2 @@
CMake Error:
Unsupported CMAKE_SYSTEM_ENVIRONMENT_ACTION 'bar'