449

How do I define a preprocessor variable through CMake?

The equivalent code would be #define foo.

0

6 Answers 6

556

For a long time, CMake only had the add_definitions command for this purpose. However, since CMake version 3.12 (released July 2018) the command has been superseded by a more fine grained approach (separate commands for compile definitions, include directories, and compiler options).

An example using the new add_compile_definitions:

add_compile_definitions(OPENCV_VERSION=${OpenCV_VERSION})
add_compile_definitions(WITH_OPENCV2)

Or:

add_compile_definitions(OPENCV_VERSION=${OpenCV_VERSION} WITH_OPENCV2)

The good part about this is that it circumvents the shabby trickery CMake has in place for add_definitions. CMake is such a shabby system, but they are finally finding some sanity.

Find more explanation on which commands to use for compiler flags here: https://cmake.org/cmake/help/latest/command/add_definitions.html

Likewise, you can do this per-target as explained in Jim Hunziker's answer.

Sign up to request clarification or add additional context in comments.

19 Comments

From the linked page: "Note This command has been superseded by alternatives: Use add_compile_definitions() to add preprocessor definitions." Maybe this answer needs an edit?
In cmake 3.10.2, add_compile_definitions throws CMake Error at CMakeLists.txt:6 (add_compile_definitions): Unknown CMake command "add_compile_definitions".. Had to use add_compile_options(-D <your-def>) instead.
@mannyglover I don't think so, but you can set the compiler flags with -D, something along the lines of cmake -D CMAKE_CXXFLAGS='-DDEBUG_CHESSBOARD' (not tested)
It's really new... in fact it's in More Modern CMake (> 3.11). A pain that it's so hard to know when a command was introduced.
@ypnos Two underscores are required: e.g. cmake -D CMAKE_CXX_FLAGS='-DDEBUG_CHESSBOARD'.
|
339

To do this for a specific target, you can do the following:

target_compile_definitions(my_target PRIVATE FOO=1 BAR=1)

You should do this if you have more than one target that you're building and you don't want them all to use the same flags. Also see the official documentation on target_compile_definitions.

3 Comments

@JimHunziker How is target_compile_definitions(my_target PRIVATE FOO=1) different from set_source_files_properties(foo.cpp PROPERTIES COMPILE_DEFINITIONS -DFOO=1) ?
@JohnStrood The difference is at scope level. target_compile_definitions sets the value for WHOLE executable/library, where as 'set_source_files_properties` sets the value for only the specified file. Latter command allows for files to compiled using a different language; i.e.: set_source_files_properties(compile_me_as_objc.c PROPERTIES -x objective-c. Note that -x objective-c here is a flag specific to GCC/Clang.
This is good, but do prefer literally #define foo in your header files whenever possible. That's the best way to make sure everything using your header is consistently setting that value, regardless of OS, build system, compiler, etc. If you must, set target_compile_definitions as demonstrated here. Note that usually you would want to set target_compile_definitions as a PUBLIC setting so that your build and your consumers' builds are consistent.
38

The other solutions proposed on this page are useful for some versions of Cmake > 3.3.2. Here the solution for the version I am using (i.e., 3.3.2). Check the version of your Cmake by using $ cmake --version and pick the solution that fits your needs. The cmake documentation can be found on the official page.

With CMake version 3.3.2, in order to create

#define foo

I needed to use:

add_definitions(-Dfoo)   # <--------HERE THE NEW CMAKE LINE inside CMakeLists.txt
add_executable( ....)
target_link_libraries(....)

and, in order to have a preprocessor macro definition like this other one:

#define foo=5

the line is so modified:

add_definitions(-Dfoo=5)   # <--------HERE THE NEW CMAKE LINE inside CMakeLists.txt
add_executable( ....)
target_link_libraries(....)

PLEASE NOTE (as @squareskittles suggests in one of the comment): "if you are using CMake 3.3.2, you have to use add_definitions() or target_compile_definitions(). The more modern command, add_compile_definitions(), was not added until CMake 3.12."

Comments

25

1.) target_compile_definitions

If you are using CMake 3.X your first choice for adding a preprocessor macro should be target_compile_definitions.

The reason you should prefer this approach over any other approach is because it granularity is target based. IE the macro will only be added to your exe/library.

Here is a common example:

if (WIN32)
    target_compile_definitions(my_lib PRIVATE   
       # Prevents Windows.h from adding unnecessary includes    
       WIN32_LEAN_AND_MEAN  
       # Prevents Windows.h from defining min/max as macros 
       NOMINMAX 
    )   
endif() 

2.) add_compile_definitions

New in version 3.12.

Find more explanation on which commands to use for compiler flags here: https://cmake.org/cmake/help/latest/command/add_definitions.html

add_compile_definitions applies macros to any targets that are defined after the call.

Here is the same logic as above with add_compile_definitions.

add_compile_definitions(WIN32_LEAN_AND_MEAN NOMINMAX)
add_library(my_lib)

If you use this approach be careful if you are the top level project. Otherwise if users consume your library using add_subdirectory they may have issues.

3.) The other less recommended ways

These approaches really aren't recommended anymore. Due to not being modular, not scaling well, not supporting generator expressions, etc.

Why is target_compile_definitions better/preferred?

  • It's much more clear to readers of your CMake code how it works.
  • Allows usage of PRIVATE/PUBLIC/INTERFACE if needed. Which can make life easier for consumers of your library.
  • It's much more modular.

Applying pre-processor flags (Or any compiler flag) globally can create hidden dependencies in your build.

Essentially think of add_compile_definitions as globals in C/C++. Sometimes you need them, but be careful.

1 Comment

Alternatively to wrapping target compile definitions in if(), you may sometimes want to use generator expressions instead. E.g. target_compile_definitions(my_lib PRIVATE "$<$<PLATFORM_ID:Windows>:WIN32_LEAN_AND_MEAN>")
4

Here is an example where you can pass values from CMake to C++ code. Say, you want to pass:

  • flag, here: BOOST ("true" or "false")
  • software version string (e.g., "1.0.0")

I recommend to pass them as strings.

So, when you build software with CMake, you can pass parameters like for example if it was built using the Boost library, the software version is pulled from a CMake variable (so that you change that number only in one place). See below.

In CMakeLists.txt:

add_compile_definitions(
    BOOST="${BOOST}"
    Software_VERSION="${PROJECT_VERSION}"
)

In your .cpp code:

std::cout << "Software version is: " << Software_VERSION << " BOOST: " << BOOST << "\n";

1 Comment

If you want to add preprocessor definition only if CMake boolean variable is TRUE, you may use generator expressions e.g. target_compile_definitions(some_lib PRIVATE "$<$<BOOL:${MY_CMAKE_VAR}>:MY_PREPROCESSOR_VAR>")
3

I'd like to recommend use target_*** operations instead of add_*** operations when your solution includes many projects.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.