I would like to change the default values for CMAKE_CXX_FLAGS_RELEASE
or CMAKE_CXX_FLAGS_DEBUG
in CMake. Basically, I have some project defaults that differ just slightly from CMake's (for release, for instance), and I shouldn't have to ask myself "Oh, does their -O3 or our -O2 take precedence when added with add_compile_options."
Now, I know how to set these values, but I don't know how to make them user editable in the two usual ways: by using -DCMAKE_CXX_FLAGS_DEBUG=yourflags
on the command line or by configuring it with ccmake or CMakeSetup.
The problem being that CMAKE sets and caches its own defaults for these, and if you try to overwrite the variables without using FORCE, the "defaults" are never changed. If I use FORCE in my set command: set(CMAKE_CXX_FLAGS_DEBUG blah CACHE STRING "" FORCE)
, it will overwrite it every time the script is run, eliminating the possibility for the user to change it if he wishes.
I managed to hack it to work with CCMAKE by doing the following, but this still doesn't work with cmake -DCMAKE_CXX_FLAGS_DEBUG
as it overwrites the user change AFTER he's done it:
set(DEFAULTS_SET FALSE CACHE BOOL "")
set(CMAKE_CXX_FLAGS_DEBUG "-this -that" CACHE STRING "" FORCE)
set(DEFAULTS_SET TRUE CACHE BOOL "" FORCE)
Obviously, this is a nasty hack and doesn't completely work(in the case of cmake -Dwhatever=thisorthat). I could add other build types as well, but I don't really see why that should be necessary just to change a few simple things.
Edit March 1st, 2015:
I've created a solution that works, though I still am not super thrilled about what I have to do. I'd seen other comments that solve the problem of setting CMAKE_CXX_FLAGS_DEBUG
and friends without having them get clobbered, but this originally didn't work for me because I was trying to also select them based on the compiler that was in use. The compiler isn't determined, however, until it has already filled the variables for me. The trick I used is as follows. You must set the flags variables to something 'special' before the project command.
set(CMAKE_CXX_FLAGS_DEBUG "_UNSET" CACHE STRING "")
project(your_project C CXX)
if(${CMAKE_CXX_FLAGS_DEBUG} STREQUAL "_UNSET")
# Do some compiler switching here and then set your flags with FORCE.
set(CMAKE_CXX_FLAGS_DEBUG "-ggdb3 -O0" CACHE STRING "" FORCE)
endif()
This now allows me to choose defaults that are completely override-able via command line with -D or in cmake-gui.
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:
*_INIT
flags.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 the project()
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With