我试图获取链接到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)
Run Code Online (Sandbox Code Playgroud)
输出:
(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
Run Code Online (Sandbox Code Playgroud)
你的愿望已经存在了一段时间,而且 - 据我所知 - 还没有(就像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}")
Run Code Online (Sandbox Code Playgroud)
会给出例如
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;
Run Code Online (Sandbox Code Playgroud)
cmake_policy(SET CMP0024 OLD)
export(
TARGETS A B
FILE Test.cmake
NAMESPACE Imp_
)
include("${CMAKE_CURRENT_BINARY_DIR}/Test.cmake")
Run Code Online (Sandbox Code Playgroud)
但是这会将生成器表达式保留在输出中,并且您需要将所有依赖目标添加到列表中,所以没有好处.
另请参阅策略CMP0024"禁止包含导出结果".
我已经从如何使用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"
)
Run Code Online (Sandbox Code Playgroud)
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()
Run Code Online (Sandbox Code Playgroud)
将在我的Windows机器上输出:
resolved_file='C:/Windows/SysWOW64/KERNEL32.dll'
resolved_file='C:/Windows/SysWOW64/MSVCR110D.dll'
Run Code Online (Sandbox Code Playgroud)参考
递归遍历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()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
5324 次 |
| 最近记录: |