RFC-0239:平台版本控制实际应用

RFC-0239:平台版本控制实际应用
状态已接受
领域
  • 常规
  • 治理
说明

Fuchsia 上兼容性的概念模型和保证。

Gerrit 更改
作者
审核人
提交日期(年-月-日)2023-11-08
审核日期(年-月-日)2024-01-30

摘要

此 RFC 旨在通过以下方式在 RFC-0002RFC-0083 的基础上进行扩展:

  • 构建了一个概念模型,说明如何在 Fuchsia 上实际使用 API 级别和 ABI 修订版本,
  • 向我们的合作伙伴提供 API 和 ABI 兼容性保证(在概念模型方面进行说明),以及
  • 概述了我们计划如何实现这些兼容性保证。

简要总结:

  • 每个 Fuchsia 里程碑都有对应的 API 级别。API 级别 15 可以视为“第 15 版 Fuchsia 平台 surface,最初由 F15 提供支持”。
  • 每个 Fuchsia 版本都支持多个 API 级别。例如,F15 里程碑版本支持 API 级别 11、12、13、14 和 15。
  • 最终开发者在构建组件时只会选择一个目标 API 级别。以 API 级别 13 为目标的组件看到的 Fuchsia 平台 surface 与 API 级别 13 中一样,并将在任何支持 API 级别 13 的 Fuchsia 系统上运行。
  • Fuchsia 内核和平台组件通过同时实现多个 API 级别来支持这些级别。

设计初衷

RFC-0227 中所述,Fuchsia 平台版本的主要组件包括:

  • 集成商开发套件 (IDK):这是构建和运行面向 Fuchsia 的组件所需的一小组库、软件包和工具;
  • 操作系统 (OS) 二进制文件,包括配置和组建 Fuchsia 产品软件包所需的内核、引导加载程序、软件包、工具以及其他材料。

IDK 是 Fuchsia SDK 的与构建系统无关的前体,用于描述构成 Fuchsia 平台 surface 的 API 元素(例如协议、方法、字段、参数、库),并允许最终开发者编写使用这些 API 元素的组件。IDK 中的一些接口由操作系统二进制文件(如内核和平台组件)实现,而其他接口则由外部组件(如应用或驱动程序)实现,并且操作系统二进制文件会调用它们。

Fuchsia 平台界面正在不断开发中,操作系统二进制文件也会不断更新以保持一致。这些更改会迅速传达给最终开发者和产品所有者。1在引入 API 级别之前,IDK 仅包含一份对平台 Surface 的说明。星期一构建的 IDK 所描述的 Fuchsia 平台版本与当周星期五晚些时候构建的操作系统实现的版本不同。这常常会引发问题:如果 FIDL 方法及其实现在星期三被删除,那么使用星期一的 IDK 构建的外部组件可能会尝试调用该方法,但只有星期五的操作系统告知该方法不存在。

我们可以单纯地尝试禁止 IDK 和操作系统版本之间不匹配。我们可以说,操作系统将仅运行使用完全相同的 Fuchsia 版本的 IDK 构建的组件(也就是说,操作系统版本 2.20210213.2.5 只会运行使用 IDK 版本 2.20210213.2.5 构建的组件)。但是,这是不可行的,我们不能要求最终开发者在我们发布新版本的 Fuchsia 时不断更新和重新构建其组件。因此,一般来说,构建外部组件的 IDK 以及运行这些组件的操作系统可能来自不同的 Fuchsia 版本,甚至可能来自不同的里程碑。

这会引发一个问题:在什么情况下,使用一个版本的 IDK 构建的组件能够在另一个版本的操作系统上成功运行?

RFC-0002 引入了 API 级别和 ABI 修订版本作为帮助解决这一问题的工具,但对实际中应如何使用它们并不是非常具体,也没有提供具体的兼容性保证。本 RFC 展示了在实践中如何使用 API 级别和 ABI 修订版本、制定兼容性保证政策,并简要讨论遵守该政策的策略,以此来弥补这些缺口。

利益相关方

教员:jamesr@google.com

审核者

  • abarth@google.com
  • ianloic@google.com
  • mkember@google.com
  • yaar@google.com

咨询人员

  • chaselatta@google.com
  • ddorwin@google.com
  • sethladd@google.com

社交

此 RFC 中的模型和政策是在过去几年逐步构建的,但迄今为止尚未在任何 RFC 中进行编码。这一特定提案是通过许多文档和对话提出的,主要发生在平台版本控制工作组的成员之间。

