When dealing with a C/C++ project that utilizes cmake one will sooner or later need to organize the available build options into profiles than enable multiple features in a convenient manners. Two of the ways to achieve that is the use of cmake cache files and cmake presets.
Let’s start with setting a common ground for this test using the vtk repository:
git clone https://gitlab.kitware.com/vtk/vtk
cd vtk/
git switch -d v9.1.0
git submodule init
git submodule update --recursive
VTK has already a number of documented build options on top of the ones that cmake provides for every project.
Let’s say we want to build VTK with some optional flags and enable caching and the generation of compile commands for this build.
cmake -DVTK_USE_MPI=ON -DVTK_BUILD_TESTING=ON -DVTK_DEBUG_LEAKS=ON -DVTK_WRAP_PYTHON=ON -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
Quite a long command ! How do we keep track of the command flags used ? Keep
looking at the history of our commands and hope that it is still there ? Have
a untracked file BUILD.txt
and record the command in there ? What about
documenting our choices to our future selves? I admit to using all of the
above in the past but thankfully there is a much better way to do it. Actually
there are at least two of them!
CMake cache files
Consider the following cache file flags.cmake
:
# common cmake commands
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Generate compile_commands.json")
set(CMAKE_C_COMPILER_LAUNCHER ccache CACHE STRING "")
set(CMAKE_CXX_COMPILER_LAUNCHER ccache CACHE STRING "")
# common vtk commands
set(VTK_DEBUG_LEAKS ON CACHE BOOL "")
set(VTK_USE_MPI ON CACHE BOOL "Enable MPI")
set(VTK_WARP_PYTHON ON CACHE BOOL "")
set(VTK_BUILD_TESTING ON CACHE BOOL "")
To use it:
cmake -C flags.cmake -B <build-directory> -S vtk
Much better! We can now track flags.cmake
with git, add documentation and
have different profiles for different cases/environments.
We can even split the file into some base common flags and profile specific. Consider the following two files:
common.cmake
:
# common cmake commands
set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Generate compile_commands.json")
set(CMAKE_C_COMPILER_LAUNCHER ccache CACHE STRING "")
set(CMAKE_CXX_COMPILER_LAUNCHER ccache CACHE STRING "")
flags.cmake
:
include("${CMAKE_CURRENT_LIST_DIR}/common.cmake")
# common vtk commands
set(VTK_DEBUG_LEAKS ON CACHE BOOL "")
set(VTK_USE_MPI ON CACHE BOOL "Enable MPI")
set(VTK_WARP_PYTHON ON CACHE BOOL "")
set(VTK_BUILD_TESTING ON CACHE BOOL "")
This is in fact the way the VTK project organizes its compile options for ci.
CMake presets
An alternative way is to organize the flags info a CMakePresets.json
file. As
per cmake
documentation
it is recommended that CMakePresets.cmake
lives in the project’s root
directory and it is tracked with the project’s version control system while
CMakeUserPresets.json
is not and it is included in .gitignore
so that users
can drop their own files in the root directory without polluting their working
directory.
Let’s organize the above into a CMakeUserPresets.json
file:
{
"version": 3,
"configurePresets": [
{
"name": "vtk-common",
"displayName": "Common vtk config",
"description": "Common flags for project tha use VTK",
"generator": "Ninja",
"cacheVariables": {
"CMAKE_EXPORT_COMPILE_COMMANDS": {
"type": "BOOL",
"value": "ON"
},
"VTK_DEBUG_LEAKS": {
"type": "BOOL",
"value": "ON"
},
"CMAKE_C_COMPILER_LAUNCHER": {
"type": "STRING",
"value": "ccache"
},
"CMAKE_CXX_COMPILER_LAUNCHER": {
"type": "STRING",
"value": "ccache"
}
}
},
{
"name": "python",
"inherits": ["vtk-common"],
"displayName": "vtk with python enabled",
"cacheVariables": {
"VTK_WARP_PYTHON": {
"type": "BOOL",
"value": "ON"
}
}
},
{
"name": "mpi",
"inherits": ["vtk-common"],
"displayName": "vtk with mpi enabled",
"cacheVariables": {
"VTK_USE_MPI": {
"type": "STRING",
"value": "ON"
}
}
},
{
"name": "testing",
"inherits": ["vtk-common"],
"displayName": "vtk tests enabled",
"cacheVariables": {
"VTK_BUILD_TESTING": {
"type": "BOOL",
"value": "ON"
}
}
},
{
"name": "my-config",
"inherits": ["mpi","python","testing"],
"displayName": "Experimental"
},
{
"name": "python-tests",
"inherits": ["python","testing"],
"displayName": "Testing python config"
}
]
}
Notice that we get the same composability options but this time in a single file. Documentation is more limited here since we cannot have free-text comments, also handling if/else
blocks is impossible right now.
It has however a nice user experience feature. We can list all available presets:
$ cmake --list-presets -B build -S vtk
Available configure presets:
"vtk-common" - Common vtk config
"python" - vtk with python enabled
"mpi" - vtk with mpi enabled
"testing" - vtk tests enabled
"my-config" - Experimental
"python-tests" - Testing python config
Also, when selecting one preset, cmake informs which options have been set via the preset:
$ cmake --preset vtk-common -B build -S vtk
Preset CMake variables:
CMAKE_CXX_COMPILER_LAUNCHER:STRING="ccache"
CMAKE_C_COMPILER_LAUNCHER:STRING="ccache"
CMAKE_EXPORT_COMPILE_COMMANDS:BOOL="ON"
VTK_DEBUG_LEAKS:BOOL="ON"
-- The C compiler identification is GNU 10.4.0
...
Some IDEs like Visual Studio Code can take advantage of CMakePresets file and present a dropdown menu for the user with the available options. Same goes for cmake-gui
that comes with cmake
.