| RFC-0239:实践中的平台版本控制 | |
|---|---|
| 状态 | 已接受 |
| 区域 |
|
| 说明 | Fuchsia 上的兼容性概念模型和保证。 |
| Gerrit 更改 | |
| 作者 | |
| 审核人 | |
| 提交日期(年-月-日) | 2023-11-08 |
| 审核日期(年-月-日) | 2024-01-30 |
摘要
此 RFC 的目标是在 RFC-0002、RFC-0083 和其他 RFC 的基础上,通过以下方式进行扩展:
- 概述了 API 级别和 ABI 修订版在 Fuchsia 上的实际使用方式的概念模型,
- 以该概念模型的术语向合作伙伴提供 API 和 ABI 兼容性保证,以及
- 其中概述了我们计划如何满足这些兼容性保证。
简要总结:
- 每个 Fuchsia 里程碑都有一个对应的 API 级别。API 级别 15 可以视为“Fuchsia 平台界面的第 15 版,首次由 F15 支持”。
- 每个 Fuchsia 版本都支持多个 API 级别。例如,F15 里程碑版本支持 API 级别 11、12、13、14 和 15。
- 最终开发者在构建组件时会选择一个目标 API 级别。 以 API 级别 13 为目标平台的组件将看到 Fuchsia 平台表面与 API 级别 13 中的相同,并且将在支持 API 级别 13 的任何 Fuchsia 系统上运行。
- Fuchsia 内核和平台组件通过同时实现多个 API 级别来支持这些级别。
设计初衷
如 RFC-0227 中所述,Fuchsia 平台版本的主要组件包括:
- 集成器开发套件 (IDK),一组用于构建和运行以 Fuchsia 为目标平台的组件所需的小型库、软件包和工具,以及
- 操作系统 (OS) 二进制文件,包括内核、引导加载程序、软件包、工具以及配置和组装 Fuchsia 产品软件包所需的其他要素。
IDK 是 Fuchsia SDK 的构建系统无关的前身,它描述了构成 Fuchsia 平台表面的 API 元素(例如协议、方法、字段、实参、库),并允许最终开发者编写使用这些 API 元素的组件。IDK 中的某些接口由内核和平台组件等操作系统二进制文件实现,而其他接口由外部组件(例如应用或驱动程序)实现,并期望操作系统二进制文件调用它们。
Fuchsia 平台表面在不断开发,操作系统二进制文件也在不断更新以与之匹配。这些更改会快速1 向下游的最终开发者和产品所有者传播。在引入 API 级别之前,IDK 仅包含平台表面的单个说明。周一构建的 IDK 所描述的 Fuchsia 平台表面与当周晚些时候(周五)构建的操作系统所实现的 Fuchsia 平台表面是不同的版本。这曾经导致过问题:如果某个 FIDL 方法及其实现是在周三删除的,那么使用周一的 IDK 构建的外部组件可能会尝试调用该方法,但周五的操作系统会告知该方法不存在。
从表面上看,我们可以尝试禁止 IDK 与操作系统版本不匹配。我们可以说,操作系统只会运行使用完全相同的 Fuchsia 版本(即操作系统版本 2.20210213.2.5 只会运行使用 IDK 版本 2.20210213.2.5 构建的组件)中的 IDK 构建的组件。不过,这不可行,因为我们不能要求最终开发者在每次发布新版 Fuchsia 时都更新并重新构建其组件。因此,一般来说,构建外部组件的 IDK 和运行这些组件的操作系统可能来自不同的 Fuchsia 版本,甚至可能来自不同的里程碑版本。
这就引出了一个问题:在什么情况下,使用一个版本的 IDK 构建的组件能够成功在另一个版本的操作系统上运行?
RFC-0002 引入了 API 级别和 ABI 修订版,以帮助回答该问题,但并未明确说明如何在实践中使用它们,也没有提供具体的兼容性保证。此 RFC 通过以下方式填补了这些空白:展示了 API 级别和 ABI 修订版在实践中的使用方式,确立了兼容性保证政策,并简要讨论了遵守该政策的策略。
利益相关方
Facilitator: 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 的另一组件之间的兼容性时,我们假设交互的一方是树外构建的外部组件(即通过 IDK 在 Fuchsia 平台 build 之外构建),而交互的另一方是树内构建的操作系统二进制文件(例如内核或 Fuchsia 平台组件,即在 Fuchsia 平台的 GN build 图中构建)。定义外部组件之间交互的版本控制实践和兼容性不在本文档的讨论范围内,留待日后处理。
设计
本部分将介绍 Fuchsia 平台版本控制背后的概念模型。此处介绍的许多概念之前已在 RFC-0002 中介绍过,但此处提供了更具体的细节和限制。
“设计”部分中的文字应视为信息性内容,而非规范性内容。下方的政策变更部分将重复显示本文本中代表现有政策变更的所有部分。
API 级别
Fuchsia 平台表面在不断变化,并且新的 Fuchsia 版本也在不断创建,每天会创建多次。我们不会从每个其他版本来推断每个 Fuchsia 版本与 Fuchsia 平台表面的兼容性(这会非常复杂),而是考虑某个版本与有限数量的 API 级别的兼容性。
您可以将 API 级别视为构成 Fuchsia 平台表面的 API 的“版本”“版次”或“快照”。API 级别 10 是“Fuchsia API 表面的第 10 版”。API 级别与里程碑同步,因此 API 级别 10 也可以视为“发布 F10 时 Fuchsia 平台的最新版本”。
您可以使用 API 级别来表达类似这样的内容:“fuchsia.lightsensor.LightSensorData FIDL 表在 API 级别 10 中有三个字段(rgbc、calculated_lux 和 correlated_color_temperature),但在 API 级别 11 中又添加了两个字段(si_rgbc 和 is_calibrated)。自 API 级别 11 以来,该表一直有五个字段,但我们可能会在 API 级别 16 中移除一些字段或添加更多字段。”
与教科书版本一样,API 级别是不可变的。教科书的内容可能会随时间推移而发生变化,但教科书第 2 版的内容一旦发布,就永远不会再发生变化。这意味着,任何两个了解给定 API 级别的 Fuchsia 版本都会就构成该 API 级别的 API 元素集达成一致。
Fuchsia 平台表面不仅限于 FIDL,还包括系统调用、C++ 库及其中的方法、持久性文件格式等。从概念上讲,这些都是按 API 级别进行版本控制的,但实际上,FIDL 库的版本控制支持比 Fuchsia 平台表面的其他方面要先进得多。
定位到某个 API 级别
最终开发者在构建组件时会选择一个目标 API 级别。 如果开发者以 API 级别 11 为目标平台,其组件将只能访问 API 级别 11 中提供的 API 元素。如果某个方法是在 API 级别 12 中添加的,相应组件将不会在其生成的绑定中看到该方法。相反,如果 API 级别 11 中的某个方法在 API 级别 12 中被移除,相应组件将继续能够使用该方法。
在构建组件时,IDK 中的工具会将目标 API 级别关联的 ABI 修订版本嵌入到组件的软件包中。这通常称为组件的“ABI 修订版本标记”。
借助 ABI 修订版本标记,我们可以保证组件观察到的运行时行为与其目标 API 级别指定的行为相符。目前,ABI 修订版本标记的使用非常粗略:如果操作系统支持相应 ABI 修订版本的 API 级别,则允许组件运行。否则(例如,如果外部组件以 API 级别 11 为目标平台,但 OS 二进制文件不再实现属于 API 级别 11 的方法),组件管理器会阻止其启动。
发布和阶段
每个 Fuchsia 版本都支持多个 API 级别,并为每个 API 级别分配一个阶段。您可以下载 Fuchsia 版本的 IDK,然后在 version_history.json 文件中查找 API 级别。
例如,假设的 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 仍会运行以已停用的 API 级别为目标平台的组件,但 IDK 在构建组件时将不再支持以这些 API 级别为目标平台。
这个假设的 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 级别;一旦合作伙伴不再希望运行以某个 API 级别为目标的二进制文件,我们就会停用该 API 级别。随着合作伙伴数量的增加,我们对合作伙伴的个性化需求了解得越来越少,届时我们将需要制定更正式的政策,但目前这种方式就足够了。
NEXT 和 HEAD 伪 API 级别
除了上述 API 级别之外,还有两个特殊的可变伪 API 级别:NEXT 和 HEAD。
与其他 API 级别不同,NEXT 和 HEAD 的内容可能会在各个版本之间以任意方式更改。可以添加、修改、替换、弃用和/或移除 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 的语言,HEAD 比 NEXT 新,这意味着 NEXT 中的所有 API 变更也包含在 HEAD 中。
最终开发者可以定位 HEAD 或 NEXT,但这样做会使 API 和 ABI 兼容性保证失效。如需详细了解从树外定位 NEXT 或 HEAD 的影响,请参阅介绍这些保证的部分。
政策变更
本部分列出了相应 RFC 提议的具体政策变更。
模型变更
此 RFC 对平台版本控制模型进行了以下更改:
- 带编号的 API 级别现在是不可变的。实际上,对于除特殊的“开发中”API 级别之外的所有 API 级别,此属性都已为 true。此 RFC 有效地弃用了“开发中”API 级别,转而使用
NEXT。 - 引入 API 级别阶段。实际上已经存在“受支持”和“已停用”状态,不过后者目前称为“不受支持”。此 RFC 建议重命名该字段,并添加“日落”字段。
- 介绍
NEXT。这是另一个特殊 API 级别,与HEAD类似。HEAD“新于”NEXT。这取代了目前将最新 API 级别指定为“开发中”API 级别的做法。 - 允许对
NEXT进行向后不兼容的更改。当前政策(由工具强制执行,但未在任何 RFC 中确立)规定,只能对开发中的 API 级别进行向后兼容的更改。此 RFC 移除了该限制,并且不会将其应用于NEXT。如需了解详情,请参阅下文。 - 将 API 级别与里程碑相关联。API 级别
N将在 FN里程碑分支切分之前不久创建,基于NEXT的内容。这在实践中是正确的,但尚未在任何 RFC 中获得批准。 - API 级别会尽快进入各个阶段,具体取决于合作伙伴的进度。一旦没有任何合作伙伴需要构建以某个受支持的 API 级别为目标的二进制文件,我们就会停用该 API 级别,但不会更早。一旦没有任何合作伙伴希望在新版 Fuchsia 上运行以已停用的 API 级别为目标平台的任何二进制文件,我们就会停用该 API 级别,但不会更早。
API 兼容性保证
Fuchsia 现在针对编号的 API 级别提供以下 API(即 build-time)兼容性保证(但需遵守下文中的注意事项):
如果最终开发者可以使用 IDK 的某个版本成功构建以编号 API 级别
N为目标的组件,那么他们将能够使用 IDK 的任何版本(只要N处于受支持阶段)成功构建同一源代码。
换句话说,在所选目标 API 级别被弃用之前,最终开发者可以放心地升级或回滚其 IDK,而不会破坏其 build。如果更新到不同的 IDK 但不更改其目标 API 级别会导致其 build 出现问题(例如,如果他们使用的某个 API 元素不再存在),则会被视为 Fuchsia 平台 bug。
另一方面,对于可变伪 API 级别 NEXT 和 HEAD,不保证 API 兼容性。当以 NEXT 或 HEAD 为目标平台时,最终开发者可能会发现,更新 IDK 会导致其代码无法再编译。
实际上,我们目前的合作伙伴与 Fuchsia 团队密切合作,我们会尽力避免破坏以 NEXT 为目标平台的最终开发者的 build。我们将尽最大努力避免破坏以 HEAD 为目标平台的最终开发者的 build。
ABI 兼容性保证
Fuchsia 现在针对编号的 API 级别做出以下 ABI(即运行时)兼容性保证(但需遵守下文中的注意事项):
以编号的 API 级别
N为目标平台构建的组件将成功运行在支持或已停止支持N的任何 Fuchsia 版本上,并且不会在版本之间看到任何令人反感的行为变化。
换句话说,在所选目标 API 级别被弃用之前,最终开发者无需更改或重新编译代码,即可在较新版本的 Fuchsia 上运行代码。如果平台在不同版本的 Fuchsia 上以会干扰其组件功能的方式表现出不同的行为,则会被视为 Fuchsia 平台 bug。
另一方面,对于以可变伪 API 级别 NEXT 或 HEAD 为目标平台的组件,不保证 ABI 兼容性。如果组件是针对 NEXT 或 HEAD 构建的,那么只有当运行该组件的 Fuchsia 版本与构建该组件的 IDK 版本完全一致时,该组件才会被允许运行。例如,使用 IDK 版本 16.20231103.1.1 构建的以 NEXT 为目标的组件将在 Fuchsia 版本 16.20231103.1.1 上运行,但不会(默认情况下)在 Fuchsia 版本为 16.20231103.2.1 的设备上运行。
在大多数情况下,确保构建组件的 IDK 版本与运行该组件的操作系统版本完全一致是不可行的。值得注意的例外情况是集成测试和本地实验。
树外代码库通常可以控制用于编译的 IDK 版本和用于测试的操作系统版本。如果他们想在 NEXT 或 HEAD 中测试功能,就应使两者保持同步。
禁止使用不受支持的配置
默认情况下,component_manager 会拒绝启动 ABI 修订版本标记表明其不在 ABI 兼容性保证范围内的组件。具体而言:
- 如果某个 Fuchsia 版本支持或已弃用 API 级别
N,则该版本只会运行以 API 级别N为目标平台的组件。 - 如果组件是使用完全相同的 Fuchsia 版本中的 IDK 构建的,则给定的 Fuchsia 版本将仅运行以
NEXT或HEAD为目标的组件。
同样,产品组装将拒绝组装包含与平台不兼容的组件的产品。
产品所有者可以选择性地停用这些检查。例如,他们将能够将单个组件列入许可名单,以定位 NEXT 或 HEAD,即使构建该组件的 IDK 来自不同的 Fuchsia 版本。产品所有者可以自行决定是否停用这些检查,但需自行承担风险。
实现
实现这些检查需要对 ABI 修订版本标记进行一些更改。这些更改的完整设计超出了此 RFC 的范围,但简而言之:
- 与目前的情况一样,每个 API 级别在发布时都会分配有一个唯一的 ABI 修订版本。以编号 API 级别为目标的组件将标记为相应 API 级别的 ABI 修订版本。
- 此外,每个版本都会分配有唯一的 ABI 修订版本。
以
HEAD或NEXT为目标的组件将使用此按版本 ABI 修订版本进行标记。
给定版本中的操作系统二进制文件将包含处于支持或已停用阶段的所有 API 级别的 ABI 修订版本列表,以及相应版本的 ABI 修订版本。默认情况下,只有标记了其中一个 ABI 修订版本的组件才能运行。
未来扩展
请注意,“每个 API 级别一个 ABI 修订版本,外加一个 NEXT/HEAD”的安排不一定就是此设计的最终版本。ABI 修订版本是我们以机器可读的方式对 ABI 兼容性保证进行编码的方式,因此,如果我们的保证变得更加细致,那么我们对 ABI 修订版本的分配也必须如此。
例如,假设我们将部分 API 表面指定为具有“长期支持 (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 级别。
举个简单的例子,如果某个方法 Foo 在 API 级别 15 中从某个 FIDL 协议中移除(即 Foo 带有 @available(added=A, removed=15) 注释),但仍支持 API 级别 14,则实现相关协议服务器的平台组件必须继续实现 Foo,直到包含该方法的所有 API 级别都已停用。
反之,如果该 FIDL 协议服务器是由树外驱动程序实现的,那么与该驱动程序通信的平台组件必须能够正常运行,无论该驱动程序是否实现了 Foo。
此策略不仅限制了平台组件的实现,还限制了我们可以对平台表面进行的修改类型。从理论上讲,API 可能会在不同 API 级别之间任意更改,但实际上,某些更改(例如,重复使用 FIDL 协议方法序号)会导致我们无法在不提供更多信息的情况下与以不同 API 级别为目标的外部组件正确通信。
我们可能会制定不同的策略来支持多个 API 级别,但这属于未来的工作。
注意事项
API 和 ABI 兼容性保证均受一些注意事项的约束。
“保证”的含义
“保证”兼容性并不意味着 Fuchsia 永远不会违背我们的兼容性承诺 - 人无完人。这意味着,如果我们根据上述条款破坏了兼容性,则会将其视为 bug。我们将负责在内部解决此问题,无需最终开发者采取任何行动,只需报告 bug 即可。
使保证失效
如果最终开发者“做了一些奇怪的事情”,则 API 和 ABI 保证不适用。其中包括但不限于:
- 修改其 IDK 副本中的文件,
- 访问内部命名空间中的成员,
- 手动合成旨在由 IDK 中的库构建的数据,
- 将由不同 IDK 构建的共享库链接到单个二进制文件中,
- 等等
一个值得注意的反例是,依赖 Fuchsia 中的 bug 行为不算作“做一些奇怪的事情”。在某些不幸的情况下,修复 bug 可能会被视为“令人反感的行为变更”。如需了解详情,请参阅令人反感的更改。
有关禁止行为的完整说明不在本 RFC 的讨论范围内。正如美国最高法院大法官波特·斯图尔特在 Jacobellis 诉俄亥俄州案中所说:“我今天不会尝试进一步定义‘做一些奇怪的事情’,或许我永远也无法清晰地定义它。但我知道它是什么。”
令人反感的变更
ABI 兼容性保证承诺,在支持组件目标 API 级别的 Fuchsia 版本之间,组件不会出现“令人反感的行为变化”。“令人反感”的定义显然是主观的,对于某些人来说是理想的更改,对于其他人来说可能令人反感(相关 xkcd)。最终,对于令人反感的行为变更的举报,我们需要根据具体情况进行处理。
安全和隐私设置
如果事后发现 Fuchsia 平台表面的某些部分存在严重的安全或隐私问题,我们可能会根据需要选择性地放弃对违规功能的支持,以实现我们的安全和隐私目标。
性能
保持与旧版 API 级别的向后兼容性会带来性能开销。如果不是这样,向后兼容的平台二进制文件将不得不更大,因为会保留更多逻辑。在某种程度上,这是不可避免的;不过,API 级别以原子方式停用这一事实可能会加剧这个问题。
向后兼容性
此 RFC 的全部内容都与兼容性(包括向后兼容性和向前兼容性)有关。
测试
测试以确保 API 和 ABI 兼容性是另一个重要主题。
Fuchsia 兼容性测试 (CTF) 旨在提供测试覆盖率,以确认我们符合 ABI 兼容性保证。目前覆盖率较低,但我们正在积极努力提高覆盖率。
目前,我们无法编写测试来验证 API 兼容性保证;我们所有的树内测试都是针对 LEGACY API 级别构建的,因此我们无法复制树外客户以编号 API 级别为目标的体验。此问题需要解决,但解决此问题不应妨碍接受此 RFC。
文档
此 RFC 获得批准后,其文本将作为面向 Fuchsia 平台开发者的多份文档的基础进行重复使用:
- 兼容性政策本身。
- 有关 API 级别和兼容性的指南。
- 一份文档的初稿,其中概述了会使我们的兼容性保证失效(即“执行某些奇怪的操作”)的使用准则,或许可以参考 Abseil 的兼容性准则文档。
还需要更新文档,以反映现有概念的重命名。
缺点、替代方案和未知因素
缺点: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 建议从 NEXT 和 HEAD 开始,但在分配数值时在它们之间留出间隙。这样一来,如果我们发现更多用例,就可以添加更多伪 API 级别。
替代方案:向后兼容的开发中 API 级别
Fuchsia 的事实版本控制政策目前规定,只能对开发中的 API 级别进行向后兼容的更改。此 RFC 会回滚该政策,因此,当最终开发者以 NEXT 为目标平台时,无法保证不同版本中的 IDK 和 OS 二进制文件之间的兼容性。
如果我们能在开发中的 API 级别(或 NEXT)内保持这种向后兼容性政策,就能做得更好:我们可以保证,只要操作系统二进制文件来自比 IDK 更新的版本,IDK 和操作系统二进制文件就能够兼容。在相对较近的时间之前,我们一直依赖此属性,直到我们将下游代码库移至目标受支持的 API 级别。
这样做的好处是,最终开发者可以在开发中的 API 级别中添加新的 Fuchsia 功能后立即开始安全地依赖这些功能,而无需等待该 API 级别发布。
不过,它会给系统带来显著的复杂性:
- Fuchsia 平台开发者不仅必须与之前的 API 级别(其 API 签名本身已在 FIDL 和头文件中编码)保持向后兼容性,还必须与正在开发的 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 级别与其版本控制方案相关联,但两者都允许在次要修订版本中进行 API 更改,这与 Fuchsia 不同。不过,这似乎主要是出于以下考虑:希望将发布版本的主要版本与日历年相关联,同时仍允许每年多次发布 API 更新。
Fuchsia 的里程碑更贴近 Chrome 的里程碑,会频繁递增主要版本,而次要版本则未使用。如果我们希望更频繁地稳定 API 级别,可以通过更频繁地递增 Fuchsia 的里程碑版本号来实现。
替代方案:编号的“开发中”API 级别
此 RFC 的早期草稿更接近当前实现的平台版本控制模型。在该模型中,NEXT 不存在,而是将编号最大的 API 级别设为可变,并将其称为“开发中”API 级别。
从机械角度来看,这些型号之间只有一些细微的差别:
- 目前,Fuchsia 平台开发者会选择他们希望发布功能的特定 API 级别。此 RFC 实现后,他们将对
NEXT进行与正式版相关的 API 更改,这些更改将自动发布到下一个编号的 API 级别。 - 目前,以开发中的 API 级别为目标平台的最终开发者会在该 API 级别发布后自动过渡到以其受支持的版本为目标平台。此 RFC 实现后,他们将继续定位
NEXT。
这两种差异似乎都不是绝对的正面或负面。
与开发中的 API 级别相比,NEXT 的主要优势在于易于通信。此 RFC 的早期草稿曾使用冗繁的措辞来表达兼容性保证:“以支持的 API 级别 N 为目标构建的组件将成功运行在任何支持的阶段中支持 API 级别 N 的 Fuchsia 版本上。”在当前提案中,所有各方都将就给定 API 级别的内容达成一致,无需任何限定条件。
替代方案:不移除任何内容
我们可以完全禁止向后不兼容的变更,从而简化平台版本控制。每个 API 级别都将支持之前的所有功能,并且通过支持最新的 API 级别,“平台支持所有 API 级别”属性将“免费”提供。
包括 Windows 和 Android 在内的许多平台在很大程度上都采用了这种方法,但结果是它们无法清理旧版 API 表面中的无用内容。
虽然 Fuchsia 平台工程师只要旧 API 元素出现在受支持或已弃用的 API 级别中,就需要继续支持这些元素,但最终开发者只需要关注所选目标 API 级别的内容,而无需看到这些过时的内容。
此外,虽然“永不移除内容”可能会使版本控制更简单,但绝不会完全解决问题。最终开发者仍需根据其预期安装库中最旧的 Fuchsia 平台版本来确定要以哪个 API 级别为目标平台。
-
我们的大多数下游合作伙伴目前依赖于每天多次生成的 Fuchsia Canary 版。未来,我们的更多合作伙伴将从里程碑版本分支使用 IDK 和 OS 二进制文件,但目前,我们非常重视这种安排带来的即时反馈。 ↩