RFC-0083:FIDL 版本控制 | |
---|---|
状态 | 已接受 |
领域 |
|
说明 | 提供对 FIDL 元素进行版本控制并为特定版本生成绑定的方法。 |
问题 | |
Gerrit 更改 | |
作者 | |
审核人 | |
提交日期(年-月-日) | 2021-02-12 |
审核日期(年-月-日) | 2021-04-05 |
摘要
本文档提出了一种使用版本和 生成指定版本绑定的机制。这一过程将 API 的演变分离 使库作者能够更轻松地进行更改 为最终开发者提供稳定性这为 FIDL 发挥的作用奠定了基础 RFC-0002:平台版本控制中。
设计初衷
虽然 FIDL 在变更期间提供了许多用于实现 ABI 兼容性的方式, 改进 API 的难度非常大。在 Fuchsia SDK 中,更改 FIDL 但与 API 不兼容,则需要仔细地协调 软转换,以避免破坏下游编译。有事物发生时 我们通常需要还原 Fuchsia 的更改。作为 SDK 的使用 库的变化,进行这些更改的难度也会增加。
FIDL 版本控制解决了这一问题,使 FIDL 库作者和使用者能够 按照自己的节奏前进当库作者添加、移除或修改 更改会在新的 API 级别中发布。以 旧 API 级别采用新 API 级别之前绑定不会有任何变化。 除了提供稳定性以外,这还有助于最终开发者迁移到新的 API 一次一个组件,因为目标 API 级别是每个组件指定的。
图 1 演示了一项重要的 API 更改。如果不进行版本控制 破坏应用并导致还原。通过版本控制功能 只是保持在旧的 API 级别上当然,同样的问题 尝试将应用的固定 API 级别从 12 提升到 13 时出现。 但可以异步修正这些错误, 紫红色,项目还在苦苦挣扎。
图 1:FIDL 版本控制之前(左侧)和之后(右侧)的 API 演变
术语
术语 API 级别和 ABI 修订版本在 RFC-0002:平台 版本控制,具体变更如下:
RFC-0002 的修订。Fuchsia API 级别是无符号的 63 位 整数。也就是说,它是 64 位,但高位必须为零。
FIDL 元素是 FIDL 库的离散部分,该元素会影响生成的 绑定。其中包括:
- FIDL 库本身
- 常量、枚举、位、结构体、表、联合、协议和服务声明
- 别名和新类型(来自 RFC-0052:类型别名和新类型)
- 枚举、位、结构体、表、联合和服务(包括
表和联合的
reserved
个成员) - 协议中的方法和
compose
节 - 方法中的请求和响应参数
FIDL 属性是 FIDL 元素的可修改方面,该元素本身并不是 不同元素。其中包括:
- 属性
- 修饰符
strict
、flexible
和resource
- 常量、枚举成员和位成员的值
- 结构体成员的默认值
- 类型限制(针对成员、参数和类型别名)
- 方法种类(单向、双向、事件)
- 方法错误语法(其存在和类型)
这样只留下一些既不是 FIDL 元素也不是属性的内容:
- 单独的
.fidl
文件 - 导入其他 FIDL 库
- 仅限 FIDL 的
using
别名,RFC-0052 会从语言中移除 - 实验性
resource_definition
声明 - 注释,包括文档注释
设计
本文档中介绍的设计提供了一种通用工具, 对 FIDL 元素进行版本控制。它的主要用例是对 FIDL 库进行版本控制, Fuchsia 平台(按 API 级别划分)。
范围
此设计引入了 FIDL 语言中的版本控制概念, FIDL 库的时间维度。它用于指定 版本控制属性,包括它们如何与 FIDL 的其他方面进行交互 例如父子关系和使用-定义关系它规定了 在生成 FIDL 绑定时作为输入提供。
此设计没有为 FIDL 提议使用软件包管理器。主题包括: 版本解析算法、软件包分发和依赖项冲突 不在范围内。话虽如此,但解决这些问题的系统应该 能够重复使用此设计提供的工具。
此方案不涉及运行时行为:它侧重于 API,而不是 ABI。 因此,协议演变的主题不在讨论范围内。这包括 问题,例如“FIDL 服务器如何支持多个 ABI 修订版本?”那里 是 FIDL 和 Component Manager 可以制定的各种协议演进策略, 。此提案为协议的演变铺平了道路, 我们在 FIDL 中对版本的概念进行了介绍,但内容不会超出此范围。
能否在此设计下表示过渡,不会影响 相应转换是否安全或兼容相反,有版本控制的 FIDL 库可以代表语法上有效的更改几乎任何序列。
形式主义
平台标识符是用于为版本提供上下文的标签。平台
必须是有效的 FIDL 库名称元素,即截至本文撰写时
匹配正则表达式 [a-z][a-z0-9_]*
。
版本标识符是一个介于 1 到 2^63-1 之间的无符号 64 位整数
(含)或等于 2^64-1。后面的版本标识符称为 HEAD
并受到特殊对待。
修正条款(2022 年 10 月)。为了支持旧版方法,我们改用
HEAD
为 2^64-2,LEGACY
为 2^64-1。
修正条款(2024 年 4 月)。RFC-0246 中重新定义了
HEAD
和LEGACY
。
版本标识符完全按照“高于”进行排序关系。 当 X > 时,版本 X 比版本 Y 更新Y。
FIDL 元素针对平台的可用性是指 版本(在引入该元素时提供),以及版本(可选) 已被弃用并移除。弃用和移除时间必须晚于 简介。1如果同时提供了这两者,移除时间必须晚于弃用时间。
如果 FIDL 元素在某个平台上可用,则该元素将归于某个平台下的版本编号。 与该平台有关的内容如果在任何平台下进行版本控制,则版本会带有版本编号。
如果某个平台版本,则相应 FIDL 元素可用。 晚于或等于该元素的简介,但早于其 弃用和移除(如果有)。如果版本较新,则已被弃用 晚于或等于该元素的弃用时间,但早于该元素的移除时间(如果有)。 如果可用或已弃用,则其值为存在。否则,不存在。
版本选择是将版本分配给一组平台。对于
例如,您可以选择 red
的版本 2 和 blue
的版本 HEAD
。
如果有 FIDL 元素,则它可用。 适用于所有平台已弃用(如果存在) 弃用了所有平台,并针对一个或多个平台弃用了 平台。否则,不存在。
语法
库存状况属性采用以下格式2(灵感源自 Swift 的
available
属性:
@available(added=<V>, deprecated=<V>, removed=<V>)
每个 <V>
都是一个版本标识符。added
、deprecated
和removed
字段表示元素的引入、弃用和移除,
。它们都是可选的,但必须至少提供一个。
在库中,必须提供 added
字段(deprecated
和 removed
是
可选)。还有一个可选字段 platform
,用于指定平台
标识符。库中的所有版本标识符都引用此
平台。例如:
@available(platform="red", added=2)
library colors.red.auth;
如果省略,则默认为库名称的第一个组成部分:
@available(added=HEAD) // implies platform="blue"
library blue.auth;
使用 deprecated
字段,可以额外为以下字段提供一个 note
字段:
。例如:
@available(added=12, deprecated=34, note="Use X instead")
库存状况属性使 RFC-0058 中的 [Deprecated]
属性成为:
引入过时的 [Deprecated]
属性。
版本控制元素
FIDL 元素使用库存状况属性进行版本控制。每个 FIDL 元素 只能有一个“库存状况”属性,且只能 库。换言之,如果对库中的任何 FIDL 元素进行了注解,则 该库也必须带有注解。
FIDL 库中的每个文件都有自己的库声明, 代表相同的 FIDL 元素:库。这与 FIDL 风格指南:
将库拆分为文件不会对 库。...将库拆分为多个文件,以提高可读性。
因此,一个库中只能有一个库声明具有可用性 属性。文档评论也受到同样的限制,因此有必要 选择同一个文件来指定库的可用性及其文档注释。
版本控制属性
无法直接对 FIDL 属性进行版本控制。要更改属性,您必须 交换其所属的元素。也就是说,复制元素 推出相同版本的新副本。例如, 更改以版本 12 绑定的字符串:
@available(removed=12)
string:50 info;
@available(added=12)
string:100 info;
或者,将枚举从 strict
更改为 flexible
:
@available(removed=12)
strict enum Color { ... };
@available(added=12)
flexible enum Color { ... };
除库之外的所有 FIDL 元素都可以交换。不会出现命名冲突 因为可用性不重叠
此提案不会排除日后应用库存状况的语法
属性直接传递到 FIDL 属性。如果引入此类语法,
只能支持 added
和 removed
,因为没有
deprecated
在所有 FIDL 属性中都有意义。
继承
FIDL 元素形成有向无环图,子元素继承 父元素是否可用。
顶级声明继承自库。枚举、位、
结构体、表、联合和服务从封闭的声明继承。
请求/响应参数继承自其方法。方法和 compose
节从封闭协议继承。组合方法继承自
原始方法和 compose
节。不使用协议组合时
这就是一棵树。
如果子元素具有库存状况属性,则会覆盖继承的 。这样做既不能重复也不冲突: 简介版本只能更新,以及废弃和移除 只能恢复到更早的版本。
对于组合方法,如果两个父级都在同一平台下进行版本控制,
其可用性取决于其父级空房情况(从新到旧)
简介、最早弃用和最早移除)。如果它们带有版本编号
组合方法会继承两个单独的
可用性在这种情况下,“可用设备”的定义
版本选择规则变得相关。在这两种情况下,弃用
note
是合并自两个父级的。
以下是在平台中进行组合的一般情况:
library foo;
protocol Def { @available(added=A, deprecated=B, removed=C) Go(); };
protocol Use { @available(added=D, deprecated=E, removed=F) compose Def; };
原始方法 foo/Def.Go
在 A
中引入,并在 B
中被废弃;
已于 C
移除。组合方法 foo/Use.Go
在 max(A,D)
中引入,
已于 min(B,E)
弃用,并于 min(C,F)
移除。这意味着
方法受 compose
节可用性的限制,但有些方法可以具有
如果 Def
在引入 compose
之后引入,则可用范围缩小;
在 compose
废弃/移除之前弃用/移除它们。
使用验证
某些 FIDL 元素是相互关联的。答 存在以下情况时,struct/table/union 成员或请求/响应参数会使用 FIDL 元素 该元素以其类型出现;方法(如果该元素发生错误) 类型;常量或枚举/位成员(如果该元素出现在其值中);结构体 成员。请参见以下示例:
const uint32 X = 10;
const uint32 Y = X; // Y uses X
table Entry {};
protocol Device {};
resource struct Info {
vector<Entry>:X entries; // entries uses Entry and X
request<Device> req; // req uses Device
uint32 val = Y; // val uses Y
Info? next; // next uses Info
};
在选择版本的情况下,如果发生以下情况,则 fidlc 会出错:
- 当前元素使用了不存在的元素;或
- 某个可用元素使用了已弃用的元素。
生命周期语义
给定版本选择后,如果 FIDL 元素可用,则会作为
如果已弃用,我们会在 JSON IR 中标注,并且
如 RFC-0058:引入 [Deprecated]
属性。如果不存在,我们会在 JSON IR 中将其省略。
如果某个 FIDL 元素未被任何其他元素使用,则为其添加注解
@available(removed=<N>)
等同于从 .fidl
不同的是,使用 removed
属性可保持历史准确性,
而删除元素则不会这样就可以避免 .fidl
随着变化的累积,文件变得臃肿、不可读。
HEAD
的用途
HEAD
版本标识符代表开发的前沿技术。
客户端可以针对 HEAD
绑定进行编程,但不应期望
以确保其稳定例如,假设您下载了 red.fidl
,其中
注解中使用的最高版本标识符为 12 和 HEAD
。如果您
下载较新版本的 red.fidl
,则合理的做法是,API 的
与版本 12 完全相同,即作者没有更改历史。但是
HEAD
API 可能会完全不同。
此功能可在采用 FIDL 版本控制时提供连续性。要依赖
版本化库的 HEAD
绑定与依赖于绑定的相同
未版本化的库
这也使得协作项目中的 FIDL 更改变得更轻松。在编写
CL,查找当前版本非常繁琐,且容易出现争用情况,尤其是在
更改。贡献者只需使用 HEAD
,并且
项目所有者之后可以将其替换为特定版本。
旧版支持
修正条款(2022 年 10 月)。此部分是在接受 RFC 后添加的。
使用 @available(removed=<N>)
移除某个 API 后,该 API 将不再出现在
为版本 N 及更高版本生成的绑定。这使得构建
支持多个 API 级别的 Fuchsia 系统映像。如果系统映像是
根据 N-1 绑定构建,因此无法为方法提供实现
在N 添加。如果是针对 N 个绑定构建的,则无法提供
从 N 移除的方法实现。
为了解决此问题,我们引入了一个名为 LEGACY
的新版本,它可充当
与 HEAD
相同,但还包含旧版方法。旧版方法是一种方法
已标记为@available(removed=<N>, legacy=true)
。这会使用新的布尔值参数
名为 legacy
,默认为 false;仅当 removed
为
存在。例如:
@available(added=1)
library example;
protocol Foo {
@available(removed=2) // implies legacy=false
NotLegacy();
@available(removed=2, legacy=true)
Legacy();
};
以下是定向目标时该示例的绑定中包含的方法 不同版本:
目标版本 | 包含的方法 |
---|---|
1 | NotLegacy 、Legacy |
2 | |
HEAD |
|
LEGACY |
Legacy |
从政策上讲,Fuchsia 平台中的所有方法都应保留旧版
提供支持。在 Fuchsia 平台停止支持所有
在移除该方法之前的 API 级别,可以放心移除 legacy=true
,并且
方法的实现。
当 Fuchsia 平台充当客户端而不是服务器时,传统方法
对于那些以旧 API 为目标平台的应用,允许平台继续调用此方法
级别。对于那些以新的 API 级别为目标平台,但实际上并不符合这一级别要求的用户,方法
必须标记为 flexible
,以便忽略调用。请参阅 RFC-0138:
如需了解详情,请参阅处理未知交互。
legacy
参数可用于任何 FIDL 元素,而不仅仅是方法。对于
例如,如果您移除某个类型及其使用方法,
类型也必须标记为 legacy=true
。这只是使用
验证,而不是新规则。
再举一个例子,假设一个请求中使用的表。移除以下任一项时
则建议您使用 legacy=true
,以便服务器可以继续
设置该字段的支持客户端。另一方面,如果忽略该字段
足以保留 ABI,因此无需旧版支持。同样,
对于响应中使用的表,只有在满足以下条件时才需要使用 legacy=true
:
移除某个字段(如果需要设置该字段以为旧的 ABI 保留 ABI)
客户。
在交换 元素,因为可用性代表更改,而非移除。如果您 则会导致错误:
protocol Foo {
@available(removed=2, legacy=true)
Bar();
@available(added=2)
Bar();
}
由于第一个 Bar
在 LEGACY
添加回,而第二个 Bar
从不添加
移除后,它们都位于 LEGACY
,并且 fidlc 会发出类似的错误
已针对具有重叠播出信息的同名元素执行相同的操作。
JSON IR
为了表示 IR 中的废弃,我们添加了两个字段:
deprecated: <bool>, // required
deprecation_note: <string>, // optional
这些内容会添加到以下 JSON IR 架构定义中:
#/definitions/bits
#/definitions/bits-member
#/definitions/const
#/definitions/enum
#/definitions/enum-member
#/definitions/interface
#/definitions/interface-method
#/definitions/service
#/definitions/service-member
#/definitions/struct
#/definitions/struct-member
#/definitions/table
#/definitions/table-member
#/definitions/union
#/definitions/union-member
#/definitions/type_alias
请注意,IR 不代表弃用某个库。它仍然 通过继承实现的效果,以及下一部分中所述的警告。
命令行界面
如需指定版本选择,fidlc 会接受 --available <P>:<V>
,其中
<P>
是平台标识符,<V>
是版本标识符。举报
针对不同的平台标识符多次指定值。例如:
fidlc --json out.json --available red:2 --available blue:HEAD
--files red.fidl --files blue.fidl
如果版本选择缺少平台或具有未使用的平台 (与给定库的版本对应的平台相比) 会产生错误。如果任何库 那么 fidlc 就会发出警告/错误。3
政策
FIDL 版本控制可在不中断应用的情况下改进 API, 但并不保证一定如此为此,我们采取了以下政策 Fuchsia 平台专用:
- 为所有新更改添加在
HEAD
进行注释。 - 请勿更改 FIDL 库的历史记录。唯一的例外是 删除旧的 FIDL 元素(如下所述)。
- 先弃用 FIDL 元素再将其移除,以下情况除外: 替换以更改属性。
- 弃用某个元素时:
<ph type="x-smartling-placeholder">
- </ph>
- 通过
note
字段告知开发者应改用什么。 - 在文档注释中编写一个
# Deprecation
部分,提供更详细的信息 说明和传达弃用时间表。
- 通过
- 更改 FIDL 属性时要小心。对于 例如,将类型从“严格”更改为“灵活”,或者 从值到资源,都可以拥有重要的 API 影响。API 委员会应根据具体情况来评判这些变更。
这些政策的执行方式如下:
- SDK 中的所有 FIDL 更改将继续需要 API 委员会批准。
fidl-lint
应检查已废弃的元素是否设置了note
字段 并在文档注释中添加# Deprecation
部分。- 今后,应该有一个强制执行其他政策的 CQ 作业 (更改历史记录、移除前废弃以及 API/ABI 不兼容 更改)。
此外,我们还将发布两个新进程,其详细信息遵循后文的 RFC:
- 发布新的 API 级别。这项工作可能会按固定时间表发生,即
自上一个 API 级别以来所做的部分或全部更改会在新的
API 级别:将出现的
HEAD
替换为新级别。 - 删除旧的 FIDL 元素。经过一段时间后,标记为
可从
.fidl
个文件中删除。只有在以下情况下,才能删除元素 由于该令牌在任何位置都未引用,因此此过程可能涉及到 所有早于特定 API 级别的元素。
我们可以使用相同的树构建工具,简化这两个过程 和访问者的方法。
实现
这种设计大多可以在保真格式中实现。解析 @available
语法
依赖于另一个 RFC 来更改 FIDL 的注解语法。通过
首先,可能需要先通过一个实验性标记来实现语义。
当 fidlc 编译库时,即使它在单个位置生成 JSON IR 它应同时验证所有可能的版本。它应该 请勿按顺序生成和检查每个版本。相反, 应暂时将元素分解成(名称、版本范围)元组。这个 流程类似于将 NFA 转换为 DFA。例如:
type MyTable = table {
@available(added=2)
1: name string;
@available(added=HEAD)
2: age uint32;
};
上述代码分解如下(使用伪语法演示):
type «MyTable, [0,1]» = table {};
type «MyTable, [2,HEAD)» = table { 1: name «string, [0,HEAD]»; }
type «MyTable, HEAD» = table { 1: name «string, [0,HEAD]»; 2: age «uint32, [0,HEAD]» };
在发出 IR 之前,fidlc 会剪除声明,使其仅包含 请求的版本。
待解决的问题。时间分解方法难以泛化 不同平台版本下的 FIDL 库一起编译的情况。 由于我们的主要用例(对 Fuchsia 版本 平台不同 API 级别),因此我们可以推迟此问题,并在初始阶段 仅允许有一个
--available
标志。
HEAD
版本标识符可以实现为特定于上下文的常量,
类似于允许以长度形式表示的 MAX
常量
对字符串和向量的限制。
此外,还有一些在 fidlc 之外进行的实现工作。首先,Fibitldoc 需要
会将版本控制考虑在内例如,如果某个元素被弃用,
文档应以醒目的方式指明这一点。它还可以提供 API
用于查看历史文档的一级下拉菜单。第二, fidlgen 后端
需要使用 JSON IR 中的 "deprecated"
字段。例如,fidlgen_rust
可以将其转换为 #[deprecated]
Rust 属性。请参阅 RFC-0058:
为其他语言的示例引入 [Deprecated]
属性。
在 SDK 中的库开始使用注解之前,我们需要添加
将 --available fuchsia:HEAD
添加到 GN 模板,以用于构建 FIDL 绑定。这个
基于所有树内代码都将使用 HEAD
绑定这一假设。时间
我们有一个针对 C++ 的平台版本控制提案,可能需要构建
对照其他版本的 FIDL 绑定进行树内代码测试。
在花瓣构建系统中,我们将添加 fuchsia_api_level
声明和连接
直到 --available
标志为止。这需要配合
CLI 会发生变化,具体表现为一开始接受并忽略
--available
标志。
性能
此方案对运行时性能没有影响。它会影响 build 性能 而 FIDLc 必须做更多工作,但 FIDL 编译从未进行过 是影响 Fuchsia 构建时间的一个重要因素。
安全注意事项
此方案应该会对安全性产生积极影响,因为版本控制 可以更轻松地迁移到具有更好的安全属性的新 FIDL API。这个 应通过控制网络攻击来抵消扩大攻击面带来的负面影响, 以支持旧版 ABI 的版本。
此方案未提供根据 应用的目标 ABI 修订版本,如 RFC-0002。虽然这样可以增强安全性, 作为协议演变综合性 RFC 的一部分。
隐私注意事项
此方案应该会对隐私保护产生积极影响,因为版本控制 可以更轻松地迁移到具有更好的隐私属性的新 FIDL API。
测试
目前,我们结合使用单元测试和 golden 来测试 FIDL 工具链
测试。单元测试主要用于 fidlc 内部构件。Golden Testing
编译一组 .fidl
文件并确保生成的工件 (JSON IR)
和所有绑定)与之前经过审核的黄金文件完全相同。
FIDL 版本控制将采用类似的方法。它将使用单元测试
例如,有一个测试
如果某个表成员
注解指明它是在表本身之前引入的。它还会使用
黄金级测试,但不能通过扩展现有的黄金级测试框架来实现。
为每个版本的库生成工件会使黄金文件变得臃肿
并难以验证正确性。相反,这个项目将拥有自己的
一组 .fidl
文件,其中包含每个版本的 JSON IR 金色 diff。这个
应能够轻松验证版本控制行为是否符合预期。
对于平台 API 的实现者而言,这不会增加测试的难度:测试将
针对 HEAD
编写,与我们目前不针对 FIDL 运行测试一样
从旧版本的 Git 修订版本中复制文件也不会增加 SDK 用户的测试难度:
他们将针对单一版本的平台进行测试
目前使用 SDK 的单个版本进行测试。
文档
@available
语法将以 FIDL 语言记录
规范。完成验证后,将需要提供更多文件
新 API 级别。例如,我们需要
库作者在添加新 API 时都使用 @available(added=HEAD)
元素。使用适当的工具,应该不会有忘记操作的风险
这个。如需了解详情,请参阅“政策”部分。
我们还需要从 FIDL 中移除 [Deprecated]
属性
属性页面上,因为库存状况属性会过时。
应更新 FIDL 源代码兼容性文档 使用库存状况属性显示 FIDL 更改,或显示如何 在使用版本控制时应用不同类型的 FIDL diff。相关文档 还应从总体上说明 FIDL 版本控制如何与转换交互。 有了版本控制功能,无论使用哪个 FIDL 元素,更改 FIDL 元素都一样容易 或不在树外。这样应该可以减少对某些类型的软 过渡效果。但它不会消除所有的多步转换;它只是 用于消除协调时单个共享时间轴的约束条件。
缺点、替代方案和未知问题
实施此提案的费用是多少?
此提案增加了 FIDL(语言)和 FIDL 的复杂性。这将使 库作者进行简单、安全的更改会更繁琐, 您可以使用 自信。
替代方案:使用旧版 SDK
FIDL 版本控制可让应用保持固定为旧的 API 级别,同时 继续推出新的 SDK不过,为什么不直接使用旧版 SDK,呈现这个 是否无需整个提案?导致这种情况的原因有以下几种:
- 有了最新的 SDK,用户就可以获得所有内容的最新副本,例如 作为 FIDL 工具链。
- 目标 API 级别是按组件指定的。针对每种类型使用不同的 SDK 这个组件非常复杂且不切实际。
替代方案:更新日志文件
您可以用单独的更新日志来记录
FIDL 库的历史。一种方法是发送一组文本差异,
从每个 .fidl
文件还原为原始文件。这样可以简化
比如难以对 FIDL 属性进行版本控制。应该是
同时验证一个库的所有版本不切实际,
设计。不过,可能没必要这样做,因为这种备选方案
意外改变历史的问题但这会使
回答一些问题,如“该元素是何时引入的?”它
基本上是重复的 git 历史记录,主要区别在于历史记录
在创建可下载的 SDK 时会保留。
如果更改 FIDL 语法,则文本 diff 将难以维护
。更新日志设计的另一个变体是定义一个新的
格式记录对 FIDL 库的更改以及更改时的版本。
此设计兼容同时验证所有版本,因为
可以读取更新日志并生成一个临时分解的 AST,
信息是否来自属性。不过,它需要更多
工具。例如,我们可能希望开发者像操作时一样修改 .fidl
文件
,并在提交前运行工具附加到更新日志文件。
替代方案:每个库的版本
另一种设计是为每个 FIDL 库提供一个单独的版本。这个
会导致从 API 级别映射到
SDK。例如,API 级别 42 可能表示 fuchsia.auth
v1.2,
fuchsia.device
v5.7 等等。
这种方法对于关注单个库的开发者来说比较优势。 每个版本对于该库都是有意义的,您可以 估算库相对于当前版本号的改进程度。在 相比之下,对于每个平台的版本,不同版本之间可能存在很大的差异 库中的某些信息
但同时也提出了很多问题。如果使用针对每个库的版本 库必须跟踪其依赖的其他 SDK 库的版本,Can SDK 消费者混用不同版本的 SDK 库?正在回答任一问题 包含“是”大大增加了 FIDL 版本控制的复杂性。我们如何知道 哪些版本可以协同工作?我们如何避免编译多个副本 同一库的绑定?如果对这两个问题的回答都是“否”,那么 按库进行版本控制似乎是不必要的间接的,因此看起来 如果不进行版本控制,则会在库级别进行。
替代方案:非对称废弃
RFC-0002 在其“生命周期”部分中的状态:
此元素可能已被弃用。以旧版 ABI 修订版本为目标的组件 在较新的平台版本上运行时仍可使用该元素。不过, 针对较新 API 级别的最终开发者不能再使用该元素。
并说明了这对 FIDL 意味着什么:
当协议元素(例如表中的字段或协议中的消息) 因此理想情况下,我们希望此类组件 该 API 级别才能接收包含该协议元素的消息 但又想阻止这些组件发送包含 该协议元素
FIDL 版本控制不同于此行为,因此作为
防止最终开发者在给定 API 中使用 FIDL 元素
而允许 Fuchsia 平台中的代码在运行时为其提供支持,
难度也很大。正如前面所说,它依赖于错误的假设,即紫红色
平台始终充当服务器,SDK 使用者始终充当客户端。
在某些情况下,角色会颠倒,甚至不明确。我们可以
通过引入 @platform_implemented
和
@user_implemented
。这对于方法很有帮助,但对于类型的非对称行为
和类型成员(以下称为类型元素)则更加困难。
实现类型元素不对称弃用的一种方法是生成桩
来防止它们被使用。例如,已弃用的表格字段可能会出现在
绑定为 FidlDeprecated
类型的值,这会生成类型检查
错误。Fuchsia 平台中的代码可以继续支持
通过新的 fidlgen 标志 --allow-deprecated
移除了已废弃的元素,该标志可生成
代码,就像什么都没有被弃用一样。但这种方法存在两个问题。
首先,这使得在
Fuchsia,因为它们并未显示为已废弃。其次,我们可以非常轻松地
供最终开发者使用。这会使所需的
激励:
这种方法会激励开发者弃用已弃用的 将对新 API 的访问权限与执行这些迁移相结合。 具体而言,要获得对新引入 API 的访问权限,开发者必须 更改目标 API 级别,这需要他们迁移任何 已在该 API 级别弃用的接口。
也就是说,通过 --allow-deprecated
,开发者可以获得
只使用 标记即可引入了 API,而没有迁移已弃用的 API。
针对类型元素的另一种方法是在运行时生成错误。对于 例如,如果表字段已弃用,绑定期间可能会在 编码(但不改变解码)。不过, 运行时行为不在此提案的范围内。
总而言之,不对称弃用过于细微且过于复杂,无法纳入 此提案。这些挑战可能会在未来的 RFC 中得到解决 不对称弃用的好处是值得的。
替代方案:完整历史记录 IR
在此方案下,版本信息仅存在于 JSON IR 之前。一次
IR 已生成,我们正在努力解决。这是
足以生成绑定,但对于 fidldoc 之类的工具用处不大
它可能需要使用版本信息这些工具无法解析
.fidl
文件,或者通过在多个位置比较 JSON IR 来推断生命周期
替代方法是引入一种新的 JSON IR 模式,
包含所有历史记录和可用性信息。这不同于
在 IR 中添加库存状况属性,因为这意味着
在最新版本中标记为“已移除”的元素。
这种替代方案存在两个问题。首先,部分用户
JSON IR 文件的架构和用途应与其他文件略有不同。
设计一种全新的格式可能会更好,但这也存在一些弊端。
其次,我们很难确定完整的历史 IR 应该是什么样的
不知道该呈现哪个界面例如,
为其 resource
修饰符添加又移除 10 次的类型显示?
对于这类问题和所用的表示法,在
单独的 RFC。
先验技术和参考资料
此提案是 RFC-0002: Platform 中列出的总体计划的一部分, 版本控制。阅读 RFC 对于理解 背景信息和动机。其先验技术和 参考部分重点介绍其他操作系统: Android、Windows 和 macOS/iOS。在本专精课程中,我们将重点介绍 和 IDL 及其 API 版本控制的方法。
Swift、Objective-C
Swift 使用的 @available
属性与本文中的属性非常相似
而 Objective-C 会使用类似的 API_AVAILABLE
属性。
它们仅限于一系列硬编码的 Apple 平台,例如 macOS 和 iOS。
他们还可以使用 swift
平台,根据
编译期间正在使用的 Swift 语言版本。版本指定为
一个、两个或三个数字(以英文句点分隔),遵循 semver 语义。两者都有
语言为在运行时检查平台版本提供了类似的语法。
Rust
Rust 使用稳定性属性为其标准库添加注解
#[stable]
、#[unstable]
和 #[rustc_deprecated]
。每个不稳定元素
链接到 GitHub 问题,并且只能由选择接受
对应的 #[feature]
属性。稳定的属性用于指明 Rust
元素的稳定版本。不过,这仅适用于
文档;并不控制可见性。
Protobuf、gRPC
Protocol Buffers 不提供版本控制工具。而是放置一个 与 FIDL 相比,更加注重向前和向后兼容性。例如: 没有结构体(只有类似于 FIDL 表的 messages),没有严格的限制 类型(所有类型都具有灵活的行为),且不支持详尽的匹配 枚举(从 proto3 开始)。
Google Cloud API 将 Protocol Buffers 与 gRPC 结合使用,并提供有关 版本控制和兼容性。通过 版本控制策略基于惯例,而不是系统内置的功能。 API 在 protobuf 软件包末尾对其主要版本号进行编码,并且 包含在 URI 路径中这样,服务就可以支持多个 而且客户端会收到就位的向后兼容更新 即不采取措施进行迁移。