1

I find myself repeating the following segment of code in my CMakeLists.txt:

option(SOME_OPT "Some option" ON)
if (SOME_OPT)
    target_compile_definitions(my_app SOME_IDENTIFIER_HERE_RELATED_TO_OPT)
endif()

(and occasionally something similar but with a string value for the -D).

Is there a mechanism in CMake (never mind the version) which makes this easier, or more terse?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • You could always define a macro: `macro(my_add_option OPTION DOC DEFAULT) option(${OPTION} ${DOC} ${DEFAULT}) if (${OPTION}) add_definitions(${ARGN}) endif() endmacro()` `my_add_option(SOME_OPT "Some option" ON -DSOME_IDENTIFIER_HERE_RELATED_TO_OPT) my_add_option(FOO_OPT "foo option" ON FOO=1 BAR=0)` – fabian Jun 28 '21 at 16:26
  • @fabian - better to do it as a function to avoid variable scoping issues. – Alex Reinking Jun 28 '21 at 16:35

1 Answers1

1

Option 1: Using generator expressions

Here's one option using target properties and generator expressions:

option(SOME_OPT1 "Some option 1" ON)
option(SOME_OPT2 "Some option 2" OFF)
option(SOME_OPT3 "Some option 3" ON)

target_compile_definitions(my_app PRIVATE
  $<$<BOOL:${SOME_OPT1}>:SOME_IDENTIFIER_HERE_RELATED_TO_OPT1>
  $<$<BOOL:${SOME_OPT2}>:SOME_IDENTIFIER_HERE_RELATED_TO_OPT2>
  $<$<BOOL:${SOME_OPT3}>:SOME_IDENTIFIER_HERE_RELATED_TO_OPT3>
)

The $<BOOL:${var}> dance is necessary to translate truthy/falsey CMake values to either 1 or 0, as the generator expression mini-language expects.

Option 2: A generated "configure file"

Another option if you have many of these is to use the configure_file command to create a header with the relevant information. Create a header called config.h.in (as an example) with the following contents:

#ifndef MYPROJ_CONFIG_H
#define MYPROJ_CONFIG_H

#cmakedefine SOME_IDENTIFIER_HERE_RELATED_TO_OPT1
#cmakedefine SOME_IDENTIFIER_HERE_RELATED_TO_OPT2
#cmakedefine01 SOME_IDENTIFIER_HERE_RELATED_TO_OPT3

#endif

The #cmakedefine VAR directive is processed by configure_file to set VAR if VAR is defined in the CMakeLists.txt as something truthy and to comment out the line if not. The 01 variant always defines VAR, but sets it to either 0 or 1.

Then in your CMakeLists.txt you could write:

option(SOME_OPT1 "Some option 1" ON)
option(SOME_OPT2 "Some option 2" OFF)
option(SOME_OPT3 "Some option 3" ON)

# Setting these variables exposes them to configure_file below
set(SOME_IDENTIFIER_HERE_RELATED_TO_OPT1 "${SOME_OPT1}")
set(SOME_IDENTIFIER_HERE_RELATED_TO_OPT2 "${SOME_OPT2}")
set(SOME_IDENTIFIER_HERE_RELATED_TO_OPT3 "${SOME_OPT3}")

configure_file(config.h.in config.h)

target_include_directories(my_app PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>")

Your source files could then #include <config.h>, which would be, by default:

#ifndef MYPROJ_CONFIG_H
#define MYPROJ_CONFIG_H

#define SOME_IDENTIFIER_HERE_RELATED_TO_OPT1
/* #undef SOME_IDENTIFIER_HERE_RELATED_TO_OPT2 */
#define SOME_IDENTIFIER_HERE_RELATED_TO_OPT3 1

#endif

Both options cost one line of overhead per option, though the first approach is a little noisy and the second approach costs a file. However, the second approach also lets you add additional preprocessor code that might be better placed together in the configured file. It can also substitute CMake variable values via the @VAR@ syntax. See the documentation for more detail: https://cmake.org/cmake/help/latest/command/configure_file.html

einpoklum
  • 118,144
  • 57
  • 340
  • 684
Alex Reinking
  • 16,724
  • 5
  • 52
  • 86
  • Alex - I amended the question to have target-specific compile definitions, since I don't want to "muddy the waters" with this issue. Sorry about that; I'm only asking about avoiding the repeated typing. – einpoklum Jun 28 '21 at 17:06
  • @einpoklum - I have edited my answer down to the two approaches I would recommend in light of that. – Alex Reinking Jun 28 '21 at 17:16