Lesson 16 · Organize your project

Options and cache variables

Make your build configurable with option() and cache variables. How cmake -D works, why first-set-wins, and cmake-gui.

Not every build is the same. Sometimes you want tests, sometimes not. Sometimes debug, sometimes release. Options and cache variables are how a project exposes switches to the person running CMake.

option(), the simple switch

option(MYAPP_BUILD_TESTS "Build the test suite" ON)
  • The first argument is the variable name. By convention PROJECTNAME_SOMETHING, prefixed so it never collides with other projects.
  • The second is the help text shown in cmake-gui and ccmake.
  • The third is the default, ON or OFF.

Use it:

if(MYAPP_BUILD_TESTS)
    add_subdirectory(tests)
endif()

The person configuring can flip it:

cmake -S . -B build -DMYAPP_BUILD_TESTS=OFF

Cache variables, options with a value

An option() is really a cache variable of type BOOL. The general form covers everything else:

set(MYAPP_LOG_LEVEL "info" CACHE STRING "Log verbosity: debug | info | warn | error")

Cache variables have two properties that normal variables do not:

  1. They persist. Stored in build/CMakeCache.txt, remembered across configurations.
  2. First set wins. Once the cache holds a value, from -D or a previous run, later set(... CACHE ...) calls do not overwrite it. They only set it if it is missing.

Why

“First set wins” is what makes -D work. Your command-line value is written to the cache once, and every later configure respects it, even when a third-party CMakeLists.txt tries to set the same variable with its own default. The cache is the user’s voice, and it outranks project defaults. Configure once, forget.

Pitfall

To change a cached value, pass -D again, cmake -S . -B build -DMYAPP_LOG_LEVEL=debug, or delete the entry from the cache. Editing the set(... CACHE ...) default in your CMakeLists.txt has no effect on an existing build directory. The old value is still cached. Fresh build dirs pick up new defaults.

The cache variable everyone uses, build type

cmake -S . -B build -DCMAKE_BUILD_TYPE=Release

CMAKE_BUILD_TYPE selects debug or release flags. For single-config generators (Ninja, Make) it is set at configure time. For multi-config generators (Visual Studio, Ninja Multi-Config) you pick per build with --config (lesson 21). A good default in your project:

# Sensible default. Users can override with -D.
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
endif()

Note

FORCE here is the rare, deliberate exception to first-set-wins. It sets the default when nobody chose anything, without overriding an explicit -D. The guard checks it is a single-config generator, so multi-config builds, which use --config instead, are left alone.

cmake-gui, the visual cache editor

cmake-gui .

It shows every cache variable with its help text, lets you flip options, and reconfigures with one click. Useful when you inherit a project and want to see what is configurable. The help text from option() and set(... CACHE ...) is exactly what you will see.

Tip

Give every cache variable a clear help string. It is user documentation. set(MYAPP_MAX_CONN 10 CACHE STRING "Maximum simultaneous connections") beats an empty string for everyone who runs cmake-gui on your project.

Read the docs