如何在 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。如果我尝试通过这种方法进行依赖复制,那么在 A 的 POST_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 中构建过程后复制目标的所有运行时依赖项?的主要内容,如果未能解决你的问题,请参考以下文章