I’ve recently added flags to a project to enable sanitizers. To work nicely with multi-config generators, I’ve split the option into one for each configuration type. Much like CMAKE_CXX_FLAGS_<CONFIG>
. However, the solution I’ve ended up with feels clumsy and verbose for something I feel like should have a simpler solution.
In the example code below, we have the cache variables SANITIZERS_<CONFIG>
. My first idea was to define a SANITIZERS_<CONFIG>_FLAGS
variable and then have a generator expression like ${SANITIZERS_$<CONFIG>_FLAGS}
to pick the right flags (in practice also needs UPPER_CASE
on the config). But it seems like CMake won’t expand generator expressions that are nested in this way. At least my local CMake installation (version 3.18.3) gave me an error that boils down to <
not allowed inside variable names.
As a workaround, I’ve ended up adding one generator expression per supported config type but there has to be a better way. I couldn’t find mentions of such “multi-config variables” in the docs and none of the CMake modules seemed to match either. Any pointer to something I’ve missed in the docs (or search terms I could’ve used) or feedback on how to do this less verbose are much appreciated.
Here’s the minimal snippet to illustrate what I’ve ended up with. The test.cpp simply contains an empty main function. I’ve wanted to keep the stripped version as short as possible, so the code assumes GCC/Clang and won’t work MSVC.
cmake_minimum_required(VERSION 3.13.5)
project(CMakeTest)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON
CACHE INTERNAL "Write JSON compile commands database")
set(SANITIZERS_DEBUG "address" CACHE STRING "sanitizers for Debug builds")
set(SANITIZERS_RELEASE "" CACHE STRING "sanitizers for Release builds")
set(SANITIZERS_RELWITHDEBINFO "" CACHE STRING "sanitizers for RelWithDebInfo builds")
set(SANITIZERS_MINSIZEREL "" CACHE STRING "sanitizers for MinSizeRel builds")
function(define_flags_variable BuildName)
set(xs "${SANITIZERS_${BuildName}}")
set(flags_varname "SANITIZERS_${BuildName}_FLAGS")
if(NOT "${xs}" STREQUAL "")
set("${flags_varname}" "-fsanitize=${xs}" "-fno-omit-frame-pointer" PARENT_SCOPE)
endif()
endfunction()
foreach(BuildName DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
define_flags_variable(${BuildName})
endforeach()
function(target_add_sanitizers_flags TargetName AccessScope)
target_compile_options(${TargetName} ${AccessScope}
$<$<CONFIG:Debug>:${SANITIZERS_DEBUG_FLAGS}>
$<$<CONFIG:Release>:${SANITIZERS_RELEASE_FLAGS}>
$<$<CONFIG:RelWithDebInfo>:${SANITIZERS_RELWITHDEBINFO_FLAGS}>
$<$<CONFIG:MinSizeRel>:${SANITIZERS_MinSizeRel_FLAGS}>)
target_link_options(${TargetName} ${AccessScope}
$<$<CONFIG:Debug>:${SANITIZERS_DEBUG_FLAGS}>
$<$<CONFIG:Release>:${SANITIZERS_RELEASE_FLAGS}>
$<$<CONFIG:RelWithDebInfo>:${SANITIZERS_RELWITHDEBINFO_FLAGS}>
$<$<CONFIG:MinSizeRel>:${SANITIZERS_MinSizeRel_FLAGS}>)
endfunction()
add_executable(test test.cpp)
target_add_sanitizers_flags(test PRIVATE)
/edit: sorry for the edits, I’ve posted this via email originally (first time using Discord), but the snippet got truncated and I had to paste it back in. Also, I noticed I could use Markdown to make the post more readable.