2015-09-24 12 views
10

Sto cercando di acquisire un elenco dei percorsi assoluti per tutte le librerie collegate a una destinazione specifica in CMake da utilizzare in una chiamata a add_custom_command. Tuttavia, get_target_property(_LINK_LIBRARIES ${TARGET} LINK_LIBRARIES include solo le dipendenze dirette (ad esempio, tutto ciò che viene utilizzato in una chiamata target_link_libraries(${TARGET} ...)).Elenco ricorsivo di LINK_LIBRARIES in CMake

Pertanto, se collego un altro target CMake, ad es. mylibrary, l'elenco include mylibrary, ma solo come nome e senza librerie collegate in modo transitorio. Poiché questo elenco può anche includere espressioni generatrici arbitrariamente complesse, controllare ciascun elemento se si tratta di un obiettivo e recuperare il suo codice LINK_LIBRARIES in modo ricorsivo non è fattibile. Inoltre, l'obiettivo potrebbe essere specificato in un momento successivo nello CMakeLists.txt e if(TARGET mylibrary) verrà saltato.

Per INCLUDE_DIRECTORIES e COMPILE_DEFINITIONS questo è facilmente risolto, come anche se entrambi comportarsi in modo simile quando get_target_property viene utilizzato (eccetto che obiettivi legati non sono ovviamente nella lista), un generatore di espressione della forma $<TARGET_PROPERTY:${TARGET},INCLUDE_DIRECTORIES> produce l'elenco desiderato include ricorsivamente richiesto e definizioni. Tuttavia, $<TARGET_PROPERTY:${TARGET},LINK_LIBRARIES> produce lo stesso elenco della variante get_target_property.

Come posso recuperare l'elenco di percorsi assoluti desiderato?

Dimostrazione:

cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) 

file(WRITE a.cpp "void foo() {};\n") 
file(WRITE b.cpp "int main(int, char**) { return 0; }\n") 

find_package(Boost REQUIRED COMPONENTS filesystem system) 

add_library(A STATIC a.cpp) 
target_include_directories(A PUBLIC ${Boost_INCLUDE_DIRS}) 
target_link_libraries(A PUBLIC ${Boost_LIBRARIES}) 

# demonstrates (at configure time) that the LINK_LIBRARIES property can contain 
# arbitrary generator expressions, making a recursive solution infeasible 
get_target_property(A_LINK_LIBRARIES A LINK_LIBRARIES) 
message(STATUS "A LINK_LIBARIES: ${A_LINK_LIBRARIES}") 

add_executable(B b.cpp b_lists) 
target_link_libraries(B PRIVATE A) 
target_include_directories(B PRIVATE .) 

get_target_property(B_INCLUDE_DIRECTORIES B INCLUDE_DIRECTORIES) 
get_target_property(B_LINK_LIBRARIES B LINK_LIBRARIES) 

# demonstrates (at compile time) that method 1 is not recursive while method 2 is (for INCLUDE_DIRECTORIES) 
# demonstrates (at compile time) that the library list is never recursive 
add_custom_command(
    OUTPUT b_lists 
    COMMAND ${CMAKE_COMMAND} -E echo "B INCLUDE_DIRECTORIES 1: ${B_INCLUDE_DIRECTORIES}" 
    COMMAND ${CMAKE_COMMAND} -E echo "B INCLUDE_DIRECTORIES 2: $<TARGET_PROPERTY:B,INCLUDE_DIRECTORIES>" 
    COMMAND ${CMAKE_COMMAND} -E echo "B LINK_LIBRARIES 1: ${B_LINK_LIBRARIES}" 
    COMMAND ${CMAKE_COMMAND} -E echo "B LINK_LIBRARIES 2: $<TARGET_PROPERTY:B,LINK_LIBRARIES>" 
    DEPENDS A 
) 
set_source_files_properties(b_lists PROPERTIES SYMBOLIC TRUE) 

uscita:

(configure) 
A LINK_LIBARIES: $<$<NOT:$<CONFIG:DEBUG>>:D:/libs/boost-1_55_0/lib/boost_filesystem-vc110-mt-1_55.lib>;$<$<CONFIG:DEBUG>:D:/libs/boost-1_55_0/lib/boost_filesystem-vc110-mt-gd-1_55.lib>;$<$<NOT:$<CONFIG:DEBUG>>:D:/libs/boost-1_55_0/lib/boost_system-vc110-mt-1_55.lib>;$<$<CONFIG:DEBUG>:D:/libs/boost-1_55_0/lib/boost_system-vc110-mt-gd-1_55.lib> 
(build) 
Generating b_lists 
B INCLUDE_DIRECTORIES 1: D:/projects/cmakeminimal/. 
B INCLUDE_DIRECTORIES 2: D:/projects/cmakeminimal/.;D:/libs/boost-1_55_0/include/boost-1_55 
B LINK_LIBRARIES 1: A 
B LINK_LIBRARIES 2: A 
+0

@ usr1234567 si prega di leggere la domanda correttamente come già afferma i due approcci che ho provato e perché non funzionano. – Joe

+0

Hai ragione, mi dispiace. Volevo vedere del codice ma non sarebbe stato d'aiuto. – usr1234567

+0

