Fuchsia build 使用 GN 的新 root_patterns 功能来大幅减小 GN 和 Ninja build 图的大小。这可显著缩短 gn gen 时间和 Ninja 启动时间。
不过,此功能在解析 BUILD.gn 文件时会以令人意外的方式更改 GN 的默认行为。本文档将对此进行说明。
GN 默认行为
出于历史原因,当在默认工具链的上下文中评估 BUILD.gn 文件时,GN 会实例化该文件中定义的每个目标,即使实际上没有任何内容依赖于这些目标。
例如,请考虑以下三个 BUILD.gn 文件,每个文件都定义了两个目标,并且它们之间存在一些依赖关系:
_//BUILD.gn____ _//foo/BUILD.gn__ _//bar/BUILD.gn_
| | | | | |
| A | | D -------------> E |
| | | | | |
| B --------------> C | | F |
|_______________| |_________________| |________________|
当 GN 解析此 build 方案时,会发生以下情况:
GN 首先加载
//BUILD.gn并对其进行评估。由于它使用默认工具链,因此会为该文件中的所有条目(即//:A和//:B)创建目标。GN 会遵循其刚刚创建的目标的依赖项,由于
//:B依赖于//foo:C,因此它会先加载//foo/BUILD.gn并对其进行评估。由于它仍位于默认工具链中,因此也会实例化该文件中的所有目标,从而创建
//foo:C和//foo:D,即使后者不是//:A或//:B的依赖项也是如此它会再次遵循依赖项,加载
//bar/BUILD.gn,对其进行评估,并创建此处定义的所有目标,即//bar:E和//bar:F
如果假设 //BUILD.gn 表示图的根,那么最终的 build 图将包含比所需更多的目标。
GN root_patterns
您可以在 .gn 文件中设置 root_patterns,或使用 --root-pattern=<pattern> 命令行选项(一次或多次)来更改此默认行为。
这些定义了目标标签模式的列表,用于过滤要在默认工具链中评估的 BUILD.gn 文件中存在的哪些非依赖项目标。
例如,使用 gn gen --root-pattern=//:* 和相同的 build 方案会以如下方式更改 GN 的行为:
GN 首先加载
//BUILD.gn并对其进行评估。由于它位于默认工具链中,因此文件中创建的任何目标(因为它们与模式匹配,//:*实际上是指“//BUILD.gn中的任何目标”)都会被纳入。因此,它会像之前一样创建//:A和//:B。GN 会遵循依赖项,然后加载
//foo/BUILD.gn并对其进行评估。它会创建//foo:C,因为这是它创建的某个先前目标的直接依赖项。不过,它不会创建//foo:D,因为其标签不符合//:*格式。GN 在此处停止,因为它未创建
//foo:D,因此没有理由加载//bar:BUILD.gn
因此,GN 会创建 3 个目标,而不是最终 build 图中的 6 个。
实际结果
在实践中,使用此功能可大幅减小 GN 构建图的大小,同时缩短 gn gen 时间。例如,使用 fx set minimal.x64 配置:
Default --root-pattern=//:* Reduction
Target count 183761 48375 -73%
Ninja files size (MiB) 571.7 180.2 -68%
`fx set` peak RAM (GiB) 5.02 2.89 -42%
`gn gen` time (s) 14.9 6.15 -58%
`fx set` time (s) 16.0 6.77 -57%
//:root_targets 目标。
//BUILD.gn 文件现在定义了一个名为 root_targets 的顶级目标,可用于向必须位于 build 图中的目标添加依赖项,即使没有其他内容依赖于这些目标也是如此。
这在以下几种情况下至关重要:
一些特殊的
generated_file()目标,其输出被其他目标用作隐式输入,但不能直接依赖于这些目标。一些硬编码的基础设施工具预期始终在树中构建的目标。
某些构建器配置本应列出但未在
universe_package_labels中列出的目标,只是由于同一BUILD.gn文件中定义了其他必需目标而假定这些目标存在。
应尽量减少向此列表添加的内容。如果您确实需要始终在最终的 Ninja build 清单中定义某些内容,最好从任何其他顶级 //BUILD.gn 目标中找到真正的传递依赖项。