FHO 子工具接口

FHO(Fuchsia 主机对象)是一组库接口,可将 ffx 与子工具(无论是已编译到 ffx cli 二进制文件中,还是作为 build 树或 SDK 中的单独二进制文件)连接起来。该库旨在实现先进的前向和向后兼容性,并允许使用 FHO 构建的二进制文件在大跨度的版本上相互通信。

在 ffx 子工具(使用 ffx 前缀运行的顶级命令)中,最好将 FHO 视为程序与运行 Fuchsia 的设备或虚拟机之间的兼容性层,让该工具忽略连接到设备所需的所有设置,为设备上的服务建立 FIDL 通道,并记录有关交互的有用信息。

FHO 主机端 (ffx CLI)

在 FHO 架构中,主机二进制文件 (ffx) 负责发现关于将运行子工具的环境的重要信息。这可能包括以下详细信息:

  • 用户和项目配置文件及其内容。
  • 管理活跃守护程序及其提供的服务。
  • 发现并连接到目标设备,包括尤其是默认设备(如果已配置)。
  • 发现子工具在项目和/或 SDK 中的位置。
  • 发现 SDK 并验证设备、SDK 和子工具之间的版本限制。

大多数工具应构建为外部进程,并可通过 SDK 或构建输出目录发现,但有些工具仍内置在 ffx 二进制文件中。这些工具不一定与单独编译的工具有很大不同,但它们的兼容性限制较为有限。不过,即使没有 SDK 也能访问某些工具,仍然很有帮助。

本文档的其余部分将介绍单独编译的子工具以及它们的发现和运行方式。

FHO 工具端

在理想情况下,基于 FHO 的子工具只是一个针对 Fuchsia 编写的程序,但使用 ffx 主机二进制文件提供的上下文信息(用于连接到设备上的服务)在主机上运行。

不过,可由 ffx 运行的工具具有特殊的入口点,用于传输工具正常运行所需的上下文信息。它们还需要具有一些元数据,可供 ffx 用于发现、验证兼容性和运行它们。

工具元数据

工具通常具有与之关联的 JSON 元数据文件,可能如下所示:

{
  "name": "echo",
  "description": "run echo test against the daemon",
  "requires_fho": 0,
  "fho_details": {
    "FhoVersion0": {}
  }
}

此 JSON 文件旨在向 ffx 提供版本控制信息,以及在运行 ffx helpffx commands 时输出的信息。上面的文件是“版本 0”FHO 说明,这是子工具接口的最低起点。

FHO 元数据版本

FHO 版本将按顺序进行编号。任何给定子工具的受支持版本可通过从 requires_fho 键到 fho_details 映射中最高值条目末尾的数字之间的范围来确定。子工具应该能够作为这两个值之间的任何版本运行。

在上面的示例中,子工具仅支持 FHO 版本 0。如果主机工具不支持版本 0,则必须忽略该子工具的实例,改为使用在别处找到的较新版本;如果根本未找到匹配的版本,则会出现错误。

主机工具预计会使用 fho_details 中比其支持的最高 FHO 版本下一个最高(或相同)条目。这意味着,例如,如果您运行的 ffx 版本对以下元数据原生支持版本 3

{
  "name": "echo",
  "description": "run echo test against the daemon",
  "requires_fho": 0,
  "fho_details": {
    "FhoVersion2": { "some_thing": "is_something" },
    "FhoVersion4": { "some_thing_else": "is_something_else" },
  }
}

主机工具将从 FhoVersion4 中提取元数据,并将程序作为 FhoVersion3 程序运行。

这样一来,随着时间的推移,您可以对元数据进行增量更改和破坏性更改,同时让工具本身能够支持各种版本。增量更改可以直接添加到当前版本的元数据字段中,较旧版本的主机工具会忽略它们,而较新的版本则会使用它们。

当需要对元数据进行重大更改时,可以创建一个新条目,先前的版本将继续使用旧的元数据结构。

