Lista recursiva de LINK_LIBRARIES en CMake

Estoy tratando de obtener una lista de las rutas absolutas a todas las bibliotecas vinculadas a un objective específico en CMake para usar en una llamada a add_custom_command . Sin embargo, get_target_property(_LINK_LIBRARIES ${TARGET} LINK_LIBRARIES solo incluye las dependencias directas (es decir, cualquier cosa que se use en una target_link_libraries(${TARGET} ...) a target_link_libraries(${TARGET} ...) ).

Por lo tanto, si enlace otro objective de CMake, por ejemplo, mylibrary , la lista incluiría mylibrary , pero solo como un nombre y sin bibliotecas vinculadas transitoriamente. Como esta lista también puede incluir expresiones de generador arbitrariamente complejas, comprobar cada elemento si es un objective y recuperar sus LINK_LIBRARIES recursivamente no es viable. Además, el objective podría especificarse en un punto posterior en CMakeLists.txt y se CMakeLists.txt if(TARGET mylibrary) .

Para INCLUDE_DIRECTORIES y COMPILE_DEFINITIONS esto se resuelve fácilmente, ya que aunque ambos se comportan de manera similar cuando se usa get_target_property (excepto que los destinos vinculados obviamente no están en la lista), una expresión generadora del formulario $ produce el lista deseada de inclusiones requeridas recursivamente y definiciones. Sin embargo, $ produce la misma lista que la variante get_target_property .

¿Cómo puedo recuperar la lista deseada de rutas absolutas?

Demostración:

 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: $" COMMAND ${CMAKE_COMMAND} -E echo "B LINK_LIBRARIES 1: ${B_LINK_LIBRARIES}" COMMAND ${CMAKE_COMMAND} -E echo "B LINK_LIBRARIES 2: $" DEPENDS A ) set_source_files_properties(b_lists PROPERTIES SYMBOLIC TRUE) 

Salida:

 (configure) A LINK_LIBARIES: $<$<NOT:$>:D:/libs/boost-1_55_0/lib/boost_filesystem-vc110-mt-1_55.lib>;$<$:D:/libs/boost-1_55_0/lib/boost_filesystem-vc110-mt-gd-1_55.lib>;$<$<NOT:$>:D:/libs/boost-1_55_0/lib/boost_system-vc110-mt-1_55.lib>;$<$: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 

Su deseo ha estado ahí por un tiempo y, hasta donde yo sé, todavía no (como para CMake 3.3.2) incrustado en CMake mismo (ver 0012435: ¿Posibilidad de obtener todas las bibliotecas de enlaces para un objective? ).

Tengo algo de esperanza porque este boleto enumera algunos posibles enfoques alternativos. Pero después de que probé esos en contra de su proyecto CMake ejemplo, diría que realmente no son una solución:

  1. export_library_dependencies() – En desuso

    Nota: Debido a que esto solo funciona para las dependencias Lib-To-Lib, para esta prueba add_executable() cambiado su add_executable() a una llamada add_library()

     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}") 

    daría por ejemplo

     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; 

    Consulte también la política CMP0033 “El comando export_library_dependencies () no se debe llamar”

  2. export(TARGETS ...)

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

    Pero esto mantiene las expresiones generadoras en la salida y necesita agregar a la lista todos los destinos dependientes, por lo que no sirve.

    Consulte también la política CMP0024 “No permitir incluir resultado de exportación” .

  3. GET_PREREQUISITES()

    He tomado el código de cómo usar las funciones cmake get_prerequisites y get_filename_component para la instalación de dependencia de destino? , pero muestra, como se describe en la documentación del módulo, que solo enumera las bibliotecas compartidas .

     add_custom_command( OUTPUT b_lists APPEND COMMAND ${CMAKE_COMMAND} -D MY_BINARY_LOCATION="$" -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() 

    saldría en mi máquina con Windows:

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

Referencias

  • Recuperar todos los marcadores de enlace en CMake
  • Obtenga todos los archivos fuente de los que depende un destino en CMake

Recursivamente atraviesa la propiedad LINK_LIBRARY es posible.

Aquí hay un get_link_libraries() que hace eso, sin embargo, no maneja todos los casos (por ejemplo, las bibliotecas no son un destino, no son bibliotecas importadas).

 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()