Рекурсивный список LINK_LIBRARIES в CMake
Я пытаюсь получить список абсолютных путей ко всем библиотекам, связанным с определенной целью в CMake для использования при вызове add_custom_command
. Однако get_target_property(_LINK_LIBRARIES ${TARGET} LINK_LIBRARIES
включает только прямые зависимости (т.е. Все, что используется при вызове target_link_libraries(${TARGET} ...)
).
Поэтому, если я связываю другую цель CMake, например. mylibrary
, список будет включать mylibrary
, но как имя и без транзитно связанных библиотек. Поскольку этот список может также включать произвольно сложные выражения генератора, проверка каждого элемента, если он является целью и получение рекурсивно его LINK_LIBRARIES
, не является жизнеспособным. Кроме того, цель может быть указана в более поздней точке CMakeLists.txt
и if(TARGET mylibrary)
будет пропущена.
Для INCLUDE_DIRECTORIES
и COMPILE_DEFINITIONS
это легко решается, так как оба они ведут себя одинаково, когда используется get_target_property
(за исключением того, что связанные цели, очевидно, не входят в список), выражение генератора формы $<TARGET_PROPERTY:${TARGET},INCLUDE_DIRECTORIES>
производит требуемый список рекурсивно необходимых включает и определений. Однако $<TARGET_PROPERTY:${TARGET},LINK_LIBRARIES>
создает тот же список, что и вариант get_target_property
.
Как получить желаемый список абсолютных путей?
Демонстрация:
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)
Вывод:
(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
Ответы
Ответ 1
Ваше желание уже какое-то время было и, насколько мне известно, еще не было (как для CMake 3.3.2), встроенного в CMake
(см. 0012435: возможность получить все библиотеки ссылок для цели?).
У меня появилась надежда, потому что в этом билете перечислены несколько возможных альтернативных подходов. Но после того, как я протестировал их против вашего проекта CMake
, я бы сказал, что они на самом деле не являются решением:
-
export_library_dependencies()
- Устаревший
Примечание. Поскольку это работает только для зависимостей Lib-To-Lib, у меня есть - для этого теста - изменил ваш add_executable()
на вызов 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}")
даст, например,
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;
См. также политика CMP0033 "Команда export_library_dependencies() не должна называться"
-
export(TARGETS ...)
cmake_policy(SET CMP0024 OLD)
export(
TARGETS A B
FILE Test.cmake
NAMESPACE Imp_
)
include("${CMAKE_CURRENT_BINARY_DIR}/Test.cmake")
Но это сохраняет выражения генератора в выходе, и вам нужно добавить в список все зависимые цели, поэтому ничего хорошего.
См. также политика CMP0024 "Disallow include export result" .
-
GET_PREREQUISITES()
Я использовал код как использовать функции cmake get_prerequisites и get_filename_component для установки на установку по умолчанию?, но он показывает - как описано в документации модуля - что в нем перечислены только разделяемые библиотеки.
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()
будет выводиться на моей машине Windows:
resolved_file='C:/Windows/SysWOW64/KERNEL32.dll'
resolved_file='C:/Windows/SysWOW64/MSVCR110D.dll'
Ссылки
Ответ 2
Возможно рекурсивное перемещение LINK_LIBRARY
.
Вот get_link_libraries()
, который делает это, однако он не обрабатывает все случаи (например, библиотеки не являются целевыми, а не импортированными библиотеками).
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()