fx 工作流程

本文档介绍了在 Fuchsia 开发环境中使用 fx 工具的基本工作流。

设置 fx

fx 是一组子命令的入口点,这些子命令可简化许多与 Fuchsia 开发相关的任务。(运行 fx help 可查看所有可用的子命令。)

如果您使用 bash 或 zsh 作为 shell,强烈建议您运行 source scripts/fx-env.sh 以在 shell 中启用自动补全功能。此设置已经过测试,并经常与 bash 和 zsh 搭配使用,也可能适用于其他兼容的 shell。

  1. 前往 Fuchsia 检出代码,例如:

    cd ~/Fuchsia
  2. 向 shell 添加配置以包含 fx-env.sh:

    echo "source \"$PWD/scripts/fx-env.sh\"" >> "$HOME/.$(basename "$SHELL")rc"
  3. (可选)如果您想从 Fuchsia 脚本中获取其他便利工具,请运行以下命令:

    echo "fx-update-path" >> "$HOME/.$(basename "$SHELL")rc"
  4. 重启 shell:

    exec "$SHELL"

这些步骤提供了最明确的特征集,并且通常是非侵入性的。如果它在您的 shell 环境中导致 bug,请提交项目 bug。

如果出于某种原因,您需要使用多个 Fuchsia 检出(以下推荐的工作流应该可以避免这种需求),那么您可能需要执行上述操作以外的操作。在这种情况下,有几种方法可以很好地支持:

  • 始终明确执行 $FUCHSIA_DIR/scripts/fx
  • 在特定 Fuchsia 目录中工作时,使用 direnv 或 dotenv 等工具将 $FUCHSIA_DIR/.jiri_root/bin 添加到 $PATH。

常用日常工具

检出 Fuchsia 树后,您首先需要构建 Fuchsia,然后将其安装到设备上。fx 提供了一些命令来帮助解决此问题:

目标、主机和端到端上下文

Fuchsia 开发涉及针对不同环境编译和运行代码:

  1. 目标:Fuchsia 设备或模拟器(例如 x64 或 arm64 Fuchsia)。
  2. 主机:您的开发工作站 (Linux)。

fx build

  • fx build(默认):构建要在 Fuchsia 目标上运行的工件(驱动程序、组件、命令行工具)。
  • fx build --host:构建旨在在开发主机上运行的工具或测试(例如 fidlc 编译器、zxdb 调试程序、主机端单元测试、端到端测试)。
  • 何时使用 --host:当您在开发开发者工具、主机端测试或需要构建主机二进制文件而无需构建整个 Fuchsia 系统映像时,请使用此工具。

fx test

  • fx test(默认):运行所选测试。它会自动检测测试是主机测试还是设备测试,并在适当的环境中执行测试。
  • fx test --host:过滤选择,仅运行主机测试(在工作站上运行)。
  • fx test --device:过滤选择,仅运行设备测试(在 Fuchsia 设备/模拟器上运行)。
  • fx test --e2e:支持运行端到端测试。这些测试在宿主机上运行,但会与 Fuchsia 设备交互,以测试系统级行为。它们通常不包含在默认测试运行中,因为它们需要已连接的设备,并且可能速度较慢或产生较重的负载。

配置 build

