RFC-0076:FIDL API 摘要

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

提供人类可读的 FIDL API Surface 的格式。

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

摘要

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

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

设计初衷

在撰写本文时,我们已开始对 Fuchsia 项目进行多项工作,共同跟踪对平台 API Surface 的更改。完成后,总体结果将使我们能够使用版本控制将平台开发与 SDK 使用方使用的库版本分离开来。

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

  • 作为 FIDL 库提供的简单易懂的 API 目录。

    其他生产 API 接口的软件(例如 go)会保留类似的目录。这样,在此类版本控制很重要的环境中,这允许将某个版本归因于特定的 API 摘要。

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

    API 摘要可用于计算两个 API Surface 之间的差异,从而提供了一种自动检查是否可以从一个 API Surface 演变为另一个 API 接口的方法。这是相对于当前所用方法的精度提升。这种方法通过以可预测的方式串联所有源文件并移除注释和不相关的空格,生成稳定(称为“标准化”)的库源代码形式。

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

    特别是,CTS 需要缩减在平台变更中运行的测试的电池电量。了解 API Surface 中的更改可以使软件仅运行受更改影响的测试,从而节省执行时间和计算资源。

入门示例

请考虑以下取自 fuchsia.accessibility.gesture 的 FIDL 库定义。注释精简,但库在其他方面是完整的。

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 Surface:

    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 摘要必须列出且仅列出 FIDL 库中会影响 API Surface 的所有元素。

设计

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 秒内完成运行。这意味着,该程序或许可以作为常规构建流程的一部分在每个 FIDL 库上运行。

安全注意事项

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

隐私注意事项

到目前为止,fidl_api_summarize 处理的信息已包含在可公开查看的代码库中。可以合理地假设,任何适用于其输入的隐私权规则也适用于其输出。

这意味着,如果将其用于汇总非公共 FIDL 库代码,则其输出应与其所用的库代码遵循相同的隐私权标准。

测试

该程序使用大量示例输入库进行测试,系统会对其进行处理并与本地输出进行比较。这样可以确保结果在 Fuchsia 代码库的整个生命周期内保持一致。

文档

您应通过 https://fuchsia.dev 上的 FIDL 帮助页面记录 fidl_api_summarize 的用法。

现有艺术和参考资料

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