Issue
I currently try to write a custom_target to print out properites of a target (e.g. COMPILE_DEFINITIONS). I've placed the invocation of this custom_target creation almost at the end of my Top-Level-CMakeLists.txt to make sure all modules have been invoked.
The goal is to print out all properties of a target including properties given by dependencies via target_link_libraries.
Simplified example:
add_library(libA STATIC)
add_library(libB STATIC)
target_compile_definitions(libA
PRIVATE
PRIV_A
PUBLIC
PUB_A
INTERFACE
INT_A
)
target_compile_definitions(libB
PRIVATE
PRIV_B
PUBLIC
PUB_B
INTERFACE
INT_B
)
# create dependency from A -> B,
# this should compile A with all PUBLIC and INTERFACE defintions from B
target_link_libraries(libA libB)
get_target_property(compile_defs libA COMPILE_DEFINITIONS)
get_target_property(compile_defs_intf libA INTERFACE_COMPILE_DEFINITIONS)
message("compile_defs: ${compile_defs}")
message("compile_defs_intf: ${compile_defs_intf}")
This will print:
compile_defs: PRIV_A; PUB_A
compile_defs_intf: PUB_A; INT_A
Actually I would like to get:
compile_defs: PRIV_A; PUB_A; PUB_B; INT_B
But obviously at this stage, the dependencies are not yet resolved / included in the properties. A possible workaround would be to iterate over all dependencies of target A and collect all the INTERFACE_PROPERTIES of the dependency target. But this would require quiet some recursion to resolve all dependencies in the tree (e.g. requires resolving of all dependencies...).
Is it possible to get properties of a target incl. his dependencies (PUBLIC, INTERFACE properties) in a more easy way?
Solution
First get all dependent libraries:
# target_get_linked_libraries.cmake
#
function(list_add_if_not_present list elem)
list(FIND "${list}" "${elem}" exists)
if(exists EQUAL -1)
list(APPEND "${list}" "${elem}")
set("${list}" "${${list}}" PARENT_SCOPE)
endif()
endfunction()
macro(_target_get_linked_libraries_in _target _outlist)
list_add_if_not_present("${_outlist}" "${_target}")
# get libraries
get_target_property(target_type "${_target}" TYPE)
if (${target_type} STREQUAL "INTERFACE_LIBRARY")
get_target_property(libs "${_target}" INTERFACE_LINK_LIBRARIES)
else()
get_target_property(libs "${_target}" LINK_LIBRARIES)
endif()
foreach(lib IN LISTS libs)
if(NOT TARGET "${lib}")
continue()
endif()
list(FIND "${_outlist}" "${lib}" exists)
if(NOT exists EQUAL -1)
continue()
endif()
_target_get_linked_libraries_in("${lib}" "${_outlist}")
endforeach()
endmacro()
function(target_get_linked_libraries _target _outlist)
set(${_outlist} "${_target}")
_target_get_linked_libraries_in(${_target} ${_outlist})
set(${_outlist} ${${_outlist}} PARENT_SCOPE)
endfunction()
Then just iterate over all dependent libraries and get the interface definitions:
cmake_minimum_required(VERSION 3.11)
project(A)
add_library(libB STATIC)
target_compile_definitions(libB
PRIVATE PRIV_B
PUBLIC PUB_B
INTERFACE INT_B
)
add_library(libA STATIC)
target_compile_definitions(libA
PRIVATE PRIV_A
PUBLIC PUB_A
INTERFACE INT_A
)
target_link_libraries(libA PUBLIC libB)
include(target_get_linked_libraries.cmake)
target_get_linked_libraries(libA libraries)
set(compile_defs)
foreach(i IN LISTS libraries)
if("${i}" STREQUAL libA)
get_target_property(tmp "${i}" COMPILE_DEFINITIONS)
else()
get_target_property(tmp "${i}" INTERFACE_COMPILE_DEFINITIONS)
endif()
list(APPEND compile_defs "${tmp}")
endforeach()
message("compile_defs: ${compile_defs}")
message(FATAL_ERROR "")
would output:
$ cmake .
compile_defs: PRIV_A;PUB_A;PUB_B;INT_B
Answered By - KamilCuk