一、编译开源库的传统方法
Windows 下开发 C/C++ 程序,少不了编译开源的第三方库。比如用于网络连接的高性能库 libcurl、用于压缩解压的 zlib 等等。使用这些库开发极大的方便了程序员,使得我们不必重复造*。但是使用这些库必须要处理以下问题。
编译方式
由于这些开源库绝大部分都来源于 Linux 系统,导致其工程文件、编译系统都使用 gnu 系列工具,使得将其移植到 Windows 的 VC 开发环境下一直是难点。尽管最近几年很多开源库都支持了跨平台的 CMake,但是编译过程仍然复杂和多样化。
常见的编译方式有:
编译方式 | 特点 | 举例 |
---|---|---|
configure、make | 需要msys这样的unix环境才可以编译 | ffmpeg |
自定义编译工具 | 需要学习特定的编译命令和工具 | openssl、boost |
cmake | 相对简单轻松 | libcurl |
VC工程文件 | 这种最简单,直接拿来即可编译 |
编译类型
当了解了这些还不够,我们还需要考虑预先编译出哪种类型的开源库程序。比如:Debug还是Release、动态库还是静态库、MD还是MT、32位还是64位。光是这三种组合就有16种可能性。如果像libcurl这种还要考虑是否引用其他开源库的功能,那么编译类型的组合会更多。管理起来很麻烦。
工程目录设定
由于多样的编译类型,工程目录也必须仔细设定才能保证自己的软件项目能够正常编译。
二、为什么要用Vcpkg
正是由于编译开源库的传统方法的缺陷,所以出现了 Vcpkg,优点如下:
- 自动下载开源库源代码。
- 源码包的缓存管理和版本管理,可以升级版本。
- 轻松编译。
- 依赖关系检查(比如编译 libcurl,会自动下载 zlib、openssl 进行编译)。
- 无缝集成 Visual Studio,不需要设置库文件、头文件的所在目录,自动集成。
- Visual Studio 全平台支持,不仅支持 Debug/Release、x86/x64 编译,还支持 UWP、ARM 平台的编译。
三、windows下安装Vcpkg
(1)由于官方建议把 vcpkg 目录放到 C:\src\
下,所以先创建再移动到该目录:
$ cd c:/src
(2)git 克隆官方的 git 仓库:
$ git clone https://github.com/microsoft/vcpkg
(3)进入到仓库中,编译 Vcpkg:
$ cd vcpkg
$ ./bootstrap-vcpkg.bat
注意:
-
Vcpkg 大量使用 psl 脚本,所以官方强烈推荐使用 PowerShell 而不是 CMD 命令行来执行各种操作。尽管在使用的时候兼容 CMD,但是在编译这一步,请使用 PowerShell,以下所有操作也如此。
-
如果下载失败,输出
Downloading vcpkg.exe failed. Please check your internet connection, ...
,说明外网下载不稳定,需要尝试多次下载或*上网。
成功后输出:
Downloading https://github.com/microsoft/vcpkg-tool/releases/download/2021-02-24-d67989bce1043b98092ac45996a8230a059a2d7e/vcpkg.exe -> C:\src\vcpkg\vcpkg.exe
Done.
更详细可以参考 Vcpkg 的官方 git 地址:https://github.com/microsoft/vcpkg/blob/master/README_zh_CN.md
四、使用Vcpkg
4.1 查看Vcpkg支持的开源库列表
$ ./vcpkg.exe search
执行命令后,两三分钟后才显示出来支持的开源库列表,基本上常用的 C++ 开源库都支持。(使用 PowerShell 执行)
4.2 指定编译某种架构的程序库
如果不指定安装的架构,vcpkg 默认把开源库编译成 x86 的 Windows 版本的库。那 vcpkg 总共支持多少种架构呢?我们可以使用如下命令便知:
$ ./vcpkg.exe help triplet
我们可以看到会列出如下清单:
- arm-uwp
- arm-windows
- arm64-uwp
- arm64-windows
- x64-uwp
- x64-windows-static
- x64-windows
- x86-uwp
- x86-windows-static
- x86-windows
这个清单以后随着版本的迭代还会再增加。vcpkg 不仅支持 x86 架构,还支持 arm 架构。注意:这里的 arm 架构特指类似于 surface 这种运行在 arm 处理器上的 Win10 平台,而并非我们传统意义上的 Linux 或 android 的 ARM 平台。
4.3 安装一个开源库
那如果要安装编译某一个架构的开源库,我们该怎么写呢?我们只需要在需要安装的包后面指定相应的 triplet 即可。例如我们需要编译 64 位 Windows 版本的 jsoncpp,那么执行如下命令即可。
$ ./vcpkg.exe install jsoncpp:x64-windows
看到Installing package jsoncpp[core]:x64-windows... done
,则说明安装成功。
如果电脑中没有安装 CMake、7zip 等软件,Vcpkg 会自动下载 portable 版本的 CMake、7zip 等软件。但是由于各种原因,下载的网速很慢,所以建议先自行下载合适版本的对应软件。最好是下载最新版本的。实际测试发现,会先同时下载以下软件:
7zip-18.1.0-windows
cmake-3.19.2-windows
nuget-5.5.1-windows
powershell-core-7.1.0-windows
报错及解决办法
(1)在下载 powershell 时会下载不成功,提示:
Failed to download from mirror set:
https://github.com/PowerShell/PowerShell/releases/download/v7.1.0/PowerShell-7.1.0-win-x86.zip: WinHttpSendRequest() failed: 12002
只能复制上面下载链接,发现手动下载正常,下载完成后拷贝 PowerShell-7.1.0-win-x86.zip
到 C:\src\vcpkg\downloads
目录下,然后再执行安装指令,会跳过下载这步直接开始解压 PowerShell 压缩包。
遇到下载其他依赖库的压缩包失败时,也可考虑使用这种手动下载的方法。
后面下载 jsoncpp/archive/9059f5cad030ba11d37818847443a53918c327b1.tar.gz
也不成功,采用上面方法后,修改名称为 open-source-parsers-jsoncpp-9059f5cad030ba11d37818847443a53918c327b1.tar.gz
。
(2)缺少英文语言包,报错:
Warning: The following VS instances are excluded because the English language pack is unavailable.
解决办法:到 VS 安装向导,修改安装,点语言包,勾选英语;安装即可。
4.4 移除一个已经安装(编译)的开源库
如果移除一个已经安装的开源库,那么执行 remove 指令即可。比如我们要移除 jsoncpp,那么执行命令:
$ ./vcpkg.exe remove jsoncpp
注意:
这个时候只是移除了默认的 x86-winodws 版本的文件,如果有其他平台的版本需要移除,需要制定相应的 triplet。移除也只是移除了二进制程序库而已,源码包和解压缩的源码并没有删除。
如果想要一键移除“过时”的包,执行命令:
$ ./vcpkg.exe remove --outdated
4.5 列出已经安装的开源库
执行 list 指令即可,例如:
$ ./vcpkg.exe list
假如前面安装了 jsoncpp,会输出:jsoncpp:x64-windows 1.9.4 jsoncpp is an implementation of a JSON reader an...
4.6 更新已经安装的开源库
一般有两种更新方式。一个是 update 指令,可以显示可以升级的开源库的列表。另一个是 upgrade 的指令,会重新编译所有需要更新的包。
4.7 导出已经安装的开源库
有的时候,一个项目组中有很多人,不需要每个人都参与编译。一个人编译好所有开源库后到处给别人即可。有的时候也是出于备份的目的,也会导出已经安装的开源库。导出可以执行 export 指令。例如,我要导出 jsoncpp 库,那么执行:
$ ./vcpkg.exe export jsoncpp --zip
注意,导出时必须指定导出的包格式。vcpkg 支持 5 种导出包格式,有:
参数 | 格式 |
---|---|
–raw | 以不打包的目录格式导出 |
–nuget | 以 nuget 包形式导出 |
–ifw | 我也不知道这是啥格式 |
–zip | 以 zip 压缩包形式导出 |
–7zip | 以 7z 压缩包形式导出 |
一般地,导出包的格式为:vcpkg-export-<日期>-<时间>
默认情况下只会导出 x86-windows 的包,如果要导出所有包,那需要制定相应的 triplet。比如,如果同时导出 x86 和 x64 版本的 jsoncpp,那执行命令:
$ ./vcpkg.exe export jsoncpp:x86-windows jsoncpp:x64-windows --zip
如果要指定输出目录和特定文件名,需使用 "–output=" 参数。
4.8 导入备份的开源库
导入比较简单,执行 import 指令即可。例如:
$ ./vcpkg.exe import xxx.7z
五、Vcpkg和Visual Studio的集成
5.1 什么是集成?
上面我们已经安装了一些第三方库,那如何使用呢?常规情况下,我们需要设置 include 目录、lib 目录等,会有很多工作量。Vcpkg 提供了一套机制,可以全自动的适配目录,而开发者不需要关心已安装的库的目录在哪里,也不需要设置。这是 Vcpkg 的一大优势。
5.2 集成到全局
“集成到全局” 适用于 Visual Studio 开发环境和 msbuild 命令行。执行命令:
$ ./vcpkg.exe integrate install
当出现 Applied user-wide integration for this vcpkg root.
字样的时候,说明已经集成成功。这时候可以在任意的工程中使用安装好的第三方库。
5.3. 移除全局集成
移除全局集成只要执行下列命令即可:
$ ./vcpkg.exe integrate remove
5.4 集成到工程
上面已经可以集成到全局,为什么还要 “集成到工程” 呢?因为在大部分情况下,我们不希望集成到全局,毕竟有很多第三方库我们希望自定义处理一下,或者干脆不想集成第三方库。那么集成到工程是最灵活的处理方式。也是工程级项目推荐的处理方式。
“集成到工程” 是整个 vcpkg 中最复杂的一项,它需要利用 Visual Studio 中的 nuget 插件来实现。我们接下来一步一步来说。
1. 生成配置
执行命令:
$ ./vcpkg.exe integrate project
这时候会在 <vcpkg_dir>\scripts\buildsystems
目录下,生成 nuget 配置文件。其中 <vcpkg_dir>
是指 vcpkg 实际所在目录。
2. 基本配置
打开 Visual Studio,点击菜单工具 -> NuGet 包管理器 -> 程序包管理器设置”,进入设置界面,点击 “程序包源”。
点击 “加号” 增加一个源。修改源的名字为 vcpkg。在“源”的选项中点击右侧的 "…" 选择 vcpkg 目录下的 “scripts\buildsystems” 目录,然后点击右侧的“更新按钮”。点击 “确定”,关闭设置对话框。
到此,全局性的设置已经完成,以后不必再重复设置了。
3. 工程配置
用 Visual Studio 打开一个工程或解决方案。右键点击需要设置的工程,选择 “管理NuGet程序包”。在右上角的 “程序包源” 中选择刚刚设置的 “vcpkg”。这样在 “浏览” 选项卡中就可以看到 “vcpkg.H.Repos.vcpkg”。点击最右侧的 “安装”。这样就可以集成到某个工程了。
5.5 测试使用
#include <iostream>
#include <fstream>
#include <cassert>
#include "json/json.h" // 直接就可以include,无需额外配置
std::string createJson(void)
{
std::string jsonStr;
Json::Value root;
Json::StreamWriterBuilder writerBuilder;
std::ostringstream os;
// 设置默认无格式化的输出
writerBuilder.settings_["indentation"] = "";
root["Name"] = "Zhangsan";
root["Age"] = 25;
// jsonWriter是智能指针, 当jsonWriter被析构时, 它所指向的对象(内存)也被自动释放
std::unique_ptr<Json::StreamWriter> jsonWriter(writerBuilder.newStreamWriter());
jsonWriter->write(root, &os);
jsonStr = os.str();
return jsonStr;
}
void saveJsonStringToFile(const char* file, std::string& jsonStr)
{
std::ofstream ofs;
ofs.open(file);
assert(ofs.is_open());
ofs << jsonStr;
ofs.close();
}
int main()
{
std::string jsonStr;
jsonStr = createJson();
saveJsonStringToFile("./test.json", jsonStr);
return 0;
}
jsoncpp 的更多使用请参考:新版jsoncpp的一些基本用法
到此,就可以在 VS 上随意使用 jsoncpp 库了,在 exe 生成目录下也发现了 jsoncpp.dll
。
5.6 集成到CMake
最新的 Visual Studio 2015、2017 和 2019 大力支持 CMake 工程,所以对 cmake 的支持当然不能少。在 cmake 中集成只要在 cmake 文件中加入下面这句话即可。
-DCMAKE_TOOLCHAIN_FILE=<vcpkg_dir>/scripts/buildsystems/vcpkg.cmake
其中 <vcpkg_dir>
是指 vcpkg 实际所在目录。
5.7 集成静态库
Vcpkg 默认编译链接的是动态库,如果要链接静态库,目前还没有简便的方法。需要做如下操作
- 用文本方式打开 vcxproj 工程文件。
- 在 xml 的段里面增加如下两句话即可:
<VcpkgTriplet>x86-windows-static</VcpkgTriplet>
<VcpkgEnabled>true</VcpkgEnabled>
在 CMake 中集成静态库,需要额外指令:
cmake .. -DCMAKE_TOOLCHAIN_FILE=.../vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x86-windows-static
六、使用Vcpkg时的注意点
- Vcpkg 仅支持 Visual Studio 2015 update 3 及以上版本,究其原因,很可能和 C++11 的支持度以及集成原理有关系。
- 目前 Vcpkg 编译静态库,默认只支持 MT 模式。
七、小结
Vcpkg 目前还在不断的完善中,但不可否认,它已经极大的减少了我们在项目启动时,准备第三方库的时间。提高了工作效率。按照时髦的话来说,就是避免了重复造*。目前 Vcpkg 已经集成了上百个常用的开源库,而且数量还在不停增长。毕竟是微软旗下的开源项目,质量还是可以得到保障的,完全可以在工业级项目中得以使用。源代码托管在 github上,github 社区很活跃,有兴趣的朋友也可以参与进来。
参考:
Visual Studio开源库集成器Vcpkg全教程--利用Vcpkg轻松集成开源第三方库