Configure, generate, cache and policies

CMake · Expert · 15 min

Mysterious 'my change has no effect' problems are almost always about the cache or a policy.

A CMake run has distinct phases:

  • Configure: the CMake language runs top to bottom. Commands like find_package and try_compile probe the system; targets and properties are recorded. message() output appears here.
  • Generate: generator expressions are evaluated per target and configuration, and the native build files are written.
  • Build: the native tool runs; CMake itself is involved only to re-run configure when a CMakeLists.txt changes, or for custom commands.

The cache (build/CMakeCache.txt) stores settings that persist between runs: everything passed with -D, option() values, and results of searches such as find_library. A cache variable (set(X value CACHE STRING "doc")) is only written if it doesn't already exist, so editing its default in CMakeLists.txt has no effect on an existing build tree. Change it with -D, ccmake/cmake-gui, or delete the cache (cmake --fresh in 3.24+). A normal variable with the same name shadows the cache entry in that scope.

Policies preserve backward compatibility. When CMake changes behaviour, it adds a policy CMPxxxx; projects get the old behaviour until they declare a minimum version that includes the new one. cmake_minimum_required(VERSION 3.25...4.1) means "works with 3.25, and uses new behaviour up to 4.1". Warnings about unset policies are hints to raise that range, not noise to suppress.

Example

cmake_minimum_required(VERSION 3.25...4.1)
project(cachedemo LANGUAGES NONE)

set(GREETING "hello" CACHE STRING "Greeting to print")   # only if not cached yet
option(USE_FAST_PATH "Enable the fast path" OFF)
message(STATUS "GREETING=${GREETING} USE_FAST_PATH=${USE_FAST_PATH}")

cmake_policy(GET CMP0135 p)          # DOWNLOAD_EXTRACT_TIMESTAMP behaviour
message(STATUS "CMP0135 is ${p}")    # NEW, because 3.25 > 3.24 introduced it

# $ cmake -S . -B build                      -> GREETING=hello USE_FAST_PATH=OFF
#   (edit the default to "hi" in CMakeLists.txt)
# $ cmake -S . -B build                      -> GREETING=hello   (cache wins!)
# $ cmake -S . -B build -DGREETING=hey       -> GREETING=hey
# $ cmake -S . -B build --fresh              -> GREETING=hi      (cache discarded)
# $ cmake -L -N build | grep GREETING        -> GREETING:STRING=hi

Remember

  • Configure runs the script, generate evaluates genexes and writes build files
  • Cache variables keep their first value; change them with -D or --fresh
  • cmake_minimum_required(VERSION min...max) sets the policy behaviour

Watch out for

  • Editing a cache variable's default in CMakeLists.txt and expecting existing build trees to pick it up.

Check yourself

  1. You change set(OPT ON CACHE BOOL "") to OFF and reconfigure an existing build. What is OPT?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: cmake.org/cmake/help/latest/manual/cmake-policies.7.html