本文档介绍了 FIDL 的 API 版本控制功能。如需了解如何改进 Fuchsia API,请参阅 Fuchsia API 演变指南。
设计初衷
借助 FIDL 版本控制功能,您可以随时间更改 FIDL 库,同时保留为旧版库生成绑定的能力。您可以通过多种方式手动执行此操作:
将
.fidl
文件存储在v1/
目录中。如需进行更改,请将v1/
复制到v2/
,然后在其中更改文件。如需为旧版本生成绑定,请使用v1/
库而不是v2/
。将
.fidl
文件存储在 Git 代码库中,并在提交中进行更改。如需为旧版本生成绑定,请查看代码库的较旧版本。
第一种解决方案非常繁琐,会产生大量的重复内容。第二个解决方案在包含特定 FIDL 库之外许多内容的大型仓库(例如主 Fuchsia 仓库)中效果不佳。
FIDL 版本控制可以实现相同的目的,但不存在这些缺点。进行更改时,您可以使用 @available
属性来描述更改的时间(即在哪个版本)。如需为旧版本生成绑定,请将 --available
标志传递给 fidlc 并指定旧版本。
关于 FIDL 版本控制,需要注意以下两点:
它仅影响 API。版本仅在编译时存在,对运行时行为没有影响。
它可以表示任何在语法上有效的更改。虽然您可以通过版本控制来表示某项更改,但这并不表示该更改可以安全地进行。
概念
版本控制的单元是一组库,称为平台。按照惯例,库的命名以平台名称开头。例如,库 fuchsia.mem
和 fuchsia.web
属于 fuchsia
平台。
每个平台都有一个线性版本历史记录。版本是 1 到 2^63-1(含 1 和 2^63-1)之间的整数,或者是特殊版本 HEAD
和 LEGACY
之一。HEAD
版本用于最新的不稳定更改。LEGACY
版本与 HEAD
类似,但它还包含旧版元素。
如果 FIDL 库没有任何 @available
属性,则它属于 unversioned
平台。此平台只有一个版本,即 HEAD
。
命令行
FIDL 编译器接受 --available
标志来指定平台版本。例如,假设 example.fidl
在 fuchsia
平台中定义了一个没有依赖项的库,您可以在版本 8 中对其进行编译,如下所示:
fidlc --available fuchsia:8 --out example.json --files example.fidl
无论您选择哪个版本,fidlc 都会始终验证所有可能的版本。例如,即使错误仅在版本 5 中出现,上述命令也可以报告错误。
如果某个库 A
依赖于另一个平台上的库 B
,您可以使用 --available
标志两次为这两个平台指定版本。不过,A
在其整个版本记录中必须与为 B
选择的固定版本兼容。
语法
任何 FIDL 元素都可以使用 @available
属性。它采用以下参数:
参数 | 类型 | 备注 |
---|---|---|
platform |
string |
只能在 library 上使用 |
added |
uint64 |
整数或 HEAD |
deprecated |
uint64 |
整数或 HEAD |
removed |
uint64 |
整数或 HEAD |
replaced |
uint64 |
整数或 HEAD |
note |
string |
搭配“deprecated ” |
legacy |
boolean |
搭配“removed ” |
这些参数存在一些限制:
- 所有参数都是可选的,但必须至少提供一个。
- 参数必须是字面量,不能是对
const
声明的引用。 removed
和replaced
参数是互斥的。- 参数必须遵循
added <= deprecated < removed
或added <= deprecated < replaced
。 - 如果未指定,
added
、deprecated
、removed
、replaced
和legacy
参数会从父元素继承。
例如:
@available(added=1, deprecated=2, removed=3, legacy=true)
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;
继承
@available
的参数从库声明流向顶级声明,再从每个顶级声明流向其成员。例如,如果某个表是在版本 5 中添加的,则无需对其成员重复此注释,因为它们在表本身之前不能存在。下面是一个更复杂的继承示例:
@available(added=2, deprecated=3)
protocol Versioned {
// Equivalent to `@available(added=2, deprecated=3, removed=4, legacy=true)`.
@available(removed=4, legacy=true)
Removed(table {
// Equivalent to `@available(added=3, deprecated=3, removed=4, legacy=true)`.
@available(added=3)
1: message string;
// Equivalent to `@available(added=2, deprecated=3, removed=4, legacy=false)`.
@available(legacy=false)
2: count uint32;
});
};
弃用
弃用用于表示某个元素将来会被移除。
弃用元素时,您应在文档注释中添加 # Deprecation
部分并提供详细说明,并为 @available
属性添加一个 note
参数,并附上简要说明。例如:
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")
Deprecated();
@available(added=5)
Replacement();
};
自 2022 年 5 月起,废弃对绑定没有任何影响。不过,FIDL 团队计划使其以目标语言发出弃用注释。例如,上面的示例可以在 Rust 绑定中生成 #[deprecated = "use
Replacement"]
。
正在替换
借助 replaced
参数,您可以通过编写全新的定义来更改特定版本中的元素。这是更改 FIDL 元素某些方面的唯一方式,包括:
- 常量的值
- 结构体、表或联合成员的类型
- 表或联合成员的序数
- 声明的类型,例如将结构体更改为别名
- 方法中存在
error
语法 - 元素的其他属性,例如
@selector
- 修饰符,例如
strict
、flexible
和resource
如需替换版本 N
中的元素,请使用 @available(replaced=N)
为旧定义添加注解,并使用 @available(added=N)
为新定义添加注解。例如,以下代码段展示了如何将枚举从 strict
更改为 flexible
:
@available(replaced=5)
type Color = strict enum {
RED = 1;
};
@available(added=5)
type Color = flexible enum { // Note: flexible instead of strict
RED = 1;
};
您还可以使用替换来清理因长期的 API 变更而变得杂乱无章的 FIDL 库。只需将所有元素替换为新的定义,然后将旧定义移至名为 history.fidl
这样的单独文件中即可。
FIDL 编译器会验证是否每个 @available(replaced=N)
元素都有匹配的 @available(added=N)
替换。它还会验证是否每个 @available(removed=N)
元素都没有这样的替换项。此验证仅适用于直接添加注解的元素,不适用于继承 removed
或 replaced
参数的元素。
旧版
移除元素时,您可以使用 legacy=true
将其保留在 LEGACY
版本中。这样一来,您就可以在移除 API 级别之前为目标 API 级别的客户端保留 ABI,因为 Fuchsia 系统映像是基于 LEGACY
FIDL 绑定构建的。例如:
protocol LegacyExample {
@available(deprecated=5, removed=6, legacy=true, note="...")
LegacyMethod();
};
在此示例中,LegacyMethod
未出现在版本 6 或更高版本的绑定中,也未出现在 HEAD
中,但它会在 LEGACY
版本中重新添加。
参考编号
一个 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
属性。例如,以下代码无效,因为从版本 1 开始就存在 A
,但它尝试引用仅存在于版本 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;