CMake configure step vs CMakeCache.txt: when do cached check results invalidate?
0 reputation · 05 Dec 2022, 18:28 UTC
0 reputation · 05 Dec 2022, 18:28 UTC
The CMake configure step runs check modules such as check_cxx_source_compiles and stores their boolean outcomes in CMakeCache.txt. On subsequent reconfigures, CMake skips a check if its cache variable already exists, which means the result can persist even when the compiler, CMAKE_REQUIRED_FLAGS, or the toolchain file has changed.
There is no built-in dependency tracking that ties a cached check result to the exact compiler invocation or flag set used when the check originally ran. Switching compilers, adjusting required flags, or editing a cross-compilation toolchain does not automatically invalidate the corresponding cache entries. Projects must decide manually when to delete CMakeCache.txt or individual entries to force re-evaluation.
Understand the precise conditions under which a cached check result remains valid versus when it becomes stale, so that a build system can reliably detect toolchain or flag changes without requiring a full cache wipe.
try_compile or check_* result should be reused?try_compile signature with SOURCE_FROM_CONTENT and CMAKE_TRY_COMPILE_TARGET_TYPE alter the invalidation semantics?33275 reputation · 06 Dec 2022, 03:23 UTC
CMake reuses a cached check_*/try_compile result whenever the result variable already exists in CMakeCache.txt. That is the entire reuse rule: the cache key is the variable name you chose, not a fingerprint of the compiler, flags, or toolchain used to produce it. Changing CMAKE_CXX_FLAGS, CMAKE_REQUIRED_FLAGS, the compiler path, or the toolchain file does not invalidate the entry. There is no built-in dependency tracking to hook into, so invalidation must be explicit.
Modules like CheckCXXSourceCompiles wrap try_compile and guard it with a simple test: if the result variable is already defined in the cache, skip the probe and report the stored value. You can confirm this yourself:
cmake -LAH . | grep -A2 HAVE_STD_FORMATThen change only CMAKE_REQUIRED_FLAGS and reconfigure — the same value comes back with no compile probe in the log. The internal bookkeeping entries CMake writes alongside results are implementation details; they do not record the probe's full command line in a way you can rely on, and they can change between releases. Do not build automation around them.
No. SOURCE_FROM_CONTENT and CMAKE_TRY_COMPILE_TARGET_TYPE (e.g., STATIC_LIBRARY to avoid linking during cross-compiles) change how the probe project is generated and built, not the reuse semantics. If the result variable is already cached, the probe is skipped regardless of which signature produced it. Recent CMake releases have expanded try_compile controls and variable propagation into the probe project, but exact names and behavior are release-dependent — check cmake --help-command try_compile on your installed version before encoding any of it in CI logic.
Since CMake won't detect staleness for you, the reliable pattern is to make staleness impossible by construction:
HAVE_FOO, use names that encode the toolchain-relevant context, e.g. HAVE_FOO_${CMAKE_CXX_COMPILER_ID}_${ABI_TAG}. When the compiler or sysroot changes, the variable name changes, so the probe reruns naturally while unrelated cached results survive.cmake -U 'HAVE_*' (or a narrower prefix) clears just those entries on the next configure — much cheaper than a full wipe:cmake -U 'HAVE_*' -U 'COMPILES_*' .
cmake . # probes rerun, everything else stays cachedReserve the full wipe for toolchain identity changes. Switching between host and cross builds, changing CMAKE_TOOLCHAIN_FILE, or moving to a different compiler family can silently affect dozens of cached entries beyond your own checks (compiler ABI detection, platform variables). A fresh build directory is the safe default there:
cmake -S . -B build-arm --toolchain arm.cmakeTreat any cached HAVE_* or COMPILES_* value as suspect after upgrading compilers, changing CMAKE_REQUIRED_LIBRARIES or CMAKE_REQUIRED_DEFINITIONS, switching sysroots, or toggling CMAKE_TRY_COMPILE_TARGET_TYPE. In CI, the cheap insurance is a fresh build directory per toolchain; on developer machines, document the cmake -U prefix your project uses so flag experiments don't leave stale results behind.
One caveat worth flagging: the namespacing trick only works if every check in the project (including third-party modules you vendor) follows the convention. If a dependency hard-codes HAVE_FOO, targeted invalidation of that entry is your only option — which is worth verifying with a grep over your tree before you rely on the pattern.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.