cmake配置VS工程配置使用dll
Author: ChrisZZ
Time: 2024-06-01 16:17:04
1. 目的
使用 CMake 生成 Visual Studio 的 .sln 解决方案文件后, 可执行目标如果依赖了 dll 文件, 在 VS 中运行或调试程序时,需要能找到这些 dll。 有这个几个问题:
- 如果找不到 dll, 会发生什么?
- 需要找到哪些 dll?
- 找到 dll 后放到哪里?
2. 如果找不到 dll, 会发生什么?
可能会弹窗提示, xxx.dll 没找到。 此时程序无法继续运行。 这种比较直观友好。
也可能没有弹窗提示, 黑框框控制台里可能有一句提示, 但打印太多被你忽略了。 程序能继续运行, 但结果不符合预期。 典型情况是 opencv_videoio490_ffmpeg.dll 文件, 获取到的视频帧数为-1.
3. 需要找到哪些 dll?
用到哪些 dll, 就找哪些。 如何确定用到了哪些 dll 文件?
3.1 “就那么几个文件,手动拷贝“
“我的工程很简单, 就需要4个dll, 分别是 protobuf 的一个, opencv 的两个, ncnn 的一个”
这种想法可以临时解决问题, 也带来新的问题:
- 原始的 dll 文件在哪里, 如何找到?
- 如果原始的 protobuf/opencv/ncnn 版本升级, dll 文件如何处理?
- 如果本机和另一台机器的 dll 版本不同, 导致运行结果有问题, 是不是在给自己找麻烦?
- 找到的 dll 文件, 应该拷贝到哪里?
- 放系统 PATH 环境变量吗?
- 放当前 VS 工程目录吗?
- 为什么 debug 和 release 模式要分别拷贝一次?
3.2 从依赖树递归查询
“我的项目使用 modern cmake 的方式标注依赖关系, 依赖关系是一棵树, 根节点是可执行目标, 从根节点遍历整个依赖树, 扫描出 dll 文件”
这种想法是通用的, 是 scalable 的。 查找 dll 的递归过程, 如果你会写 leetcode 就应该能用 cmake 写出。
具体的依赖写法, 可以是手动的、逐 target 标注依赖关系,也可以基于包管理器, 这里略过。
3.3 额外的 dll 文件
VS2022 从 17.7 版本开始, 无论是 MT(d) 还是 MD(d), 开启 Address Sanitizer 的编译链接选项 /fsanitize=address
后, 都只需要一个对应的 ASAN 的 dll 文件。
这个 dll 算是链接选项隐式引入的。 在配置找到其他 dll 文件时, 如果处理不当, 可能会把这一 dll 文件变得不再能被找到。
4. 找到 dll 后放到哪里?
如果打算拷贝 dll 到 VS 的工程路径, 那么或多或少要经历“失败“:
- VS 的运行路径是什么?
- exe 文件的所在路径是什么?
- 工作路径(workding directory)是什么?
也有人觉得,放 PATH 里最省事, 比如经典的配置 OpenCV, 把 d:/pkgs/opencv/4.8.0/build/x64/vc16/bin
目录放到 PATH 环境变量中。 这种做法, 一旦遇到系统需要有多份同名 dll 文件时, 就容易冲突。
其实, 只要让 VS 的运行或调试阶段找到 dll 文件就行了, 其他时候不需要找到。 那么只要关心,如何在 VS 工程中, 临时设定 dll 的查询路径即可。 也就是临时修改可执行文件的 PATH 环境变量。
4.1 VS 工程属性中的调试环境设定
从 VS 工程属性角度来讲, 工程 -> 属性 -> 调试 -> 环境, 设定 PATH 的取值为 “包含所需的 dll 的路径” 即可。
例如默认是 PATH=$(VC_Executable_x64);%PATH%
.
可以改为 PATH=$(VC_Executable_x64);%PATH%;D:/pkgs/opencv/4.8.0/build/x64/vc16/bin
.
4.2 通过 cmake 设置
可以逐个 target 设置 VS_DEBUGGER_ENVIRONMENT
属性:
set_target_properties(demo PROPERTIES
VS_DEBUGGER_ENVIRONMENT "PATH=D:/pkgs/opencv/4.8.0/build/x64/vc16/bin;%PATH%"
)
也可以设置 CMAKE_VS_DEBUGGER_ENVIRONMENT
这一全局属性, 它用来初始化 VS_DEBUGGER_ENVIRONMENT
取值。
set(CMAKE_VS_DEBUGGER_ENVIRONMENT "D:/pkgs/opencv/4.8.0/build/x64/vc16/bin")
上述两个写法, 在没有开启 ASAN 的时候都是可以正常使用的。
4.3 cmake 设置 - 处理 ASAN
VS2022 通常使用 ASAN 的 dll 文件, 也就是动态库。 在前一节设定的方式写法下, 会把临时设置的 PATH 中原本继承的 $(VC_Executable_x64)
这一 VS 工程宏(取值为 cl.exe 所在目录)删除。 这导致了 ASAN 的 dll 文件找不到。
我的解决方法是, 在引入 ASAN 选项的时候,设定 CMAKE_VS_DEBUGGER_ENVIRONMENT
为 $(VC_Executable_x64)
. 这个全局的cmake变量, 会被追加到每个target的 VS_DEBUGGER_ENVIRONMENT 属性上, 每个 target 只需要增加设置需要的 dll 路径即可。