假设

在描述公开 API 的组件与使用该 API 的其他组件之间的兼容性时,我们假定交互的一端是树外(即在 Fuchsia 平台 build 之外,通过 IDK)构建的外部组件,另一端是操作系统二进制文件,例如该内核,或 Fuchsia 平台组件在 Fuchsia 平台树内构建的 GND 平台组件。为外部组件之间的交互定义版本控制做法和兼容性不在讨论范围内,有待后续工作。

设计

本部分将介绍 Fuchsia 平台版本控制背后的概念模型。本文介绍的许多概念之前都是在 RFC-0002 中引入的,但本文介绍了更多详细信息和限制。

此“设计”部分的文字应具有参考性,而非规范性。下面的政策变更部分将重复本文中代表现有政策变更的任何部分。

API 级别

Fuchsia 平台界面在不断变化,并且新的 Fuchsia 版本也在不断创建 - 一天多次。我们会考虑每个 Fuchsia 版本与有限数量的 API 级别的兼容性,而不是推断每个 Fuchsia 版本与每个 Fuchsia 平台 Surface 的兼容性(这会让人感到无所适从)。

您可以将 API 级别视为构成 Fuchsia 平台 surface 的 API 的“版本”“版本”或“快照”。API 级别 10 是“第 10 版 Fuchsia API Surface”。API 级别与里程碑同步,因此 API 级别 10 也可视为“F10 发布时最新版本的 Fuchsia 平台 surface”。

您可以使用 API 级别发出如下语句:“fuchsia.lightsensor.LightSensorData FIDL 表在 API 级别 10 中有三个字段(rgbccalculated_luxcorrelated_color_temperature),但 API 级别 11 中又添加了两个字段(si_rgbcis_calibrated)。自 API 级别 11 起,它有五个字段,但我们可能会在 API 级别 16 中移除一些字段或添加更多字段。”

与教科书的各个版本一样,API 级别也是不可变的。教科书的内容可能会随时间而变化,但在教科书第二版发布之后,其内容就不会再改变。这意味着,了解给定 API 级别的任何两个 Fuchsia 版本都将同意构成该 API 级别的 API 元素集。

Fuchsia 平台界面不限于 FIDL,还包含系统调用、C++ 库及其中的方法、永久性文件格式等。从概念上讲,这些版本均按 API 级别进行版本控制,但实际上,对 FIDL 库的版本支持比对 Fuchsia 平台 surface 的其他方面支持更远。

以 API 级别为目标

最终开发者在构建组件时只会选择一个目标 API 级别。如果开发者以 API 级别 11 为目标平台,其组件将能访问在 API 级别 11 中提供的确切 API 元素。如果某个方法是在 API 级别 12 中添加的,该组件将不会在其生成的绑定中看到该方法。相反,如果 API 级别 11 中的某个方法已在 API 级别 12 中移除,组件将能够继续使用该方法。

在构建组件时,IDK 中的工具会将目标 API 级别关联的 ABI 修订版本嵌入到组件的软件包中。这通常称为组件的“ABI 修订版本标志”。

ABI 修订戳记让我们可以保证组件观察到的运行时行为与其目标 API 级别指定的行为相匹配。目前, ABI 修订版本标记使用得非常粗略:如果操作系统支持该 ABI 修订版本的对应 API 级别,则允许运行该组件。否则(例如,如果外部组件以 API 级别 11 为目标,但操作系统二进制文件不再实现 API 级别 11 中的方法),则组件管理器将阻止其启动。

版本和阶段

每个 Fuchsia 版本支持多个 API 级别,并为每个 API 级别分配一个阶段。如需查找 Fuchsia 版本中的 API 级别,请下载其 IDK 并查看 version_history.json 文件。

例如,假设的 Fuchsia 版本 20.20240203.2.1 可能包含:

  • 支持阶段的 API 级别 17、18 和 19。这意味着 IDK 版本 20.20240203.2.1 可以构建以其中任何 API 级别为目标平台的组件,Fuchsia 版本 20.20240203.2.1 可以运行以其中任何 API 级别为目标平台的组件。大多数情况下,大多数最终开发者都应以受支持的 API 级别为目标。
  • 停用阶段的 API 级别为 15 和 16。Fuchsia 仍会run面向弃用 API 级别的组件,但 IDK 将不再支持在构建组件时以这些级别为目标平台。

