Issue
I have a code base (say, a repository) containing several software components (executables, libraries). All is built using CMake with individual targets for each component.
Now, I know that the modern way to set compiler flags is target-specific:
target_compile_options(foo PRIVATE -Werror -Wall -Wextra -pedantic)
target_compile_features(foo PRIVATE cxx_std_17)
However, I want to have the same flags for all targets in the entire project. Writing these lines for each single target leads to code duplication and is error-prone (especially when the number of flags increases). Is there some way to attach compiler flags to projects rather than targets?
I know that I can add options to the top-level directory like that:
add_compile_options(-Werror -Wall -Wextra -pedantic)
But I'm not sure if that's the way to go, and also I'm not sure how to do the same for the compile features (cxx_std_17
).
Solution
Now, I know that the modern way to set compiler flags is target-specific:
target_compile_options(foo PRIVATE -Werror -Wall -Wextra -pedantic) target_compile_features(foo PRIVATE cxx_std_17)
This is actually not the best/most modern way. If you're using CMake 3.19+, the best thing to do is to create a preset.
Create a file called CMakePresets.json
next to your top-level CMakeLists.txt
with the following content:
{
"version": 1,
"cmakeMinimumRequired": {
"major": 3,
"minor": 19,
"patch": 0
},
"configurePresets": [
{
"name": "default",
"displayName": "Default",
"description": "Default build using Ninja",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_CXX_FLAGS": "-Werror -Wall -Wextra -pedantic",
"CMAKE_CXX_STANDARD": "17",
"CMAKE_CXX_STANDARD_REQUIRED": "YES",
"CMAKE_CXX_EXTENSIONS": "OFF"
}
}
]
}
Then configure with:
$ cmake --preset=default
The reason for doing things this way is because (ideally) nothing should be in the CMakeLists.txt that isn't absolutely required to build your project. Warning flags are not in this set, so they belong here. Otherwise, you inflict potentially invalid flags on your end-users. This is surprisingly common with warning flags. There are some flags that are GCC or Clang only, others that have been removed or renamed, and others whose sensitivity has changed across releases. If your user manually adds -Werror
via CMAKE_CXX_FLAGS
and you add a flag some other way, problems are likely.
This does mean that for libraries, it is very often appropriate to set cxx_std_NN
via target_compile_features
, where NN
is the minimum compatible C++ version (not the preferred version, though they might coincide) of the library. This is because the feature can be set in the INTERFACE
(equiv. PUBLIC
) property to make sure that linkees are using a new enough version for the library's headers. If both CMAKE_CXX_STANDARD
and the compile feature are set, the maximum will be used.
In older versions of CMake, a toolchain file could be used for this same purpose.
Answered By - Alex Reinking