第92章:使用vcpkg:集成vcpkg包管理器

做C++开发的朋友应该都有过这种经历:项目里要用个第三方库,比如OpenSSL、Boost、CURL这些,手动下载、编译、配置路径,折腾半天。更头疼的是换台机器或者换个人接手,这套流程又得重来一遍。嗯,我当年刚入行时就吃过这个亏——项目里用了七八个库,光写FindXXX.cmake就写了一周,后来同事接手直接崩溃。

vcpkg就是来解决这个痛点的。它是微软开源的一个C++包管理器,说白了就是帮你自动下载、编译、安装第三方库的工具。你只需要告诉它"我要用这个库",剩下的它全包了。

92.1 vcpkg是什么?

vcpkg是一个跨平台的C++包管理器。它支持Windows、Linux、macOS,也支持各种编译器和架构。我个人习惯把它理解成"C++界的pip"——虽然不完全一样,但思路是相通的。

它的核心工作流程很简单:

  1. 从GitHub或上游仓库拉取库的源码
  2. 根据当前平台和编译器配置编译参数
  3. 编译成静态库或动态库
  4. 安装到指定目录
  5. 生成CMake集成文件,让find_package能找到它

你看,整个过程完全自动化。我在项目中遇到过最典型的场景:新同事入职,拉完代码后只需要执行vcpkg install,所有依赖就齐活了。再也不用写那种又臭又长的"环境搭建文档"了。

92.2 安装vcpkg

安装vcpkg其实就两步,没什么复杂的:

# 克隆仓库
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg

# 运行引导脚本
# Windows:
./bootstrap-vcpkg.bat

# Linux/macOS:
./bootstrap-vcpkg.sh

引导完成后,你会得到一个vcpkg可执行文件。我建议把它加到PATH里,这样全局都能用。当然,你也可以直接用相对路径调用。

小技巧:我个人习惯把vcpkg放在C:\dev\vcpkg或者~/tools/vcpkg这种固定的位置。这样不管开多少个项目,都共用同一个vcpkg实例,省磁盘空间。

92.3 安装库的基本操作

安装一个库,命令非常简单:

# 搜索可用库
vcpkg search fmt

# 安装库(默认x64)
vcpkg install fmt

# 安装指定架构
vcpkg install fmt:x86-windows
vcpkg install fmt:arm64-linux

# 查看已安装的库
vcpkg list

这里有个细节要注意:vcpkg默认安装的是x64版本的库。如果你需要32位或者ARM版本,得显式指定triplet(就是冒号后面那串)。

为什么会这样?因为vcpkg的设计理念是"显式优于隐式"。它不想替你做决定,万一猜错了你的目标平台,编译出来的库不能用,那才叫坑。

92.4 在CMake中集成vcpkg

这才是本章的重点。vcpkg装好了库,怎么让CMake找到它们?

有两种方式:

方式一:通过工具链文件

这是最推荐的方式。vcpkg会生成一个CMake工具链文件,你只需要在CMake配置时指定它:

cmake -B build -S . -DCMAKE_TOOLCHAIN_FILE=[vcpkg-root]/scripts/buildsystems/vcpkg.cmake

然后在CMakeLists.txt里,你就可以直接用find_package了:

cmake_minimum_required(VERSION 3.14)
project(MyApp)

# 注意:不需要手动设置CMAKE_PREFIX_PATH
# vcpkg的工具链文件会自动处理

find_package(fmt CONFIG REQUIRED)

add_executable(myapp main.cpp)
target_link_libraries(myapp PRIVATE fmt::fmt)

你看,代码干净多了。不需要写一堆set(CMAKE_PREFIX_PATH ...),也不用担心路径写死的问题。

核心要点:vcpkg的工具链文件会自动把库的安装路径注入到CMake的搜索路径中。你只需要保证find_package能找到对应的Config文件就行。

方式二:通过CMAKE_PREFIX_PATH

如果你不想用工具链文件,也可以手动设置路径:

cmake -B build -S . \
  -DCMAKE_PREFIX_PATH=[vcpkg-root]/installed/x64-windows/share

但我不推荐这么做。原因很简单——你手动指定的路径是硬编码的,换台机器就得改。而工具链文件是vcpkg自带的,只要vcpkg的位置不变,它就永远有效。

92.5 实战:一个完整的例子

我们来写一个完整的例子,用vcpkg管理fmt库和nlohmann/json库。

首先,安装依赖:

vcpkg install fmt nlohmann-json

然后,项目结构如下:

