使用 GN 工具链的最佳实践

概览

在 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