RFC-0076:FIDL API 摘要

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

提供了一种人类可读的 FIDL API 接口格式。

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

摘要

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

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

设计初衷

在撰写本文时,Fuchsia 项目已开始进行多项工作,共同目标是跟踪平台 API 表面上的更改。完成后,我们将能够使用版本控制来将平台开发与 SDK 使用者所用的库版本分离。

具体而言,在 FIDL 领域,需要一种人类可读的 FIDL 库 API 表面表示形式。这种表示形式(以下称为“摘要”)可用于多种用途:

  • 作为 FIDL 库提供的 API 的人类友好型清单。

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

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

    API 总结可用于计算两个 API Surface 之间的差异,从而提供一种自动检查一个 API Surface 是否可以演变为另一个 API Surface 的方法。与目前使用的方法相比,这种方法可提高精确度。目前使用的方法是通过以可预测的方式连接所有源文件并移除注释和无关的空格来生成库源的稳定(称为“已归一化”)形式。

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

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

入门示例

请看以下 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 Surface)

要求

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

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

设计

API 摘要格式包含有关具有 API 影响的库的信息。此信息在 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 秒内完成运行。这意味着,该程序很可能可以作为常规 build 流程的一部分在每个 FIDL 库上运行。

安全注意事项

fidl_api_summarize 的当前实现不会尝试验证 FIDL IR,并假定其输入始终是 fidlc 的有效输出。这可能会使程序容易受到格式错误输入的干扰,但很难判断这是否可以用作 Fuchsia 构建过程的攻击媒介。

隐私注意事项

fidl_api_summarize 进程目前所含的信息一直以来都是公开可见的代码库的一部分。合理假设是,适用于其输入的任何隐私权规则也适用于其输出。

这意味着,如果它用于总结非公开 FIDL 库代码,其输出应符合与所用库代码相同的隐私标准。

测试

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

文档

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

在先技术和参考资料

Go 语言 API 会定期生成 API 接口摘要