Fuchsia build 使用 GN 的新 root_patterns
功能,以大幅缩减 GN 和 Ninja build 图的大小。这样可以显著缩短 gn gen
时间,并缩短 Ninja 启动时间。
不过,在以可能出乎意料的方式解析 BUILD.gn
文件时,此功能改变了 GN 的默认行为。本文档介绍了操作方法。
GN 默认行为
由于历史原因,当在默认工具链的上下文中对 BUILD.gn
文件中定义的每个目标进行求值时,GN 都会实例化 BUILD.gn
文件中定义的每个目标,即使实际上没有任何目标依赖于这些目标也是如此。
例如,请考虑以下三个 BUILD.gn
文件,它们各自定义了两个目标,并且它们之间存在一些依赖关系:
_//BUILD.gn____ _//foo/BUILD.gn__ _//bar/BUILD.gn_
| | | | | |
| A | | D -------------> E |
| | | | | |
| B --------------> C | | F |
|_______________| |_________________| |________________|
当 GN 解析此构建计划时,会发生以下情况:
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 图假定 //BUILD.gn
表示该图的根,那么这会生成包含比所需数量远多的目标的最终 build 图。
GN root_patterns
您可以通过在 .gn
文件中设置 root_patterns
或使用 --root-pattern=<pattern>
命令行选项(一次或多次)来更改此默认行为。
这些标签定义了目标标签模式的列表,用于过滤要创建在默认工具链评估的 BUILD.gn
文件中的哪些非依赖项目标。
例如,使用具有相同构建方案的 gn gen --root-pattern=//:*
会在以下方面改变 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 build 图的大小,从而缩短 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()
目标,其输出会被其他目标用作隐式输入,但无法直接依赖于此类目标,例如//build/bazel:legacy_ninja_build_outputs
硬编码的基础架构工具应始终在树中构建一些目标。
某些构建器配置需要的一些目标错误地未在其
universe_package_labels
中列出,只是由于同一BUILD.gn
文件中定义的其他必需目标而假定存在它们。
尽量不要向此名单添加内容。如果您确实需要始终在最终 Ninja build 清单中定义某些内容,最好从任何其他顶级 //BUILD.gn
目标中查找真正的传递依赖项。