CMake中的LINK_LIBRARIES的递归列表 [英] Recursive list of LINK_LIBRARIES in CMake

查看:477
本文介绍了CMake中的LINK_LIBRARIES的递归列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想获取一个链接到CMake中特定目标的所有库的绝对路径列表,用于调用 add_custom_command 。但是, get_target_property(_LINK_LIBRARIES $ {TARGET} LINK_LIBRARIES 仅包含直接依赖关系(即在 target_link_libraries($ {TARGET})中使用的任何内容。因此,如果我链接另一个CMake目标,例如 mylibrary >,列表将包括 mylibrary ,但是作为一个名称,没有传递链接库,因为这个列表还可以包括任意复杂的生成器表达式,检查每个项目,如果它是一个目标并且递归地检索它的 LINK_LIBRARIES 是不可行的。此外,目标可以在 CMakeLists.txt

INCLUDE_DIRECTORIES c $ c>和 COMPILE_DEFINITIONS 这很容易解决,因为虽然两个行为类似,当使用 get_target_property 目标显然不在列表中),形式 $ 的生成器表达式产生递归需要的包括和定义的期望列表。但是, $< TARGET_PROPERTY:$ {TARGET},LINK_LIBRARIES> 产生与 get_target_property / p>

如何检索所需的绝对路径列表?



演示:



cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR)

文件(WRITE a.cppvoid foo(){}; \\\

文件(WRITE b.cppint main(int,char **){return 0;} \\\


find_package(Boost REQUIRED COMPONENTS文件系统)

add_library(A STATIC a.cpp)
target_include_directories(A PUBLIC $ {Boost_INCLUDE_DIRS})
target_link_libraries(A PUBLIC $ {Boost_LIBRARIES})

#演示在配置时),LINK_LIBRARIES属性可以包含
#任意生成器表达式,使得递归解不可行
get_target_property(A_LINK_LIBRARIES A LINK_LIBRARIES)
消息(状态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)

#演示(在编译时)方法1不是递归的,而方法2是(对于INCLUDE_DIRECTORIES)
#演示)库列表从不递归
add_custom_command(
OUTPUT b_lists
COMMAND $ {CMAKE_COMMAND} -E echoB INCLUDE_DIRECTORIES 1:$ {B_INCLUDE_DIRECTORIES}
COMMAND $ {CMAKE_COMMAND } -E echoB INCLUDE_DIRECTORIES 2:$< TARGET_PROPERTY:B,INCLUDE_DIRECTORIES>
COMMAND $ {CMAKE_COMMAND} -E echoB LINK_LIBRARIES 1:$ {B_LINK_LIBRARIES}
COMMAND $ {CMAKE_COMMAND} -E echoB LINK_LIBRARIES 2:$< TARGET_PROPERTY:B,LINK_LIBRARIES>
DEPENDS A

set_source_files_properties(b_lists PROPERTIES SYMBOLIC TRUE)



输出:

  $ b 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)
生成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)嵌入到 CMake 本身(参见 0012435:可以获取目标的所有链接库吗?)。



一些希望,因为这张票列出了几种可能的替代方法。但是在我测试这些对你的例子 CMake 项目后,我会说他们不是一个真正的解决方案:


  1. export_library_dependencies( ) - 已弃用



    注意:因为这仅适用于Lib-To-Lib依赖项has - for this test - changed your add_executable()更改为 add_library()调用

      cmake_policy(SET CMP0033 OLD)
    export_library_dependencies(LibToLibLinkDependencies.cmake)
    include($ {CMAKE_CURRENT_BINARY_DIR} /LibToLibLinkDependencies.cmake)

    消息(A_LIB_DEPENDS:$ {A_LIB_DEPENDS})
    消息(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;

    另请参阅 policy 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)

    但是这会在输出中保留生成器表达式,并且您需要添加到列表中所有取决于目标,所以没有好处。



    另请参阅 policy CMP0024Disallow include export result


  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)
    $ b get_prerequisites $ {MY_BINARY_LOCATION} DEPENDENCIES 0 0)

    foreach(DEPENDENCY_FILE $ {DEPENDENCIES})
    gp_resolve_item($ {MY_BINARY_LOCATION}$ {DEPENDENCY_FILE} resolved_file)
    消息(resolved_file ='$ {resolved_file}')
    endforeach()

    会在我的Windows机器上输出:

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


参考




I am trying to acquire a list of the absolute paths to all libraries linked to a specific target in CMake for use in a call to add_custom_command. However, get_target_property(_LINK_LIBRARIES ${TARGET} LINK_LIBRARIES only includes the direct dependencies (i.e. anything that is used in a target_link_libraries(${TARGET} ...) call).

Therefore, if I link another CMake target, e.g. mylibrary, the list would include mylibrary, but as a name only and without transitively linked libraries. As this list can also include arbitrarily complex generator expressions, checking each item if it is a target and retrieving its LINK_LIBRARIES recursively is not viable. Furthermore the target could be specified at a later point in the CMakeLists.txt and if(TARGET mylibrary) would be skipped.

For INCLUDE_DIRECTORIES and COMPILE_DEFINITIONS this is easily solved, as although both behave similarly when get_target_property is used (except that linked targets are obviously not in the list), a generator expression of the form $<TARGET_PROPERTY:${TARGET},INCLUDE_DIRECTORIES> produces the desired list of recursively required includes and definitions. However, $<TARGET_PROPERTY:${TARGET},LINK_LIBRARIES> produces the same list as the get_target_property variant.

How can I retrieve the desired list of absolute paths?

Demonstration:

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)

Output:

(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

解决方案

Your wish has been out there for a while and is - as far as I know - not yet (as for CMake 3.3.2) embedded into CMake itself (see 0012435: Possibility to get all link libraries for a target?).

I got some hope because this ticket lists a few possible alternative approaches. But after I tested those against your example CMake project I would say they are not really a solution:

  1. export_library_dependencies() - Deprecated

    Note: Because this works only for Lib-To-Lib dependencies I have - for this test - changed your add_executable() to an add_library() call

    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}")
    

    would give e.g.

    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;
    

    See also policy CMP0033 "The export_library_dependencies() command should not be called"

  2. export(TARGETS ...)

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

    But this keeps the generator expressions in the output and you need add to the list all depending targets, so no good.

    See also policy CMP0024 "Disallow include export result".

  3. GET_PREREQUISITES()

    I've taken the code from how to use the cmake functions get_prerequisites and get_filename_component for target dependency installation?, but it shows - as described in the module's documentation - that it lists only the shared libraries.

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

    would output on my Windows machine:

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

References

这篇关于CMake中的LINK_LIBRARIES的递归列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