RFC-0246:API 级别为 32 位

RFC-0246:API 级别为 32 位
状态已接受
区域
  • 常规
说明

将 API 级别重新定义为 32 位数字,而不是 64 位数字

Gerrit 更改
作者
审核人
提交日期(年-月-日)2024-04-01
审核日期(年-月-日)2024-04-29

摘要

此提案将 API 级别的定义更改为无符号 32 位整数。此规范取代了 RFC-0002,后者将其定义为无符号 64 位整数。

设计初衷

以下代码目前可在 target_api_level.gni 中找到(为清晰起见,已进行释义):

if (override_target_api_level == -1) {
    clang_fuchsia_api_level = 4294967295
    fidl_fuchsia_api_level = "LEGACY"
}

粗略地说,这表示如果在 fuchsia.git 中构建时未指定 API 级别,则应将 clang_fuchsia_api_level 设置为 0xFFFFFFFF,并将 fidl_fuchsia_api_level 设置为字符串值 "LEGACY"。此字符串稍后会被 fidlc 解释为值 0xFFFFFFFFFFFFFFFF 的别名,如 RFC-0083 中所定义。

这演示了两个问题:

  1. 存在“clang API 级别”和“FIDL API 级别”,它们通常具有不同的值。
  2. API 级别有时以字符串表示,有时以整数表示。

clang vs FIDL vs... ?

RFC-0002 将 API 级别定义为 64 位整数,后续 RFC 分配了特定的 64 位整数,并为其命名和赋予含义。例如,LEGACY0xFFFFFFFFFFFFFFFF 标识。但根据上面的代码,我们将向 Clang 传递 0xFFFFFFFF,因为遗憾的是,API 级别在 Clang 中仅限于 32 位。

嗯,这并不完全正确。

Clang 通过 availability 属性来推断兼容性,该属性将版本表示为 VersionTupleVersionTuple 将表示版本的四个整数 major[.minor[.subminor[.build]]] 的元组打包到 128 位结构中。Fuchsia API 级别在此结构中以“主要版本”的形式建模,因此限制为 32 位。

Clang 是 Fuchsia 平台版本控制的关键部分,因此必须解决此不一致问题。

string vs int vs... ?

主机工具和 build 系统在用于表示 API 级别的类型方面不一致。即使在 fuchsia.git 的 build 系统中,也存在不一致的情况,如上面的代码块所示。

这不一定是个问题,并且此 RFC 也无法完全解决此问题。 不过,本文将尝试提供指导,帮助您消除这种模糊性。

利益相关方

Facilitator: abarth@google.com

审核者

  • ddorwin@google.com
  • mkember@google.com
  • haowei@google.com

已咨询

  • chaselatta@google.com
  • phosek@google.com
  • ianloic@google.com

共同化

Google 内部 bug 中与平台版本控制工作组和工具链团队的成员进行了讨论。

要求

API 级别必须可以表示为字符串,包括在命令行中以及在文件和目录的名称中。

API 级别必须完全有序,这样我们才能说出“Foo 是在 API 级别 N 添加的,而 N <= M,因此 Foo 是 API 级别 M 的一部分”之类的话。

我们必须能够为某些 API 级别分配特殊名称和行为。本部分中的其他要求也适用于这些“特殊”API 级别。

设计

整数表示形式

API 级别将重新定义为无符号 32 位整数。

此空间中的较低一半(即 API 级别低于 0x80000000)被视为“正常”API 级别。此空间的上半部分已预留。此 RFC 和未来的 RFC 将定义大于或等于 0x80000000 的特定值的含义。

将所有 API 级别同等对待的工具可能会忽略“正常”值和预留值之间的区别,并将它们全部视为以典型方式排序的无符号 32 位整数。

如果工具具有针对特定 API 级别的特殊逻辑,则应拒绝指定了它不了解的预留 ABI 级别值的输入。

字符串表示形式

API 级别可以通过多种不同的方式以字符串形式表示:

  • 表示区间 [0, 232) 中十进制数的任何 UTF-8 字符串都是 API 级别的字符串表示形式(例如 "7")。
  • 特殊 API 级别可以用其名称(大写)表示(例如 "HEAD")。
  • 工具不应接受 "0016""0x20" 等更深奥的值,因为这样做可能会造成歧义。例如,"0016" 是八进制还是十进制?"2A" 可能是十六进制,但如果它被接受为十六进制,那么 "29" 是十六进制还是十进制?不过,字符串解析逻辑通常由超出单个工具作者控制范围的库处理,因此工具可能会接受此类值。

每个 API 级别都有一个规范的字符串表示形式

  • 对于“正常”API 级别,以 10 为基数的 UTF-8 字符串表示形式是规范的(例如 "13")。
  • 对于“特殊”API 级别,大写名称是规范名称(例如 "NEXT")。

如果工具需要获取预留 API 级别(即大于或等于 0x80000000 的 API 级别)的规范表示形式,但不知道该 API 级别的特殊名称,则必须返回错误。

特殊 API 级别

