24. 6.4 发布到包管理器

引言:把你的”楼盘”挂上”房产平台”

在前几节中,我们的”施工队长”CMake已经完成了大楼的建造(编译)、房间钥匙的交付(install),甚至把精装礼盒(CPack包)也准备好了。但如果你想让全世界的开发者都能方便地使用你的库,单靠手动分发安装包是不够的——你需要把”楼盘”挂到各大”房产平台”(包管理器)上,让其他项目经理(开发者)动动手指就能”采购”到你的建筑材料。

在C++生态中,vcpkgConanHunter是开发者最常用的三大”房产平台”。它们的工作逻辑各不相同:vcpkg更像一个严格管理的中央仓库,Conan像支持私人定制的灵活中介,而Hunter则是完全扎根在CMake生态里的原生方案。本节我们就从库作者的视角,学习如何让你的CMake项目入驻这些平台。

vcpkg端口文件编写

vcpkg是微软发起的开源C++包管理器,采用端口(Port)机制。一个端口本质上是一份”构建食谱”,告诉vcpkg如何下载、编译和安装你的库。想要发布到vcpkg,你需要在官方注册表(registry)或私有注册表中提交一个端口目录,通常包含两个核心文件:vcpkg.json(库的身份信息)和portfile.cmake(构建步骤)。

vcpkg.json:库的”身份证”

vcpkg.json采用声明式JSON格式,定义了库的名称、版本、依赖关系和功能特性(features)。以下是一个典型示例:

{
  "name": "my-awesome-lib",
  "version": "1.2.3",
  "description": "A modern C++ utility library built with CMake",
  "homepage": "https://github.com/yourname/my-awesome-lib",
  "license": "MIT",
  "dependencies": [
    {
      "name": "fmt",
      "version>=": "9.1.0"
    },
    {
      "name": "spdlog",
      "version>=": "1.12.0"
    }
  ],
  "features": {
    "tests": {
      "description": "Build unit tests",
      "dependencies": [
        "catch2"
      ]
    }
  }
}

其中features字段非常实用,它允许消费者按需安装。例如,用户可以通过vcpkg install my-awesome-lib[tests]来开启测试支持,这对应到CMake中就是你使用MY_AWESOME_LIB_BUILD_TESTS之类的选项进行条件编译。

portfile.cmake:构建”施工指南”

如果说vcpkg.json是门头招牌,那么portfile.cmake就是后厨的操作手册。它使用vcpkg提供的一系列辅助函数来驱动CMake构建流程:

vcpkg_from_github(
    OUT_SOURCE_PATH SOURCE_PATH
    REPO yourname/my-awesome-lib
    REF "v${VERSION}"
    SHA512 0a1b2c3d...  # 源码包的校验值
    HEAD_REF main
)

vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
    FEATURES
        tests   MY_AWESOME_LIB_BUILD_TESTS
)

vcpkg_cmake_configure(
    SOURCE_PATH "${SOURCE_PATH}"
    OPTIONS
        ${FEATURE_OPTIONS}
        -DBUILD_SHARED_LIBS=OFF
)

vcpkg_cmake_install()
vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/my-awesome-lib)
vcpkg_copy_pdbs()
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")

file(INSTALL "${SOURCE_PATH}/LICENSE" 
     DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" 
     RENAME copyright)

注意最后一步vcpkg_cmake_config_fixup:它会修正你安装在lib/cmake/下的CMake导出配置文件,确保路径在消费者端可用。这正是我们在6.2节中费心思配置install(EXPORT ...)configure_package_config_file的价值所在——vcpkg直接复用了这些成果。

Conan recipe编写基础

如果说vcpkg是”国有大药房”(统一配方、严格审核),那么Conan就是”灵活的食材供应商”。它基于Python编写食谱(recipe),支持私有仓库(如JFrog Artifactory),并且能管理预编译二进制包,避免每次都在用户机器上重新编译。

conanfile.py的核心结构

作为库作者,你需要在源码仓库根目录提供一个conanfile.py。以下是一个适配Conan 2.x的现代recipe:

from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps

class MyAwesomeLibConan(ConanFile):
    name = "my-awesome-lib"
    version = "1.2.3"
    
    # 包的基本元数据
    license = "MIT"
    author = "Your Name"
    url = "https://github.com/yourname/my-awesome-lib"
    description = "A modern C++ utility library"
    
    # 支持的设置:操作系统、编译器、构建类型、架构
    settings = "os", "compiler", "build_type", "arch"
    options = {"shared": [True, False], "fPIC": [True, False]}
    default_options = {"shared": False, "fPIC": True}
    
    # 需要打包的源文件
    exports_sources = "CMakeLists.txt", "src/*", "include/*", "cmake/*"

与CMake的深度集成

