CMake conditional preprocessor define on code

2020-05-14 06:08发布

问题:

I'm migrating a makefile project to CMake. The person who wrote the makefile the first time had done a module for writing certain values in an include file.

There's a main config.h file that includes a config_in.h. The config.h file contains something like this:

#ifndef USE_FEATURE_A
#define USE_FEATURE_A 0
#endif

#ifndef USE_FEATURE_B
#define USE_FEATURE_B 0
#endif

In the makefile there's a fake target like with_feature_a that writes in config_in.h

#define USE_FEATURE_A 1

In this way someone can type

make with_feature_a
make

to get the right build.

I want to replicate something like this using this codebase but using CMake. I tried a couple of approaches suggested on the net, but I didn't get it to work.

set_target_properties(with_feature_a PROPERTIES COMPILE_DEFINITIONS 
    "WITH_FEATURE_A=1"
)

This isn't working because if I run

make with_feature_a

I don't see with_feature_a in the preprocessor command line.

The second attempt I made is to write a file directly with the content set to whatever I want, but I didn't understand how to connect the file() command to my target.

I placed this in my CMakeLists.txt

file(WRITE 
local/config_in.h 
    "#define WITH_FEATURE_A 1"
)

but this isn't executed everytime and I don't know how to set it to a single target.

Any help is appreciated. Thank you for reading all this stuff. Sorry for the long story :)

UPDATE

The solution provided here is a big enhacement on the road to solution. The problem is that is don't allow recursive definitions. I show an example:

in CMakeLists.txt I placed:

if (WITH_FEATURE_A)
MESSAGE(STATUS "WITH_FEATURE_A")
add_definitions(-DUSE_FEATURE_A=1)
    add_definitions(-DWITH_FEABURE_B=1)
endif()

if (WITH_FEABURE_B)
MESSAGE(STATUS "WITH_FEATURE_B")
add_definitions(-DUSE_FEATURE_D=1)
endif()


if (WITH_FEABURE_C)
MESSAGE(STATUS "WITH_FEATURE_C")
add_definitions(-DUSE_FEATURE_D=1)
endif()


if (WITH_FEABURE_D)
MESSAGE(STATUS "WITH_FEATURE_D")
endif()

in this case if I execute cmake with -DWITH_FEATURE_A=1 I'd love to see in the output:

WITH_FEATURE_A
WITH_FEATURE_B
WITH_FEATURE_D

actually this code print just

WITH_FEATURE_A

回答1:

You can simplify things by avoiding creating the dummy targets and removing the config file. Instead, if you pass the requirements via the command line when you invoke CMake (or via the CMake GUI), you can run make only once.

For example, you could add the following to your CMakeLists.txt:

option(WITH_FEATURE_A "Option description" ON)
option(WITH_FEATURE_B "Option description" OFF)

if(WITH_FEATURE_A)
  add_definitions(-DUSE_FEATURE_A)
endif()
if(WITH_FEATURE_B)
  add_definitions(-DUSE_FEATURE_B)
endif()

By default, if you just run CMake, it will set the CMake variable WITH_FEATURE_A to ON which consequently adds USE_FEATURE_A as a preprocessor definition to the build. USE_FEATURE_B is undefined in the code.

This would be equivalent to doing #define USE_FEATURE_A in your code.


If you really need the equivalent of

#define USE_FEATURE_A 1
#define USE_FEATURE_B 0

then in your CMakeLists.txt you can do:

option(WITH_FEATURE_A "Option description" ON)
option(WITH_FEATURE_B "Option description" OFF)

if(WITH_FEATURE_A)
  add_definitions(-DUSE_FEATURE_A=1)
else()
  add_definitions(-DUSE_FEATURE_A=0)
endif()
if(WITH_FEATURE_B)
  add_definitions(-DUSE_FEATURE_B=1)
else()
  add_definitions(-DUSE_FEATURE_B=0)
endif()


To change these defaults from the command line, simply do (e.g.):

cmake . -DWITH_FEATURE_A=OFF -DWITH_FEATURE_B=ON
make

Once a variable has been set via the command line this way, it is cached and will remain unchanged until either it is overwritten with a different value on the command line, or you delete the CMakeCache.txt file in your build root.


Response to update:

As @Peter noted, you appear to be mixing up CMake variables (the WITH_FEATURE... ones) and the preprocessor definitions (the USE_FEATURE... ones). You can as suggested resolve all the dependencies between options first, then set the resulting preprocessor definitions, or in this case where the flow is quite straightforward, just do it all in one go:

if(WITH_FEATURE_A)
  message(STATUS "WITH_FEATURE_A")
  add_definitions(-DUSE_FEATURE_A=1)
  set(WITH_FEATURE_B ON)
endif()

if(WITH_FEATURE_B)
  message(STATUS "WITH_FEATURE_B")
  add_definitions(-DUSE_FEATURE_B=1)
  set(WITH_FEATURE_D ON)
endif()

if(WITH_FEATURE_C)
  message(STATUS "WITH_FEATURE_C")
  add_definitions(-DUSE_FEATURE_C=1)
  set(WITH_FEATURE_D ON)
endif()

if(WITH_FEATURE_D)
  message(STATUS "WITH_FEATURE_D")
  add_definitions(-DUSE_FEATURE_D=1)
endif()




回答2:

It sounds like you want to introduce relationships between your options. This will be easier if you separate the steps. First resolve the relationships, then set the C defines on the results. Remember that WITH_FEATURE_A is a cmake variable, and USE_FEATURE_A is C preprocessor define set with ADD_DEFINITIONS:

# Specify the inter-feature dependencies
if (WITH_FEATURE_A)
    # A requires B because <somereason>
    set(WITH_FEATURE_B ON)
endif()

if (WITH_FEATURE_B)
    set(WITH_FEATURE_D ON)
endif()

if (WITH_FEATURE_C)
    set(WITH_FEATURE_D ON)
endif()

# Now generate the C defines for passing the options to the compiler
if (WITH_FEATURE_A)
    MESSAGE(STATUS "WITH_FEATURE_A")
    add_definitions(-DUSE_FEATURE_A=1)
endif()

if (WITH_FEATURE_B)
    MESSAGE(STATUS "WITH_FEATURE_B")
    add_definitions(-DUSE_FEATURE_B=1)
endif()

if (WITH_FEATURE_C)
    MESSAGE(STATUS "WITH_FEATURE_C")
    add_definitions(-DUSE_FEATURE_C=1)
endif()

if (WITH_FEATURE_D)
    MESSAGE(STATUS "WITH_FEATURE_D")
    add_definitions(-DUSE_FEATURE_D=1)
endif()


回答3:

I stumbled on this question and I figured I shared another option: TARGET_COMPILE_DEFINITIONS. You could have two targets in your CMakeLists.txt file, one per configuration, and have something like this

ADD_EXECUTABLE (versionA, ...)
TARGET_COMPILE_DEFINITIONS (versionA, PUBLIC -DWITH_FEATURE_A=1 -DWITH_FEATURE_B=0)

ADD_EXECUTABLE (versionB, ...)
TARGET_COMPILE_DEFINITIONS (versionB, PUBLIC -DWITH_FEATURE_A=0 -DWITH_FEATURE_B=1)

This is tells cmake to add the preprocessors definitions of macros WITH_FEATURE_A and WITH_FEATURE_B (with the proper value), just like if you defined them in your *pp files. Then you can tell make which version to compile:

make versionA
make versionB