此假设的 version_history.json 还会列出“已停用”阶段的 API 级别 14 及更早级别。Fuchsia 不会构建或运行以已弃用的 API 级别为目标平台的组件,但会保留这些组件,以便日后使用。

API 级别在受支持的阶段(相应里程碑分支被弃用)之前不久发布。上述假设的 20.20240203.2.1 Canary 版本是在 API 级别 20 发布之前发布的,因此明显没有 API 级别 20。在 F20 分支被弃用之前不久,我们将发布 API 级别 20,而后续版本(例如,20.20240301.4.1)会将 API 级别 20 列为“受支持”。特别是,所有里程碑版本都会在支持的阶段包含其相应的 API 级别。

当我们选择停止对某个 API 级别的支持时,会首先将其移至弃用阶段。这样会形成一种后退环境 - 以该 API 级别为目标平台的现有二进制文件会继续运行,但不会创建以该 API 级别为目标平台的新二进制文件(至少,在 IDK 的新版本中也不会创建)。逐步淘汰以给定 API 级别为目标平台的所有现有二进制文件后,我们可以将其停用。

API 级别不会按照任何特定的时间表弃用或停用。相反,一旦合作伙伴不再基于某个 API 级别进行构建,我们就会停用该 API 级别;一旦合作伙伴不再希望运行以该 API 级别为目标平台的二进制文件,我们就会将其停用。总有一天,随着合作伙伴数量的增长,我们对他们个人需求的了解也越来越少,因此需要制定一个更正式的政策,但目前还是会这样。

NEXTHEAD 伪 API 级别

除了上面讨论的 API 级别之外,还有两个特殊的可变伪 API 级别:NEXTHEAD

与其他 API 级别不同,NEXTHEAD 的内容在不同版本之间可以任意更改。可以添加、修改、替换、废弃和/或移除 API 元素。原则上,20.20240203.1.1 中的 NEXT 看起来可能与 20.20240203.2.1 中的 NEXT 完全不同,但实际上,更改是增量更改。

RFC-0083 中所述,HEAD 代表开发的最前沿,适合供没有稳定性要求的树内客户端(例如其他平台组件或集成测试)使用。HEAD 中引入的 API 元素甚至可能无法正常运行;例如,某个方法可能已被添加到位于 HEAD 的协议中,但协议服务器可能尚未实现该方法。

NEXT 表示下一个编号 API 级别的“草稿”版本。在每个里程碑分支被剪切之前,我们会将 NEXT 的内容发布为新的 API 级别。为此,我们会创建一个更改列表,将 FIDL 注解和 C 预处理器宏中的 NEXT 实例替换为下一个里程碑分支的具体数量。NEXT 中的 API 元素应可正常工作,并且可在下一个 API 级别发布之前更改,尽管可能仍会进行更改。

在 RFC-0083 的语言中,HEADNEXT 更新,这意味着 NEXT 中的所有 API 更改也包含在 HEAD 中。

最终开发者可能会以 HEADNEXT 为目标平台,但这样做会使 APIABI 兼容性保证失效。如需详细了解从树外定位 NEXTHEAD 的影响,请参阅介绍这些保证的部分。

政策变更

本部分列出了此 RFC 建议的具体政策变更。

模型更改

此 RFC 对平台版本控制模型进行了以下更改:

  • 编号 API 级别现在不可变。在实践中,除了特殊的“开发中”API 级别之外,所有 API 级别都已启用此属性。此 RFC 有效地废弃了“开发中”API 级别,取而代之的是 NEXT
  • 引入了 API 级别阶段。在实践中已经存在“受支持”和“已弃用”,但后者目前称为“不支持”。此 RFC 建议对其进行重命名,并添加“sunset”。
  • 引入了 NEXT这是另一个特殊的 API 级别,例如 HEADHEAD “晚于”NEXT。这取代了将最新 API 级别指定为“开发中”API 级别的当前做法。
  • 允许对 NEXT 进行向后不兼容的更改。当前政策(由工具强制执行,但未在任何 RFC 中确立)是只能对开发中的 API 级别进行向后兼容的更改。 此 RFC 移除了该限制,不会将其应用于 NEXT如需了解更多讨论,请参阅下文。
  • 将 API 级别与里程碑相结合。系统将根据 NEXT 的内容,在 FN 里程碑分支切入前不久创建 API 级别 N。这种做法在实践中是正确的,但尚未在任何 RFC 中得到批准。
  • 在合作伙伴允许的最大时间内,API 级别会逐步进入阶段。一旦我们的合作伙伴不需要针对某个 API 级别构建针对该级别的二进制文件,我们便会立即停用该级别支持的 API 级别。我们即将停用已弃用的 API 级别,因为我们的所有合作伙伴都不想在新的 Fuchsia 版本上运行以该级别为目标平台的任何二进制文件。