Conan 2.x推荐使用CMakeToolchainCMakeDeps生成器,它们会产生原生的CMake配置文件,与你的构建系统无缝协作。你需要在recipe中实现几个关键生命周期方法:

    def requirements(self):
        # 声明依赖
        self.requires("fmt/9.1.0")
        self.requires("spdlog/1.12.0")

    def layout(self):
        # 定义构建目录布局
        cmake_layout(self)

    def generate(self):
        # 为CMake生成依赖信息和工具链文件
        deps = CMakeDeps(self)
        deps.generate()
        tc = CMakeToolchain(self)
        tc.generate()

    def build(self):
        # 调用CMake构建
        cmake = CMake(self)
        cmake.configure()
        cmake.build()

    def package(self):
        # 调用CMake安装(复用你在6.1节写的install规则!)
        cmake = CMake(self)
        cmake.install()

    def package_info(self):
        # 向消费者暴露库信息
        self.cpp_info.libs = ["my_awesome_lib"]
        self.cpp_info.set_property("cmake_file_name", "MyAwesomeLib")
        self.cpp_info.set_property("cmake_target_name", "MyAwesomeLib::MyAwesomeLib")

看到cmake.install()了吗?这正是我们在6.1节中配置的install(TARGETS ...)6.2节中的导出配置在发挥作用。Conan并不替代CMake的安装流程,而是包装和自动化它。写好CMake的install规则,你的Conan发布工作就完成了一大半。

发布与消费

编写好recipe后,你可以在本地测试:conan create .。验证无误后,通过conan upload将包推送到ConanCenter或私有Artifactory仓库。消费者只需在conanfile.txt中写下:

[requires]
my-awesome-lib/1.2.3

[generators]
CMakeDeps
CMakeToolchain

然后运行conan install,Conan会自动下载库并生成CMake能识别的配置文件,消费者在CMakeLists.txt中直接用find_package(MyAwesomeLib CONFIG REQUIRED)即可。

Hunter包管理集成

Hunter是一个完全基于CMake的包管理方案,它的最大特点是零外部工具依赖——用户不需要安装Python或额外的包管理器客户端,只要CMake本身即可。对于库作者而言,Hunter的发布模型与其他两者截然不同:你通常需要向Hunter官方仓库提交一个Pull Request,添加cmake/projects/<YourProject>/hunter.cmake文件。

hunter_add_package的使用方式

虽然Hunter的”上架”流程偏中心化,但理解消费者如何使用它,有助于你设计CMakeLists.txt的兼容性。典型的消费者代码如下:

include("cmake/HunterGate.cmake")
HunterGate(
    URL "https://github.com/cpp-pm/hunter/archive/v0.25.3.tar.gz"
    SHA1 "abc123..."
)

cmake_minimum_required(VERSION 3.16)
project(MyApp)

hunter_add_package(my-awesome-lib)
find_package(my-awesome-lib CONFIG REQUIRED)

add_executable(app main.cpp)
target_link_libraries(app PRIVATE my-awesome-lib::my-awesome-lib)

hunter_add_package会在配置阶段自动下载并构建my-awesome-lib及其所有传递依赖。作为库作者,你需要确保:

  • 你的项目能被ExternalProject_Add正确构建(Hunter底层使用类似机制);
  • 你正确导出了CMake目标(6.2节的导出配置包),因为Hunter最终也是通过find_package来定位你的库;
  • 你的依赖链清晰,不隐含系统级库,否则会在不同构建环境中出现不一致。

版本控制与缓存机制

Hunter通过HunterGate锁死整个依赖宇宙的版本。你在hunter.cmake中定义的版本号一旦合并到Hunter主仓库,全球消费者都会使用这个固定版本。这种强一致性是Hunter的优势,但也意味着更新版本需要重新提交PR。相比之下,vcpkg允许通过overlay-ports覆盖,Conan则支持灵活的版本范围(version>=),各有各的适用场景。

三种方案的选择建议

作为CMake项目的作者,你应该如何选择发布渠道?以下是简单的决策参考:

  • 选择vcpkg:如果你的项目面向Windows/MSVC用户较多,或者团队希望使用微软官方严格审核的库生态。vcpkg的”端口”机制对CMake原生支持极好,且Visual Studio能直接识别vcpkg安装的库。
  • 选择Conan:如果你需要支持私有仓库、管理大量预编译二进制包,或者团队已经在使用JFrog Artifactory。Conan的灵活性最高,适合企业级复杂依赖图谱。
  • 选择Hunter:如果你希望消费者端完全不需要安装额外工具(连vcpkg客户端都不需要),且能接受中心化仓库的更新节奏。Hunter非常适合纯CMake生态的嵌入式或工具链项目。

当然,三者并非互斥。许多优秀的CMake库(如fmt、spdlog)会同时维护vcpkg端口和Conan recipe,让不同偏好的用户各取所需。作为作者,最核心的投入仍然是:写好CMake的安装规则导出配置6.16.2节的内容),因为这才是所有包管理器复用的基石。

小结

本节我们把CMake项目从”工地交付”推进到了”正式上市”的阶段。我们学习了:

  1. vcpkgvcpkg.jsonportfile.cmake如何封装CMake构建流程;
  2. Conanconanfile.py如何通过CMakeToolchainCMake辅助类复用CMake安装规则;
  3. Hunterhunter_add_package机制及其对CMake导出配置的依赖。

至此,第六章”安装、打包与发布”已全部完结。从下一章开始,我们将进入测试与质量保障的领域,学习如何让CMake这位”施工队长”兼任”质检总监”,通过CTest和各类静态分析工具为你的代码保驾护航。

请登录后发表评论

    没有回复内容

正在唤醒异次元光景……