112

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?

Peter Clark
  • 2,863
  • 3
  • 23
  • 37
  • Possible duplicate of [How do I add a linker or compile flag in a CMake file?](https://stackoverflow.com/questions/11783932/how-do-i-add-a-linker-or-compile-flag-in-a-cmake-file) – vitaut Mar 20 '19 at 22:59

1 Answers1

131

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
starball
  • 20,030
  • 7
  • 43
  • 238
ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
  • 15
    Note that `target_compile_options` **add** options, so you can modify last line like [this](http://pastebin.com/jCDW5Aa9) to make it more readable) –  Jun 02 '14 at 13:21
  • 2
    @ComicSansMS Great answer and the best (and most up-to-date) that I could find on the internet. Thanks! – Ela782 Jun 05 '15 at 17:32
  • If I want to have the same strict set of compilation flags globally, what's the best modern way to do it? Specifying the same flags again and again for different targets with target_compile_options seems like a bad idea – user3667089 Sep 23 '17 at 21:21
  • 1
    @user3667089 The target-specific properties usually get initialized by a variable or directory property. The manpage for the respective property tells you which exactly. In the case of compile options, the [`COMPILE_OPTIONS`](https://cmake.org/cmake/help/v3.0/prop_dir/COMPILE_OPTIONS.html#prop_dir:COMPILE_OPTIONS) directory property does this. You can use the [`add_compile_options`](https://cmake.org/cmake/help/v3.0/command/add_compile_options.html#command:add_compile_options) command to set this. The options for which this is a good approach are usually few and far between. – ComicSansMS Sep 23 '17 at 23:43
  • I'm confused, if I have ---set(CMAKE_CXX_FLAGS_DEBUG "-g3 -ggdb -Og -Wall")--- can I change for ---set(DEBUG_FLAGS "-g3 -ggdb -Og -Wall") target_compile_options(hsm-parser PUBLIC "$<$:${DEBUG_FLAGS}>")--- – m4l490n Mar 27 '19 at 17:05
  • 6
    @ComicSansMS What is the use of `PUBLIC` in `target_compile_options`? – Ramana Reddy Feb 25 '20 at 11:56
  • 1
    @RamanaReddy I added additional explanation of the `PUBLIC` keyword to the answer. – Stefan Profanter Dec 15 '20 at 07:35
  • 2
    Note that if you're `MY_DEBUG_OPTIONS ` contains spaces you will need the `SHELL: ` prefix, like this: `target_compile_options(${NAME} PUBLIC $<$:SHELL:${MY_DEBUG_OPTIONS}>) `. Otherwise you will get compilation errors as Cmake will wrap your compiler options in double quotes. – Nathanael Weiss Aug 15 '22 at 18:29
  • Could you add an example to the answer? – Thuong Vo Apr 14 '23 at 02:36