CMake中LINK_LIBRARIES的recursion列表

我正在尝试获取链接到CMake中特定目标的所有库的绝对path列表,以用于调用add_custom_command 。 但是, get_target_property(_LINK_LIBRARIES ${TARGET} LINK_LIBRARIES只包含直接依赖关系(即在target_link_libraries(${TARGET} ...)调用中使用的任何东西)。

因此,如果我链接另一个CMake目标,例如mylibrary ,则该列表将包括mylibrary ,但仅作为名称,不包含传递链接的库。 由于该列表还可以包含任意复杂的生成器expression式,因此检查每个项目是否为目标并recursion检索其LINK_LIBRARIES不可行。 此外,目标可以在CMakeLists.txt的稍后时间点指定,并且if(TARGET mylibrary)将被跳过。

对于INCLUDE_DIRECTORIESCOMPILE_DEFINITIONS这很容易解决,因为尽pipeget_target_property在使用时(除了链接的目标明显不在列表中),它们的行为都是相似的, get_target_property $<TARGET_PROPERTY:${TARGET},INCLUDE_DIRECTORIES>recursion所需的包含和定义的期望列表。 但是, $<TARGET_PROPERTY:${TARGET},LINK_LIBRARIES>产生与get_target_property变体相同的列表。

我如何检索所需的绝对path列表?

示范:

 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 

你的愿望已经存在了一段时间,而且据我所知 – 还没有(和CMake 3.3.2一样)embeddedCMake本身(参见0012435:可以获得目标的所有链接库? )。

我有一些希望,因为这张票列出了一些可能的替代方法。 但是,当我testing了那些对你的例子CMake项目的人,我会说他们不是一个真正的解决scheme:

  1. export_library_dependencies()不推荐使用

    注意:因为这只适用于Lib-To-Lib依赖项,所以对于此testing,将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()命令称为”

  2. export(TARGETS ...)

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

    但是,这将保持输出中的生成器expression式,并且需要将所有依赖的目标添加到列表中,所以不好。

    另请参阅策略CMP0024“不允许包含导出结果” 。

  3. 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' 

参考

  • 检索CMake中的所有链接标志
  • 获取所有源文件的目标取决于CMake

recursion遍历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()