本文档介绍了 FIDL 的 API 版本控制功能。如需了解如何 改进 Fuchsia API,请参阅 Fuchsia API 演变指南。
摘要
通过 FIDL 版本控制,您可以表示 FIDL 库随时间的变化。时间
进行更改后,可以使用 @available
属性来说明何时(即
哪个版本)会发生更改。要生成绑定,您需要将
--available
标志,用于指定一个或多个版本的 fidlc。
FIDL 版本控制提供 API 版本控制,而不是 ABI 版本控制。无法 查询版本。更改可能具有 API 破坏性,但应该 与 ABI 兼容。FIDL 编译器会执行一些基本的验证, 并不保证 ABI 兼容性。
概念
版本控制的单元是一组库,称为“平台”。修改者
惯例以平台名称开头,为库命名。例如:
库 fuchsia.mem
和 fuchsia.web
属于 fuchsia
平台。
每个平台都有一个线性版本历史记录。版本号是 1 中的整数
2^31-1(含)或特殊版本 NEXT
和 HEAD
之一。通过
“NEXT
”版本用于计划在下一个编号版本中执行的更改
版本。HEAD
版本用于最新的不稳定更改。
如果 FIDL 库没有任何 @available
属性,则它属于
unversioned
平台。此平台只有一个版本,即 HEAD
。
命令行
FIDL 编译器接受 --available
标志来指定平台版本。
例如,假设 example.fidl
在 fuchsia
平台中定义了一个库
可以按如下方式在版本 8 中编译它:
fidlc --available fuchsia:8 --out example.json --files example.fidl
您可以定位多个版本,只需使用英文逗号进行分隔即可,例如
--available fuchsia:7,8,9
。
如果库 A
依赖于来自其他平台的库 B
,
您可以使用 --available
标志两次为这两个平台指定版本。
不过,A
必须在其整个版本记录中与已修复的
为 B
选择的版本。
目标版本
以单个版本为目标时,绑定会包含
该版本中可用(由 FIDL 中的 @available
参数指定)
文件。
当您以一组版本为目标时,绑定会包含
任何版本的可用情况对于
replaced
,则绑定仅包含最新定义。
无论您以哪组版本为目标,如果 FIDL 编译成功, 也保证对该集合的所有子集都成功, 单例集。
语法
任何 FIDL 元素都允许使用 @available
属性。需要
以下参数:
参数 | 类型 | 备注 |
---|---|---|
platform |
string |
只能在 library 使用 |
added |
uint64 |
整数、NEXT 或 HEAD |
deprecated |
uint64 |
整数、NEXT 或 HEAD |
removed |
uint64 |
整数、NEXT 或 HEAD |
replaced |
uint64 |
整数、NEXT 或 HEAD |
note |
string |
与deprecated 搭配使用 |
renamed |
string |
与 removed 或 replaced 搭配使用;仅允许用于成员 |
参数有一些限制:
- 所有参数都是可选的,但必须至少提供一个。
- 参数必须是字面量,而不是对
const
声明的引用。 removed
和replaced
参数是互斥的。- 参数必须遵循
added <= deprecated < removed
,或added <= deprecated < replaced
。 added
、deprecated
、removed
和replaced
参数 继承父元素(如果未指定)。
例如:
@available(added=1, deprecated=2, removed=3)
const ANSWER uint64 = 42;
如果在库中的任何位置使用 @available
,则它必须也出现在
库声明。对于单文件库,这很简单。对于
具有两个或更多 .fidl
文件的库中,只有一个文件可以具有其库
声明。(该库在逻辑上被视为单个元素,
属性与各文件合并,因此对多个文件进行注释会得到结果
。)FIDL 样式指南建议
为此,我们将创建一个名为 overview.fidl
的文件。
在库声明中,@available
属性需要 added
实参,并允许使用 platform
实参。如果省略 platform
,则会
默认为库名称的第一个组成部分。例如:
// Equivalent to `@available(platform="fuchsia", added=1)`.
@available(added=1)
library fuchsia.examples.docs;
修饰符
通过 FIDL 版本控制,您可以在特定版本中添加或移除修饰符。在
修饰符,可以在括号中编写实参,就像在
@available
。但只能使用 added
和 removed
参数。
以下是将枚举从严格更改为柔性的示例:
type Color = strict(removed=2) flexible(added=2) enum {
RED = 1;
};
所有修饰符都支持此语法:strict
、flexible
、resource
、closed
ajar
和 open
。不过,更改对象上的 strict
或 flexible
修饰符
不允许使用不含错误语法的双向方法。
当您以一组版本为目标时,编译器会使用 最新的修饰符。在上面的示例中,如果将 包含等于或大于 2 的任何版本,即使还包括版本 1。
继承
@available
的参数从库声明流向顶级
从每个顶级声明传递给其成员。例如:
如果表是在版本 5 中添加的,则无需在
其成员,因为它们不能存在于表本身之前。这里有一个
更复杂的继承示例:
@available(added=2, deprecated=3)
open protocol Versioned {
// Equivalent to `@available(added=2, deprecated=3, removed=4)`.
@available(removed=4)
flexible Removed(table {
// Equivalent to `@available(added=3, deprecated=3, removed=4)`.
@available(added=3)
1: message string;
});
};
弃用
弃用用于表示某个元素将在将来被移除。
弃用某个元素时,您应将 # Deprecation
部分添加到
包含详细说明和 note
参数的
@available
属性。例如:
open protocol Example {
/// (Description of the method.)
///
/// # Deprecation
///
/// (Detailed explanation of why the method is deprecated, the timeline for
/// removing it, and what should be used instead.)
@available(deprecated=5, removed=6, note="use Replacement")
flexible Deprecated();
@available(added=5)
flexible Replacement();
};
自 2024 年 6 月起,弃用对绑定没有任何影响。不过,FIDL 团队
计划在目标环境中发出弃用注解
语言。例如,上面的示例可能会在 Rust 绑定中生成 #[deprecated = "use
Replacement"]
。
身份
FIDL 版本控制可以区分移除和替换 元素。为此,它依赖于 API 和 ABI 标识的概念。API 一个元素的名称就是它的名称ABI 标识取决于元素的类型:
- 位/枚举成员:值,例如5 英寸(
VALUE = 5;
) - 结构成员:offset,例如第一个成员为 0
- 表/联合成员:序数,例如5 英寸(
5: name string;
) - 协议成员:选择器,例如“example/Foo.Bar”在
library example; protocol Foo { Bar(); };
其他元素(例如类型声明和协议)没有 ABI 标识。
正在替换
借助 replaced
参数,您可以通过以下方式更改特定版本中的元素:
编写一个全新的定义。这是更改
FIDL 元素的各个方面,包括:
- 常量的值
- 结构体、表或联合成员的类型
- 声明的类型,例如将结构体更改为别名
- 方法是否存在
error
语法 - 元素的属性
要替换版本 N
的元素,请使用以下定义为旧定义添加注释
@available(replaced=N)
和 @available(added=N)
的新定义。
例如,下面展示了如何更改常量的值:
@available(replaced=5)
const MAX_NAME_LEN uint32 = 32;
@available(added=5)
const MAX_NAME_LEN uint32 = 64;
又例如,下面展示了如何更改表字段的类型:
type Data = resource table {
@available(replaced=5)
1: name string:32;
@available(added=5)
1: name string:64;
};
FIDL 编译器会验证相应位置的每个 @available(replaced=N)
元素是否
是具有相同身份的匹配 @available(added=N)
元素。
它还验证每个 @available(removed=N)
元素是否不
进行替代。此验证仅适用于直接注解的元素,
不应用于继承 removed
或 replaced
的元素
参数。
正在重命名
要重命名某个成员,请将其替换为新定义,并指定该成员
带有 renamed
参数的新名称(旧定义)。例如:
type User = table {
@available(replaced=2, renamed="first_name")
1: name string;
@available(added=2)
1: first_name string;
};
renamed
参数只能用于成员,因为 FIDL 编译器
依赖于其 ABI 身份进行验证。要重命名
声明,只需移除旧定义改为使用新定义即可:
@available(deprecated=2, removed=3, note="renamed to Information")
type Info = table {};
@available(added=2)
type Information = table {};
移除之后
通常,renamed
实参会与 replaced=N
搭配使用,但您也可以使用
并将其与 removed=N
搭配使用。这会为成员提供一个新名称,以便在其
移除。其运作方式取决于目标
版本:
- 如果您仅定位到
N
之前的版本,则绑定将使用旧名称。 - 如果您只以等于或大于
N
的版本为目标,则绑定不会 来添加这个成员 - 如果您定位的集合包含低于
N
的版本,并且包含 版本大于或等于N
,则绑定将使用新名称。
这样做的一个原因是,不鼓励 API 的新用法,同时继续 支持其实现。例如:
open protocol Door {
@available(removed=5, renamed="DeprecatedOpen")
flexible Open() -> ();
};
如果 Door
服务器是在面向版本集的代码库中实现
{4, 5},则该方法将被命名为 DeprecatedOpen
,不建议开发者使用。
添加方法的新用途如果其他代码库以版本 4 或
那么该方法将被命名为 Open
。如果它以版本 5 为目标平台,则此方法
完全不会显示
使用此功能的另一个原因是为新 ABI 重复使用一个名称。对于
例如,考虑更改 Open
方法以返回错误:
open protocol Door2 {
@available(removed=5, renamed="DeprecatedOpen")
flexible Open() -> ();
@available(added=5)
@selector("NewOpen")
flexible Open() -> () error uint32;
};
我们需要定义一个新方法,因为预计会出错的客户端
如果收到错误响应,请关闭通道。不过,我们可以继续使用
名称 Open
,前提是我们 (1) 使用 @selector
为新方法提供
不同的 ABI 身份;(2) 针对旧版定义使用 renamed
;
允许版本集 {4, 5} 的绑定包含这两个方法。
参考文档
一个 FIDL 元素可以通过多种方式引用另一个 FIDL 元素。例如:
const VALUE uint32 = 5;
const REFERENCES_VALUE uint32 = VALUE;
type Type = struct {};
type ReferencesType = table {
1: t Type;
};
alias ReferencesTypeAndValue = vector<Type>:VALUE;
引用元素时,必须遵守 @available
属性。对于
以下代码无效,因为 A
从版本 1 开始就存在,
但它会尝试引用仅存在于版本 2 中的 B
:
// Does not compile!
@available(added=1)
const A bool = B;
@available(added=2, removed=3)
const B bool = true;
同样,非弃用元素引用已弃用的
元素。例如,以下代码在版本 1 中无效,因为 A
引用了 B
,但 B
已被弃用,而 A
并非如此。
// Does not compile!
@available(deprecated=2)
const A bool = B;
@available(deprecated=1)
const B bool = true;