cmake

Lista recursiva de LINK_LIBRARIES en CMake



(2)

Estoy tratando de adquirir una lista de las rutas absolutas a todas las bibliotecas vinculadas a un objetivo específico en CMake para usarlas en una llamada a add_custom_command . Sin embargo, get_target_property(_LINK_LIBRARIES ${TARGET} LINK_LIBRARIES solo incluye las dependencias directas (es decir, todo lo que se usa en una target_link_libraries(${TARGET} ...) ).

Por lo tanto, si vinculo otro objetivo CMake, por ejemplo, mylibrary , la lista incluiría mylibrary , pero solo como un nombre y sin bibliotecas vinculadas transitivamente. Como esta lista también puede incluir expresiones generadoras arbitrariamente complejas, verificar cada elemento si es un objetivo y recuperar sus LINK_LIBRARIES recursiva no es viable. Además, el objetivo podría especificarse en un punto posterior en CMakeLists.txt y 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 objetivos vinculados obviamente no están en la lista), una expresión generadora de la forma $<TARGET_PROPERTY:${TARGET},INCLUDE_DIRECTORIES> produce el lista deseada de recursivamente requiere incluye y definiciones. Sin embargo, $<TARGET_PROPERTY:${TARGET},LINK_LIBRARIES> 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: $<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)

Salida:

(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


Es posible atravesar recursivamente la propiedad LINK_LIBRARY .

Aquí hay un get_link_libraries() que hace eso, sin embargo, no maneja todos los casos (por ejemplo, las bibliotecas no son un objetivo, las bibliotecas no 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()


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

Tengo algunas esperanzas porque este boleto enumera algunos enfoques alternativos posibles. Pero después de probarlos con su proyecto CMake ejemplo, diría que en realidad no son una solución:

  1. export_library_dependencies() - En desuso

    Nota: Debido a que esto funciona solo 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 "No se debe llamar al comando export_library_dependencies ()"

  2. export(TARGETS ...)

    cmake_policy(SET CMP0024 OLD) export( TARGETS A B 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 objetivos dependientes, por lo que no es bueno.

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

  3. GET_PREREQUISITES()

    ¿Tomé 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 enumera solo las bibliotecas compartidas .

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

    saldría en mi máquina Windows:

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

Referencias