如何在 CMake 中构建过程后复制目标的所有运行时依赖项?

Posted

技术标签:

【中文标题】如何在 CMake 中构建过程后复制目标的所有运行时依赖项?【英文标题】:How to copy target's all runtime dependencies after build process in CMake? 【发布时间】:2021-07-29 16:46:04 【问题描述】:

我正在尝试将可执行目标的运行时依赖项自动复制到其构建目录中。

作为案例,我使用 GTest 进行作为共享库链接的单元测试。构建后,如果我尝试启动单元测试,将会出现类似“gtest.dll is not found”的错误。对于其他外部依赖项,同样的问题(与本地共享库一样)。

我可以在该目录中链接并运行测试后添加每个外部库来安装进程,但这不是启动 CTest 的实用方法。

我想使用file(GET_RUNTIME_DEPENDENCIES),但它必须在构建后调用,我没有找到INSTALL(CODE) POST_BUILD 阶段的模拟。

仅仅用add_custom_command 处理DLLs 也不起作用,因为可能存在依赖继承。 例如,A 依赖于 B,而 B 又依赖于 C。如果我尝试通过这种方法进行依赖复制,那么在 APOST_BUILD 过程中只会复制 B

我不喜欢将所有构建的工件存储在一个目录中,因为它会造成混乱并可能在未来造成冲突。

【问题讨论】:

使用 CMake 3.21 和新的install(RUNTIME_DEPENDENCY_SET) 命令而不是普通的file(GET_RUNTIME_DEPENDENCIES): cmake.org/cmake/help/latest/command/… @Alex Reinking 我读过它并同意它更好,但我想在 POST_BUILD 步骤复制文件,而不是 INSTALL 步骤。如果 CMake 可以替代所需任务的功能,那就太好了。 【参考方案1】:

找到了一个解决方案,但它不可移植,只能在 Windows 中使用。

CMake 版本 3.21 中有一个新的 TARGET_RUNTIME_DLLS 生成器表达式,它将运行时目标的所有运行时依赖项作为文件路径列表返回。 该表达式可以在 POST_BUILD 阶段的ADD_CUSTOM_COMMAND 命令中使用。

项目中有一个共享库SomeLib的目标和一个测试可执行文件SomeLib_tests。 GTest 共享库的目标由FIND_PACKAGE 函数找到。

cmake_minimum_required(VERSION 3.21)
project(ExampleProject)

add_library(SomeLib SHARED
            "include/SomeLib/api.h"
            "src/SomeLib/api.cpp")
target_include_directories(SomeLib PUBLIC "$CMAKE_CURRENT_SOURCE_DIR/include")


find_module(GTest COMPONENTS gtest gtest_main REQUIRED)

add_executable(SomeLib_tests
               "ut/api_tests.cpp")
target_link_libraries(SomeLib_tests
                      PRIVATE SomeLib
                      PRIVATE GTest::gtest GTest::gtest_main)

gtest_add_tests(TARGET SomeLib_tests)

这一步,如果我在Windows下调用CTest会出现“gtest.dll is not found”这样的错误。

需要将可执行文件的所有依赖项复制到其目录。可以通过调用ADD_CUSTOM_COMMAND 和TARGET_RUNTIME_DLLS 表达式来实现。目标目录取自TARGET_FILE_DIR生成器表达式。

add_custom_command( TARGET SomeLib_tests POST_BUILD
                    COMMAND $CMAKE_COMMAND -E copy
                        $<TARGET_RUNTIME_DLLS:SomeLib_tests>
                        $<TARGET_FILE_DIR:SomeLib_tests>
                    COMMAND_EXPAND_LISTS)

这样,如果我构建项目并再次调用 CTest,测试将按预期运行。

【讨论】:

以上是关于如何在 CMake 中构建过程后复制目标的所有运行时依赖项?的主要内容,如果未能解决你的问题,请参考以下文章

CMake构建后如何在c ++库中运行示例

CMake 多配置生成器使用错误的构建目录

cmake 把中间文件放到某个目录下

使用 CMake 将 Qt DLL 复制到 Windows 上的可执行目录

Cmake 问题。复制 dll 构建后

CMake基础教程(30)CMake构建系统概览