导语
在前三节中,我们系统学习了 install() 规则的配置、目标导出与 Config 包的制作,以及 CPack 打包系统的实战应用。至此,你的项目已经能够生成规范的安装包和可复用的 CMake 配置。然而,在 Modern C++ 生态中,仅仅生成安装包已经不够了。
如今的 C++ 开发者更习惯通过包管理器来获取依赖:一句 vcpkg install、一个 conanfile.txt 引用,或者一次 hunter_add_package 调用,就能自动下载、编译并链接第三方库。如果你的库无法被这些主流包管理器识别和安装,那么它的分发效率将大打折扣。
本节作为第六章的收官,将带你跳出”单一项目”的视角,学习如何将你的 CMake 项目发布到三大主流 C++ 包管理器——vcpkg、Conan 与 Hunter。我们将手把手编写 portfile.cmake、conanfile.py,并演示 Hunter 的集成方式,让你的项目真正融入现代 C++ 生态。
vcpkg 端口文件编写
Microsoft 的 vcpkg 是目前社区活跃度最高的 C++ 包管理器之一。要将你的 CMake 项目发布到 vcpkg,核心工作是编写端口(Port)文件。一个最小化的端口通常包含两个文件:
vcpkg.json:描述包的元数据(名称、版本、依赖、特性等)。portfile.cmake:定义如何下载源码、配置 CMake、构建、安装,以及修复路径等。
vcpkg.json 结构详解
vcpkg.json 采用 JSON 格式,vcpkg 2.0 版本后强烈推荐使用 manifest 模式。以下是一个示例,假设我们要发布名为 mygraphics 的图形库:
{
"name": "mygraphics",
"version": "1.2.0",
"description": "A lightweight 2D graphics library built on Modern CMake",
"homepage": "https://github.com/yourname/mygraphics",
"license": "MIT",
"dependencies": [
{
"name": "fmt",
"version>=": "9.1.0"
},
{
"name": "sdl2",
"platform": "!(uwp | android)"
}
],
"features": {
"opengl": {
"description": "Enable OpenGL backend",
"dependencies": [
"opengl"
]
},
"tests": {
"description": "Build test suite",
"dependencies": [
"catch2"
]
}
},
"default-features": []
}
关键字段说明:
version:支持语义化版本。vcpkg 也支持version-semver、version-date、version-string等精确类型。dependencies:可声明版本约束(version>=)和平台过滤(platform)。features:定义可选组件(如opengl),用户可通过vcpkg install mygraphics[opengl]启用。
portfile.cmake 完整示例
portfile.cmake 是端口的构建脚本。vcpkg 提供了一系列辅助函数(以 vcpkg_* 开头),大幅简化了流程。下面的示例展示了从 GitHub Release 下载、CMake 构建到安装补丁的完整过程:
# portfile.cmake for mygraphics
vcpkg_from_github(
OUT_SOURCE_PATH SOURCE_PATH
REPO yourname/mygraphics
REF v1.2.0
SHA512 0a1b2c3d4e5f... # 下载文件的校验和,必须准确
HEAD_REF main
)
# 检查并拉取依赖(可选,通常 vcpkg.json 已声明)
vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
FEATURES
opengl MYGRAPHICS_ENABLE_OPENGL
tests MYGRAPHICS_BUILD_TESTS
)
# 配置 CMake
vcpkg_cmake_configure(
SOURCE_PATH "${SOURCE_PATH}"
OPTIONS
${FEATURE_OPTIONS}
-DMYGRAPHICS_BUILD_EXAMPLES=OFF
-DCMAKE_REQUIRE_FIND_PACKAGE_fmt=ON
)
# 构建并安装
vcpkg_cmake_install()
# 修复 CMake 目标导出文件中的绝对路径(关键步骤!)
vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/mygraphics)
# 移除 debug 版本的 include 目录(节省空间)
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
# 安装 LICENSE 文件
file(INSTALL "${SOURCE_PATH}/LICENSE" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright)
# 安装 usage 提示文件(可选但推荐)
file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")
几个必须掌握的细节:
- vcpkg_cmake_config_fixup:vcpkg 要求所有 CMake 配置文件必须位于
share/<port>下。此命令会自动将lib/cmake/xxx中的*Config.cmake文件移动到正确位置,并修正内部的相对路径引用。 - file(REMOVE_RECURSE …/debug/include):debug 版本的 include 头文件与 release 完全一致,保留会造成重复,这是 vcpkg 的规范要求。
- SHA512:首次编写时可以先留空,运行
vcpkg install时 vcpkg 会提示正确的哈希值。
本地测试端口
编写完成后,建议先在本地 overlay 端口目录测试,避免直接提交到官方仓库:
# 目录结构
# /my-overlay/
# └── mygraphics/
# ├── portfile.cmake
# ├── vcpkg.json
# └── usage
# 测试安装
vcpkg install mygraphics --overlay-ports=/path/to/my-overlay
测试成功后,你就可以向 microsoft/vcpkg 官方仓库提交 Pull Request,让整个社区都能通过 vcpkg install mygraphics 使用你的库。
Conan recipe 编写基础
Conan 是另一款跨平台、去中心化的 C++ 包管理器,其核心是 Python 编写的 conanfile.py(或简化的 conanfile.txt)。与 vcpkg 的”端口”概念不同,Conan 的 recipe 更强调构建逻辑的可编程性。
最小可运行的 conanfile.py
对于 CMake 项目,Conan 2.x 推荐继承 ConanFile 并显式定义 layout()、generate()、build() 等方法。以下是一个完整的 recipe 模板:
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps
from conan.tools.files import copy, get
from conan.tools.scm import Git
import os
class MyGraphicsConan(ConanFile):
name = "mygraphics"
version = "1.2.0"
license = "MIT"
url = "https://github.com/yourname/mygraphics"
description = "A lightweight 2D graphics library"
topics = ("graphics", "2d", "cmake")
# 二进制包兼容的标准设置
settings = "os", "compiler", "build_type", "arch"
options = {
"shared": [True, False],
"fPIC": [True, False],
"with_opengl": [True, False],
}
default_options = {
"shared": False,
"fPIC": True,
"with_opengl": True,
}
# 导出源码补丁(如果有)
exports_sources = "CMakeLists.txt", "src/*", "include/*", "cmake/*"
def config_options(self):
# Windows 下通常不需要 fPIC
if self.settings.os == "Windows":
del self.options.fPIC
def configure(self):
if self.options.shared:
self.options.rm_safe("fPIC")
def requirements(self):
# 声明依赖
self.requires("fmt/9.1.0")
if self.options.with_opengl:
self.requires("opengl/system")
def layout(self):
# 使用 CMake 默认布局,支持 multi-config generator
cmake_layout(self)
def generate(self):
# 生成 CMakeDeps 和 CMakeToolchain
deps = CMakeDeps(self)
deps.generate()
tc = CMakeToolchain(self)
tc.variables["MYGRAPHICS_ENABLE_OPENGL"] = self.options.with_opengl
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
# 拷贝 License
copy(self, "LICENSE", src=self.source_folder, dst=os.path.join(self.package_folder, "licenses"))
# 调用 CMake install
cmake = CMake(self)
cmake.install()
def package_info(self):
# 暴露给消费者的 CMake 目标名
self.cpp_info.set_property("cmake_file_name", "MyGraphics")
self.cpp_info.set_property("cmake_target_name", "MyGraphics::MyGraphics")
self.cpp_info.libs = ["mygraphics"]
if self.settings.os in ["Linux", "FreeBSD"]:
self.cpp_info.system_libs.append("m")
关键点解析:
settingsvsoptions:settings是项目外部环境的描述(如编译器版本、OS),通常不由 recipe 作者修改;options是库本身的构建特性(如是否编译为动态库),由消费者通过-o mygraphics:shared=True指定。generate():Conan 2.x 的核心改进。它会生成conan_toolchain.cmake和依赖的*-config.cmake占位文件,确保你的find_package()能在隔离环境中正确工作。package_info():定义了消费者通过find_package(MyGraphics)时应找到的 target 名称。这里务必与你项目安装的MyGraphicsConfig.cmake中定义的目标名保持一致,否则会出现”找到包但找不到目标”的链接错误。
构建与上传到私有仓库
在源码目录放置上述 conanfile.py 后,执行以下命令:
# 创建包(本地缓存)
conan create . --build=missing
# 指定 profile(例如 C++17)
conan create . --build=missing -s compiler.cppstd=17
# 上传到远程仓库(如 Artifactory 或 ConanCenter)
conan remote add myrepo https://artifactory.yourcompany.com/artifactory/api/conan/conan-local
conan upload mygraphics/1.2.0 -r myrepo
对于闭源商业项目,Conan 的私有仓库支持使其成为比 vcpkg 更灵活的选择。
Hunter 包管理集成
Hunter 是一个”以 CMake 为中心”的去中心化包管理器,它的最大特点是完全无外部依赖——使用者只需要在 CMakeLists.txt 中引入 Hunter 的初始化脚本,剩下的工作全部由 CMake 完成。
Hunter 的工作原理
Hunter 通过维护一个名为 huntergate 的 CMake 模块实现自举。在配置阶段,它会根据 CMakeLists.txt 中声明的依赖,自动下载并构建外部库,所有构建产物缓存于 ~/.hunter 目录。
在项目中启用 Hunter
首先,在你的项目根目录放置 cmake/HunterGate.cmake(可从 Hunter 官方仓库获取)。然后修改根 CMakeLists.txt:
cmake_minimum_required(VERSION 3.15)
project(MyProject LANGUAGES CXX)
# --- Hunter 初始化 ---
include("cmake/HunterGate.cmake")
HunterGate(
URL "https://github.com/cpp-pm/hunter/archive/v0.24.18.tar.gz"
SHA1 "3b9c76f1558fd1256c4b8c6854c3c7c620df9b42"
)
# --- 声明依赖 ---
hunter_add_package(fmt)
find_package(fmt CONFIG REQUIRED)
hunter_add_package(Boost COMPONENTS system filesystem)
find_package(Boost CONFIG REQUIRED system filesystem)
# --- 你的目标 ---
add_executable(myapp src/main.cpp)
target_link_libraries(myapp PRIVATE fmt::fmt Boost::system Boost::filesystem)
对于库作者而言,如果你想让自己的库也能通过 hunter_add_package(mygraphics) 被他人使用,需要向 Hunter 官方仓库提交一个 Hunter 包定义 PR。这个定义本质上也是一个 CMake 脚本,告诉 Hunter 如何下载和构建你的项目。
创建 Hunter 包定义(供上游贡献)
Hunter 的包定义位于其官方仓库的 cmake/projects/<Name>/hunter.cmake 路径下。以下是为 mygraphics 添加 Hunter 支持的示例:
# cmake/projects/mygraphics/hunter.cmake
include(hunter_add_version)
include(hunter_cacheable)
include(hunter_download)
include(hunter_pick_scheme)
hunter_add_version(
PACKAGE_NAME mygraphics
VERSION "1.2.0"
URL "https://github.com/yourname/mygraphics/archive/v1.2.0.tar.gz"
SHA1 "a1b2c3d4e5f6..." # 压缩包哈希
)
hunter_pick_scheme(DEFAULT url_sha1_cmake) # 使用标准 CMake 构建方案
hunter_cacheable(mygraphics)
hunter_download(PACKAGE_NAME mygraphics)
此外,你还需要在 Hunter 主仓库的 cmake/configs/default.cmake 中为新包添加默认版本:
hunter_default_version(mygraphics VERSION 1.2.0)
Hunter 的优势在于对消费者极度友好:开发者不需要安装 Python、Node 或任何包管理器二进制,只要有一个能上网的 CMake,就能自动拉取依赖。但其缺点是上游贡献门槛略高,且对新版本的支持速度通常慢于 vcpkg 和 Conan。
三种方案选型建议
至此,我们已经完整走通了 vcpkg、Conan 和 Hunter 的发布流程。下表从库作者和消费者两个维度进行对比,帮助你在实际项目中做出选择:
| 维度 | vcpkg | Conan | Hunter |
|---|---|---|---|
| 生态与仓库 | 微软官方维护,中心化仓库,社区活跃 | ConanCenter + 私有 Artifactory,去中心化 | 去中心化,依赖社区 PR 合并 |
| 消费者体验 | 需要安装 vcpkg 工具,支持 manifest 模式 | 需要安装 Conan (Python),功能最丰富 | 纯 CMake,零额外工具安装 |
| 库作者工作 | 编写 portfile.cmake + vcpkg.json | 编写 conanfile.py,逻辑最灵活 | 向 Hunter 仓库提交 cmake 定义 |
| 二进制缓存 | 支持,通过 GitHub Packages | 原生支持,可部署私有缓存服务器 | 本地缓存于 ~/.hunter |
| 适用场景 | 开源库、跨平台项目、Visual Studio 用户 | 企业闭源项目、复杂配置矩阵 | CMake 极客、嵌入式、限制外网工具的环境 |
结语
发布到包管理器是现代 C++ 库从”能用”走向”易用”的关键一步。通过本节的学习,你已经掌握了:
- 为 vcpkg 编写
vcpkg.json和portfile.cmake,并处理路径修复和特性开关; - 为 Conan 编写
conanfile.py,利用CMakeToolchain和CMakeDeps实现现代化的 CMake 集成; - 在项目中引入 Hunter,以及为 Hunter 生态贡献包定义的流程。
至此,第六章”安装、打包与发布”已全部完结。从 install() 的基础规则,到导出目标与 Config 包,再到 CPack 生成系统安装包,最终融入 vcpkg/Conan/Hunter 包管理生态——你的 CMake 项目现在已经具备了工业级的分发能力。
在下一章中,我们将进入测试与质量保障的领域,学习如何使用 CTest 搭建自动化测试框架、集成代码覆盖率分析与静态检查工具。敬请期待!


没有回复内容