myapp/
├── CMakeLists.txt
├── main.cpp
└── vcpkg.json   (可选,用于清单模式)

CMakeLists.txt:

cmake_minimum_required(VERSION 3.14)
project(JsonLogger)

# 使用C++17
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 查找vcpkg安装的库
find_package(fmt CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)

add_executable(json_logger main.cpp)

target_link_libraries(json_logger PRIVATE
    fmt::fmt
    nlohmann_json::nlohmann_json
)

main.cpp:

#include <fmt/core.h>
#include <nlohmann/json.hpp>
#include <iostream>

int main() {
    nlohmann::json config;
    config["name"] = "vcpkg demo";
    config["version"] = 1.0;

    fmt::print("Config: {}\n", config.dump(4));
    return 0;
}

配置和编译:

cmake -B build -S . \
    -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake

cmake --build build

./build/json_logger

输出结果:

Config: {
    "name": "vcpkg demo",
    "version": 1.0
}

整个过程一气呵成。你想想看,如果没有vcpkg,你得手动下载fmt和nlohmann-json的源码,编译,然后配置CMake路径。现在只需要两行命令加几行CMake代码就搞定了。

92.6 清单模式(Manifest Mode)

vcpkg还支持清单模式。说白了就是在项目根目录放一个vcpkg.json文件,里面声明项目依赖。这样别人拉代码后,只需要执行vcpkg install,所有依赖自动装好。

vcpkg.json示例:

{
  "name": "myapp",
  "version": "1.0.0",
  "dependencies": [
    "fmt",
    "nlohmann-json",
    "spdlog"
  ]
}

然后在CMake里启用清单模式:

cmake -B build -S . \
    -DCMAKE_TOOLCHAIN_FILE=/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake \
    -DVCPKG_MANIFEST_MODE=ON

我个人非常推荐这种做法。它把依赖管理做到了项目级别,而不是机器级别。每个项目都有自己的依赖清单,互不干扰。

注意:如果你同时使用vcpkg.json和手动vcpkg install,可能会产生冲突。建议二选一:要么纯清单模式,要么纯手动模式。我曾经在项目里混用过,结果vcpkg报了"端口冲突"的错误,排查了半天才发现是两种模式打架了。

92.7 知识体系图

下面这张图展示了vcpkg与CMake集成的完整流程:

vcpkg + CMake 集成流程 vcpkg install 下载并编译第三方库 生成工具链文件 vcpkg.cmake 安装到installed/ 头文件 + 库文件 + .cmake cmake -DCMAKE_TOOLCHAIN_FILE=... 指定vcpkg工具链文件 find_package(fmt CONFIG REQUIRED) 自动找到vcpkg安装的库 最终:target_link_libraries(myapp PRIVATE fmt::fmt)

92.8 常见问题与避坑

用vcpkg的过程中,有几个坑我踩过,分享给你:

问题 原因 解决方案
find_package找不到库 没有指定工具链文件,或者路径不对 检查CMAKE_TOOLCHAIN_FILE是否指向正确的vcpkg.cmake
编译报错:找不到头文件 库没有安装,或者安装的是不同架构 运行vcpkg list确认库已安装,且triplet匹配
链接错误:未定义的符号 库的编译选项与项目不一致 检查VCPKG_TARGET_TRIPLET是否设置正确
vcpkg install很慢 从GitHub下载源码,国内网络不稳定 配置镜像源,或者使用--binarysource=clear跳过二进制缓存
避坑指南:我曾经在Windows上用vcpkg安装OpenSSL,结果编译了整整40分钟。后来发现是因为没有启用二进制缓存。建议第一次安装时加上--binarysource=default参数,vcpkg会优先使用预编译的二进制包,速度能快10倍。

92.9 总结

vcpkg和CMake的集成,说白了就是三步:

  1. 用vcpkg安装你需要的库
  2. 在CMake配置时指定vcpkg的工具链文件
  3. 在CMakeLists.txt里直接用find_package

就这么简单。但它的价值在于——你不再需要手动管理第三方库的下载、编译、路径配置。所有依赖都通过vcpkg统一管理,项目可移植性大大提升。

我个人建议,新项目从一开始就用vcpkg + 清单模式。虽然前期多花几分钟配置,但后续维护省下的时间,绝对值得。

最后说一句:vcpkg不是万能的。有些库没有收录到vcpkg的端口列表中,或者版本太旧。这时候你可以自己写端口文件,或者用FetchContent。但80%的常见库,vcpkg都能搞定。够用了。

公众号:蓝海资料掘金营,微信deep3321