返回 2026-04-13
🛠 工具 / 开源

通用软件包规范(非跨平台格式)Common Package Specification

nesbitt.io·2026-04-13

Nesbitt 提出的 Common Package Specification 并非如其名称暗示的跨平台解决方案,而是一个专注于单一生态系统内软件分发的新标准。该规范旨在统一包管理行为,解决现有工具碎片化问题,但目前仍处于概念阶段,未获广泛支持。

Andrew Nesbitt

去年发布的 CMake 4.3 中,通用包规范(Common Package Specification)正式成为稳定版。这个名字引起了我的注意,因为它听起来似乎旨在解决我以前写过的跨生态系统依赖问题。阅读该规范后发现,这里的“通用”原来是指跨构建系统的通用,而不是跨语言生态系统的通用:它是一种 JSON 格式,CMake、Meson 和 autotools 都能读取,以查明已安装库的存放位置及其链接方式,从而取代目前承担该职责的 .pc 文件和 *Config.cmake 脚本的混合体。

该模式中包含了大量的头文件包含路径(include paths)、预处理器定义(preprocessor defines)、链接标志(link flags)、组件类型(如动态库 dylib、静态库 archive 以及用于仅头文件库的 interface),以及特性字符串(如 c++11 和 gnu)。考虑到它是由 Kitware 和 C++ 工具研究组推出的,并且其推动者正是那些开发大型 C++ 应用程序的人(他们早已厌倦了每种构建系统都以互不兼容的方式来描述同一个已安装的库),这一切就显得合情合理了。

Conan 已经能够为 ConanCenter 中的所有包生成 CPS 文件,而 CMake 的 find_package() 会读取这些文件,并带有对旧格式的回退(fallback)机制。因此,通过该工具链构建的库将开始在安装前缀(install prefixes)目录中留下 .cps 文件,无论 C++ 领域之外的人是否注意到这一点。每个文件都是已安装二进制文件的一条小型结构化记录:记录了其在磁盘上的位置、版本、依赖的其他组件,以及为之构建的目标平台。

对于像 Vlad 和我一直在研究的二进制依赖追踪这类工作来说,这是一个非常有用的数据源,可以与我们原本就要提取的符号表配合使用。特别是其中的 version(版本)字段,这是你无法从 nm 的输出中可靠还原的信息,目前只能通过文件名或发行版的包数据库进行猜测。

更契合的应用场景是语言包管理器中的原生扩展构建。Ruby 的 mkmf 内置了 pkg_config(),而用纯 Ruby 重新实现该格式的 pkg-config gem 下载量高达数千万次;同时,node-gyp 用户在安装时,会通过 binding.gyp 的 action 块调用外部的 pkg-config 来查找头文件和库。这些做法正是 CPS 旨在取代的。虽然为 mkmf 编写一个 CPS 读取器只需要一小段代码,但是 gems 实际构建所依赖的库(如 libxml2、libpq、libsqlite3、openssl)发布的都是 .pc 文件,因为 pkg-config 自 2000 年就已经存在;它们目前还不提供 .cps 文件,因为除了 CMake 之外,几乎没有其他工具会生成这种文件。

目前有一个开放提案,建议使用 purl 标识符添加一个 package_url 字段,这样 CPS 文件就能记录它来源于哪个 conan、vcpkg 或发行版包。这将打通构建系统领域的描述格式与其他领域已经普遍采用的标识符方案之间的闭环。

Python 一直在独立推进相关问题的解决,例如 PEP 770 在 wheel 包内的 .dist-info/sboms/ 目录下预留空间,用于存放描述捆绑库的 CycloneDX 或 SPDX 文档;而 auditwheel 也已经实现了该功能,它在修复阶段查询 dpkg、rpm 或 apk,以查明每个被嫁接(grafted)的 .so 文件来源于哪个系统包,随后将结果写为 purls。CPS 在这里帮不上忙。wheel 包的使用者从不编译任何内容,因此他们需要的是所捆绑组件的来源(provenance)信息,而 Python 正确地采用了 SBOM 格式。我特意下载了 numpy 2.2.6 的 wheel 包来检查,发现其中仍然没有包含 SBOM,尽管该规范在一年前就已获批准,这主要反映出重建整个生态系统的长尾周期有多么漫长;这也是为什么即使在元数据标准已经落地的情况下,事后从二进制文件中重建这些数据依然具有价值的原因之一。

PEP 725 在 pyproject.toml 中声明了 dep:generic/openssl 风格的依赖要求,以便构建工具在启动前了解需要准备哪些环境。它使用了一种派生自 purl 的方案,尽管涵盖的领域是 pkg-config 用户所熟悉的,但该方案同样与 CPS 毫无关联。

这些工作彼此之间并没有太多相互参考,这与你的预期大致相符,因为 C 语言依赖问题正由受其冲击最大的社区以各个击破的方式逐步解决。但好在这些零散的方案现在至少都在使用兼容的标识符,因此,一个包含 purl 的 CPS 文件是可以一路追踪到 PEP 770 的 SBOM 条目的,哪怕并没有人事先规划过让它们能够这样协同运作。

需要完整排版与评论请前往来源站点阅读。