I've got the following generator expression working, which sets the /GS
flag if the compiler is MSVC and it sets it for the build configurations RelWithDebInfo
and Release
:
target_compile_options(mytarget PRIVATE
"$<$<CONFIG:Release>: $<$<CXX_COMPILER_ID:MSVC>:/GS>>
$<$<CONFIG:RelWithDebInfo>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>")
Now I also want to let the user configure this, and I've added an option
:
option(MYTARGET_ENABLE_GS "Enable /GS" OFF)
So now, I (obviously) want to enable the /GS
flag if the user enabled this option, and if they did, I want to add it if the compiler is MSVC, and it should be added to the Release
and RelWithDebInfo
configurations.
This is pretty nested, and I can't seem to get it right. This is as far as I got:
target_compile_options(mytarget PRIVATE
"$<$<BOOL:MYTARGET_ENABLE_GS>:
$<$<CONFIG:Release>: $<$<CXX_COMPILER_ID:MSVC>:/GS>>
$<$<CONFIG:RelWithDebInfo>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>>")
Edit: Fixed, see below.
I've had to use the $<$<BOOL:...>>
because that "translates" the option
(which can be on/off or true/false, to 0
or 1
, which the generator expression needs.
However above line doesn't work: It doesn't add (or not add) /GS
.
I'd like to know:
1) Where is my mistake? How to do this? And
>
or something like that.
I could probably use "manual" if
s to make this more readable, but imagine having 5-10 of such options - writing an if
/end
with a target_compile_options
inside results in like 15-30 lines of if
/end
code, which is also not very pretty to look at. What's the best way to do this?Edit: I was nearly there. Variables have to be enclosed with ${...}
in generator expressions. So it's for example:
target_compile_options(mytarget PRIVATE
"$<$<BOOL:${MYTARGET_ENABLE_GS}>:
$<$<CONFIG:Release>: $<$<CXX_COMPILER_ID:MSVC>:/GS>>
$<$<CONFIG:RelWithDebInfo>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>>")
and that works great.
Which still leaves point "2)", which I would be keen to getting insights on.
CMake Generators are platform-specific so each may be available only on certain platforms. The cmake(1) command-line tool --help output lists available generators on the current platform. Use its -G option to specify the generator for a new build tree.
Generator expressions are evaluated during build system generation to produce information specific to each build configuration. Generator expressions are allowed in the context of many target properties, such as LINK_LIBRARIES , INCLUDE_DIRECTORIES , COMPILE_DEFINITIONS and others.
Specifies the build type on single-configuration generators (e.g. Makefile Generators or Ninja ). Typical values include Debug , Release , RelWithDebInfo and MinSizeRel , but custom build types can also be defined.
Python generators are a simple way of creating iterators. All the work we mentioned above are automatically handled by generators in Python. Simply speaking, a generator is a function that returns an object (iterator) which we can iterate over (one value at a time).
When you generate a MSVC solution, the build type (Release, Debug, etc.) is unknown until you actually run the build. Thus, using generator expression for that is correct.
But for the 2 other variables (does user set MYTARGET_ENABLE_GS to OFF and is generation performed for MSVC), they are resolved at configuration time. So, you don't have to check them in a generator expression. You could simply write:
if(MSVC AND MYTARGET_ENABLE_GS)
target_compile_options(mytarget PRIVATE "$<$<OR:$<CONFIG:Release>,$<CONFIG:RelWithDebInfo>>:/GS>")
endif()
This solution also uses $<OR:?[,?]...>
generator expression to regroup under the same expression both cases you build in Release OR RelWithDebInfo
Whenever you may use if
command, use it. Generator expressions are not replacement for if
command.
Generator expressions allow to use conditions dependent on build type. Because on multi-configuration build systems, like Visual Studio, build type isn't known at configuration stage, you cannot use such condition in if
command.
But generator expressions are ugly, so do not use them when it is not needed.
There is nothing bad in
if(MYTARGET_ENABLE_GS)
target_compile_options(mytarget PRIVATE "$<$<CONFIG:Release>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>$<$<CONFIG:RelWithDebInfo>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>")
endif()
If you want to check an option at the beginning, but create a target later, you may store generator expression in the variable, and use this variable later:
set(additional_options)
# Depending on parameters, add options to 'additional_options' list.
if(MYTARGET_ENABLE_GS)
list(APPEND additional_options "$<$<CONFIG:Release>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>$<$<CONFIG:RelWithDebInfo>:$<$<CXX_COMPILER_ID:MSVC>:/GS>>")
endif()
if(<other option>)
list(APPEND additional_options <...>)
endif()
# ...
target_compile_options(mytarget PRIVATE ${additional_options})
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