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?
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.
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