在 Visual Studio (MSBuild) 中构建和配置 OpenSSL。





5.00/5 (11投票s)
将第三方工具和库集成到 Visual Studio (MSBuild) 配置环境中。
引言
在本文中,我将展示如何使用 Visual Studio 和 MSBuild 系统配置和构建 OpenSSL 库。本文的目标是解决以下问题:
- 提供针对 openssl 库的专用 UI 属性页,以便只能设置有效选项
- 提供一种简单的方法来微调构建并传递编译器选项
- 没有硬编码
- 按顺序构建多个平台(x86、x64...)
- 在源代码树之外构建
- 与 Visual Studio 集成,允许项目与 OpenSSL 相互引用
- 无缝调试和符号加载
背景
OpenSSL 项目是一个开源项目,实现了安全套接字层 (SSL v2/v3) 和传输层安全 (TLS) 协议,以及一个通用加密库。它是一个跨平台库,因此编写时会兼容多种构建工具和操作系统。
构建说明非常简单
perl Configure VC-WIN32 --prefix=C:\Build-OpenSSL-VC-32 ms\do_ms nmake -f ms\nt.mak nmake -f ms\nt.mak install
看起来很简单,但正如往常一样,总有陷阱
- 它会在 OpenSSL 发行版的根目录下构建所有二进制文件和对象文件。
- 如果需要构建多个配置(Release、Debug 等),则必须逐个进行。
- 它不以任何方式与 Visual Studio 集成
在这里,我们将尝试修复所有这些问题。
配置
在我之前的文章中,我描述了如何为第三方工具创建自定义属性页。更多信息请参阅 此处 和 此处。长话短说,我们创建了以下属性页:
“常规”页面允许设置常规项目属性,例如输出和中间目录,以及大多数 OpenSSL 选项。它还提供了一个 UI,用于在“命令行”控件中添加任何任意参数。
输出、中间和包含目录将在构建过程中用于放置相应的文件。如果为空,构建将使用 OpenSSL 根文件夹中的默认位置(例如,如果“包含”为空,则为 inc32)。
展开左侧窗格中的“选项”会产生以下视图:
此页面允许禁用密码。如果页面上缺少某个密码,仍可通过提供
“常规”页面上的“命令行”中的 `no-missing_cither` 选项来禁用它。
Kerberos 5 页面提供了一种将 Kerberos 5 库添加到构建中的方法。
“C++”页面允许微调构建并将其他开关和定义发送给编译器。
通过这些属性页,我们可以配置所有 OpenSSL 选项,以及传递一些额外的参数给编译器。
从属性页获取正确的值并将其传递给配置的详细说明请参阅 此处。配置由 `GetXmlConfig` 目标处理,并返回一个参数字符串,该字符串被传递给相应的脚本。
现在我们准备好构建了。
构建
检查 _Configure_ 脚本和批处理文件,揭示了以下步骤:
perl Configure VC-WIN32 --prefix=C:\Build-OpenSSL-VC-32
在 OpenSSL 目录的根目录下创建 Makefile。
ms\do_ms
创建 MINFO 文件。
创建模块定义和资源文件。
创建合并的 Makefile。
nmake -f ms\nt.mak
构建库。
将这些任务包装到原生的 MSBuild 目标中可以更精确地控制构建过程。我们创建了以下目标:
<Target Name="Configure" ... />
<Target Name="MINFO" ... />
<Target Name="ModuleDefinitions" ... />
<Target Name="CreateMakefile" ... />
<Target Name="CompileAsm" ... />
<Target Name="Build" ... />
<Target Name="Rebuild" ... />
<Target Name="Clean" ... />
添加目标之间的必要依赖关系,插入相应的 Perl 脚本的 `
配置选项
OpenSSL 构建分两步配置:在 `Configure` 目标和 `CreateMakefile` 目标中。
`Configure` 目标处理来自 UI 属性页的参数,并将它们传递给 Configure 脚本。配置值通过执行 `ConfGen.targets` 中实现的 `GetXmlConfig` 目标来检索。有关该过程的详细解释,请参阅此 帖子。
`CreateMakefile` 目标执行 **mk1mf.pl** 脚本,并设置输出、包含和中间目录的位置。它还指示脚本创建静态或共享库,以及是否使用汇编器。
Clean
构建后的清理工作有点棘手。执行 **nmake -f ms\nt.mak clean** 会通过运行 **del *.*** 来擦除所有内容。
对于 Visual Studio 来说,这种行为是不可接受的。同一目录中可能存在其他项目的文件。为了避免删除错误的文件,Makefile 会被解析以获取它构建的目标列表。Makefile 的格式定义良好,因此相对容易找到所有这些文件(请注意,为简化起见,脚本已修改)。
<ReadLinesFromFile File="$(BuildDir)\openssl.mak" Condition="Exists('$(BuildDir)\openssl.mak')">
<Output ItemName="ConfigLines" TaskParameter="Lines"/>
</ReadLinesFromFile>
<ItemGroup>
<BuildDirSelected Include="$([Regex]::Match('%(ConfigLines)', (?<=\\)(.*?\b)(?<=\.obj) ))" />
<BuildDirSelected Include="$([Regex]::Match('%(ConfigLines)', (?<=\\)(.*?\b)(?<=\.asm) ))" />
<BuildDirLibrary Include="$([Regex]::Match('%(ConfigLines)', (?<=\\)(.*?\b)(?<=\.lib) ))" />
...
<OutputDirLibrary Include="$([Regex]::Match('%(ConfigLines)', (?<=\\)(.*?\b)(?<=\.dll) ))" />
<OutputDirLibrary Include="openssl.exe;libeay32.dll;ssleay32.dll" />
</ItemGroup>
<CombinePath BasePath="$(BuildDir)" Paths="@(BuildDirSelected)">
<Output ItemName="DelFiles" TaskParameter="CombinedPaths"/>
</CombinePath>
<CombinePath BasePath="$(OutputDir)" Paths="@(OutputDirSelected)">
<Output ItemName="DelFiles" TaskParameter="CombinedPaths"/>
</CombinePath>
<Delete Files="@(DelFiles)" ContinueOnError="true" />
在此代码中,我们读取 Makefile,通过匹配模式解析所有目标文件,添加这些文件的完整路径,然后删除它们。
构建多个平台
如果中间、包含和输出目录都不同,则可以按顺序构建多个平台。这可以通过使用宏(如 $(Platform) 和 $(Configuration))来实现。例如,请参阅示例项目的 x64 平台配置。
构建既可以从 VisualStudio 执行,也可以从命令提示符执行。
参考文献
此项目的一个目标是将其集成到 Visual Studio 项目引用系统中。
引用依赖项
OpenSSL 依赖于两个外部库:ZLib 和 Kerberos 5。Kerberos 不支持 MSBuild,因此无法集成到原生引用系统中。另一方面,ZLib 带有与库一起分发的 MSBuild 项目,可以轻松集成。
ZLib 也有一个与项目相关的知名 ID,可用于在构建中识别它。
<MSBuild Projects="@(ProjectReference)" Targets="GetResolvedLinkLibs"
Condition="'%(ProjectReference.Project)'=='{8fd826f8-3739-44e6-8cc8-997122e53b8d}'" >
<Output ItemName="ZLibPath" TaskParameter="TargetOutputs"/>
</MSBuild>
这会获取 ZLib 库的路径。
构建还需要 ZLib 库的包含目录的路径。通常,它会在 C++ 编译器设置的“附加包含目录”字段中手动设置。如果可以从 zlib.h 文件的位置推断出该位置,我们可以省去手动步骤。此代码获取该位置。
<MSBuild Projects="@(ProjectReference)" Targets="SourceFilesProjectOutputGroup"
Condition="'%(ProjectReference.Project)'=='{8fd826f8-3739-44e6-8cc8-997122e53b8d}'" >
<Output ItemName="SourceFiles" TaskParameter="TargetOutputs"/>
</MSBuild>
<PropertyGroup>
<LibEnvVar Condition="'@(SourceFiles)'!='' And
'%(SourceFiles.Identity)'!='' And
'@(SourceFiles->Contains("zlib.h"))'=='True'">INCLUDE=%(RelativeDir)/>
</PropertyGroup>
这两个参数都作为环境变量 LIB 和 INCLUDE 传递给构建。
提供引用
为了在上游项目中被引用,需要几个目标:
<Target Name="GetNativeTargetPath"/ > <Target Name="GetTargetPath" /> <Target Name="GetNativeManifest" /> <Target Name="GetResolvedLinkLibs" /> <Target Name="GetCopyToOutputDirectoryItems" />
有关项目引用的详细说明以及所需内容的说明,请参阅 Visual Studio 中的原生项目引用。OpenSSL 项目遵循文章中描述的指南。
测试应用程序
创建了一个测试应用程序来演示 OpenSSL 集成和引用。该示例来自早期 IBM 文章,此处。
示例需要将 OpenSSL 添加到项目依赖项并设置包含目录的路径。完成后,就可以构建和调试测试应用程序。
调试
Visual Studio 的最大优势之一是其强大的调试器。此集成项目使您无需任何额外工作即可利用此工具的所有功能。您可以从应用程序中进入代码,或者可以直接调试 openssl.exe。进入 OpenSSL 代码无需任何额外步骤。 PDB 文件的位置可以从 `GetResolvedLinkLibs` 提供的库文件的位置推断出来。
要调试 `openssl.exe`,请右键单击解决方案资源管理器中的 `openssl` 项目,选择“调试”,然后选择“新实例”。
这将启动新的调试器会话,加载可执行文件并进入 main 方法。
从现在开始,您可以逐行执行代码,设置断点等。
测试
此项目允许执行通常由 `nmake -f makefile test` 命令执行的单元测试。测试由 `Test` 目标运行。Visual Studio 不支持此目标,因此应从命令提示符执行,如下所示:**MSBuild /target:Test openssl.vcxproj**
如果一切顺利,它将如上所示报告成功。
Perl
OpenSSL 需要 Perl 来配置构建和创建 Makefile。Windows 没有内置的语言支持,Visual Studio 也没有。OpenSSL 组织推荐使用 ActivePerl 进行构建。
使用代码
源存档包含用于构建 OpenSSL 的项目文件、目标和 XML 文件。示例包含 Visual Studio 解决方案和其他项目。
您需要下载 OpenSSL 和 ZLib 源代码,并将它们解压到相应的目录中,然后才能构建解决方案。
提供的代码旨在作为示例,未经全面测试。如果您发现任何问题,请告诉我。如果您知道如何修复,请将拉取请求发送到 GitHub。
历史
2015 年 3 月 30 日 - 发布
2015 年 3 月 31 日 - 扩展了原生引用,添加了调试和测试部分。