使用 Visual Studio (MSBuild) 项目引用系统在 Boost 中使用 ZLib






4.47/5 (5投票s)
将第三方工具和库集成到 Visual Studio (MSBuild) 的构建和引用系统中。
引言
这是关于将第三方工具和库集成到 Visual Studio (MSBuild) 的系列文章中的第三篇。我将使用 Boost C++ 开源库来演示如何将工具或库集成到 MSBuild 环境中。在第一篇文章中,我讨论了如何创建自定义构建并与 Visual Studio 的配置 UI 集成。 第二篇文章介绍了项目被其他项目通过项目引用引用的要求。在本文中,我将解释自定义构建如何引用其他项目。我将使用 Boost 所需的 ZLib C++ 开源库来演示必要的步骤。
背景
ZLib 是一个免费的开源库,可以作为下载或通过 GitHub 存储库获取。Boost 需要它来启用某些压缩功能。它可以作为指向源代码目录的链接,或作为 DLL 或静态链接库。我将解释如何在 Visual Studio 中构建 ZLib 并将其添加为 Boost 构建的项目引用,以实现集成。在撰写本文时,ZLib 支持 Visual Studio 高达 VC11。我们将添加对 VC12 和 VC14 的支持,并在进行的同时启用一些 UI 配置。
项目
标准的 ZLib 发行版或存储库的克隆版本在 contrib\vstudio 文件夹中包含 Visual Studio 项目。每个支持的 Visual Studio 版本(VC9、VC10、VC11)都有一个子文件夹。
尝试使用 Visual Studio 13 构建最新版本失败了,我决定创建一个新项目来集成我们到目前为止学到的所有增强功能,而不是试图修复它。我决定添加一个属性页,其中仅包含适用于该项目的有效设置,例如 DLL/LIB、调用约定等。有关如何执行此操作的详细信息,请参阅本文。
添加所有必需字段后,我得到了这些选项
通常,Visual Studio 项目文件位于 contrib\vstudio\vcXX 目录中。但有时,在一台计算机上只保留一份 ZLib 源代码,并在多个项目中仅引用它会很有益。为了实现这一点并防止在多个项目中覆盖此文件中的设置,我添加了一个额外的参数指向源的根目录。现在,项目文件可以复制到工作解决方案目录,指向已安装的源并进行更改和构建,而不会影响其他项目。
我试图仅添加适用于该库的选项。例如,配置类型只能是动态库或静态库,不能是标准模板中的 EXE 或 Makefile。**目标名称**最初被指定为 `zlib1`(该库的知名名称),但如果启用了 **WINAPI** 选项,则可以更改为 `zlibwapi` 或旧的 `zlib`。
默认情况下,构建使用 **contrib** 目录中的汇编实现。要禁用汇编并回退到 C 代码,请将 **使用汇编实现** 设置为 `否`。
实现
该项目在 zlib.vcxproj 文件中实现,并针对较新的 zlib1 库。它已从 zlibvc.vcxfroj 更改,以防止兼容性问题。如果您可能需要 zlibvc.vcxproj 或 zlibstat.vcxproj,只需在项目 UI 中设置正确的选项,然后将其另存为 zlibvc.vcxproj 或 zlibstat.vcxproj。
项目 ID
ZLib 有一个众所周知的项目 ID:`{8FD826F8-3739-44E6-8CC8-997122E53B8D}`。这很重要,因为此 ID 可用于将此库标识给使用它的项目,以便唯一地区分它与其他项目。Boost 将使用它来解析对库的引用。
源文件
对源文件的引用已更改为相对于根文件夹
<ItemGroup>
<ClInclude Include="$(ZLibDir)deflate.h" />
<ClInclude Include="$(ZLibDir)infblock.h" />
<ClInclude Include="$(ZLibDir)infcodes.h" />
<ClInclude Include="$(ZLibDir)inffast.h" />
<ClInclude Include="$(ZLibDir)inftrees.h" />
<ClInclude Include="$(ZLibDir)infutil.h" />
<ClInclude Include="$(ZLibDir)zconf.h" />
<ClInclude Include="$(ZLibDir)zlib.h" />
<ClInclude Include="$(ZLibDir)zutil.h" />
</ItemGroup>
汇编文件
原始项目使用自定义构建步骤在汇编文件可以链接到项目之前对其进行编译。
最新的 Visual Studio 发行版内置了 MASM 工具,并支持原生构建 .ASM 文件。它需要将 MASM 目标添加到项目中,这通过添加构建自定义来完成
- 右键单击“项目浏览器”窗口中的项目名称
- 单击“项目依赖项”
- 单击“构建自定义项”
- 选择 MASM(.target .props)
- 保存,您就完成了。
现在将 .ASM 文件添加到项目中
<ItemGroup>
<MASM Include="$(ZLibDir)contrib\masmx86\inffas32.asm" />
<MASM Include="$(ZLibDir)contrib\masmx86\match686.asm" />
<MASM Include="$(ZLibDir)contrib\masmx64\gvmat64.asm" />
<MASM Include="$(ZLibDir)contrib\masmx64\inffasx64.asm" />
</ItemGroup>
添加构建条件以正确选择要构建的文件后,我们就可以运行编译。
汇编模块的问题
有人提醒我,这些模块存在一些问题。如果您计划处理这些问题,则可能需要启用 ASM 模块,否则请继续使用 C++ 实现。
模块定义文件
要创建导出链接库,我们必须指定模块定义文件。这可以通过将 `win32\zlib.def` 文件的路径分配给 Link 项的 `ModuleDefinitionFile` 元素来完成。
<ItemDefinitionGroup Label="Globals">
<Link>
<ModuleDefinitionFile>$(ZLibDir)win32\zlib.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
预处理器定义
项目创建以下定义:`_CRT_NONSTDC_NO_DEPRECATE`、`_CRT_SECURE_NO_DEPRECATE` `_CRT_NONSTDC_NO_WARNINGS`
默认情况下,项目使用汇编实现并定义 `ASMV` 和 `ASMINF`。如果 **使用汇编实现** 设置为 `否`,则不定义这些。如果 **WINAPI 调用约定** 选项设置为“是”,则项目定义:`ZLIB_WINAPI`。有关此选项的更多信息,请参阅 **contrib\vstudio** 文件夹中的 readme.txt 文件。
输出
构建项目会创建 `zlib1.lib`,如果 **配置类型** 设置为 **动态库**,还会创建 `zlib1.dll` 文件。
引用 ZLib
ZLib 项目使用以下文件中定义的构建过程的默认实现:
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets
不需要任何自定义。这是一个普通的 C++ 项目,具有所有默认的目标和任务。要引用 ZLib,我们必须执行以下操作:
- 将 zlib 项目添加到 Boost 解决方案
- 右键单击 Boost 项目,选择“属性”,然后打开“属性”对话框
- 转到“通用属性”并单击“引用”
- 单击“添加引用”按钮
- 选择 zlib 项目,然后单击“确定”
Boost
正如我们在本文中讨论的那样,依赖库的使用归结为链接到正确的链接库文件并将相应的 DLL 复制到正确的目录。
普通应用程序依赖链接器来完成所有对象操作,但 Boost 不同。它使用自己的构建系统进行编译和链接(当然,它仍然使用链接器,但它是自行完成的)。因此,我们不必将库的引用传递给链接器,而是必须将其传递给 Jam Build 工具。这可以通过环境变量或调用该工具时的命令行属性来完成。它发生在 MSBuild 目标调用序列之外,因此我们必须单独解析它并将其传递给工具。
我将使用命令行属性将此信息传递给 Jam Build Tool。
引用
有关依赖项引用的信息存储在项目文件中的 `Reference` 项中。它由 Visual Studio 添加和修改,不需要我们进行任何自定义。我们所要做的就是获取被引用项目的名称,调用 `GetResolvedLinkLibs` 获取链接库列表,选择一个引用 `zlib1.lib` 的,然后将其传递给 `b2.exe`。
我们可以通过两种方式来实现:
- 我们可以使用 MSBuild 任务直接调用 zlib 项目的 `GetResolvedLinkLibs` 目标,或者
- 调用我们自己的项目的 `GetResolvedLinkLibs` 来获取所有引用,然后选择属于 zlib 的那个
目标返回值被缓存到 MSBuild 中,并且 `GetResolvedLinkLibs` 无论如何都会被调用,所以调用我们自己的目标更经济。我们将使用 `ZLIB_LIBPATH` 开关将此信息单独传递给 `b2.exe`。
<Target Name="PrepareForBoostBuild" Returns="@(boost-options)" >
...
<MSBuild Targets="GetResolvedLinkLibs" Projects="$(MSBuildProjectFullPath)" >
<Output ItemName="Dependencies" TaskParameter="TargetOutputs"/>
</MSBuild>
<ItemGroup Label="Dependency Libraries">
<boost-options Condition="'%(Project)'=='{8fd826f8-3739-44e6-8cc8-997122e53b8d}'"
Include="-sZLIB_LIBPATH="%(Dependencies.Identity)"" />
</ItemGroup>
...
正如您在此代码示例中看到的,我们调用 `GetResolvedLinkLibs` 目标来获取所有链接库的列表,并根据项目 ID 过滤出属于 ZLib 项目的那个。
对其他依赖项的支持将以相同的方式添加。
优先级
Boost 库允许指定 ZLib 源代码路径以及要链接的二进制文件。选择哪一个具有优先权留给 Boost 构建。
使用代码
源代码下载 包含构建 Boost 库所需的五个文件(项目、目标和三个 xml)。将其复制到目录中。指定 Boost 库根目录的位置并进行构建。
示例下载 包含一个具有三个项目的解决方案:Boost-MSBuild 和 Sample。构建并执行后,它们将允许您看到本文所述的功能生效。
历史
2015 年 3 月 20 日 - 发布。