4

I've been told it's bad practice to do things like seting CFLAGS directly in CMake, and that, instead, I should use the target_compile_definitions() command.

Ok, but - what if I want to use similar/identical definitions for multiple (independent) targets? I don't want to repeat myself over and over again.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • The link is not working (at least now). `bad practice to do things like seting CFLAGS directly in CMake` - If you talk about setting variable `CMAKE_CXX_FLAGS`, then it depends from what kind of options you want to set. No needs to completely avoid setting the variable. – Tsyvarev Dec 02 '17 at 21:36
  • @Tsyvarev: Added a video link. – einpoklum Dec 02 '17 at 22:18
  • @einpoklum for our internal build system, used by everyone in our company for all our builds, we set `CMAKE_CXX_FLAGS` directly, so they are the same for all targets. In our case it makes sense. If you want to open source your project, and have others consume your CMakeLists via ExternalProject, then doing so would be considered bad practice, as you're foisting your build flags on your users. It depends on your situation – Steve Lorimer Dec 02 '17 at 22:55
  • @SteveLorimer - `... have others consume your CMakeLists via ExternalProject, then doing so would be considered bad practice` - ExternalProject doesn't affect on project's `CMAKE_CXX_FLAGS`. Do you mean `add_subdirectory` instead? – Tsyvarev Dec 02 '17 at 23:11
  • 1
    @SteveLorimer: Actually, on second though, note that it doesn't matter whether others consume your your CMakeLists via ExternalProject or whether it's just users building it - it's still bad practice since if a user has a different C/C++ compiler with a different flag syntax then you could be screwed either way. – einpoklum Dec 02 '17 at 23:23

3 Answers3

7

I see three possible ways:

  1. The preferred one using target_compile_definitions(... INTERFACE/PUBLIC ...) which would self-propagate the compiler definitions to targets depending on it via target_link_libraries() command.

  2. Using the set_property(TARGET target1 target2 ... APPEND PROPERTY COMPILE_DEFINITIONS ...) to set the same definitions to multiple targets.

  3. You may still use the "old commands" of add_definitions() and remove_definitions() to modify COMPILE_DEFINITIONS directory property (which would pre-set all COMPILE_DEFINITIONS target properties in this directories scope).

References

Florian
  • 39,996
  • 9
  • 133
  • 149
  • Edited my question to rule out option 1 - that's not the situation I meant. I'll try option 2... and it works. Thanks. – einpoklum Dec 02 '17 at 21:00
  • 1
    Note that option 2. is probably only a good idea if all the targets are being created in the same directory as the `set_property` call. Modern CMake relies on you keeping settings localized and close to the definition of the targets. I personally would consider 'long-distance' manipulation of target properties bad style. – ComicSansMS Dec 04 '17 at 08:12
2

tl;dr: You can iterate the targets in a loop.

If you have a bunch of targets with some common/similar features, you may want to simply manipulate them all in a loop! Remember - CMake is not like GNU Make, it's a full-fledged scripting language (well, sort of). So you could write:

set(my_targets
    foo
    bar
    baz)

foreach(TARGET ${my_targets})
    add_executable(${TARGET} "${TARGET}.cu")
    target_compile_options(${TARGET} PRIVATE "--some_option=some_value")
    target_link_libraries(${TARGET} PRIVATE some_lib)
    # and so on
    set_target_properties(
        ${TARGET}
        PROPERTIES
        C_STANDARD 99
        C_STANDARD_REQUIRED YES
        C_EXTENSIONS NO )
endforeach(TARGET)

And you could also initialize an empty list of targets, then add to it here-and-there, and only finally apply your common options and settings to all of them, centrally.

Note: In this example I added PRIVATE compile options, but if you need some of them to propagate to targets using your targets, you can make them PUBLIC).

einpoklum
  • 118,144
  • 57
  • 340
  • 684
1

another neat solution is to define an interface library target (a fake target that does not produce any binaries) with all required properties and compiler definitions, then link the other existing targets against it

example:

add_library(myfakelib INTERFACE)
target_compile_definitions(myfakelib INTERFACE MY_NEEDED_DEFINITION)

add_executable(actualtarget1 main1.cpp)
add_executable(actualtarget2 main2.cpp)

set_property(
    TARGET actualtarget1 actualtarget2
    APPEND PROPERTY LINK_LIBRARIES myfakelib
)

refs:

https://cmake.org/cmake/help/latest/command/add_library.html#interface-libraries

demon36
  • 377
  • 3
  • 10
  • Does it have to be an interface library target? Can't it be some kind of generic artificial target? Also, will a library target propagate the C-language properties? – einpoklum Mar 12 '21 at 13:03
  • any library target will propagate properties, "INTERFACE" type is actually an "artificial" type that does not produce any binaries, just as I've mentioned – demon36 Mar 16 '21 at 14:19