ABI 版本控制
C++ 驱动程序使用 Ulrich Drepper 的关于 定义稳定性 的指南、Linux 共享库 "soname" 规范以及 CMake 的 SOVERSION
目标属性进行 ABI 版本管理。SOVERSION
目标属性用于表示 ABI 版本。
因此,稳定性的定义应使用 已记录 的接口作为基础。接口的合法使用不应受实现更改的影响;以未定义的方式使用接口将使保修无效。使用完全未记录的内部符号也是如此。这些符号根本不应使用。
根据 Linux 文档项目:
每个共享库都有一个特殊名称,称为 "soname"。soname 以 "lib" 为前缀,后跟库的名称," .so" 短语,然后是一个点和一个版本号,每当接口更改时(作为一个特殊例外,最低级别的 C 库不以 "lib" 开头)。完全限定的 soname 以包含它所在目录的前缀;在一个工作系统中,完全限定的 soname 简单地是共享库 "真实名称" 的符号链接。
根据 CMake 文档:
对于共享库,可以使用
VERSION
和SOVERSION
分别指定构建版本和 ABI 版本。如果平台支持符号链接并且连接器支持 so-name,则在构建或安装时会创建适当的符号链接。如果只指定其中之一,则缺失的假定具有相同的版本号。
为了 ABI 版本管理的目的,我们区分 稳定 ABI 和 不稳定 ABI。
每当稳定 ABI 发生向后不兼容的更改时,都会增加 ABI 版本号。
重要
仅通过ABI版本号传达稳定ABI的稳定性。不稳定ABI的向后(不)兼容更改不通过ABI版本号传达。
注意
ABI版本策略仅适用于共享库。它不适用于静态库。
二进制兼容性
在库的根命名空间内声明了一个ABI命名空间(参见源兼容性),以字母v
开头,后跟整数或_noabi
。
ABI命名空间的示例包括(相对于全局命名空间作用域)
bsoncxx::v_noabi
bsoncxx::v1
bsoncxx::v2
bsoncxx::v99
不稳定ABI命名空间是具有名称v_noabi
的命名空间。任何其他ABI命名空间是稳定ABI命名空间。根命名空间不是ABI命名空间。
稳定ABI是一组由公共API使用的导出符号,并在稳定ABI命名空间中声明,有以下例外
符号或相应的公共API实体明确记录为实验性或尚未准备进行ABI稳定性。
实体未在ABI命名空间内声明。
包括这些例外,所有其他导出符号都视为不稳定ABI的一部分。
只有通过导出宏显式声明的对应API实体的符号才会被导出,由CXX_VISIBILITY_PRESET目标属性控制。此外,声明为inline
的实体,无论是显式(例如,使用inline
)还是隐式(例如,类定义中的成员函数定义、变量/函数模板实例化等),都不会导出,由VISIBILITY_INLINES_HIDDEN目标属性控制。
如果有符号应该被导出或作为稳定ABI的一部分,但目前没有,请提交错误报告。
重要
不支持绕过公共API直接使用导出符号。所有导出符号(稳定或不稳定)都必须通过公共API使用。如果有符号应该被导出或作为稳定ABI的一部分,但目前没有,请提交错误报告。
重要
一个符号只需被公共API(即使间接地)使用,就可以被认为是稳定ABI的一部分。如果一个导出符号在之前的任何版本中都没有被任何公共API实体使用过,那么它不被认为是稳定ABI的一部分(见上述说明)。
重要
稳定ABI符号的行为也是稳定ABI的一部分。这是为了确保具有相同ABI版本号的共享库之间运行时行为的兼容性。行为必须与使用该符号的公共API实体(即使间接地)的文档一致,以确保在具有相同ABI版本号的发布之间,公共API行为没有可观察的变化(在明确记录的范围内)。
注意
一些属于公共API的实体可能不是稳定ABI的一部分(例如,内联函数、内联变量以及函数和变量的模板实例化)。相反,一些属于稳定ABI的实体可能不是公共API的一部分(例如,导出的私有成员函数)。参见API版本控制。
构建系统兼容性
关于构建系统属性的稳定ABI策略主要与公共API策略相同(见API构建系统兼容性),以下有一些不同
与直接影响公共API的属性不同,直接影响稳定ABI的属性被视为稳定ABI的一部分。
soname被视为稳定ABI的一部分。这意味着,与公共API相反,
BSONCXX_OUTPUT_NAME
被视为稳定ABI的一部分,因为它直接影响了生成的bsoncxx共享库的soname。在Windows上,“soname”不适用。请参阅共享库(仅限MSVC)。
注意
我们支持每个构建配置的稳定API的稳定性。我们不支持跨不同构建配置的稳定API的兼容性。例如,使用BSONCXX_OUTPUT_NAME=bsoncxx
生成的共享库与使用构建配置BSONCXX_OUTPUT_NAME=bsoncxx-custom-basename
产生的稳定ABI编译的程序不兼容。(这在使用Visual Studio等多配置生成器时尤其重要!)
根命名空间重声明
根命名空间提供了对ABI命名空间中声明的实体的“重声明”。
例如,以下所有重声明可能同时存在
bsoncxx::example::foo
-->bsoncxx::v_noabi::example::foo
bsoncxx::example::bar
-->bsoncxx::v1::example::bar
bsoncxx::example::baz
-->bsoncxx::v2::example::baz
根命名空间重声明设计用于允许在 vB
中添加不兼容的二进制符号,同时不破坏 vA
符号的二进制兼容性。它们便于稳定 ABI 符号的弃用,同时提供从 vA
到 vB
清晰过渡的机会,而不会破坏二进制兼容性。它们允许用户选择参与“重声明升级”,以减少从 vA
到 vB
过渡时对源代码的更改。
这些重声明升级旨在支持从弃用、即将删除的 ABI 符号到发布之间的清晰过渡。因此,鼓励用户默认使用根命名空间声明来选择这些升级。然而,根据他们自己的稳定性策略,用户可能需要在引用自己的稳定 ABI 中的 C++ 驱动程序实体时使用显式的 ABI 命名空间限定符。
以下兼容性表描述了由于不兼容更改何时必须增加 API 主版本号或 ABI 版本号
[1] | vB 实体可能完全与 vA API 兼容,尽管需要使用新的、不兼容的稳定 ABI 符号。 |
[2] | (1, 2) 可以从公共 API 中删除稳定 ABI 符号(例如,通过文档或从公共头文件中删除),同时仍然提供向后兼容的导出符号定义。这种情况可能不太可能发生,但如果确实发生,那么它将是一个罕见的情况,其中源代码和不兼容的更改可以分开发布。 |
重要
ABI 命名空间使用的整数不直接对应于 ABI 版本号。每当发生任何二进制不兼容的更改时,都会增加 ABI 版本号,即使是从稳定 ABI 中删除单个符号。从 A
到 B
的 ABI 版本号增加并不暗示弃用或删除 ABI 命名空间 vA
中声明的符号(如果存在)。
弃用和删除
稳定 ABI 中符号的弃用和删除策略与公共 API 相同。有关更多信息,请参阅API 弃用和删除。
包含二进制不兼容更改的版本将增加ABI版本号,而不是API主版本号。然而,当ABI版本号增加时,API主版本号也可能增加,因为二进制不兼容更改很可能也是源代码不兼容更改。