子工具可以包含一个 FhoVersion0 部分(即使 requires_fho 较高),以表明可以在该更简单的模式下直接调用该工具。FhoVersion0 部分不应包含任何键。

可以在源代码树中找到当前的 FHO 元数据 JSON 架构。

工具发现

目前,可在以下位置搜索子工具:

  • 在 FFX 二进制文件本身中。
  • 在项目特定的构建输出位置。
  • 在已配置的 SDK 的元数据中。

基于路径的发现

如果 ffx 配置了 build 输出目录或任何其他用于搜索子工具的位置,则可能会在这些位置搜索与以下格式匹配的二进制文件:

/ffx-([-_a-zA-Z0-9]/)ffx-toolname,其中 toolname 是调用函数时依据的子命令名称。请注意,它不会将后面的连字符视为更深层的嵌套,即ffx-some-sub-tool 作为 ffx some-sub-tool(而非 ffx some sub tool)运行。子工具负责自己的嵌套命令层,不过 FHO 库本身可能为此提供了帮助程序。

这些文件必须带有一个名称相同且扩展名为 .json 的文件。此文件将是前面部分中描述的元数据,其中的详细信息必须与正在运行的二进制文件匹配(例如,顶层的 name 键必须与二进制文件和元数据文件名中的 toolname 相同)。

符合此模式且没有附带元数据文件的文件不会被视为子工具,并会被忽略。

SDK 发现

在 SDK 中,工具是通过元数据搜索发现的。系统会在 SDK 清单中搜索类型为 ffx_toolatoms,它指向元数据和可执行二进制文件的相对 SDK 位置。

为了运行子命令,系统将扫描与 ffx_tool 类型匹配的 SDK 条目,直到找到匹配的名称。与基于路径的搜索一样,名称中的连字符不会创建命令名称的更深层嵌套。

主机工具可以将 SDK 中的主要 ffx host_tool 二进制文件视为“默认”命令,在未找到子工具时服从,从而将其作为 FHO 版本 0(实质上调用子工具时就像只带有一个子命令的 ffx 一样)。

工具界面

如上所述,元数据在 ffx 二进制文件和正在运行的子工具之间声明了版本化接口。这样做的目的是随着时间的推移该接口将变得更有条理,但一开始会相对简单,以避免破坏子工具中当前内置的任何预期。

子工具可能支持比其元数据中所代表的更低版本的 FHO 接口,特别是在 ffx 不再支持版本 0 后,某些子工具可能需要继续实现版本 0,具体取决于它们在构建脚本等内容中的使用情况。

FHO 版本 0

FHO 版本 0 是第一个也是最简单的 FHO 版本。SDK 中不应包含仅支持版本 0 的工具,但 SDK 中包含的工具可能额外支持版本 0。

FHO 版本 0 子工具的调用过程非常简单。它只需使用调用 ffx 时指定的参数(包括 ffx 本身的所有参数)即可运行。也就是说,如果您调用:

> ffx --isolate-dir "/tmp/blah" echo stuff

您运行的 ffx 二进制文件会搜索匹配的 ffx-echo 二进制文件,如果其支持的最高版本为零,将按以下方式调用它:

> ffx-echo --isolate-dir "/tmp/blah" echo stuff

子工具必须支持其构建时使用的 ffx 版本已知的所有顶级 ffx 参数。它还会使用环境变量 FFX_BIN_ENV 运行,该变量指向运行它的 ffx 的顶级调用的文件系统路径(尽管不应覆盖它),或者该变量可以运行以从运行子工具的同一上下文环境中重新开始处理。

否则,子进程将被提供相同的 (stdin,stdout,stderr) 三元组和运行原始 ffx 调用的环境变量。

其原因如下:

  • 它允许在 build 中直接调用子工具,而无需构建主要 ffx 二进制文件,从而从 build 的关键路径中移除不必要的目标。
  • 它不会干扰希望完全控制运行中的输入和输出环境的现有插件。
  • 这样,此架构的第一个版本就可以专注于构建相应功能的更重要方面。