Change default value of CMAKE_CXX_FLAGS_DEBUG and friends in CMake
Florian's answer using toolchain files is a good one for earlier versions of CMake. But CMake 3.19 added a feature called presets that helps manage common sets of cache variables for your project. Basically, you create at least one of two files, CMakePresets.json
and CMakeUserPresets.json
(usually added to .gitignore
or similar), that contain specifications of how to configure the project.
For example, you might write:
{
"version": 1,
"cmakeMinimumRequired": {
"major": 3,
"minor": 19,
"patch": 0
},
"configurePresets": [
{
"name": "default",
"displayName": "Default",
"description": "Build using Ninja and a GCC-like compiler",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_CXX_FLAGS_DEBUG": "-ggdb3 -O0"
}
},
{
"name": "default-vcpkg",
"displayName": "Default (vcpkg)",
"description": "Default build with vcpkg (from VCPKG_ROOT)",
"inherits": "default",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
}
}
]
}
Then, from the source directory, your CMake command line would just become:
$ cmake --preset=default
This approach has a few advantages:
- It makes the command line a lot simpler
- It's compatible with other toolchain files (like vcpkg's in the second preset)
- It can override flags that are usually unconditionally added to the
*_INIT
flags. - You don't have to write awkward logic in your CMakeLists.txt.
- Presets are opt-in for the user, which is important if you're distributing a library.
Expanding on points 4 and 5: it is a bad idea to add flags unless they absolutely must be there to compile correctly and there isn't a built-in feature for reaching those flags (eg. CMAKE_CXX_STANDARD
). If someone tries to compile your library with a different compiler (or even a different version of the same compiler) they could run into issues if, for example, you add a warning flag that's too new or not supported. You can work around this with generator expressions and/or complex logic (like the _UNDEF
trick above), but it's generally just easier and more convenient to use a toolchain or these new presets.
For instance, to correctly add -Wsuggest-override
, you would need to write:
target_compile_options(lib PRIVATE $<$<AND:$<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,5.1>,$<COMPILE_LANG_AND_ID:CXX,GNU>>:-Wsuggest-override>)
# ... or ...
# Note: only correct if using "PRIVATE". Must use a genex for INTERFACE/PUBLIC because the whole genex gets exported, whereas this flag will get exported verbatim.
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 5.1)
target_compile_options(lib PRIVATE -Wsuggest-override)
endif ()
Or you could just put the flag in a toolchain/preset where you already know what compiler you're using.
I just wanted to add the four possibilities I see:
Having your own toolchain files containing the presets for each compiler you support like:
GNUToolchain.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "")
And then use it with
cmake -DCMAKE_TOOLCHAIN_FILE:string=GNUToolchain.cmake ...
You can try to determine the compiler by checking
CMAKE_GENERATOR
(which is valid before theproject()
command):CMakeLists.txt
if("${CMAKE_GENERATOR}" MATCHES "Makefiles" OR ("${CMAKE_GENERATOR}" MATCHES "Ninja" AND NOT WIN32)) set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "") endif() project(your_project C CXX)
You can use
CMAKE_USER_MAKE_RULES_OVERRIDE
to give a script with your own..._INIT
values:It is loaded after CMake’s builtin compiler and platform information modules have been loaded but before the information is used. The file may set platform information variables to override CMake’s defaults.
MyInitFlags.cmake
# Overwrite the init values choosen by CMake if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(CMAKE_CXX_FLAGS_DEBUG_INIT "-ggdb3 -O0") endif()
CMakeLists.txt
set(CMAKE_USER_MAKE_RULES_OVERRIDE "MyInitFlags.cmake") project(your_project C CXX)
You can simplify your solution from March 1st by checking against the
..._INIT
variants of the compiler flag variables:CMakeLists.txt
project(your_project C CXX) if (DEFINED CMAKE_CXX_FLAGS_DEBUG_INIT AND "${CMAKE_CXX_FLAGS_DEBUG_INIT}" STREQUAL "${CMAKE_CXX_FLAGS_DEBUG}") # Overwrite the init values choosen by CMake if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "" FORCE) endif() endif()
Comments:
I prefer and use the toolchain variant. But I admit it has the disadvantage of having to give the toolchain file manually (if you are not calling cmake
via a script/batch file).
References:
- CMake: In which order are files parsed (cache, toolchain, etc.)?
- cmake - Global linker flag setting (for all targets in directory)
- Switching between GCC and Clang/LLVM using CMake