API 兼容性保证

Fuchsia 现在为编号 API 级别提供以下 API(即构建时)兼容性保证(遵守以下注意事项):

如果最终开发者可以使用 IDK 的某个版本成功构建以编号 API 级别 N 为目标平台的组件,他们将能够在受支持的阶段使用 N 的任意版本成功构建相同的源代码。

换句话说,最终开发者可以放心地升级或回滚其 IDK,而不会破坏其 build,直到他们选择的目标 API 级别被弃用。如果在不更改其目标 API 级别的情况下更新到其他 IDK 会导致其构建中断(例如,如果他们使用的 API 元素不再存在),这将被视为 Fuchsia 平台 bug。

另一方面,可变伪 API 级别 NEXTHEAD 提供 API 兼容性保证。以 NEXTHEAD 为目标平台时,最终开发者可能会发现更新其 IDK 会导致其代码无法再编译。

实际上,我们当前的合作伙伴与 Fuchsia 团队密切合作,我们将“尽最大努力”避免破坏以 NEXT 为目标平台的最终开发者的 build。我们将几乎不做任何努力,以免破坏最终开发者以 HEAD 为目标的 build。

ABI 兼容性保证

Fuchsia 现在可为编号 API 级别提供以下 ABI(即运行时)兼容性保证(须遵守以下注意事项):

以编号 API 级别 N 为目标平台的组件可以在支持或弃用阶段使用 N 的任何 Fuchsia 版本成功运行,并且在各个版本之间不会出现任何不良行为变更

换言之,在所选目标 API 级别停用之前,最终开发者无需更改或重新编译其代码即可在较新版本的 Fuchsia 上运行。如果平台在其他版本的 Fuchsia 上表现出不同的行为方式,干扰其组件的功能,则会被视为 Fuchsia 平台 bug。

另一方面,对于以可变伪 API 级别 NEXTHEAD 为目标平台的组件,无法提供 ABI 兼容性保证。当且仅当运行该组件的 Fuchsia 版本与构建该组件的 IDK 版本完全匹配时,以 NEXTHEAD 构建的组件才允许运行。例如,使用 IDK 版本 16.20231103.1.1 构建的以 NEXT 为目标平台的组件将在 Fuchsia 版本 16.20231103.1.1 上运行,但不会默认情况下)在版本为 16.20231103.2.1 的设备上运行。

在大多数情况下,确保用于构建组件的 IDK 的版本与其运行组件的操作系统版本完全匹配并不可行。值得注意的例外情况是集成测试和本地实验。树外代码库通常可以控制它们用于编译的 IDK 版本以及用于测试的操作系统版本。如果用户希望能够测试 NEXTHEAD 中的功能,则应使两者保持同步。

禁止使用不受支持的配置

默认情况下,如果组件的 ABI 修订版本标记表示这些组件不在 ABI 兼容性保证范围内,则 component_manager 会拒绝启动这些组件。具体而言:

  • 如果某个 Fuchsia 版本支持或停止提供 N,则该组件只会运行以 API 级别 N 为目标平台的组件。
  • 给定的 Fuchsia 版本将仅运行以 NEXTHEAD 为目标平台的组件,前提是该组件是使用完全相同版本的 IDK 构建的。

同样,产品组装也会拒绝将与平台不兼容的组件组装成产品。

产品所有者可以选择性地停用这些检查。例如,他们可以将单个组件列入许可名单,使其以 NEXTHEAD 为目标平台,即使构建该组件的 IDK 来自其他 Fuchsia 版本也是如此。产品所有者停用这些检查将自行承担风险。

实现

实现这些检查需要对 ABI 修订版本标记进行一些更改。这些更改的完整设计超出了此 RFC 的范围,但简而言之:

  • 与目前的情况一样,每个 API 级别在发布后都会获得一个唯一的 ABI 修订版本。以编号 API 级别为目标平台的组件将标注该 API 级别的相应 ABI 修订版本。
  • 此外,每个版本都将分配一个唯一的 ABI 修订版本。以 HEADNEXT 为目标的组件将使用此基于版本的 ABI 修订版本进行标记。

