使用 Visual Studio 调试共享库¶
在前面的示例中,我们讨论了如何在 Visual Studio 中调试依赖项,但在项目中使用 Conan 依赖项时,原始构建文件夹和构建文件可能不存在。Conan 包默认不包含使用 Visual Studio 调试库所需的必要信息,这些信息存储在库编译期间生成的 PDB 文件中。使用 Conan 时,这些 PDB 文件在构建文件夹中生成,而构建文件夹仅在构建库期间需要。因此,使用 conan cache clean
清理 Conan 缓存以移除构建文件夹并节省磁盘空间是一种常见的操作。
对于构建文件夹不存在的情况,我们创建了一个钩子,它将构建文件夹中生成的 PDB 文件复制到包文件夹。默认情况下不能强制执行此行为,因为 PDB 文件通常比整个包更大,这会大大增加包的大小。
本节将通过一些示例演示如何在不同情况下调试项目,以展示用户如何利用 PDB 钩子。
创建项目并像往常一样调试¶
首先,我们将像往常一样调试我们的项目,具体细节在前面的示例中已详细解释。我们可以像上一节中那样从源代码开始构建我们的依赖项,只是这次我们将它们构建为共享库。首先,从 GitHub 上的 examples2 仓库克隆示例所需的源代码并创建项目。
$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/tutorial/consuming_packages/simple_cmake_project
$ conan install . -o="*:shared=True" -s build_type=Debug --build="zlib/*"
...
Install finished successfully
# CMake presets require CMake>=3.23
$ cmake --preset=conan-default
注意
我们将只介绍依赖项构建为共享库的情况,因为 PDB 文件及其与库的链接方式对于静态库是不同的。
现在我们可以打开解决方案 compressor.sln
,在 Visual Studio 中打开我们的项目并按照前面的示例进行调试。在第 22 行设置断点,运行调试器并使用步进(step into)功能,将使我们能够调试依赖项文件 deflate.c
的内部。

在这种情况下,原始构建文件全部存在,因此调试器正常工作。接下来我们将看到从 Conan 缓存中移除构建文件后调试器如何工作。
从 Conan 缓存中移除构建文件¶
有多种原因可能导致依赖项编译后构建文件不存在。我们将使用 conan cache clean
从缓存中清理构建文件,以模拟其中一种情况。--build
标志确保我们只移除构建文件,因为本示例需要我们的源文件。
$ conan list "zlib/1.2.11:*"
$ conan cache path --folder build zlib/1.2.11:17b26a16efb893750e4481f98a154db2934ead88
$ conan cache clean zlib/1.2.11 --build
$ conan cache path --folder build zlib/1.2.11:17b26a16efb893750e4481f98a154db2934ead88
在 Visual Studio 中关闭并重新打开我们的解决方案后,我们可以再次尝试调试。如果你尝试在第 22 行的断点处步进(step into)到依赖项中,你会发现它会直接跳到下一行,因为 Visual Studio 没有关于依赖项的任何调试信息。
安装钩子将 PDB 文件复制到包文件夹¶
为了解决包文件夹中没有 PDB 文件的问题,我们创建了一个钩子,它将 PDB 文件从构建文件夹复制到包文件夹。该钩子可在 conan-extensions 仓库中找到。安装整个仓库也可以,但我们建议只从 conan-extensions
仓库安装 hooks
文件夹,使用命令:
$ conan config install https://github.com/conan-io/conan-extensions.git -sf=extensions/hooks -tf=extensions/hooks
该钩子被设计为默认不运行,因为这会显著增加包的大小。正如钩子文档中解释的,我们需要将钩子的名称更改为以 hook_
开头。要查找钩子所在的路径,运行命令 conan config home
找到你的本地缓存路径,然后进入 extensions/hooks
文件夹,重命名 _hook_copy_pdbs_to_package.py
文件。请注意,此钩子将在每次运行 package()
方法时执行,要禁用钩子,只需将钩子名称改回以 _hook_
开头即可。
该钩子被实现为一个 post-package 钩子,这意味着它将在通过 recipe 的 package()
方法创建包之后执行。这避免了任何潜在问题,因为执行顺序如下:
Recipe 的
build()
方法执行,生成 DLL 和 PDB 文件Recipe 的
package()
方法执行,将必要文件复制到包文件夹(本例中是 DLL 文件,但不包括 PDB 文件)钩子执行,将构建文件夹中的 PDB 文件复制到包文件夹中对应 DLL 旁边
该钩子使用了包含在 Visual Studio 安装中的 dumpbin
工具。此工具允许我们获取 DLL 的信息,在本例中是其关联 PDB 文件所在的路径。它将用于包中的每个 DLL,以找到其 PDB 文件并将其复制到包文件夹。
有关 PDB 文件在 Visual Studio 中如何工作以及我们如何使用它创建钩子的更多信息,请参阅钩子 README 文件。
没有构建文件时的调试¶
安装钩子后,我们将再次从源代码创建项目,以便钩子现在可以将 PDB 文件复制到包文件夹中,与包 DLL 文件放在一起,以便调试器能够找到它们。
$ conan install . -o="*:shared=True" -s build_type=Debug --build="zlib/*"
...
zlib/1.2.11: Calling package()
...
[HOOK - hook_copy_pdbs_to_package.py] post_package(): PDBs post package hook running
...
Install finished successfully
# CMake presets require CMake>=3.23
$ cmake --preset=conan-default
请注意,现在运行 conan install 时,你会在调用 package()
之后看到钩子的运行输出。为了测试钩子,我们可以再次清理缓存以移除构建文件,这包括用于构建库的源代码以及最初生成的 PDB 文件。
$ conan cache clean zlib/1.2.11 --build
再次在 Visual Studio 中打开解决方案并启动调试器。当你在第 22 行尝试步进到依赖项中时,会弹出一个错误消息,告诉我们文件未找到,并询问文件位于何处。我们可以关闭此窗口,它将提供查看反汇编代码的选项,借助 PDB 可以调试反汇编。PDB 文件只包含调试信息,但 Visual Studio 缺少源文件,因此它将无法像最初那样直接调试源代码。

为调试器定位源文件路径¶
删除原始构建文件后,Visual Studio 将无法自行找到源文件。为了能够调试源代码文件,有一个选项可以手动设置源文件夹路径,以便能够调试源代码文件。这要求依赖项的源文件存在。在本例中,我们可以通过运行 `conan cache path
命令来获取这些源文件的位置。
$ conan cache path --folder source zlib/1.2.11
如果此源路径不存在,我们可以使用配置(config)再次下载源代码。
$ conan install . -o="*:shared=True" -s build_type=Debug -c:a="tools.build:download_source=True"
获得源路径后,我们可以在 Visual Studio 中进行设置,以便调试器能够找到源文件。右键单击解决方案资源管理器中的解决方案,选择“属性”。转到“通用属性”部分中的“调试源文件”,添加我们的源路径。

再次启动调试器将允许像我们第一个示例那样步进到依赖项的代码中。
注意
如果源文件有补丁(patches),我们将无法调试修改后的文件,因为我们使用的是源文件夹中的文件,而补丁是在稍后的步骤中、紧接在构建文件夹中编译之前应用的。
对源文件的任何修改都将导致无法对其进行调试,因为 Visual Studio 会进行校验和(checksum)检查,因此它们必须与编译库时的文件完全一致。