christopo
christopo

Reputation: 186

CMake: Print properties of a target including its dependencies

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. its dependencies (PUBLIC, INTERFACE properties) in a more easy way?

Upvotes: 3

Views: 4597

Answers (1)

KamilCuk
KamilCuk

Reputation: 142080

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

Upvotes: 6

Related Questions