CMake Presets
Learn the fundamentals of CMake Presets. This lesson introduces CMakePresets.json
to simplify complex build commands.
Throughout the last few chapters, our build commands have been getting progressively more complex. We've added toolchain files for package managers, specified build types, and configured cross-compilation.
This has led to a new problem: our command-line invocations have become long, hard to remember, and difficult to share with others using our project who may find them useful.
This is where CMake Presets come in. Presets are a modern, standardized way to define and share common build configurations in a simple JSON file. They allow you to replace a long, error-prone command with a single, memorable name.
In this lesson, we'll learn the fundamentals of presets. We'll start by encapsulating a complex command into our first configure preset, then add a corresponding build preset, and finally understand the distinction between shared team presets and personal user presets.
In these examples, we'll use our vcpkg project from the previous chapter. The code is provided below if you want to follow along. Note that the include()
command in the toolchain file (highlighted) may need updated to point to your vcpkg location. We'll learn how to make this portable soon.
Files
Preset Version Requirements
Support for presets were first introduced in version 3.19 of CMake, and were expanded in subsequent versions. Later in the chapter, we'll use features added in 3.21 so, if you want to follow along, having at least that version is recommended.
Remember, you can check the installed cmake
version using the command:
cmake --version
Using a recent addition to the cmake
executable doesn't necessarily require us to update the cmake_minimum_requirements()
for our project. Someone on an older version could still use our project without using our presets.
However, if our project is designed around the use of presets, and our documentation recommends using them, bumping the cmake_minimum_required()
version is a reasonable choice anyway. This signals to users that a more modern version of CMake is expected in order to use the project as designed.
CMakeLists.txt
cmake_minimum_required(VERSION 3.21)
project(Greeter)
// ...
The Command-Line Chore
Let's revisit the command we used to cross-compile our Greeter
application for Windows from a macOS or Linux host. It required setting the source and build directories, the toolchain file, and the build type.
For example, if our terminal was in our project root, and we wanted to configure a vcpkg project in /build/windows-debug
, our command to provide all of the required arguments might look like this:
cmake -S . -B ./build/windows-debug \
-DCMAKE_TOOLCHAIN_FILE=./toolchains/mingw-windows-x64.cmake \
-DCMAKE_BUILD_TYPE=Debug
And after that, we still need to repeat the correct build directory for the build command:
cmake --build ./build/windows-debug
This is a lot to type correctly every time. If a new developer joins the team, you have to document this command precisely. If you need to change a flag, you have to tell everyone to update their personal scripts.
Creating a Configure Preset
CMakePresets.json
is the solution. It's a file you create in the root of your project that lets you give names to these complex configurations.
Let's create this file and define our first preset to encapsulate the command above.
CMakePresets.json
{
"version": 3,
"configurePresets": [{
"name": "windows-x64-debug",
"displayName": "Windows x64 Debug",
"description": "Build for 64-bit Windows with MinGW (Debug)",
"binaryDir": "${sourceDir}/build/windows-x64-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/toolchains/mingw-windows-x64.cmake"
}
}]
}
Let's break down this JSON file:
"version"
: The schema version for the presets file. CMake adds support for new keys and features in presets over time. Version3
is a relatively common baseline and is supported by CMake versions 3.21 and later."configurePresets"
: An array containing one or more configure preset objects."name"
: The unique, machine-readable ID for the preset. This is what you'll use on the command line to declare that you want to use this preset. It's best to use a simple, consistent naming scheme likeos-arch-config
."displayName"
and"description"
: Human-readable strings that GUI tools such as an IDE can use to present the preset in a friendly way."binaryDir"
: The output directory for the build. This corresponds to the-B
argument we'd pass on the command line. The${sourceDir}
variable is automatically provided by CMake and points to the directory containing theCMakePresets.json
file."cacheVariables"
: An object containing key-value pairs that will be passed as-D
arguments to thecmake
command. This is where we can set things likeCMAKE_BUILD_TYPE
andCMAKE_TOOLCHAIN_FILE
.
Listing Presets
We can ask CMake to list all of the available presets using the following command:
cmake --list-presets
The output should list our new preset as an option:
Available configure presets:
"windows-x64-debug" - Windows x64 Debug
Using Presets
With this file in our project root, our long, complex configure command is now replaced with a simple, memorable one:
cmake --preset=windows-x64-debug
CMake will read CMakePresets.json
, find the preset named "windows-x64-debug", and apply all the settings defined within it.
With that one command, we should now see our project configured in the /build/windows-x64-debug
directory, just like our preset requested.
Overriding Preset Values
If a preset isn't defined exactly how we want, we can still override its settings on the command line.
For example, we could use all the preset values, but override the preset-defined binaryDir
of /build/windows-x64-debug
to instead use ./output
:
cmake --preset=windows-x64-debug -B "./output"
CMake allows us to save personal preferences like this in a CMakeUserPresets.json
file, which doesn't interfere with the configurations shared among the wider team in the CMakePresets.json
file. We cover user presets in the next lesson.
Adding Build Presets
Simplifying the configure step is great, but we can create presets for the build step too. This lets us create a complete, named workflow from start to finish.
Let's add a buildPresets
section to our file:
CMakePresets.json
{
"version": 3,
"configurePresets": [{
"name": "windows-x64-debug",
"displayName": "Windows x64 Debug",
"description": "Build for 64-bit Windows with MinGW (Debug)",
"binaryDir": "${sourceDir}/build/windows-x64-debug",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug",
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/toolchains/mingw-windows-x64.cmake"
}
}],
"buildPresets": [{
"name": "windows-x64-debug",
"displayName": "Build Windows x64 (Debug)",
"configurePreset": "windows-x64-debug"
}]
}
In this example, our three values are:
"name"
: The name of the build preset, which we'll use to identify it on the command line. It's a common convention to give the build preset the same name as the configure preset it's associated with."displayName"
: As with configure presets, we can give our build presets a friendly name that can be displayed on GUI tools."configurePreset"
: This is the crucial key. It links this build preset to a specific configure preset. When you use this build preset, CMake knows it corresponds to the configuration done bywindows-x64-debug
.
Now, we can also complete the build step from the project root, using our named preset:
cmake --build --preset=windows-x64-debug
In this case, the build preset isn't quite as useful as the configure preset. The only benefit is that we no longer need to provide the build directory (./build/windows-x64-debug
) because that was already set in the configurePreset
that our build preset links to.
However, as we'll see later, the commands needed to execute build steps can also get quite complex. But with our new knowledge of build presets, rather than those commands needing to be typed, we can just add the options to our CMakePresets.json
collection, making them easier to use and share.
Summary
CMake Presets are a feature that improves the usability of the CMake command line, turning complex, error-prone commands into simple, named workflows.
- The Problem: Long
cmake
command lines with many arguments are hard to remember, type, and share. - The Solution: Define configurations in a
CMakePresets.json
file. - Configure Presets: Define values like the generator, build directory, and cache variables for the
cmake
command. - Build Presets: Link to a configure preset to define a corresponding workflow for the
cmake --build
command.