CMake find_package引入依赖后构建exe未自动复制dll到输出目录
问题原因
CMake 默认不会在构建阶段自动将外部导入的动态链接库(.dll)复制到可执行文件的构建输出目录(build/Release/),你之前尝试的三个方案均存在用法错误:
- 方案1的
RUNTIME_DEPENDENCIES仅在安装阶段生效,不会作用于构建输出目录,且未指定第三方dll的搜索路径,CMake无法定位到你安装在C:\Program Files下的自定义库dll,因此不会执行复制。 - 方案2直接在
RUNTIME_DEPENDENCIES后传入目标名不符合语法规则,该参数后仅支持DIRECTORIES、PRE_EXCLUDE_REGEXES等配置项,因此报参数非法错误。 - 方案3的自定义宏报错有两个核心原因:一是传入目标列表时加了双引号,导致整个列表被识别为单个目标名;二是宏逻辑仅作用于install阶段,不会修改构建目录的输出内容,另外如果上游库的导出配置未正确写入dll路径,也会出现属性读取失败的问题。
解决方案
分两种常见场景配置,可按需选择:
场景1:构建阶段自动复制dll到build/Release输出目录
最简写法(要求CMake ≥ 3.26)
直接利用CMake内置的TARGET_RUNTIME_DLLS生成器表达式自动识别所有依赖的dll,无需手动枚举依赖目标,在target_link_libraries配置后添加如下代码即可:
if(MSVC) add_custom_command(TARGET example POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_RUNTIME_DLLS:example> $<TARGET_FILE_DIR:example> COMMAND_EXPAND_LISTS COMMENT "Copying dependent DLLs to output directory" ) endif()
该命令会在example目标编译链接完成后,自动将所有依赖的非系统dll复制到exe所在的输出目录。
兼容低版本CMake写法(支持CMake 3.21)
手动遍历依赖目标读取dll路径执行复制,可适配Debug/Release/RelWithDebInfo多配置:
if(MSVC) # 枚举所有依赖的第三方库目标 set(DEPENDENT_TARGETS sortdemo::my_sort_lib sortdemo::my_print_lib) foreach(dep_tgt ${DEPENDENT_TARGETS}) if(NOT TARGET ${dep_tgt}) continue() endif() # 遍历所有生成配置 foreach(cfg Release Debug RelWithDebInfo) string(TOUPPER ${cfg} cfg_upper) get_target_property(dll_path ${dep_tgt} IMPORTED_LOCATION_${cfg_upper}) if(dll_path MATCHES "\\.dll$") add_custom_command(TARGET example POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${dll_path}" $<TARGET_FILE_DIR:example> COMMENT "Copying ${dep_tgt} (${cfg}) to output directory" CONFIGURATIONS ${cfg} ) endif() endforeach() endforeach() endif()
注意:传入目标列表时不要给变量加双引号,否则会被识别为单个字符串导致遍历失败。
场景2:安装阶段将dll和exe一起打包到安装目录
修正RUNTIME_DEPENDENCIES的写法,显式指定第三方dll的搜索路径,配置后执行cmake --install命令时会自动复制依赖:
include(GNUInstallDirs) install(TARGETS example RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} RUNTIME_DEPENDENCIES # 替换为你实际存放sortdemo相关dll的目录,即截图中bin文件夹的绝对路径 DIRECTORIES "C:/Program Files/sortdemo/bin" # 排除Windows系统dll,避免把系统文件打包进去 PRE_EXCLUDE_REGEXES "api-ms-" "ext-ms-" POST_EXCLUDE_REGEXES ".*[Ss]ystem32/.*\\.dll" )
排查提示
如果上述配置仍然无法读取到dll路径,先检查上游库的导出配置是否正确:在CMakeLists中添加如下打印代码,确认是否能正确读取到dll的绝对路径:
get_target_property(check_dll_path sortdemo::my_sort_lib IMPORTED_LOCATION_RELEASE) message(STATUS "my_sort_lib release dll path: ${check_dll_path}")
如果打印结果为NOTFOUND,说明你预先安装的sortdemo库的CMake导出配置仅写入了.lib导入库的路径,未关联.dll动态库的实际位置,需要修改上游库的CMake导出逻辑,将dll安装路径正确写入导出的target配置中。
内容的提问来源于stack exchange,提问作者Petras Vestartas
相关产品推荐
相关产品推荐