首先,我们来配置 build。为此,您需要做出一些选择:

  • 您想要哪种产品配置? (不确定:请尝试 workbench_eng)
  • 您要为哪个开发板构建?(不确定:请尝试 x64)
  • 您还需要哪些额外的build 目标?(例如: --with-test //my/team:tests)

根据上述选择(如果您尚未阅读上述内容,请立即阅读),您就可以配置 build 了:

fx set workbench_eng.x64 --with-test //my/team:tests

在结账流程中运行 fx set 后,您无需再次运行,除非您需要修改后面的参数。

如需在不运行 fx set 的情况下向 build 配置添加更多测试,您可以使用以下命令:

  • fx add-test <label>:附加目标端测试标签(添加到 developer_test_labels)。
  • fx add-host-test <label>:附加符合主机工具链条件的主机端测试标签(添加到 host_test_labels)。

fx set 会将其配置存储在输出目录中的 args.gn 文件内。输出直接为 out/default。您可以使用 fx --dir <out_dir> set <set_args> 指定其他目录。

您可以使用 fx args 命令修改生成的 args.gn 文件,以创建更复杂的配置。fx args 将在编辑器中打开 args.gn,让您进行更改,并重新生成 build 图。

刚刚发生了什么?

  • 您选择了产品 workbench_eng(运行 fx list-products 可查看其他产品配置的列表)。
  • 您选择了开发板 x64,它支持许多基于 x64 架构的典型开发板。(请注意,arm64 开发板的可互换性较低,因此在针对 arm64 目标硬件进行构建时,您很可能需要为 fx set 提供特定的开发板。另请注意,通用 arm64 板配置不支持在模拟器中运行;您必须改用 qemu-arm64。运行 fx list-boards 可查看已知开发板配置的列表。)
  • 您正在临时构建 tests,作为 universe 软件包集的一部分。

软件包部署选项

--with 选项有三种变体,与软件包如何部署到 Fuchsia 设备相关:--with-base、--with-cache 和 --with(表示 universe)。

除了这些部署轴之外,fx set 还为特定上下文提供了标志:

  • --with-host:添加在开发主机上运行的工具或测试(例如 zxdb)。这些工具或测试不会部署到 Fuchsia 设备。请参阅主机上下文。
  • --with-test:向 build 添加特定测试(例如 //my/team:tests)。这些测试会根据需要临时提供。

那么,base、cache 和 universe 是什么?

配置最终会指定有助于构建输出工件(主要是映像和软件包代码库)的依赖项(主要是软件包)。构建过程已参数化,以确定将哪些依赖项(主要是软件包)添加到哪些输出工件(映像或软件包代码库)。这三个轴分别称为“base”“cache”和“universe”:

  • Base:添加到 Base 的软件包会包含在 build 生成的系统映像中。它们包含在无线下载更新中,并且始终作为一个单元进行更新。基础软件包无法在运行时从设备中逐出,它们对配置的最小可能大小进行编码。
  • 缓存:缓存中的软件包包含在系统映像中,但不包含在无线下载系统更新中,并且允许在系统资源需求(例如磁盘可用空间压力)发生变化时从系统中逐出。缓存中的软件包可以随时更新(只要有可用更新),并且每个软件包都可以单独更新。这些软件是“可选”的,但最好能“开箱即用”。
  • Universe:Universe 中的软件包是额外的可选软件包,可以按需提取和运行,但不会预先嵌入到任何系统映像中。

“board”和“product”配置会为每个软件包集选择一组预定义的成员。最常见的情况是,开发板配置指定一组要添加到基本依赖项集的启动关键驱动程序,并且可能在缓存集中包含一些可选但常见的驱动程序。开发板配置还可能包含一些特定于开发板的开发者工具(通常是主机工具,而不是目标软件包),用于与“universe”中的开发板进行交互。产品配置会根据其所代表产品的定义和特征集,选择向基础、缓存或通用软件包集添加更多或更少的软件。例如,音响设备产品会向基础映像添加许多与音频媒体相关的软件包。工作台产品会向基础产品添加各种 GUI、媒体和许多其他软件包。

主要产品配置

除了以下配置之外,还有许多其他配置,但您需要特别熟悉以下三种重要配置:

  • bringup 是一款功能集极简的产品,专注于提供非常简单、非常精简的功能。它旨在提供快速构建和小映像(主要用于 netboot,而不是时尚),非常适合处理非常低级的设施,例如 Zircon 内核或特定于开发板的驱动程序和配置。它缺少大多数网络功能,因此无法在运行时添加新软件或自行升级。这也意味着,某些 fx 命令(例如 fx serve 和 fx shell)无法与 bringup 产品搭配使用。
  • core 是一个最小特征集,可以安装其他软件(例如添加到“universe”依赖项集的项)。它是所有更高级别产品配置的起点。它具有常见的网络功能,并且可以通过无线下载方式更新系统。
  • workbench_eng 是通用开发环境的基础,非常适合用于开发界面、媒体和许多其他高级功能。这也是爱好者玩耍和探索的最佳环境。

关键的其他构建目标

fx set 的 --with 和 --with-test 标志接受任意构建目标。为方便起见,我们定义了许多软件包,其中包括各种常用的构建目标。请务必熟悉以下软件包:

  • //bundles/tools 包含各种最常用的开发者工具。 这包括用于从命令行 shell 生成组件的工具、用于重新配置和测试网络、发出 HTTP 请求、调试程序、更改音量等的工具。默认情况下,核心产品在 universe 软件包集中包含 //bundles/tools。
  • //bundles/tests 会导致构建所有测试程序。大多数测试程序都可以使用设备上的 run-test-suite 或通过 fx test 调用。
  • //bundles/kitchen_sink 是一个会导致包含所有其他 build 目标的目标。在测试核心更改的影响或在代码库中进行大规模更改时,此功能非常有用。对于爱好者来说,这也是一个有趣的配置,因为它包含源代码树中提供的所有软件。请注意,Kitchen Sink 会生成超过 20GB 的 build 工件,并且目标设备上至少需要 2GB 的存储空间(大小估算值来自 2019 年第 1 季度)。

执行构建

如上所示,使用 fx set 配置构建后,您可以使用 fx build 运行构建。此命令会构建所有必需的目标及其输出。

清理 build

您可以继续对检出中的文件进行更改,并运行 fx build 进行重新构建。如果存在先前构建的结果,构建系统会尝试减少工作量。使用先前构建结果的构建称为增量构建,通常比全新重新构建快得多。

更改配置不应导致增量 build 损坏,但在极少数情况下,由于build 系统的限制,仍可能会发生这种情况。如果发生这种情况,请提交详细的 bug 报告,其中应包含重现问题的所有步骤以及任何诊断信息(例如 build 日志)。然后,使用以下命令进行恢复:

  • fx clean 将清除所有 build 工件。
  • fx clean-build 相当于 fx clean,之后价格为 fx build。
  • fx cleandead-build 相当于 fx cleandead,之后价格为 fx build

启用增量软件包重新构建

默认情况下,fx build 会为指定的产品配置构建所有软件包。这对于某些开发者工作流是必要的,但对于许多其他工作流来说则过于繁琐。 如果您只是在迭代测试,则无需重新构建和重新发布所有临时(universe)软件包。

您可以通过将 export FUCHSIA_DISABLED_incremental=0 添加到 ~/.bashrc 或等效项来启用增量重新构建。此变更会产生以下影响:

  • (fx serve) fx-serve-ref 会监控在 Fuchsia build 中创建或修改的软件包,并自动发布这些软件包。这样,您就可以让 fx serve 从空树运行,并且它会在您工作时以增量方式发布更改。

  • fx test 仅构建运行所需的最小目标。对于组件测试,则是软件包、其 GN 依赖项和 //zircon。

请注意,fx build 的行为保持不变。

迭代缓存软件包

运行 fx build 会触发完整的系统组装,并编译和发布缓存软件包。如果您的工作流只需要迭代重新构建缓存软件包,则运行 fx build 可能会过多并导致速度变慢。

对于重新构建缓存软件包,您可以通过将 fx build 替换为 fx publish cache 来提高构建速度。以下是它在典型开发工作流中的可能形式:

# Set up your workspace here.
$ fx serve

# Make changes to cache packages here.

# Rebuild and publish cache packages:
$ fx publish cache

# Restart your component, for example:
$ ffx component stop /core/your_component
$ ffx session restart

已知问题:重启 Fuchsia 模拟器不会导致组件从更新后的软件包运行。这些组件需要手动重启。

如需查看所有缓存软件包的列表,您可以运行 fx list-packages --cache。

构建特定目标

可以为 fx build 指定要构建的特定目标或文件的名称。

如果启用了实验性 build-with-labels 功能,则可以直接将 GN 标签传递给 fx build。例如,fx build //examples/hello_world 将为 Fuchsia 构建 //examples/hello_world:hello_world GN 目标,而 fx build --host //examples/hello_world 将为主机进行构建。

如果未启用该功能,您也可以为其中一个 GN 目标的输出传递 Ninja 目标路径。对于在默认工具链中定义的 GN 目标,GN 会定义类似于 GN 标签的 Ninja 别名,但不带起始 // 前缀,因此 fx build examples/hello_world:hello_world 将在默认工具链中构建 GN 目标 //examples/hello_world 的输出。

不过,如果目标是在非默认工具链中声明的,您将不得不猜测 Ninja 输出路径,这通常非常棘手。不过,对于主机二进制文件,它通常位于 host_x64 子目录下。因此,如果 //foo/bar:blah(//build/toolchain:host_x64) 目标仅定义为 executable(),则可以使用 fx build host_x64/blah 构建该目标。

如需详细了解 build 目标,请参阅构建系统概览。

创建商品套装 ZIP 文件

如果您已经构建了 Fuchsia 产品(通过运行 fx build),则可以运行以下命令来创建包含产品软件包的 ZIP 文件:

fx create-pb-zip -o <path-to-pb-zip>

此命令会在您指定的路径中创建一个包含产品包的 ZIP 文件。

构建和上传商品包 ZIP 文件

fx build-and-upload 命令提供了一种便捷的方法,只需一步即可构建 Fuchsia、创建产品软件包 ZIP 文件并将其上传到 Google Cloud Storage (GCS) 存储桶。

fx build-and-upload

默认情况下,此命令会将产品包上传到 gs://fuchsia-build-stage 存储桶。不过,您可以使用 --bucket(或 -b)标志指定其他 GCS 存储桶:

fx build-and-upload --bucket gs://<bucket-name>

上传完成后,输出的 GS 网址可以直接与 ffx 命令搭配使用,以刷写目标设备或启动模拟器,例如:

# Flash a target device
$ ffx target flash -b gs://fuchsia-build-stage/pb.zip
# Start the emulator
$ ffx emu start gs://fuchsia-build-stage/pb.zip -H

准备 Zedboot U 盘

将 Fuchsia 刷写到目标设备所需的确切准备工作因具体设备而异,但您可以使用 fx mkzedboot 为 x64 设备准备可启动的 U 盘,以便启动到 Zedboot。

什么是 Zedboot?

Zedboot 是 Zircon 的一种特殊配置,包含一个简单的网络栈、一个简单的设备通告和发现协议,以及一套用于将 Fuchsia 写入目标磁盘和/或通过网络启动目标系统的协议。Zedboot 是一个术语,既指整个过程,也指一种特殊的 build 配置。许多人将其称为“带有 ASCII 艺术的蓝屏”。

如需在 arm64 目标上进入 Zedboot,请在触发启动到 fastboot 刷写模式的同时开启设备(这通常涉及在重启或开机时按住特定按钮,具体取决于特定硬件目标)。进入刷写模式后,在主机系统上执行 fx flash。

如需在 x64 目标设备上进入 Zedboot,请先使用 fx mkzedboot <path-to-usb-device> 生成 Zedboot USB 密钥(如需列出系统上合适的 USB 设备,请执行 fx list-usb-disks)。完成后,移除 USB 密钥,将其插入目标设备,然后重新启动目标设备,从启动选项或设备 BIOS 中选择“从 USB 启动”。

什么是网络启动?

在 Fuchsia 中,“netboot”是指将一组工件发送到 Zedboot 实例,该实例不会对磁盘进行更改,而只会从 RAM 启动。用户可以执行“网络启动”,方法是先使用 fx flash (arm64) 或 fx mkzedboot (x64) 将设备启动到 Zedboot,然后在主机系统上执行 fx netboot。

提供 build

Fuchsia 的许多 build 配置都包含一些软件,这些软件不会立即包含在 build 生成的基础映像中,而是在刷写期间写入设备。相反,此类软件会按需提供给目标设备,通常被通俗地称为“临时软件”。

fx serve 命令在内部执行两项功能:

  • 启动 Package Repository 服务器,用于在运行时动态安装软件以及进行整个系统更新。

在内部,fx serve 命令还会搜索要配置的设备,并在发现设备(可能会受到 fx set-device 或 fx -t 的限制/调节)后,将目标设备配置为使用代码库服务器作为动态软件包和系统更新的来源。

更新目标设备

如前面部分所述,Fuchsia 设备上有不同的软件组:

  • 属于核心系统“基础”部分的软件,在单个事务中更新。
  • Zedboot 映像中除基础(缓存)映像之外、可临时更新的软件。
  • 始终为临时性的软件(宇宙)。

对于新用户开发工作流,用于协助更新目标设备的最通用命令是 fx ota。fx ota 命令会先更新“base”和“cache”软件,然后在更新完成后重新启动目标设备。此过程的最终结果在软件版本方面应与对设备执行全新刷写的结果没有区别。

由于 fx ota 过程会导致设备重新启动,因此有时对于诊断、调试或其他非基于测试的工作流或需求而言,它并不是最高效的过程。在这种情况下,用户可以通过一些方法来确保设备上的软件定期更新。

fx serve 进程会配置具有自动更新功能的 Fuchsia 软件代码库。每次底层代码库更新时(在每次成功 fx build 结束时发生),代码库都会向目标设备通知新更新的软件。对于许多软件组件,在开发期间更新它们的最简单方法是确保它们不包含在基本集中,而是包含在“缓存”或“通用”中。在这种情况下,只需在目标设备上重启软件(例如,通过完全关闭软件或调用 killall),软件就会在再次启动时立即更新。

执行测试

Fuchsia 代码库包含许多测试。这些测试大多本身就是组件,可以像其他组件一样在目标设备上启动。在目标设备上,一些程序还会协助处理组件启动时与测试相关的问题,例如 run-test-suite。您还可以通过 fx test 从开发主机轻松控制该过程。如需了解详情,请参阅运行 Fuchsia 测试。

一些用户发现,一种有效的高专注度工作流是让系统在他们每次保存源代码时构建、推送和执行测试。这可以通过 fx 非常轻松地实现,例如:

fx -i test hello-world-rust-tests

上述命令会在每次对树中的源代码进行更改时执行测试。fx 的 -i 标志会导致 fx 在每次树中的源代码发生更改时重复其命令的其余部分。由于 fx test 命令会先执行构建,然后对目标执行测试,因此这种组合提供了一个便捷的自动测试循环,非常适合测试驱动开发等高度集中的工作流。

连接到目标 shell

大多数产品配置都包含具有 Fuchsia 专用配置的 SSH 服务器。fx shell 命令是一个便捷的封装容器,用于通过 SSH 连接到目标设备,并提供对非常简单的 POSIX 样式 shell 的访问权限。用户应注意,虽然该 shell 是 POSIX shell 的分支,但它不提供常见 Unix shell 的所有功能。特别是,用户会发现 CTRL+C 具有奇怪的特性,并且可能经常会发现子 shell 表达式和某些更高级的 IO 重定向或环境变量传播的特性。这些错误功能是 Fuchsia 不是 POSIX 系统的副作用。

不过,通过 fx shell 提供的 shell 非常有用,可用于在 Fuchsia 目标上强制执行程序,以及探索文件系统树中提供的一些诊断/调试接口,例如 /hub 和 /dev。它还可用于调用 /bin/run 等程序,这些程序提供了启动 Fuchsia 组件的功能。如果 build 配置中提供了 tools 软件包,则许多 Unix shell 环境中常见的工具都已移植并可供使用,例如 ps、ls、cat、curl、vim 等。

执行其他常见任务

获取日志

fx log 会捕获来自低级和高级程序的所有日志,包括内核、驱动程序和其他用户空间程序。fx log 依赖于正常运行的高级网络栈和 SSH。因此,fx log 不适用于 Zedboot 或“bringup”产品配置。如果设备处于 fx log 停止运行的状态,切换到 fx klog 通常有助于捕获有关可能原因的更多信息。

fx klog 仅捕获名为“klog”的底层日志流。klog 流包含来自 Zircon 内核本身的日志,以及一部分用户空间软件(最明显的是驱动程序和低级核心软件)。fx klog 依赖于名为 netsvc 的轻量级网络栈,即使在更高级别的软件出现问题后,该网络栈也往往会保持可用。netsvc 套件也始终在“启动”产品配置中提供,因此,fx klog 在处理低级软件(例如 Zircon 内核或驱动程序)时最有用。

如需了解详情,请参阅查看日志。

复制文件

fx cp 围绕 scp 提供了一个基本封装容器,类似于 fx shell 是 ssh 的封装容器。

# copy ./book.txt from the host, to /tmp/book.txt on the target
$ fx cp book.txt /tmp/book.txt
# copy /tmp/poem.txt on the target to poem.txt on the host
$ fx cp --to-host /tmp/poem.txt poem.txt

使用多台 Fuchsia 设备

有些用户在网络上会有多个 Fuchsia 设备,并且希望将各种命令的效果限制在这些设备中的特定设备上。fx set-device 命令可帮助您解决此应用场景。

fx set-device 命令会将特定设备节点名称绑定到特定 build 目录。当用户希望在多个 build 配置中保留多个不同的设备时,此功能特别有用,可以按如下方式进行设置:

$ fx --dir out/workbench set workbench_eng.x64
$ fx build
$ fx set-device <workbench-node-name>

$ fx --dir out/core set core.qemu-arm64
$ fx build
$ fx set-device <core-node-name>

# Start a server for the workbench:
$ fx --dir=out/workbench serve
# Set the default build-dir and target device to the arm64 core, and
# connect to a shell on that device:
$ fx use out/core
$ fx shell

此外,对于希望从当前默认 build 目录针对单个 Fuchsia 设备执行命令的用户,fx 全局标志 -d 允许在单次命令调用中替换目标节点名称,作为一次性命令。

重启设备

fx reboot

在某些设备(目前大多数 arm64 设备)上,还有一些有用的标志:

  • fx reboot -r 重新启动到“恢复”模式 (Zedboot)
  • fx reboot -b 重启到“引导加载程序”(刷写)

确定 CL 的状态

您可以使用 fx cl 命令在 Gerrit 中使用新的浏览器标签页打开当前 CL。

调试和开发 fx 命令

  • fx -x -x 标志会开启对 fx 脚本的跟踪,输出在 fx 调用期间求值的所有表达式。
  • fx exec 在当前 fx 环境中执行后续的任意程序。例如,fx exec env 会输出该环境中的所有环境变量(fx exec env | grep FUCHSIA 可能是您感兴趣的环境变量)。

获取fx方面的帮助

fx help <command> 为该命令提供了最佳入门文档。部分命令还支持并提供 fx <command> -h 或 fx <command> --help,但并非所有命令都提供此帮助。 这种情况并不常见,但属于实现细节。在内部,许多 fx 命令只是运行其他程序(通常是由 build 生成的程序),并且在许多情况下,标志会原封不动地传递给这些程序。在这些情况下,传递常用的 -h 或 --help 标志可能不会提供 fx <command> 的文档,而是提供 fx 下游调用的程序的文档。

用户应始终从 fx help <command> 开始。

不带其他参数的 fx help 会提供 fx 中所有可用命令的列表,以及 fx 全局标志的文档。

显示待处理的提交

fx pending-commits 会显示尚未发布到全局集成的提交。

如需查看 Fuchsia 的集成信息中心,请参阅构建器。

将 Fuchsia 检出同步到发布分支

如需将本地 Fuchsia 检出同步到特定发布分支,请运行以下命令:

fx sync-to RELEASE_BRANCH

将 RELEASE_BRANCH 替换为您要切换到的发布分支(例如 refs/heads/releases/f6)。如需查看所有可用发布分支的列表,请参阅 Fuchsia 全局集成代码库页面上的“分支”部分。

以下示例命令会将 Fuchsia 检出同步到 F6 版本分支:

fx sync-to refs/heads/releases/f6

如需将 Fuchsia 检出重置为树的顶部,请运行以下命令:

fx sync-to reset

如需了解更多选项,请参阅 fx sync-to 参考页面。

定义永久性本地构建参数

如果您想定义每次运行 fx set 时都会包含的 build 参数,请将其添加到 $FUCHSIA_DIR/local/args.gn。每当重新生成 build 参数时,这些参数都会附加到 $FUCHSIA_BUILD_DIR/args.gn。

如需禁止包含 local/args.gn,请运行 fx set ... --skip-local-args。