以下 API 级别具有特殊名称:

  • PLATFORM = 0xFFF00000 = 4293918720PLATFORM 扮演着之前由 LEGACY 提供的角色,即默认情况下,平台将以 API 级别 PLATFORM 构建。之前,LEGACY 在 FIDL 中的值为 0xFFFFFFFFFFFFFFFF,无法在 Clang 中表示。

    LEGACY 已被 RFC-0232 弃用,目前正在从 FIDL 中移除,但即使移除工作完成后,平台 build、C++ 和 Rust 代码仍会使用 PLATFORM 来检测正常的平台 build。

    PLATFORM 仅在操作系统 build 中和作为 IDK 的一部分使用的代码中才有用。在这些库中,目标 API 级别低于 PLATFORM 意味着代码是作为 SDK 的一部分构建的,并且只能使用构建所针对的特定 API 级别中可用的 API 元素。如果目标 API 级别等于 PLATFORM,则代码正在作为操作系统的一部分进行构建,并且必须为“受支持”或“日落”阶段的所有 API 级别提供支持。如需了解详情,请参阅 RFC-0239

  • HEAD = 0xFFE00000 = 4292870144。之前,HEAD 在 FIDL 中的值为 0xFFFFFFFFFFFFFFFE,在 Clang 中的值为 0xFFFFFFFF

  • NEXT = 0xFFD00000 = 4291821568. NEXTRFC-0239 中进行了描述,但未分配数值。

此集合可能会随着时间的推移根据需要增大或缩小。

最初,这些值将硬编码到支持以 HEADNEXT 为目标平台的 SDK 中,但最终应在 //sdk/version_history.json 中定义。

何时使用整数与字符串

命令行工具接受输入并生成字符串形式的输出,因此,它们应接受上述任何 API 级别的字符串表示形式,并应优先使用规范字符串形式输出 API 级别。不过,有时无法实现或非常不方便,因此不需要使用规范字符串形式。例如,Clang 不知道特殊 Fuchsia API 级别的名称,因此 -ffuchsia-api-level 的值必须以整数形式提供。

在构建工具的实现中,最好以整数形式存储 API 级别。

构建系统可以采用最适合该构建系统的方式来表示 API 级别。

性能

此更改应该不会对效果产生任何影响。

向后兼容性

严格来说,LEGACYHEAD 的数值变化不向后兼容。不过,之前的 64 位值实际上仅在 fidlc 中使用,对于 Fuchsia SDK 的用户来说基本上是不可见的。

C++ 代码中 HEAD 的当前定义(即 0xFFFFFFFF)在理论上对 SDK 用户可见,但由于 Fuchsia 源代码树之外的任何代码目前都不以 HEADLEGACY 为目标,因此此更改也应不会被注意到。LSC 预提交会确认这一点。

之所以选择新的 PLATFORM(之前为 LEGACY)、HEADNEXT 值,是为了在它们之间提供较大的差距。这样一来,我们就可以创建其他特殊 API 级别,而无需重新定义现有级别。我们创建的任何此类新 API 级别都应添加到两个相邻 API 级别之间的中间位置。在最糟糕的情况下,我们将能够将每个区间细分为 20 次。

安全注意事项

此更改应该不会对安全性产生任何影响。

隐私注意事项

此项变更应该不会对隐私权产生影响。

测试

此 RFC 主要涉及 Fuchsia 的 build 系统和 SDK 中的代码。无论好坏,对该代码的专用自动化测试都很少。不过,在实践中,如果构建系统出现故障,当测试失败、构建中断和本地开发流程出错时,很快就会被发现。

文档

NEXTHEADPLATFORM 的含义和值将包含在即将发布的 RFC-0239 中引入的概念的文档中。

缺点、替代方案和未知因素

缺点:32 位是否足够?

如果按顺序分配,即使我们每小时发布一个新的 API 级别,也需要大约 24.5 万年才能用完此 RFC 中预留的 231 个 API 级别。这似乎就足够了。

不过,API 级别不一定总是需要密集分配。此 RFC 定义了 3 个特殊的 API 级别,每个级别之间有 1048576 个未使用的 API 级别。可以吗?

在 64 位 API 级别下,即使我们以非常稀疏的方式分配版本号(例如,像 Clang 对 VersionTuple 所做的那样,在连续版本之间留出数千、数百万或数十亿的间隙),也很难想象会用完版本号。

对于 32 位 API 级别,我们必须更加谨慎地进行分配。

冒着成为历史笑柄的风险,我还是要说:232 个 API 级别对于任何人来说都应该足够了。

替代方案:改用 Major.Minor 方案

VersionTuple.h 的编写显然基于以下假设:功能可以在版本(例如 12.5 甚至 30.1.2.3)中引入或移除。以这种方式构建版本的平台最多可以表示 21251 个不同的版本。或许 Fuchsia 只能使用这些值中的 232 个这一事实表明,Fuchsia 的版本控制方案并不合适?

有多个成功平台使用单个整数来确定 API 表面的版本(例如 Chromium 和 Android)。没有充分的理由相信,我们无法通过相同的策略取得成功。


  1. Clang 使用 minorsubminorbuild 中最重要的位作为标志。