There are multiple mechanisms offered by CMake for getting flags to the compiler:
Is there one method that is preferred over the other in modern use? If so why? Also, how can this method be used with multiple configuration systems such as MSVC?
There are multiple mechanisms offered by CMake for getting flags to the compiler:
Is there one method that is preferred over the other in modern use? If so why? Also, how can this method be used with multiple configuration systems such as MSVC?
For modern CMake (versions 2.8.12 and up) you should use target_compile_options
, which uses target properties internally.
CMAKE_<LANG>_FLAGS
is a global variable and the most error-prone to use. It also does not support generator expressions, which can come in very handy.
add_compile_options
is based on directory properties, which is fine in some situations, but usually not the most natural way to specify options.
target_compile_options
works on a per-target basis (through setting the COMPILE_OPTIONS
and INTERFACE_COMPILE_OPTIONS
target properties), which usually results in the cleanest CMake code, as the compile options for a source file are determined by which project the file belongs to (rather than which directory it is placed in on the hard disk). This has the additional advantage that it automatically takes care of passing options on to dependent targets if requested.
Even though they are little bit more verbose, the per-target commands allow a reasonably fine-grained control over the different build options and (in my personal experience) are the least likely to cause headaches in the long run.
In theory, you could also set the respective properties directly using set_target_properties
, but target_compile_options
is usually more readable.
For example, to set the compile options of a target foo
based on the configuration using generator expressions you could write:
target_compile_options(foo PUBLIC "$<$<CONFIG:DEBUG>:${MY_DEBUG_OPTIONS}>")
target_compile_options(foo PUBLIC "$<$<CONFIG:RELEASE>:${MY_RELEASE_OPTIONS}>")
The PUBLIC
, PRIVATE
, and INTERFACE
keywords define the scope of the options. E.g., if we link foo
into bar
with target_link_libraries(bar foo)
:
PRIVATE
options will only be applied to the target itself (foo
) and not to other libraries (consumers) linking against it.INTERFACE
options will only be applied to the consuming target bar
PUBLIC
options will be applied to both, the original target foo
and the consuming target bar
target_compile_options
add options, so you can modify last line like this to make it more readable)
COMPILE_OPTIONS
directory property does this. You can use the add_compile_options
command to set this. The options for which this is a good approach are usually few and far between.
Commented
Sep 23, 2017 at 23:43
PUBLIC
in target_compile_options
?
Commented
Feb 25, 2020 at 11:56
PUBLIC
keyword to the answer.
Commented
Dec 15, 2020 at 7:35
MY_DEBUG_OPTIONS
contains spaces you will need the SHELL:
prefix, like this: target_compile_options(${NAME} PUBLIC $<$<CONFIG:DEBUG>:SHELL:${MY_DEBUG_OPTIONS}>)
. Otherwise you will get compilation errors as Cmake will wrap your compiler options in double quotes.
Commented
Aug 15, 2022 at 18:29