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 的试运行或详细调用(即使用
-n或--verbose标志)中,此功能会自动停用。如果 Ninja 未在交互式 / 智能终端中运行,此功能会自动停用。
运行控制台命令时,此功能也会暂停(在上面的示例中运行 Bazel 操作时可以看到)。
借助此功能,您可以轻松直观地了解 build 中的瓶颈,即阻止其他命令并行运行的长时间运行命令。
命令表默认每秒更新 10 次,这对于了解哪些长时间运行的命令会阻碍 build 过程非常有用。您可以将 NINJA_STATUS_REFRESH_MILLIS 设置为以毫秒为单位的十进制值,从而更改刷新周期(不过,由于经过的时间仅打印到一位小数,因此低于 100 的任何值都会被忽略)。
功能:支持 GNU Make Jobserver
GNU Make Jobserver 协议允许构建系统在任何时间点限制并发作业(即线程或进程)的总数,即使在存在递归构建工具调用时也是如此。
它需要一个顶级服务器来设置一个由参与的客户端(例如编译器、链接器,甚至是 build 工具)共享的作业槽池。
Fuchsia 特定的 Ninja 二进制文件既可以充当协议的客户端,也可以充当协议的服务器。
您可以在启动 Ninja 时通过 --jobserver 命令行标志或在环境中设置 NINJA_JOBSERVER=1 来启用服务器模式。
当 Ninja 启动时,它会查看 MAKEFLAGS 环境变量的值,并自动启用客户端模式。当 Ninja 从充当服务器的其他 build 调用时,此功能非常有用。
在 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。
功能:用户发起的正常关停
当用户按 Ctrl-C 停止 build 时,Ninja 会向其子进程发送 SIGINT,然后等待它们完成,这在某些极少数情况下可能需要很长时间。
如果用户在等待时第二次按 Ctrl-C,Ninja 现在会打印一条消息,其中包含一个显示仍在运行的命令的表格,还会向这些命令发送 SIGTERM 信号,以正常请求它们停止运行。
如果这还不够,用户按下的第三个 Ctrl-C 将向所有子进程发送 SIGKILL 以强制终止它们,并将控制权返回给用户。
以下动画图片展示了实际效果:

功能:对失败命令进行结构化日志记录
构建失败后,构建目录中的 .ninja_errors.json 文件将包含有关每个失败操作的详细信息,例如其命令、状态代码和缓冲输出(Bazel 构建操作除外)。
此文件的 JSON 架构在此处进行了说明,工具和开发者可以使用该架构报告和重现失败的操作,以便进行调试。
功能:持久模式,可缩短启动时间
通过在环境中设置 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显式停止服务器的任何正在运行的实例。服务器将日志消息写入 build 目录中的
.ninja_persistent_log文件。不过,您可以在启动服务器之前通过在环境中设置NINJA_PERSISTENT_LOG_FILE=<path>来更改其位置。对于
core.x64build 配置,每个服务器进程目前大约需要 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 上进行。