给定版本中的操作系统二进制文件将列出受支持或弃用阶段中所有 API 级别的 ABI 修订版本,以及该版本的 ABI 修订版本。默认情况下,只有标记有其中一个 ABI 修订版本的组件才能运行。

未来扩展程序

请注意,“每个 API 级别一个 ABI 修订版本,NEXT/HEAD 再添加一个 ABI”排列方式不一定是此设计中的最后一个字。ABI 修订版本是我们以机器可读的方式对 ABI 兼容性保证进行编码的方式,因此,如果保证变得更加精细,则 ABI 修订版本的分配也必须如此。

例如,假设我们将部分 API Surface 指定为具有“长期支持 (LTS)”,并针对某个 API 级别中的 LTS API 提供比其他 API 更长的继续支持时间。假设的 F30 版本可能支持 API 级别 25 的 LTS API,但不支持非 LTS API。该版本将能够运行仅使用 LTS API 以 API 级别 25 为目标的组件,但无法运行以 API 级别 25 为目标平台且使用了非 LTS API 的组件。因此,这两种组件需要使用不同的 ABI 修订版本进行标记,为此,我们可以通过为每种(API 级别、LTS 状态)对指定各自的 ABI 修订版本来实现此目的。

我们围绕这些思路有想法,如果我们选择实现这些想法,则会在后续的 RFC 中描述。

平台组件同时支持所有支持的 API 级别

由于我们目前还没有平台组件可以根据连接另一端的组件的 ABI 修订版本标记更改其行为的机制,因此支持多个 API 级别意味着同时支持多个 API 级别。平台组件的行为必须满足支持或停用阶段中每个 API 级别的规范。

更确切地说,平台组件不得对外部组件的目标 API 级别做出任何假设,除非它以某个受支持的 API 级别或某个已弃用的 API 级别为目标平台。

举个简单的例子,如果方法 Foo 已从 API 级别 15 的 FIDL 协议中移除(即 Foo 带有 @available(added=A, removed=15) 注解),但 API 级别 14 仍受支持,实现相关协议服务器的平台组件必须继续实现 Foo,直到所有包含该方法的 API 级别均弃用。

相反,如果该 FIDL 协议服务器由树外驱动程序实现,则与该驱动程序通信的平台组件必须可以正常工作,无论该驱动程序是否实现了 Foo

此策略不仅限制了平台组件的实现,还限制了我们可以对平台界面进行的各种修改。从理论上讲,API 可能会从 API 级别任意更改为 API 级别,但实际上,有些变更(例如,重复使用 FIDL 协议方法序数)会导致我们无法在没有更多信息的情况下与针对不同 API 级别的外部组件正确通信。

我们可能会制定不同的策略来支持多个 API 级别,但这属于未来工作的领域。

注意事项

API 和 ABI 兼容性保证需要注意一些事项。

“保证”的含义

“保证”兼容性并不意味着 Fuchsia 永远不会违反我们的兼容性承诺,人为本。这意味着,当我们根据上述条款破坏兼容性时,我们会将 bug 视为 bug。我们将负责在内部采取纠正措施,除了报告 bug 之外,最终开发者无需采取任何行动。

使担保作废

如果最终开发者“执行了异常操作”,API 和 ABI 保证不适用。其中包括但不限于:

  • 在 IDK 副本中修改文件
  • 访问内部命名空间中的成员
  • 手动合成打算由 IDK 中的库构建的数据,
  • 将由不同 IDK 构建的共享库链接到单个二进制文件;
  • 等等

一个值得注意的反例,依赖 Fuchsia 中有 bug 的行为并不计为“做奇怪的事情”。在某些不幸的情况下,fixing bug 可能会被视为“不良行为变更”。有关更多讨论,请参阅不良更改

禁止行为的完整说明不在此 RFC 的讨论范围内。用美国最高法院法官波特·斯图尔特在雅各贝利斯诉俄亥俄州中所说的话:“我今天不应试图进一步定义 [‘采取奇怪行为’],或许我永远无法明智地做出这种行为。不过,当我看到时我就知道了。”

不良更改

ABI 兼容性保证可承诺在支持组件目标 API 级别的 Fuchsia 版本之间,组件不会看到“不良行为变更”。“不良”的定义显然是主观的,让某些用户满意的更改可能对其他人不利(相关 xkcd)。最终,不良行为变更报告需要根据具体情况进行处理。

