构建样式指南

BUILD 文件的格式设置方法与 Go 相同,即使用标准化 工具来处理大多数格式问题。 Buildifier 是一种用于解析源代码并 以标准样式输出源代码的工具。因此,每个BUILD文件都以相同的自动化方式进行 格式设置,这使得格式设置在 代码审核期间不再是问题。此外,它还使工具能够更轻松地理解、修改和 生成 BUILD 文件。

BUILD 文件的格式设置必须与 buildifier 的输出相匹配。

格式设置示例

# Test code implementing the Foo controller.
package(default_testonly = True)

py_test(
    name = "foo_test",
    srcs = glob(["*.py"]),
    data = [
        "//data/production/foo:startfoo",
        "//foo",
        "//third_party/java/jdk:jdk-k8",
    ],
    flaky = True,
    deps = [
        ":check_bar_lib",
        ":foo_data_check",
        ":pick_foo_port",
        "//pyglib",
        "//testing/pybase",
    ],
)

文件结构

建议:使用以下顺序(每个元素都是可选的):

  • 软件包说明(注释)

  • 所有 load() 语句

  • package() 函数。

  • 对规则和宏的调用

Buildifier 会区分独立注释和附加到元素的注释 。如果注释未附加到特定元素,请在其后使用 空行。在进行自动化 更改时(例如,在删除规则时保留或移除注释),这种区分非常重要。

# Standalone comment (such as to make a section in a file)

# Comment for the cc_library below
cc_library(name = "cc")

对当前软件包中的目标的引用

应通过相对于软件包目录的路径来引用文件 (切勿使用向上引用,例如 ..)。生成的文件应以“:”为前缀 ,以表明它们不是来源。源文件 不应以 : 为前缀。规则应以 : 为前缀。例如,假设 x.cc 是一个源文件:

cc_library(
    name = "lib",
    srcs = ["x.cc"],
    hdrs = [":gen_header"],
)

genrule(
    name = "gen_header",
    srcs = [],
    outs = ["x.h"],
    cmd = "echo 'int x();' > $@",
)

目标命名

目标名称应具有描述性。如果目标包含一个源文件, 则目标通常应具有从该源派生的名称(例如, cc_librarychat.cc 可以命名为 chat,或者 java_libraryDirectMessage.java 可以命名为 direct_message)。

软件包的同名目标(与包含目录同名的目标)应提供目录名称所描述的功能。如果没有此类目标,请勿创建同名 目标。

引用同名目标时,最好使用短名称(//x 而不是 //x:x)。如果您位于同一软件包中,最好使用本地 引用(:x 而不是 //x)。

避免使用具有特殊含义的“保留”目标名称。这包括 all__pkg____subpackages__,这些名称具有特殊的 语义,使用时可能会导致混淆和意外行为。

在没有普遍适用的团队惯例的情况下,以下是一些在 Google 广泛使用的非约束性 建议:

  • 一般来说,使用“snake_case”
    • 对于具有一个 srcjava_library,这意味着使用与不带扩展名的文件名不同的名称
    • 对于 Java *_binary*_test 规则,请使用 “Upper CamelCase”。 这样,目标名称就可以与其中一个 src 相匹配。对于 java_test,这样就可以从目标的名称推断出 test_class 属性。
  • 如果特定目标有多个变体,请添加后缀以 消除歧义(例如:foo_dev:foo_prod:bar_x86:bar_x64)
  • _test 目标添加后缀 _test_unittestTestTests
  • 避免使用无意义的后缀,例如 _lib_library(除非有必要 避免 _library 目标与其对应的 _binary 之间发生冲突)
  • 对于与 proto 相关的目标:
    • proto_library 目标的名称应以 _proto 结尾
    • 特定于语言的 *_proto_library 规则应与底层 proto 相匹配,但将 _proto 替换为特定于语言的后缀,例如:
      • cc_proto_library_cc_proto
      • java_proto_library_java_proto
      • java_lite_proto_library_java_proto_lite

公开范围

公开范围应尽可能缩小,同时仍允许 测试和反向依赖项进行访问。根据需要使用 __pkg____subpackages__

避免将软件包 default_visibility 设置为 //visibility:public//visibility:public 仅应为项目公共 API 中的目标单独设置 。这些目标可能是旨在供外部项目依赖的库,也可能是可供外部项目的构建过程使用的二进制文件。

依赖项

依赖项应仅限于直接依赖项(规则中列出的来源所需的依赖项 )。请勿列出传递依赖项。

软件包本地依赖项应先列出,并以与 兼容的方式引用 上述对当前软件包中的目标的引用 部分(而不是通过其绝对软件包名称)。

最好将依赖项直接列为单个列表。将多个目标的“通用” 依赖项放入变量会降低可维护性,使 工具无法更改目标的依赖项,并可能导致 未使用的依赖项。

Glob

使用“无目标”表示 []。请勿使用与任何内容都不匹配的 glob:它 比空列表更容易出错且不太明显。

递归

请勿使用递归 glob 来匹配源文件(例如 glob(["**/*.java"]))。

递归 glob 会跳过包含 BUILD 文件的子目录,因此很难推断出 BUILD 文件。

递归 glob 通常不如每个目录都有一个 BUILD 文件且它们之间定义了依赖项图更高效,因为这样可以实现更好的 远程缓存和并行性。

最好在每个目录中编写一个 BUILD 文件,并在它们之间定义 依赖项图。

非递归

非递归 glob 通常是可以接受的。

其他惯例

  • 使用大写字母和下划线来声明常量(例如 GLOBAL_CONSTANT), 使用小写字母和下划线来声明变量(例如 my_variable)。

  • 标签绝不应拆分,即使它们超过 79 个字符也是如此。 标签应尽可能使用字符串字面量。理由:这样可以轻松 查找和替换。此外,它还可以提高可读性。

  • name 属性的值应为字面量常量字符串( 宏除外)。理由:外部工具使用 name 属性来引用 规则。它们需要查找规则,而无需解释代码。

  • 设置布尔值类型的属性时,请使用布尔值,而不是整数值。 出于历史原因,规则仍会根据需要将整数转换为布尔值, 但不建议这样做。理由flaky = 1 可能会被误读为 "通过重新运行一次来取消此目标的 flaky 状态"。flaky = True 明确表示 "此测试是 flaky 的"。

与 Python 样式指南的区别

虽然与 Python 样式指南 兼容是我们的目标,但还是存在一些区别:

  • 没有严格的行长度限制。长注释和长字符串通常会拆分为 79 列,但这不是必需的。不应在代码 审核或预提交脚本中强制执行此操作。理由:标签可能很长,并且会超出此 限制。BUILD 文件通常由工具生成或修改, 这与行长度限制不太一致。

  • 不支持隐式字符串串联。请使用 + 运算符。 理由BUILD 文件包含许多字符串列表。很容易忘记 逗号,这会导致完全不同的结果。过去,这导致了许多 bug 。另请参阅此讨论。

  • 在规则中使用空格将 = 符号括起来,以表示关键字实参。理由: 命名实参比 Python 中更常见,并且始终位于单独的一行中。 空格可以提高可读性。此惯例已存在 很长时间,不值得修改所有现有的 BUILD 文件。

  • 默认情况下,对字符串使用双引号。理由:Python 样式指南中未 指定此项,但建议保持一致性。因此,我们 决定仅使用双引号字符串。许多语言都对字符串字面量使用双引号 。

  • 在两个顶级定义之间使用一个空行。理由: ファイルの構造は、典型的な Python ファイルとは異なります。BUILD它只有 顶级语句。使用单行空白行可以缩短 BUILD 文件。