Roknus
Roknus

Reputation: 113

Defining preprocessor in modern CMake

I'm currently learning CMake and don't want to get bad habits since there is always 2 ways to do something in CMake (old way and modern way from what I have seen)

After reading some documentation about preprocessor in CMake and checking this post: Define preprocessor macro through cmake

I've come to the conclusion that I could either define a preprocessor as :

and after some test they does actually all works as intended and define FOO

But now my question is what's the most "modern" way that I should use and what is really the difference between each functions, the only difference I noticed is that if I use target_compile_definitions(myTarget PUBLIC FOO) it then define FOOin parent target.

Upvotes: 6

Views: 9121

Answers (2)

The general trend in modern CMake is to move from global settings to target-centric settings. Based on this rule alone, target_compile_definitions() is the most modern approach. It also allows control of whether the settings are used in the target only (PRIVATE), in other targets consuming this target (INTERFACE), or both (PUBLIC). Internally, it works by modifying the target's properties COMPILE_DEFINITIONS and INTERFACE_COMPILE_DEFINITIONS.

Next down in terms of being modern is add_compile_definitions(). It adds the macro definitions to all targets defined in the current directory and subdirectories; its scope is similar to include_directories() in this regard. Internally, it works by modifying the COMPILE_DEFINITIONS property of the current directory. So: it's still using the correct "modern" mechanisms, but is directory-oriented instead of target-oriented.

At the bottom of the list, we have the very old function add_definitions(). This is really best avoided in modern CMake. While intended for specifying preprocessor definitions (hence its name), it actually allows passing arbitrary compiler options in (which is also why you need to specify -DFOO instead of just FOO as its argument). It tries to figure out whether the things passed in are actually preprocessor macro definitions, in which case they are moved into the directory's COMPILE_DEFINTIIONS property. If they are not identified as such (which can happen for macros with complicated replacement strings), they are left in the list of flags.

Upvotes: 10

Matthieu Brucher
Matthieu Brucher

Reputation: 22023

Indeed, target_compile_definitions is the most adequate way of adding preprocessing macros.

The reason target_compile_definitions(myTarget PUBLIC FOO) propagates FOO to dependencies is also one of the reasons you should use it, as you can properly define them per target instead of per folder. You can now attach these definitions to a target so that dependencies also declare them. If you try creating targets from different folders, you will see this behavior more precisely and see that it actually makes far more sense than the previous very unreliable one.

For instance, on Windows, all the usual "exports" macros should be declared this way and PRIVATE so they don't get propagated, as they are really private definitions.

Upvotes: 4

Related Questions