特定于 Fuchsia 的 Ninja 改进

Fuchsia 构建系统使用自定义 Ninja 二进制文件,该文件可为开发者体验带来多项改进。本页将介绍这些功能。

设计初衷

有关为 Fuchsia 自定义 Ninja 的动机,请参阅 RFC-0153

简而言之,上游版本中很难获得的一些功能将使 Fuchsia 开发者受益匪浅。

所有 Fuchsia 特有的更改都在本地 Ninja Git 镜像的本地 fuchsia-rfc-0153 分支上执行,并定期进行变基,以便轻松地将它们作为 GitHub 拉取请求发送到上游项目,如 RFC 的策略部分中所述。

功能:正在运行的命令的状态

将环境中的 NINJA_STATUS_MAX_COMMANDS 设置为严格正整数,以便在智能终端中运行时,Ninja 可以在状态行下方打印运行时间最长的命令及其所用时间的表格。例如,使用 export NINJA_STATUS_MAX_COMMANDS=4 时,状态可能如下所示:

[0/28477](260) STAMP host_x64/obj/tools/configc/configc_sdk_meta_generated_file.stamp
  0.4s | STAMP obj/sdk/zircon_sysroot_meta_verify.stamp
  0.4s | CXX obj/BUILD_DIR/fidling/gen/sdk/fidl/fuchsia.me...chsia.media/cpp/fuchsia.media_cpp_common.common_types.cc.o
  0.4s | CXX obj/BUILD_DIR/fidling/gen/sdk/fidl/fuchsia.me...fuchsia.media/cpp/fuchsia.media_cpp.natural_messaging.cc.o
  0.4s | CXX obj/BUILD_DIR/fidling/gen/sdk/fidl/fuchsia.me...dia/cpp/fuchsia.media_cpp_natural_types.natural_types.cc.o

以下动画图片展示了实际效果:

Ninja 多行状态示例

请注意:

  • 在 Ninja 的试运行或详细调用(即使用 -n--verbose 标志)中,此功能会自动停用。

  • 当 Ninja 未在交互式 / 智能终端中运行时,此功能会自动停用。

  • 运行控制台命令时,此功能也会暂停(在上面的示例中运行 Bazel 操作时可以看到)。

  • 借助此功能,您可以轻松直观地了解 build 中的瓶颈,即阻止其他命令并行运行的长时间运行的命令。

默认情况下,命令表每秒更新 10 次,这对于了解哪些长时间运行的命令会阻碍 build 过程非常有用。您可以将 NINJA_STATUS_REFRESH_MILLIS 设置为以毫秒为单位的十进制值,从而更改刷新周期(不过,由于经过的时间仅打印到一位小数,因此低于 100 的任何值都会被忽略)。

功能:支持 GNU Make Jobserver

GNU Make Jobserver 协议允许构建系统在任何时间点限制并发作业(即线程或进程)的总数,即使存在递归构建工具调用也是如此。

它需要一个顶级服务器来设置一个由参与的客户端(例如编译器、链接器,甚至构建工具)共享的作业槽池。

Fuchsia 特定的 Ninja 二进制文件既可以充当协议的客户端,也可以充当协议的服务器。

您可以在启动 Ninja 时通过 --jobserver 命令行标志或在环境中设置 NINJA_JOBSERVER=1 来启用服务器模式。

当 Ninja 启动时,它会查看 MAKEFLAGS 环境变量的值,并自动启用客户端模式。当从充当服务器的不同 build 调用 Ninja 时,此功能非常有用。

在 Fuchsia build 中,在 args.gn 中设置 enable_jobserver = true 可让顶级 Ninja 调用以服务器模式启动。

例如,它已针对核心 IDK 和核心 SDK 构建器配置设置,可节省 6 到 12 分钟的构建时间。因为这些需要从顶级 build 启动 24 个以上的 Ninja 子 build,而这些子 build 可以利用该协议更好地协调它们各自如何生成多个并行命令。

功能:以 Chrome Trace JSON 数组格式生成 build 轨迹

--chrome_trace FILENAME 选项可用于告知 Ninja 在构建完成后(即使在失败的情况下)生成构建事件的轨迹。

建议在输出 FILENAME 中使用 .gz 后缀,以直接生成 gzip 压缩的轨迹文件,因为这些文件通常要小 20 倍。

该文件采用 Chrome Trace JSON Array 格式,可以直接加载到任何基于 Chromium 的浏览器的 chrome://tracing 标签页中,更有趣的是,还可以通过 https://ui.perfetto.dev 加载,后者还支持将压缩的轨迹作为输入进行读取。

请注意,生成的轨迹文件还包含有助于直观呈现 build 关键路径的流事件。相应 build 事件的 cat 字段的值为 critical_path

功能:持久模式,可缩短启动时间

通过在环境中设置 NINJA_PERSISTENT_MODE=1 来加快后续 Ninja 调用速度。此功能使 Ninja 启动后台服务器进程来读取一次 build 清单,然后在连续 build 之间将 build 图保留在内存中。

请注意:

  • 此功能应完全透明,不应以其他方式影响 Ninja 的行为。如果您发现任何问题或差异,请发送电子邮件至 fuchsia-build-team@google.com 告知我们!

  • 系统会自动检测输入 .ninja 文件的任何更改。在这种情况下,系统会关闭现有服务器,并自动启动新服务器。更改 GN build 文件或执行 jiri update 后,无需进行额外的用户互动。

  • 服务器进程在空闲 5 分钟后将正常关闭。 在您的环境中设置 NINJA_PERSISTENT_TIMEOUT_SECONDS=<count> 即可更改此延迟时间。

  • 使用 fx build -t server status 检索当前 build 目录的服务器状态。

  • 使用 fx build -t server stop 显式停止服务器的任何正在运行的实例。

  • 设置 NINJA_PERSISTENT_LOG_FILE=<path> 以将与持久模式相关的日志发送到指定的文件路径。

  • 对于 core.x64 build 配置,每个服务器进程目前大约需要 1 GiB 的 RAM。确切的数字将取决于 Ninja 图的大小,而这取决于您的 args.gn 配置。

  • 每个 Ninja build 目录最多只能由一个服务器进程提供服务。不过,如果使用多个 build 目录,则可能会有多个进程。

  • 忍者工具(例如 ninja -C <dir> -t commands <target>)尚未在服务器上运行,因此仍会使用缓慢的启动。我们会在未来修复此问题,以加快查询速度。

已知 bug / 注意事项(将解决):

  • 目前,在同一目录下混合使用持久 build 和非持久 build 可能会使服务器感到困惑,因为对 Ninja build 和依赖项日志的独立更改无法得到正确检测。此问题将会得到解决。

    解决方法:在环境中取消设置 NINJA_PERSISTENT_MODE 之前,使用 -t server stop 停止服务器。

  • “快速启动”需要几秒钟的时间。目前,每个增量 build 仍需要对 build 图中的所有文件调用 stat(),这目前需要几秒钟的时间。未来,我们将使用基于 inotify / kqueue 的主机操作系统文件系统监控功能来解决此问题,以便在仅修改了少量文件时立即开始。

  • 目前不适用于 Windows。这是因为存在技术 Win32 限制,该限制会阻止将控制台句柄复制到其他进程。这主要是上游 Ninja 团队的问题,因为 Fuchsia 开发不在 Windows 上进行。