在现代软件开发中,集成第三方工具和库是常见的需求。Visual Studio提供了强大的MSBuild系统,使得集成过程变得相对简单。本文将以Boost C++开源库为例,展示如何在MSBuild环境中集成工具或库。
ZLib是一个免费的开源库,可在GitHub上下载或克隆。Boost库需要ZLib来启用某些压缩功能。ZLib可以作为源代码目录的链接、DLL或静态链接库使用。本文将解释如何在Visual Studio中构建ZLib,并将其作为项目引用添加到Boost构建中,以实现集成。
标准的ZLib分发或克隆的仓库在contrib\vstudio文件夹中包含Visual Studio项目。它为每个支持的Visual Studio版本设有子文件夹:VC9、VC10、VC11。尝试使用Visual Studio 13构建最新版本失败后,决定创建一个新项目,集成迄今为止学到的所有增强功能。决定添加一个属性页,只包含对项目有效的设置,如DLL/LIB、调用约定等。
通常,Visual Studio项目文件位于contrib\vstudio\vcXX目录中。但有时,拥有电脑上ZLib源代码的单一副本并在多个项目中使用引用是有益的。为了实现这一点并防止在多个项目中覆盖此文件中的设置,添加了一个额外的参数指向源代码的根目录。现在项目文件可以复制到工作解决方案目录,指向安装的源代码,并在不影响其他项目的情况下进行更改和构建。
目标名称最初被分配为zlib1(这个库的众所周知的名称),但如果启用了WINAPI选项,它可以更改为zlibwapi或旧的zlib。
项目在zlib.vcxproj文件中实现,并针对较新的zlib1库。它已从zlibvc.vcxfroj更改,以防止兼容性问题。如果需要zlibvc.vcxproj或zlibstat.vcxproj,只需在项目UI中设置正确的选项,然后将其另存为zlibvc.vcxproj或zlibstat.vcxproj。
ZLib有一个众所周知的项目ID:{8FD826F8-3739-44E6-8CC8-997122E53B8D}。这很重要,因为这个ID可以用于识别这个库,以供消费项目唯一地区分它与其他项目。Boost将使用它来解析对库的引用。
对源文件的引用已更改为相对于根文件夹:XML
<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>
要创建导出链接库,必须指定模块定义文件。这是通过将路径分配给win32\zlib.def文件到链接项目中的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。如果将“使用汇编实现”设置为No,则这些不被定义。如果将“WINAPI调用约定”选项设置为Yes,则项目定义:ZLIB_WINAPI。有关此选项的更多信息,请参见contrib\vstudio文件夹中的readme.txt文件。
构建项目会创建zlib1.lib,如果“配置类型”设置为“动态库”,则还会创建zlib1.dll文件。
ZLib项目使用在:XML中定义的默认构建过程
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets"
它不需要任何自定义。它是一个正常的C++项目,具有所有默认目标和任务。要引用ZLib,必须执行以下操作:
将zlib项目添加到Boost解决方案
右键单击Boost项目并选择属性,打开属性对话框
转到公共属性并单击引用
单击添加引用按钮
选择zlib项目并单击确定
正如在本文中讨论的,消费依赖库归结为链接到正确的链接库文件,并将适当的DLL复制到正确的目录。
普通应用程序依赖于链接器来执行所有对象操作,但boost不同。它使用自己的构建系统进行编译和链接(好吧,它仍然使用链接器,但它自己做)。因此,不是将库的引用传递给链接器,而是必须将其传递给Jam构建工具。这是通过环境变量或调用工具时的命令行属性来完成的。这是在MSBuild目标调用序列之外完成的,因此必须单独解决它并将信息传递给工具。
将使用命令行属性将此信息传递给Jam构建工具。
依赖引用信息存储在项目文件中的Reference项目中。它由Visual Studio添加和修改,不需要进行任何自定义。所要做的就是获取引用项目的名称,调用GetResolvedLinkLibs获取链接库列表,选择一个引用zlib1.lib的库,并将其传递给b2.exe。
可以采取两种不同的方法:可以直接在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>
...
</Target>
正如在这段代码示例中看到的,调用GetResolvedLinkLibs目标以获取所有链接库的列表,并根据项目ID过滤出属于ZLib项目的库。将以同样的方式添加对其他依赖项的支持。