构建样式指南

报告问题 查看源代码

BUILD 文件格式遵循与 Go 相同的方法,即标准化工具会处理大多数格式问题。Builder 这款工具能够以标准样式解析和发出源代码。因此,每个 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() 函数。

  • 调用规则和宏

Builder 会区分独立注释和附加到元素的注释。如果注释未附加到特定元素,请在它后添加一个空行。在进行自动更改(例如,在删除规则时保留或移除注释)时,请务必进行区分。

# 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();' > $@",
)

目标命名

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

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

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

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

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

  • 一般而言,请使用 "snake_case"
    • 对于具有一个 srcjava_library,这意味着使用的名称与不带扩展名的文件名不同
    • 对于 Java *_binary*_test 规则,请使用“大写驼峰式大小写”。 这样,目标名称就可以与其中一个 src 匹配。对于 java_test,可以使用 test_class 属性从目标名称推断出来。
  • 如果特定目标有多个变体,则添加后缀以消除歧义(例如,:foo_dev:foo_prod:bar_x86:bar_x64
  • 使用 _test_unittestTestTests 作为后缀 _test 的目标
  • 避免使用 _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。 只能为项目的公共 API 中的目标单独设置 //visibility:public。这些库可能是设计为由外部项目或可供外部项目的构建流程使用的二进制文件所依赖的库。

依赖项

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

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

倾向于直接使用单一列表作为依赖项。将多个目标的“常见”依赖项放入变量中会降低可维护性,使工具无法更改目标的依赖项,并且可能会导致未使用的依赖项。

glob

使用 [] 指示“无目标”。请勿使用不匹配的 glob:它比空列表更易出错,也更不明显。

递归

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

递归 glob 导致 BUILD 文件难以推断,因为它们会跳过包含 BUILD 文件的子目录。

递归 glob 通常比每个目录上都定义一个依赖关系图的 BUILD 文件效率低,因为这样可以实现更好的远程缓存和并行性。

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

非递归

通常可以接受非递归 glob。

其他惯例

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

  • 绝不能拆分标签,即使其长度超过 79 个字符也是如此。标签应尽可能为字符串字面量。说明:查找和替换非常容易。还提高了可读性。

  • name 属性的值应为字面常量字符串(在宏中除外)。说明:外部工具使用 name 属性来引用规则。他们需要找到规则,而不必解释代码。

  • 设置布尔值类型的属性时,请使用布尔值,而不是整数值。 由于传统原因,规则仍根据需要将整数转换为布尔值,但不建议这样做。理由flaky = 1 可能会被误读为“通过重新运行此目标来破坏此目标”。flaky = True 明确表示“此测试不稳定”。

与 Python 样式指南的差异

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

  • 没有严格的行长度限制。长注释和长字符串通常拆分为 79 列,但并非必须如此。不应在代码审核或提交前脚本中强制执行。说明:标签可能过长,超出了此限制。由工具生成或修改 BUILD 文件的情况很常见,这不符合行长度限制。

  • 隐式字符串串联不受支持。使用 + 运算符。说明BUILD 文件包含许多字符串列表。我们很容易忘记使用逗号,这会导致出现完全不同的结果。这在过去造成了很多错误。另请参阅此讨论。

  • 在规则中的 = 符号周围使用空格作为关键字参数。说明:命名的参数比 Python 中的频率高得多,并且始终位于单独的一行。聊天室可提高可读性。这种惯例已已存在很长时间,不值得修改所有现有的 BUILD 文件。

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

  • 在两个顶级定义之间使用单个空白行。说明BUILD 文件的结构与典型 Python 文件不同。它只有顶级语句。使用单行代码可以缩短 BUILD 文件的大小。