概览
在 GN 中,工具链提供了一种以多种方式构建目标的方法。为了了解
和调试 GN 代码时,需要知道自己使用的工具链。由于 GN 代码
可以基于 current_toolchain
条件式,该目标在
工具链 A 可能会在工具链 B 中执行完全不同的操作,并且
它可能完全不存在于工具链 C 中。
本文档详细介绍了使用工具链解决常见问题的最佳做法,
GN 代码(.gn
和 .gni
文件)中的问题。这些最佳实践
对 Fuchsia 构建系统中概述的最佳做法
政策。
如需了解详情,请参阅 GN 工具链和 Fuchsia 构建
工具链的工作原理,或者运行 fx gn help toolchain
以查看 GN 的内置
文档。
目标
本文档中的最佳做法基于 以下目标:
- 一致性。更喜欢一种行事方式。
- 清晰度。用断言清晰地传达意图。
- 性能。避免在构建过程中执行不必要的工作。
最佳做法
在预期的工具链上断言
如果某个文件只应用在一个工具链或某些工具链中, 在顶部添加断言
推荐:在is_host
仅构建主机可执行文件的 BUILD.gn
文件。
assert(is_host)
# ...
建议:在仅对默认工具链有意义的模板中断言 current_toolchain ==
default_toolchain
。
template("foo") {
assert(current_toolchain == default_toolchain,
"The foo template can only be used in the default toolchain")
}
用条件封装目标
如果您无法对预期 工具链,因为文件需要使用更多的 请将目标封装在条件块中,以避免不必要的 扩展。这样更方便您了解在什么位置使用什么目标,以及 还有助于缩短 GN 生成时间。
建议:在 is_host
中封装目标
和 is_fuchsia
检查。
# example/BUILD.gn
executable("built_everywhere") {
# ...
}
if (is_host) {
executable("only_on_host") {
# ...
}
}
if (is_fuchsia) {
executable("only_on_fuchsia") {
# ...
}
}
不建议:定义所有定位条件 。
# example/BUILD.gn
executable("built_everywhere") {
# ...
}
executable("only_on_host") {
# ...
}
executable("only_on_fuchsia") {
# ...
}
这种方法增加了目标数量,降低了 GN 和 ninja 的速度。
例如,当 GN 发现对 example:only_on_fuchsia
的引用时,
默认工具链,它会在默认工具链中评估所有 example/BUILD.gn,
工具链,包括 only_on_host
目标。由于这会以传递方式
而在某些地方出错可能会导致
成千上万个不需要的目标。
使用 is_*
变量检查工具链
在断言或编写
针对当前工具链进行条件式,请使用
变量的is_*
BUILDCONFIG.gn(如果其中一个符合您的需求):
is_android = false
is_chromeos = false
is_fuchsia = false
is_fuchsia_host = false
is_host = false
is_ios = false
is_linux = false
is_mac = false
is_win = false
is_component_build = false
is_official_build = false
建议:使用 is_host
检查
主机工具链。
if (is_host) {
# ...
}
不推荐:使用 current_toolchain ==
host_toolchain
检查主机工具链。
if (current_toolchain == host_toolchain) {
# ...
}
检查 current_toolchain == host_toolchain
通常是错误的,因为
在涉及变体时,是多个主机工具链。
请仅在有理由的情况下检查 current_toolchain
的值。
例如,一个有效的用例是检查 current_toolchain ==
default_toolchain
是否定义与工具链无关的
操作。
首选进行较少且较早的工具链重定向
如需访问非默认工具链,您必须在某个时候重定向到它。 尽可能将这些重定向推到 build 图中尽可能靠上的位置。这会导致 减少重定向次数 对预期的工具链进行断言。
推荐:重定向到 host_toolchain
创建一次
# example/BUILD.gn
group("tests") {
testonly = true
deps = [ "foo:tests($host_toolchain)" ]
}
# examples/foo/BUILD.gn
assert(is_host)
test("foo_unit_tests") {
# ...
}
test("foo_integration_tests") {
# ...
}
group("tests") {
testonly = true
deps = [
":foo_unit_tests",
":foo_integration_tests",
]
}
不推荐:重定向到
host_toolchain
。
# example/BUILD.gn
group("tests") {
testonly = true
deps = [ "foo:tests" ]
}
# examples/foo/BUILD.gn
if (is_host) {
test("foo_unit_tests") {
# ...
}
test("foo_integration_tests") {
# ...
}
}
group("tests") {
testonly = true
deps = [
":foo_unit_tests($host_toolchain)",
":foo_integration_tests($host_toolchain)",
]
}
此方法会不必要地处理 examples/foo/BUILD.gn 两次,一次在 默认工具链,在主机工具链中再次执行。
避免自动工具链转发
如果目标仅在特定的工具链中有意义,只需在 预期的工具链。
建议:断言预期值 工具链并定义一次目标。
assert(current_toolchain == desired_toolchain)
action(target_name) {
# ...
}
不推荐:隐藏工具链 对 GN 组的要求,该组会自动重定向所有其他工具链。
if (current_toolchain == desired_toolchain) {
action(target_name) {
# ...
}
} else {
group(target_name) {
public_deps = [ ":$target_name($desired_toolchain)" ]
}
}
虽然让目标在任何工具链中工作似乎都很方便, 这种做法会更难理解真正的意图。
将与工具链无关的操作放入默认工具链中
无论工具链是什么,有些操作的行为都是相同的,因此会造成浪费
以便在多个工具链中重复执行这些任务。最常见的示例是
生成代码:尽管我们可能在多个工具链中构建生成的代码,
应该不必每次都重新生成代码。要解决此问题,请确保
操作仅在 default_toolchain
中定义。
建议:在 默认工具链。
if (current_toolchain == default_toolchain) {
action("codegen") {
visibility = [ ":*" ]
outputs = [ "$target_gen_dir/main.cc" ]
# ...
}
}
executable("program") {
deps = [ ":codegen($default_toolchain)" ]
sources = get_target_outputs(deps[0])
# ...
}
不建议:在 每种工具链
action("codegen") {
visibility = [ ":*" ]
outputs = [ "$target_gen_dir/main.cc" ]
# ...
}
executable("program") {
deps = [ ":codegen" ]
sources = get_target_outputs(deps[0])
# ...
}
使用 :anything
标签获取输出目录
当您使用“target_gen_dir”调用 get_label_info
时或“target_out_dir”,只是
标签的目录,而不是其目标名称。如果没有
目标,请使用名为“anything”的虚构目标。
建议:将虚构目标命名为“任何内容”。
codegen_dir = get_label_info(":anything($default_toolchain)", "target_gen_dir")
不建议:将虚构目标命名为“anything”以外的名称。
codegen_dir = get_label_info(":bogus($default_toolchain)", "target_gen_dir")
避免使用特定于语言的工具链
请勿为特定编程语言创建工具链。我们做到了
后来发现这是个坏主意。例如,我们过去使用的是
rust_toolchain
,但后来移除了它。我们还计划
移除 fidl_toolchain
。