安全和隐私设置

如果事后回溯发现 Fuchsia 平台表面的某些部分存在重大安全或隐私问题,我们可能会根据需要选择性地停止对违规功能的支持,以满足我们的安全和隐私保护目标。

性能

保持与旧版 API 级别的向后兼容性会降低性能。如果没有其他问题,向后兼容的平台二进制文件必须更大,因为将保留更多逻辑。在某种程度上,这是不可避免的 - 但是,API 级别以原子方式停用这一事实可能会加剧此问题。

向后兼容性

此 RFC 的全部内容都涉及后向和向前兼容性。

测试

确保 API 和 ABI 兼容性的测试是另一个重要主题。

针对 Fuchsia (CTF) 的兼容性测试旨在提供测试范围,以确认我们满足 ABI 兼容性保证。目前覆盖率较低,但我们正在积极努力。

目前,我们无法编写测试来验证 API 兼容性保证;我们的所有树内测试都是针对 LEGACY API 级别构建的,因此无法重现以编号 API 级别为目标平台的树外客户的体验。这种情况需要解决,但这应该不会妨碍用户接受此 RFC。

文档

此 RFC 获得批准后,其文本将重复使用作为多个面向 Fuchsia 平台开发者的文档的基础:

此外,您还需要更新文档,以反映现有概念的重命名。

缺点、替代方案和问题

缺点:API 级别是原子级别的

RFC-0002 所述,API 级别涵盖整个 Fuchsia 平台表面。因此,对 API 级别阶段的更改(例如,停用 API 级别)会影响整个 API 级别;我们无法只停用部分功能。同样,如果最终开发者将目标 API 级别设为整个 Fuchsia 平台界面,则以该 API 级别为目标平台;例如,他们不能将大多数 API 的目标 API 级别设为 13,而不能将某个特定 API 的目标 API 级别设为 16。

未来对平台版本控制模型的扩展可能会允许进行有意义的细分,但 IDK 中各种库之间的依赖关系使这项工作具有挑战性。

替代方案:无弃用阶段

此 RFC 的早期草稿省略了弃用阶段,留作“潜在的未来工作”。弃用阶段是对模型的一个非常合理的扩展,并且其在讨论中提到的频率足够高,因此在讨论中应主动将其加入,这似乎很合适。

替代方案:其他可能的伪 API 级别

如上所述,HEAD 有多种潜在的用例:树内组件之间的通信、提供给集成测试的 API、实验性 API 等。这些用例实际上应该由不同的伪 API 级别表示吗?

此 RFC 建议仅从 NEXTHEAD 开始,但在分配数值时应在二者之间留出间隙。这样,如果我们找到了其他伪 API 级别的用例,就能添加更多的伪 API 级别。

替代方案:向后兼容的开发中 API 级别

目前,Fuchsia 实际上的版本控制政策表示,只能对开发中的 API 级别进行向后兼容的更改。此 RFC 会回滚该政策,因此,当最终开发者以 NEXT 为目标时,无法保证 IDK 与不同版本的操作系统二进制文件之间的兼容性。

如果我们在开发中的 API 级别(或 NEXT)内遵循向后兼容政策,我们就能做得更好:只要操作系统二进制文件来自比 IDK 更高的版本,我们就可以保证 IDK 和操作系统二进制文件兼容。直到最近,我们将下游代码库迁移到支持的 API 级别,我们一直依赖此属性。

这样做的好处是,一旦将新的 Fuchsia 功能添加到开发中的 API 级别,最终开发者就可以放心地开始依赖该功能,而无需等待该 API 级别发布。

