2022 年 BazelCon 将于 11 月 16 日至 17 日在纽约和线上举办。
立即报名!

.bzl 样式指南

使用集合让一切井井有条 根据您的偏好保存内容并对其进行分类。

本页介绍了 Starlark 的基本样式指南,还介绍了有关宏和规则的信息。

Starlark 是一种定义软件构建方式的语言,因此既是编程语言,又是配置语言。

您将使用 Starlark 编写 BUILD 文件、宏和构建规则。宏和规则本质上是元语言,用于定义 BUILD 文件的编写方式。BUILD 文件旨在保持简洁和重复。

所有软件的读取频率都高于写入的频率。这对 Starlark 来说尤其如此,因为工程师会读取 BUILD 文件以了解其目标的依赖项和 build 的详细信息。这种读数通常在通过时迫切地发生,或是在执行其他任务时急速或并行执行。因此,简洁性和可读性非常重要,以便用户快速解析和理解 BUILD 文件。

当用户打开 BUILD 文件时,他们想要知道文件中的目标列表,或者查看该 C++ 库的源代码列表,或者从该 Java 二进制文件中移除依赖项。每增加一个抽象层,用户就会更加难以完成这些任务。

许多其他工具也会分析和更新 BUILD 文件。如果您的 BUILD 文件使用抽象函数,可能无法修改该文件。让 BUILD 文件保持简单,可以获得更好的工具。随着代码库的不断发展,为了更新库或进行清理,对多个 BUILD 文件进行更改的频率越来越高。

一般建议

样式

Python 样式

如有疑问,请尽可能遵循 PEP 8 样式指南。尤其要注意,使用 4 个(而不是 2 个)空格进行缩进,以遵循 Python 惯例。

由于 Starlark 并非 Python,因此 Python 样式的某些方面不适用。例如,PEP 8 建议使用 is(而不是 Starlark 运算符)完成比较单例。

文档字符串

使用文档字符串记录文件和函数。 在每个 .bzl 文件的顶部使用文档字符串,并为每个公共函数使用文档字符串。

文档规则和方面

规则和方面及其属性,以及提供程序及其字段,应使用 doc 参数进行记录。

命名惯例

  • 变量和函数名称使用小写形式,且以下划线 ([a-z][a-z0-9_]*) 分隔的字词,例如 cc_library
  • 顶级不公开值以下划线开头。Bazel 强制要求无法从其他文件使用私有值。局部变量不应使用下划线前缀。

行长

与在 BUILD 文件中一样,行没有严格的长度限制,因为标签可能很长。如有可能,请尝试每行最多 79 个字符(遵循 Python 的样式指南,PEP 8)。不应严格遵守该准则:编辑器应显示超过 80 列,自动更改经常会引入更长的行,并且用户不应花费时间拆分可读的行。

关键字参数

在关键字参数中,最好在等号两边加上空格:

def fct(name, srcs):
    filtered_srcs = my_filter(source = srcs)
    native.cc_library(
        name = name,
        srcs = filtered_srcs,
        testonly = True,
    )

布尔值

首选布尔值 TrueFalse(而非 10),例如在规则中使用布尔值特性时。

不要在正式版代码中使用 print() 函数;该函数仅供调试使用,并且会向您的 .bzl 文件的所有直接和间接用户发送垃圾内容。唯一的例外是,如果代码默认停用,并且只能通过修改源代码来启用,则可以提交使用 print() 的代码;例如,如果对 print() 的所有使用都由 if DEBUG: 保护(其中 DEBUG 硬编码为 False)。请注意这些语句是否实用,足以证明它们对可读性的影响。

宏是一个函数,用于在加载阶段实例化一条或多条规则。一般来说,应尽可能使用规则,而不是宏。用户看到的 build 图与 Bazel 在构建时使用的 build 图不同 - 宏会在 Bazel 进行任何构建图分析之前展开。

因此,当出现问题时,用户需要了解宏的实现情况以排查构建问题。此外,bazel query 结果可能难以解释,因为结果中显示的目标来自宏扩展。最后,各个方面并不了解宏,因此依赖于方面(IDE 及其他)的工具可能会失败。

宏的一个安全用途是定义要在 Bazel CLI 或 BUILD 文件中直接引用的其他目标:在这种情况下,只有这些目标的最终用户才需要了解这些目标,而且由宏引入的任何构建问题都绝不会远离其使用。

对于定义生成的目标(不应在 CLI 中引用或由未由该宏实例化的目标的宏的实现细节)的宏,请遵循以下最佳做法:

  • 宏应采用 name 参数,并使用该名称定义目标。该目标将成为该宏的主要目标
  • 生成的目标(即宏定义的所有其他目标)应:
    • <name>_<name> 为前缀。例如,使用 name = '%s_bar' % (name)
    • 公开范围受限(//visibility:private)和
    • 使用 manual 标记以避免通配符目标扩展(:all...:* 等)。
  • name 应仅用于派生宏定义的目标的名称,而不能用于任何其他用途。例如,请勿使用该名称来派生不是由宏本身生成的依赖项或输入文件。
  • 在宏中创建的所有目标都应该以某种方式与主目标相关联。
  • 确保宏中的参数名称保持一致。如果将参数作为属性值传递给主目标,请保持其名称不变。如果某个宏参数的用途与通用规则属性(例如 deps)的用途相同,则按您为其命名的方式命名(请参阅下文)。
  • 调用宏时,请仅使用关键字参数。这与规则一致,并大大提高了可读性。

工程师通常在相关规则的 Starlark API 不足以满足其特定用例的情况下编写宏,无论该规则是在 Bazel 中的原生代码还是在 Starlark 中定义的。如果您遇到此问题,请询问规则作者是否可以扩展该 API 以完成您的目标。

一般来说,与规则相仿的宏越多越好。

另请参阅

规则

  • 规则、方面及其属性应使用小写形式(“蛇形大小写”)。
  • 规则名称是从其依赖项的角度描述规则生成的主要工件类型的名词(对于叶规则,则为用户)。此后缀不一定是文件后缀。例如,用于生成要用作 Python 扩展程序的 C++ 工件的规则可以称为 py_extension。对于大多数语言,常见的规则包括:
    • *_library - 编译单元或“模块”。
    • *_binary - 生成可执行文件或部署单元的目标。
    • *_test - 一个测试目标。这可能包含多个测试。预计 *_test 目标中的所有测试都是针对同一主题的变体,例如测试单个库。
    • *_import:用于封装预编译工件(例如 .jar)或编译期间使用的 .dll 的目标。
  • 请为属性使用一致的名称和类型。一些通常适用的属性包括:
    • srcslabel_list,允许文件:源文件(通常由作者编写)。
    • depslabel_list,通常不允许文件:编译依赖项。
    • datalabel_list,允许文件:数据文件,例如测试数据等。
    • runtime_depslabel_list:编译不需要的运行时依赖项。
  • 对于任何不明显的行为(例如,带有特殊替换的字符串模板,或根据特定要求调用的工具),请在属性声明中使用 doc 关键字参数(attr.label_list() 或类似元素)来提供文档。
  • 规则实现函数应几乎都是私有函数(以前导下划线命名)。一种常见样式是为 myrule 的实现函数指定名称 _myrule_impl
  • 使用明确定义的 provider 接口在规则之间传递信息。声明并记录提供程序字段。
  • 设计规则时要考虑到可扩展性。请注意,其他规则可能希望与您的规则进行交互、访问您的提供程序并重复使用您创建的操作。
  • 遵守规则中的性能准则