Sì, potrei provare a fare un esempio minimale ma non mi è sembrato tutto così utile. – Joe

risposta

7

Il tuo desiderio è stato lì fuori per un po 'ed è - per quanto ne so - non ancora (come per CMake 3.3.2) incorporato nello stesso CMake (vedere 0012435: Possibility to get all link libraries for a target?).

Ho qualche speranza perché questo ticket elenca alcuni possibili approcci alternativi. Ma dopo che ho provato quelli contro il vostro progetto di esempio CMake direi che non sono davvero una soluzione:

  1. export_library_dependencies() - deprecato

    Nota: Poiché questo funziona solo per i Lib-To-Lib dipendenze I hanno - per questo test - ha cambiato la tua add_executable() a un add_library() chiamata

    cmake_policy(SET CMP0033 OLD) 
    export_library_dependencies(LibToLibLinkDependencies.cmake) 
    include("${CMAKE_CURRENT_BINARY_DIR}/LibToLibLinkDependencies.cmake") 
    
    message("A_LIB_DEPENDS: ${A_LIB_DEPENDS}") 
    message("B_LIB_DEPENDS: ${B_LIB_DEPENDS}") 
    

    darebbe ad esempio,

    A_LIB_DEPENDS: optimized;../libboost_filesystem-vc110-mt-1_53.lib;debug;../libboost_filesystem-vc110-mt-gd-1_53.lib;... 
    B_LIB_DEPENDS: general;A; 
    

    Vedi anche policy CMP0033 "The export_library_dependencies() command should not be called"

  2. export(TARGETS ...)

    cmake_policy(SET CMP0024 OLD) 
    export(
        TARGETS A B 
        FILE Test.cmake 
        NAMESPACE Imp_ 
    ) 
    include("${CMAKE_CURRENT_BINARY_DIR}/Test.cmake") 
    

    Ma questo mantiene le espressioni generatore nell'output ed è necessario aggiungere alla lista tutti gli obiettivi a seconda, così non va bene.

    Vedere anche policy CMP0024 "Disallow include export result".

  3. GET_PREREQUISITES()

    ho preso il codice how to use the cmake functions get_prerequisites and get_filename_component for target dependency installation?, ma dimostra - come descritto nella documentazione del modulo - che elenca solo la condiviso librerie.

    add_custom_command(
        OUTPUT b_lists 
        APPEND 
        COMMAND ${CMAKE_COMMAND} -D MY_BINARY_LOCATION="$<TARGET_FILE:B>" -P "${CMAKE_CURRENT_LIST_DIR}/ListSharedLibDependencies.cmake" 
    ) 
    

    ListSharedLibDependencies.cmake

    include(GetPrerequisites) 
    
    get_prerequisites(${MY_BINARY_LOCATION} DEPENDENCIES 0 0 "" "") 
    
    foreach(DEPENDENCY_FILE ${DEPENDENCIES}) 
        gp_resolve_item("${MY_BINARY_LOCATION}" "${DEPENDENCY_FILE}" "" "" resolved_file) 
        message("resolved_file='${resolved_file}'") 
    endforeach() 
    

    sarebbe uscita sulla mia macchina Windows:

    resolved_file='C:/Windows/SysWOW64/KERNEL32.dll' 
    resolved_file='C:/Windows/SysWOW64/MSVCR110D.dll' 
    

Riferimenti

+0

L'ultimo approccio funziona anche solo per le DLL che sono già state trovate nei percorsi predefiniti, per quanto ne so, rendendola ancora meno utile. Infatti sul mio caso d'uso è pre-compilato l'elenco delle directory passate a 'gp_resolve_items' con valori di default utili. – Joe

+1

Vedere che qualcosa di così importante è nell'arretrato è deprimente. – Joe

+0

E come "non è attualmente possibile" è apparentemente la risposta che accetterò per ora. – Joe

4

ricorsivamente traslazione LINK_LIBRARY proprietà è possibile.

Ecco uno get_link_libraries() che lo fa, tuttavia non gestisce tutti i casi (ad es. Le librerie non sono una destinazione, non sono importate librerie).

function(get_link_libraries OUTPUT_LIST TARGET) 
    get_target_property(IMPORTED ${TARGET} IMPORTED) 
    list(APPEND VISITED_TARGETS ${TARGET}) 
    if (IMPORTED) 
     get_target_property(LIBS ${TARGET} INTERFACE_LINK_LIBRARIES) 
    else() 
     get_target_property(LIBS ${TARGET} LINK_LIBRARIES) 
    endif() 
    set(LIB_FILES "") 
    foreach(LIB ${LIBS}) 
     if (TARGET ${LIB}) 
      list(FIND VISITED_TARGETS ${LIB} VISITED) 
      if (${VISITED} EQUAL -1) 
       get_target_property(LIB_FILE ${LIB} LOCATION) 
       get_link_libraries(LINK_LIB_FILES ${LIB}) 
       list(APPEND LIB_FILES ${LIB_FILE} ${LINK_LIB_FILES}) 
      endif() 
     endif() 
    endforeach() 
    set(VISITED_TARGETS ${VISITED_TARGETS} PARENT_SCOPE) 
    set(${OUTPUT_LIST} ${LIB_FILES} PARENT_SCOPE) 
endfunction()