Standardize CMake builds across your team with presets
Stop duplicating generator flags and build paths across laptops and CI. Use CMakePresets.json to name one configuration and run it everywhere.
10 Sept 2026, 23:18 UTC

The problem: everyone runs CMake differently
You clone a CMake project, read the README, and run cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release. It works on your laptop. A teammate runs the same commands on macOS with Xcode, and the build directory ends up in a different place. CI uses Ubuntu with Make and a different install prefix. Everyone is technically following the instructions, yet no two builds match.
The practical fix is CMake Presets: a JSON file checked into version control that names the generator, build directory, and cache variables so cmake --preset <name> produces the same configuration everywhere. A generator is the build tool CMake invokes, such as Ninja or Unix Makefiles. A cache variable is a CMake setting stored in CMakeCache.txt, such as CMAKE_BUILD_TYPE.
What a preset controls
CMake 3.19 introduced configure presets. CMake 3.21 added build and test presets, plus inheritance through the inherits field. A preset declared in CMakePresets.json can specify:
generator: the build system to usebinaryDir: where build output goescacheVariables: values such asCMAKE_BUILD_TYPEorCMAKE_INSTALL_PREFIXenvironment: environment variables for the configure step
Once defined, you can configure with cmake --preset <name>, build with cmake --build --preset <name>, and test with ctest --preset <name> without repeating paths or options.
Worked example: shared debug and release presets
Start with a minimal CMakeLists.txt in the project root:
cmake_minimum_required(VERSION 3.21)
project(example VERSION 1.0)
add_executable(example main.cpp)
Add CMakePresets.json next to it. This file is meant to be committed:
{
"version": 4,
"cmakeMinimumRequired": {
"major": 3,
"minor": 21,
"patch": 0
},
"configurePresets": [
{
"name": "base",
"hidden": true,
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/${presetName}",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "debug",
"inherits": "base",
"displayName": "Debug build"
},
{
"name": "release",
"inherits": "base",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
}
],
"buildPresets": [
{
"name": "debug",
"configurePreset": "debug"
},
{
"name": "release",
"configurePreset": "release"
}
]
}
Run these commands in a terminal at the project root. You need normal user permissions to write to the project directory.
cmake --list-presets
cmake --preset debug
cmake --build --preset debug
Expected checks: cmake --list-presets should print debug and release under configure presets. After the configure command, a build/debug directory should exist relative to the source tree. You do not need to repeat -G Ninja or -DCMAKE_BUILD_TYPE=Debug.
Risk: do not commit secrets or machine-specific absolute paths in CMakePresets.json. Use CMakeUserPresets.json or environment variables for those instead.
Local overrides and CI
CMakeUserPresets.json is intended for local overrides and is typically excluded from version control. A developer can add it next to CMakePresets.json to point at a local compiler or override the build directory without touching the shared file.
On CI, verify the CMake version before relying on presets. Configure presets require CMake 3.19 or newer; build and test presets require 3.21 or newer.
cmake --version
If the installed CMake is too old, presets are silently unrecognized and the command will fail. Add the version check to your CI pipeline before the build step.
Limitation: paths and schema drift
The preset JSON schema has evolved across CMake minor versions. A file written for CMake 3.24 may not behave identically on CMake 3.19. Validate presets with cmake --list-presets before relying on them in CI.
binaryDir can use ${sourceDir} inside the preset, but relative paths outside the preset may resolve differently on Windows and Linux. If your team mixes operating systems, prefer explicit subdirectories under the build root rather than relative paths that climb out of the source tree.
Because cmake --preset debug creates a build directory, the rollback is simply removing that directory:
rm -rf build/debug
Closing checklist
- Add
CMakePresets.jsonto version control with shared generator, build directory, and cache variables. - Add
CMakeUserPresets.jsonto.gitignoreso local overrides are never committed. - Pin a minimum CMake version in CI that supports the preset features you use.
- Verify with
cmake --list-presets, then runcmake --preset <name>and confirm the expected build directory appears.
Presets do not replace CMake logic, but they do replace the tribal knowledge of which flags belong on which machine. Commit the preset, ignore the user override, and let cmake --preset do the repeating.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.