但是,这会大大增加系统的复杂性:

  • Fuchsia 平台开发者不仅必须保持与旧版 API 级别(其 API 签名本身在 FIDL 和头文件中编码的 API 签名)的向后兼容性,还必须保持与开发中 API 级别的先前版本(并非如此)的向后兼容性。查看这些先前版本的唯一方法是查看 git 历史记录(实际上并不会发生这样的情况)。
  • 由于我们需要能够从平台中移除功能,因此需要某个 API 级别支持向后不兼容的更改。这意味着 Fuchsia 平台开发者必须在开发中的 API 级别之后对编号 API 级别进行不向后兼容的更改。因此,实际上有两个正在开发的 API 级别,Fuchsia 平台开发者需要对两者进行推理。
  • 这会使 ABI 修订版本更加复杂。为了支持开发中的 API 级别中向后兼容的更改,我们需要无限期地保留按版本划分的 ABI 修订版本(其中有些修订版本每天会创建)。操作系统二进制文件需要支持每个版本的 ABI 修订版本(其支持的 API 级别之一是处于开发中的 API 级别),以及当前里程碑版本至今的所有 ABI 修订版本。按照我们目前的发布节奏,每个 API 级别可增加约 168 个 ABI 戳记,在撰写本文时,Fuchsia 的一个版本需要支持超过 800 个 ABI 戳记。虽然这种解决方案的内存成本不太可能昂贵,但维护与成百上千或数万个不同 ABI 的兼容性的复杂性目前似乎不值得,尤其是由于我们的客户目前均未使用开发中的 API 级别。

此 RFC 并不排除将来提供额外的兼容性保证的可能性。

替代方案:将 API 级别与里程碑分离

此 RFC 明确将 API 级别与里程碑编号相结合。这并非绝对必要 - 值得注意的是,Android 将两者分离开来。Android 项目会利用此功能发布 API 更改,以及次要版本的操作系统。例如,API 级别 24 对应于 Android 7.0,API 级别 25 对应于 Android 7.1。

在研究中,我没有找到另一个选择采用此路径的平台示例。我找到的其他平台在发布版本方面说明了 API 元素的可用性,例如“自 Chrome 88 起可用”或“在 iOS 6.1 中已弃用”。

macOS 和 iOS API 级别与其版本控制方案相关联,但与 Fuchsia 不同,两者都允许在次要修订版本中进行 API 更改。不过,这似乎在很大程度上是因为他们希望将发行版的主要版本与日历年相结合,同时允许每年多次发布 API 更新。

Fuchsia 的里程碑更贴近 Chrome 的里程碑,因此频繁递增主要版本,而不使用次要版本。如果我们想要更频繁地稳定 API 级别,可以通过更频繁地增加 Fuchsia 的里程碑编号来实现。

替代方案:编号为“开发中”的 API 级别

此 RFC 的早期草稿更接近当前实现的平台版本控制模型。在该模型中,NEXT 不存在,而最大的编号 API 级别是可变的,并称为“开发中”API 级别。

从机制上讲,这两种模型之间只有细微的差异:

  1. 目前,Fuchsia 平台开发者选择了希望启动其功能的特定 API 级别。实现此 RFC 后,他们会对 NEXT 进行生产绑定的 API 更改,这些更改会自动发布到下一个编号 API 级别。
  2. 目前,以开发中的 API 级别为目标平台的最终开发者会在发布后自动转换为以该 API 级别的受支持版本为目标平台。实现此 RFC 后,它们将继续以 NEXT 为目标平台。

这两种差异似乎都不是积极或消极的。

与处于开发阶段的 API 级别相比,NEXT 的主要优势在于易于通信。此 RFC 的早期草稿通过口头体操表达兼容性保证:“如果某个组件以支持的 API 级别 N 为目标构建,则可在受支持阶段支持 API 级别 N 的任何 Fuchsia 版本上成功运行。”在当前方案中,各方将就给定 API 级别的内容达成一致,不需要限定词。

替代方案:不移除内容

我们可以完全禁止向后不兼容性更改,从而简化平台版本控制。每个 API 级别都将支持之前级别的所有功能,并且“平台支持所有 API 级别”属性将“免费”因支持最新 API 级别而提供。

包括 Windows 和 Android 在内的许多平台在很大程度上都采用了此方法,但因此,它们无法清除其 API Surface 的旧版本中的多余的内容。

虽然旧版 API 元素只要出现在受支持的 API 级别或已弃用的 API 级别中,Fuchsia 平台工程师就将继续支持这些元素,但最终开发者只需处理所选目标 API 级别的内容,而不需要看到多余的内容。

此外,虽然“永不移除内容”可能会使版本控制更简单,但这决不能彻底解决问题。最终开发者仍需要根据预期安装量中最旧的 Fuchsia 平台版本来推断目标 API 级别。


  1. 目前,我们的大多数下游合作伙伴都依赖于 Fuchsia 的 Canary 版本,该版本每天生成多次。未来,我们会有更多合作伙伴使用里程碑版本分支中的 IDK 和操作系统二进制文件,但目前,我们非常重视这种安排带来的即时反馈。