CMake 3.19:如何复制已链接目标的依赖DLL文件?
CMake 3.19 自动复制依赖DLL到构建目录
问题场景
- 只能使用CMake 3.19版本
- 项目包含多子项目,已通过
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})将所有DLL/EXE统一输出到构建目录 - 依赖引入有四种方式:
- 仅头文件库(如Eigen3)
- 手动导入的二进制DLL/LIB(如FreeImage)
- 用
find_package获取的目标(如Qt5::Core) - 直接在
target_link_libraries中指定系统DLL名称(如version.dll)
- 需要将可执行文件
my_binary的直接依赖DLL全部复制到构建目录
现有方案的问题
之前的实现只能识别手动导入的IMPORTED库,无法处理find_package引入的Qt目标;启用CMP0026旧行为会导致Debug模式错误复制Release版本的Qt DLL;使用$<TARGET_FILE:>时需要判断目标是否为DLL,避免无效执行。
解决方案
下面是适配CMake 3.19的完整实现,覆盖所有依赖场景:
实现代码
function(copy_target_dependencies TARGET_NAME) # 获取目标的直接链接库列表 get_target_property(LINK_LIBS ${TARGET_NAME} LINK_LIBRARIES) if(NOT LINK_LIBS) return() endif() foreach(LIB IN LISTS LINK_LIBS) # 跳过非CMake目标的系统DLL(比如windows/system32里的version.dll) if(NOT TARGET ${LIB}) continue() endif() # 跳过仅头文件的INTERFACE库 get_target_property(TARGET_TYPE ${LIB} TYPE) if(TARGET_TYPE STREQUAL "INTERFACE_LIBRARY") continue() endif() # 区分导入的SHARED库和find_package生成的目标 get_target_property(IS_IMPORTED ${LIB} IMPORTED) if(IS_IMPORTED) # 导入库优先取对应配置的DLL路径,兼容Debug/Release set(DLL_PATH "$<IF:$<TARGET_PROPERTY:${LIB},IMPORTED_LOCATION_$<CONFIG>>,$<TARGET_PROPERTY:${LIB},IMPORTED_LOCATION_$<CONFIG>>,$<TARGET_PROPERTY:${LIB},IMPORTED_LOCATION>>") else() # 非导入目标用TARGET_FILE自动获取对应配置的DLL路径 set(DLL_PATH "$<TARGET_FILE:${LIB}>") endif() # 添加复制命令,仅当DLL路径有效时执行 add_custom_command(TARGET ${TARGET_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${DLL_PATH}" "${CMAKE_BINARY_DIR}" COMMENT "Copying dependency: ${DLL_PATH}" ) endforeach() endfunction() # 给目标绑定依赖复制逻辑 copy_target_dependencies(my_binary)
关键细节
- 配置兼容:针对导入库优先使用
IMPORTED_LOCATION_$<CONFIG>,自动匹配Debug/Release版本;Qt这类find_package生成的目标,用$<TARGET_FILE:>会自动关联对应配置的DLL路径 - 过滤无效项:跳过INTERFACE头文件库和非CMake目标的系统DLL,避免无意义操作
- 高效复制:用
copy_if_different只在文件有变化时执行复制,减少构建开销
内容的提问来源于stack exchange,提问作者Benjamin Buch
相关产品推荐
相关产品推荐

