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:
-
export_library_dependencies()
- En desusoNota: Debido a que esto funciona solo para las dependencias Lib-To-Lib, para esta prueba
add_executable()
cambiado suadd_executable()
a una llamadaadd_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 ()"
-
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" .
-
¿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