Lesson 20 · Build & ship

Presets

Replace the configure and build incantation with cmake --preset. The CMakePresets.json format, configure, build, test and workflow presets.

Every project has a right way to configure it, and most projects only document it in a README that goes stale. Presets encode the right way in the project, so everyone, you, teammates, CI, IDEs, runs the exact same commands.

The problem presets solve

# What you type today, from memory, hoping it is right.
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DMYAPP_BUILD_TESTS=ON -G Ninja
cmake --build build -j 8
# What presets give you.
cmake --preset release
cmake --build --preset release

Why

Presets are the project’s official instructions, machine-readable and committed to git. CI uses the same file as your laptop. No more “works with my flags” divergence. This is why every major project ships CMakePresets.json today.

The file

CMakePresets.json
{ "version": 12, "cmakeMinimumRequired": { "major": 4, "minor": 4 }, "configurePresets": [ { "name": "dev", "displayName": "Dev (Ninja, Debug)", "generator": "Ninja", "binaryDir": "${sourceDir}/build/dev", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug", "MYAPP_BUILD_TESTS": "ON" } }, { "name": "release", "displayName": "Release", "generator": "Ninja", "binaryDir": "${sourceDir}/build/release", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } } ], "buildPresets": [ { "name": "dev", "configurePreset": "dev" }, { "name": "release", "configurePreset": "release" } ], "testPresets": [ { "name": "dev", "configurePreset": "dev", "output": { "outputOnFailure": true } } ], "workflowPresets": [ { "name": "dev", "steps": [ { "type": "configure", "name": "dev" }, { "type": "build", "name": "dev" }, { "type": "test", "name": "dev" } ] } ] }

Now the whole cycle is two words:

cmake --workflow --preset dev

One command. Configure, build, test, using the project’s own definition.

The preset kinds

Preset typeUsed by
configurePresetscmake --preset <name>
buildPresetscmake --build --preset <name>
testPresetsctest --preset <name> (lesson 25)
packagePresetscpack --preset <name> (lesson 24)
workflowPresetscmake --workflow --preset <name>, a sequence of the above

One rule for workflows. The first step must be a configure preset, and every later step must reference that same configure preset.

Hygiene rules

  • CMakePresets.json. Commit it. It is the project’s contract.
  • CMakeUserPresets.json. Do not commit it. Gitignore it. It is for personal overrides, your machine’s toolchain, your paths.
  • Presets can inherit from each other and be marked hidden. The classic shape is a shared base plus small variants:
{
  "name": "base",
  "hidden": true,
  "generator": "Ninja",
  "binaryDir": "${sourceDir}/build/${presetName}"
},
{ "name": "dev", "inherits": "base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" } },
{ "name": "release", "inherits": "base", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } }
  • conditions let a preset apply only on certain platforms or OS. For example, a Windows-only preset that exists only there.

Note

The preset format is a versioned JSON schema. It is a stable, supported format. Current schema version 12 maps to CMake 4.4. Each version number corresponds to a CMake release, so you can require a specific one via cmakeMinimumRequired.

Tip

cmake --list-presets shows what is available. The fastest way to orient yourself in any repository. IDEs (VS Code, CLion, Visual Studio) read presets too, so the one-command story extends to one-IDE-config.

Pitfall

Do not hardcode machine paths, like /home/you/..., inside presets. Use ${sourceDir}, ${presetName}, or environment variables via the environment field. Presets are shared with CI and teammates.

Read the docs