CMake documentation states the following:
Usage requirements are propagated by reading the INTERFACE_ variants of target properties from dependencies and appending the values to the non-INTERFACE_ variants of the operand.
This seems to be wrong. Please see the minimal example below:
cmake_minimum_required(VERSION 3.31.3)
project(main)
add_library(lib OBJECT lib.cpp)
set_property(TARGET lib PROPERTY INTERFACE_COMPILE_DEFINITIONS "DEFS")
add_executable(main main.cpp)
target_link_libraries(main PUBLIC lib)
function(print_property)
set(oneValueArgs TARGET PROPERTY)
cmake_parse_arguments(PARSE_ARGV 0 arg "" "${oneValueArgs}" "")
set(PROP "NONE")
get_property(PROP TARGET ${arg_TARGET} PROPERTY ${arg_PROPERTY})
message("${PROP}")
endfunction()
print_property(TARGET lib PROPERTY INTERFACE_COMPILE_DEFINITIONS)
print_property(TARGET lib PROPERTY COMPILE_DEFINITIONS)
print_property(TARGET main PROPERTY INTERFACE_COMPILE_DEFINITIONS)
print_property(TARGET main PROPERTY COMPILE_DEFINITIONS)
Upon configuration stage, only the first message prints something (the DEFS
string), the rest of them prints nothing.
Question: shouldn't the COMPILE_DEFINITIONS
of main
be populated with DEFS
, according to the quote in the beginning of the question. The question is kinda relevant for INTERFACE_COMPILE_DEFINITIONS
of the main
too.
Building stage goes fine - main.o
is being built with -DDEFS
, as expected.
Propagation of properties occurs at the very end of configuration process, at generation stage. With command get_target_property
you cannot check the results of propagation - this command extracts value of the property immediately, when the propagation hasn't been performed yet. Instead, refer to the property via generator expression
$<TARGET_PROPERTY:main,COMPILE_DEFINITIONS>
and use one of the method for output its value. E.g.
add_custom_target(mytarget ALL
COMMAND ${CMAKE_COMMAND} -E echo
"Compile definitions for main: $<TARGET_PROPERTY:main,COMPILE_DEFINITIONS>"
)
(the output from custom target will be created when you build the project).
The funny thing is that even the linkage in target_link_libraries
call is deferred until the end of the configuration process. E.g. in that call you may use library targets, created after it:
add_executable(main main.cpp)
# Link with the library target which is not created yet.
target_link_libraries(main PUBLIC lib)
# Create the library target now.
add_library(lib OBJECT lib.cpp)
# And assign a property, which will be finally propagated to the executable.
set_property(TARGET lib PROPERTY INTERFACE_COMPILE_DEFINITIONS "DEFS")