CMake Presets in Practice
CMake 3.19 was released late last year and came with the announcement of a new feature: CMake presets. And in 3.20 — which came out this year — the feature was extended even more, making it useful for a wide range of situations.
In this post, I’m not going to tell you how to use CMake presets; that’s what the documentation is for. Instead, I’ll discuss the reasons why we hope to be using presets at PSPDFKit in the future.
What Are CMake Presets?
CMake presets are a collection of build and test recipes for your CMake project. These recipes are described in a simple JSON file named CMakePreset.json
, which can be committed into version control. This enables you to share build knowledge both with other users and with build instances such as continuous integration (CI).
CMakePreset.json
describes:
-
Configuration — The data that’s normally passed on the command line to the CMake executable. Examples of this include options, environment variables, toolchain information, and generator information (Visual Studio/Xcode projects).
-
Build — A list of targets to build, along with other minor build options describing how to drive the compiler.
-
Test — Instructions on how to execute CTest. Information such as which tests to run, how to output the results, and what to do when a failure occurs.
So at this point, you may be asking the question, “Do we really need another way of describing our build procedures?”
I’d say yes, and here’s why.
Why Use CMake Presets?
There are a few good reasons to use CMake presets, which I’ll outline below.
Help Collate Build and Test Recipes for Your Project
For any large project, reproducibility is paramount. At PSPDFKit, we build for many platforms, targets, and flavors, each with its own unique requirements. As a result of these unique requirements, we have many options for our CMake project.
We’ve been documenting this information in various scripts and README files, but both these options have downfalls, such as:
-
Scripts aren’t cross-platform.
-
Scripts don’t integrate well with IDEs.
-
Unless they’re well-structured, scripts can be difficult to maintain.
-
Copying from READMEs introduces the possibility of human error.
-
READMEs and documents are often forgotten about and become outdated.
It may seem logical to replace both scripts and documented build procedures with CMake presets, but there are important tools that live outside CMake, meaning that scripts will still have their place. However, CMake presets will take the burden of configuration and build knowledge away from scripts, and they’ll also replace most build information in documentation.
One example of where scripts will still be useful is with test interpretation on CI. We could technically build the functionality into our CMake project with a custom command, but keeping tasks not related to build procedures in CMake is much like trying to fit a square peg into a round hole. CMake’s strengths are in build configuration, not in scripting.
Having the presets defined in version control in a single location helps when communicating between, or even within, teams. Which build flavor is used? How to reproduce an issue? Many situations require reproducibility, meaning that another team member needn’t know the details of a configuration and can focus more on the problem at hand. And that ties nicely into the next point.
Help Document How to Build Targets and Projects
On a distributed team, it’s really important to concisely convey how to achieve certain actions in the codebase. For CMake presets, that means how to build a project or target. If another team member asks how to build such a target, we’d normally have to point to scripts, refer to a README, or show what CI is doing, but now that’s different.
With CMake presets, there’s one place for the recipes. Each recipe can have a descriptive name and a longer description to fully convey what the preset will achieve. In the case of CMake configuration, it means users don’t have to know all the options required for a platform or product. In terms of build targets, the user doesn’t need to know the specific target binary or binaries required. With a large project like PSPDFKit, it can sometimes be daunting to see all the targets available. Whether they’re individual libraries, test applications, or production applications, having a build preset allows the user to build the correct target, or targets, for the solution they require.
Better Integration with IDEs
IDE integration is the area I’m most excited about. Here at PSPDFKit, we don’t limit the environment employees work in. Everyone can choose what software and tools they want to use to perform their job to the best of their ability. That means we have many people working on/with different platforms, text editors, IDEs, etc. Along with this comes the issue of making sure we’re all developing with reproducible results.
As I mentioned before, prior to CMake presets, we achieved reproducible results by describing dependencies and recipes in a README. That meant that if one of us preferred to use an IDE, we had to manually ensure we met all the requirements and set up the configuration and build procedures in accordance with the README. That procedure opens up the possibility of mistakes, because, well, we’re human.
With CMake presets, the recipes are held in a machine-readable JSON format, meaning that tools can read them too! In fact, we’ve seen Microsoft heavily investing in CMake presets with support in Visual Studio 2019 and the VS Code CMake plugin. The latter only further backs up the arguments for VS Code being a first-class citizen in the C++ IDE space.
We’ve also seen that the JetBrains team will be working on bringing the feature to CLion starting in 2021.2, and hopefully other tools with follow suit in the coming months.
All this means less setup for the developer and less chance of human error. Just choose your favorite tool and point it at the preset file.
There Are Things We Shouldn’t Share
As with any tool or feature, there are times and places to use them…
And, there are times when we should keep recipes to ourselves to reduce complexity. For me, that means adding unique debugging configurations, like ways of finding issues in our CMake files. In such a case, I’d add VERBOSE
and --warn-unused-vars --warn-uninitialized
, but adding a preset to a version-controlled file with these enabled would be too noisy and possibly just confuse others coming to the project.
Lucky for us, CMake presets also read a second preset file named CMakeUserPresets.json
. This is where developers can define their own presets, and for that reason, it’s important not to check that file into version control.
Again, the advantage is there’s no need to remember all the CMake variables to set or how to set up the environment required to reproduce a configuration. It’s all stored in JSON, and it can be recalled in an instant.
Conclusion
I hope you now understand how CMake presets could help your project. For both newcomers to and veterans of a project, using them can reduce the cognitive load and number of mistakes that may occur along the way.
For now, we’ll slowly be investing more in the feature as all our platforms update to a recent version of CMake, and hopefully that’ll mean increased simplicity for everyone working on the project now and in the future!
When Nick started tinkering with guitar effects pedals, he didn’t realize it’d take him all the way to a career in software. He has worked on products that communicate with space, blast Metallica to packed stadiums, and enable millions to use documents through Nutrient, but in his personal life, he enjoys the simplicity of running in the mountains.