RFC-0076:FIDL API 摘要

RFC-0076:FIDL API 摘要
状态已接受
区域
  • FIDL
说明

提供可读性良好的 FIDL API 接口的格式。

问题
Gerrit 更改
作者
审核人
提交日期(年-月-日)2021-03-16
审核日期(年-月-日)2021-03-16

摘要

介绍了一种用于描述 FIDL API Surface 的总结方法,其中以人类可读的格式作为第一个输出,并提出利用此总结来识别 Fuchsia 源代码树中对 FIDL 库的 API 更改。

修订版(2022 年 8 月)。本文档介绍了一种人类可读的文本格式,其中每个 API 元素都位于单独的一行中。在实现过程中,我们添加了包含相同信息的 JSON 格式 (https://fxrev.dev/480357)。与文本格式不同,JSON 格式可以解析回 Go 数据结构,这对 fidl_api_diff 特别有用。由于目前只使用 JSON 格式,因此我们移除了文本格式,以便于维护。

设计初衷

在撰写本文时,我们已经在 Fuchsia 项目中启动了多项工作,它们的共同目标是跟踪平台 API 接口的更改。完成后,我们将能够使用版本控制将平台开发与 SDK 使用者的库版本分离。

具体而言,在 FIDL 领域,需要对 FIDL 库的 API Surface 进行人类可读的表示。这种表示法(以下简称“摘要”)可通过多种方式使用:

  • 作为 FIDL 库提供的 API 的易于人阅读的清单。

    生成 API Surface 的其他软件(例如 go)也会保留类似的清单。这样,在这种版本控制至关重要的情境中,就可以将版本归因于特定的 API 摘要。

  • 作为检测 FIDL API 中向后不兼容的更改的基础。

    API 总结可用于计算两个 API Surface 之间的差异,从而以自动化方式检查一个 API Surface 是否可以演变为另一个 API Surface。与目前使用的算法相比,这种方法在精确性方面有所改进。该算法会以可预测的方式串联所有源文件并移除注释和不相关的空格,从而生成库源代码的稳定(称为“标准化”)形式。

  • 作为其他工作(例如兼容性测试套件 [CTS,请参阅 RFC-0015])的基石,用于检测 API 更改后需要运行的测试。

    特别是,CTS 需要缩减在平台更改后运行的一系列测试。了解 API Surface 中发生了哪些变化,有助于软件仅运行受更改影响的测试,从而节省执行时间和计算资源。

入门示例

请考虑以下 FIDL 库定义,该定义取自 fuchsia.accessibility.gesture。我们已精简了注释,但该库的其他部分保持不变。

library fuchsia.accessibility.gesture;

/// Maximum size of a returned utterance.
const uint64 MAX_UTTERANCE_SIZE = 16384;

/// Gesture types that accessibility offers to a UI component for listening.
enum Type {
    THREE_FINGER_SWIPE_UP = 1;
    THREE_FINGER_SWIPE_DOWN = 2;
    THREE_FINGER_SWIPE_RIGHT = 3;
    THREE_FINGER_SWIPE_LEFT = 4;
};

/// An interface to listen for accessibility gestures.
protocol Listener {
    /// When accessibility services detect a gesture, the listener is informed
    /// of which gesture was performed.
    OnGesture(Type gesture_type) -> (bool handled, string:MAX_UTTERANCE_SIZE? utterance);
};

/// An interface for registering a listener of accessibility gestures.
[Discoverable]
protocol ListenerRegistry {
    /// A UI registers itself to start listening for accessibility gestures
    /// through `listener`.
    Register(Listener listener) -> ();
};

上述库的 API 摘要如下所示:

protocol/member fuchsia.accessibility.gesture/Listener.OnGesture(fuchsia.accessibility.gesture/Type gesture_type) -> (bool handled,string:16384? utterance)
protocol fuchsia.accessibility.gesture/Listener
protocol/member fuchsia.accessibility.gesture/ListenerRegistry.Register(fuchsia.accessibility.gesture/Listener listener) -> ()
protocol fuchsia.accessibility.gesture/ListenerRegistry
const fuchsia.accessibility.gesture/MAX_UTTERANCE_SIZE uint64 16384
enum/member fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_DOWN 2
enum/member fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_LEFT 4
enum/member fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_RIGHT 3
enum/member fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_UP 1
strict enum fuchsia.accessibility.gesture/Type uint32
library fuchsia.accessibility.gesture

请注意以下几点:

  • 每个 API 元素都是一行文本。
  • 每个 API 元素都通过其完全限定名称进行引用。
  • API 元素在摘要中的显示顺序是固定的。如果更改 FIDL 文件中的声明顺序,这对 API 摘要的格式没有影响。
  • 您可以轻松使用 grep 等文本工具提取摘要的部分内容。例如,假设 API 摘要位于名为 fidl.api_summary 的文件中,则以下命令行仅会提取该协议的 API 接口:

    cat fidl.api_summary | grep "fuchsia.accessibility.gesture/ListenerRegistry"
    

    同样,只提取方法也很简单:

    cat fidl.api_summary \
      | grep "fuchsia.accessibility.gesture/ListenerRegistry" \
      | grep "protocol/member"
    
  • 您可以通过以下方式生成基本 API Surface 差异:

    diff -u fidl.old.api_summary fidl.new.api_summary
    

    (假设 fidl.{old,new}.api_summary 分别包含原始和修改后的 API Surface)

要求

  • API 摘要应采用人类可读的格式,并且可使用 grepdiff 等简单工具进行处理。

  • 生成的 API 摘要必须列出 FIDL 库中所有会影响 API 接口的元素,且仅列出这些元素。

设计

API 摘要格式包含与 API 影响相关的库信息。此信息在 Definitions: Source Compatibility and Transitionability of RFC-0024 部分中定义,并在 FIDL 绑定规范中捕获。它实际上只是 FIDL IR 中已有信息的一部分,但以更易于人类阅读和文本实用程序处理的方式呈现。如需查看完整列表,请参阅规则摘要

所有 FIDL 语言结构都涵盖在总结规则中。

每个 FIDL 声明均使用完全限定名称进行命名。例如,在以下摘要中(摘自上述示例):

library fuchsia.accessibility.gesture;
enum Type { THREE_FINGER_SWIPE_UP = 1; };
protocol Listener {
  OnGesture(Type gesture_type);
};

标识符 OnGesture 始终称为 fuchsia.accessibility.gesture/Listener.OnGesture

文件格式刻意保持扁平,以便于读取和处理。也就是说,FIDL 成员(显示在 structprotocol 等作用域中)会列在单独的文本行中。这样一来,我们日后便可以在需要时扩展此格式。例如,在未来版本控制属性推出后,您就可以添加这些属性了。

单个 API 摘要文件会列出整个 FIDL 库中显示的所有声明,无论这些声明在多少个文件中指定。

声明在 API 摘要中显示的顺序与声明顺序无关且稳定。相关声明会被刻意放置在一起,以便于后续处理,但这并不是正确性要求:任何与声明顺序无关且稳定的排序都已足够。

API 摘要排序

声明的顺序派生自 FIDL AST:声明的编写方式与 AST 的后序遍历一致,在选择同级兄弟姐妹时,会优先选择字母数字较小的完全限定名称的标识符。

我们将这种排序称为“API 摘要排序”。

这种方法建议在 API 摘要文件中按以下顺序排列声明:

fuchsia.accessibility.gesture/Listener.OnGesture
fuchsia.accessibility.gesture/Listener
fuchsia.accessibility.gesture/ListenerRegistry.Register
fuchsia.accessibility.gesture/ListenerRegistry
fuchsia.accessibility.gesture/MAX_UTTERANCE_SIZE
fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_DOWN
fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_LEFT
fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_RIGHT
fuchsia.accessibility.gesture/Type.THREE_FINGER_SWIPE_UP
fuchsia.accessibility.gesture/Type
fuchsia.accessibility.gesture

如上所述的示例 FIDL 库。

无论声明在 .fidl 文件中的实际排序方式如何(包括是否分布在多个文件中),API 摘要中的声明排序都将保持不变。

API 摘要声明架构

下面列出了 API 摘要文件的简化 BNF,供您参考。

summary          ::= declaration_list

declaration_list ::= declaration
                   | declaration "\n" declaration_list
declaration      ::= library
                   | const
                   | bits
                   | bits_member
                   | enum
                   | enum_member
                   | struct
                   | struct_member
                   | union
                   | union_member
                   | protocol
                   | protocol_member
                   | alias

alias           ::= "alias" fqn
bits            ::= strictness "bits" fqn fp
bits_member     ::= "bits/member" fqn
const           ::= "const" fqn d fv
enum            ::= strictness "enum" ft
enum_member     ::= "enum/member" fqn fv
library         ::= "library" fqn
protocol        ::= "protocol" fqn
protocol_member ::= "protocol/member" fqn d
struct          ::= resourceness "struct" fqn
struct_member   ::= "struct/member" fqn ft [ fv ]
union           ::= strictness "union" fqn
union_member    ::= "union/member" fqn

resourceness    ::= "" | "resource"
strictness      ::= "flexible" | "strict"

d   ::= <FIDL protocol member type signature>
fp  ::= <FIDL primitive type>
fqn ::= <FIDL identifier>
ft  ::= <FIDL type>
fv  ::= <FIDL value>

实现

API 摘要由程序 fidl_api_summarize 实现。该程序将 FIDL IR 作为输入,并输出 FIDL API 摘要,其中两个文件名都指定为标志。调用方应遵循惯例,为此程序的输出使用扩展名 .api_summary,但这绝不是硬性要求。

性能

fidl_api_summarize 是对 FIDL IR 文件的简单转换。抽查结果表明,在足够大的库上运行时,程序会在大约 0.1 秒内完成运行。这意味着,该程序很可能可以在常规构建流程中在每个 FIDL 库上运行。

安全注意事项

fidl_api_summarize 的当前实现不会尝试验证 FIDL IR,并假定其输入始终生成为 fidlc 的有效输出。这可能会导致程序容易被格式错误的输入混淆,但很难说这是否可以用作 Fuchsia 构建过程的攻击媒介。

隐私注意事项

fidl_api_summarize 处理的信息到目前为止都是公开可见的代码库的一部分。我们有理由假设,适用于其输入的所有隐私权规则也适用于其输出。

这意味着,如果它曾经用于汇总非公开 FIDL 库代码,则其输出应遵循与其所使用的库代码相同的隐私保护标准。

测试

该程序使用丰富的示例输入库进行测试,这些输入会经过处理并与本地输出进行比较。这可确保在 Fuchsia 代码库的生命周期内获得一致的结果。

文档

fidl_api_summarize 的使用应记录在 https://fuchsia.dev 上的 FIDL 帮助页面中。

在先技术和参考文档

go 语言 API 会定期生成 API Surface 摘要