.bzl 样式指南

报告问题 查看源代码 每夜版 · 8.4 · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

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

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

您将使用 Starlark 编写 BUILD 文件、宏和 build 规则。宏和规则本质上是元语言,它们定义了 BUILD 文件的编写方式。BUILD 文件应简单且重复。

所有软件的读取频率都高于写入频率。对于 Starlark 来说,这一点尤其重要,因为工程师会读取 BUILD 文件来了解其目标的依赖项和 build 的详细信息。这种阅读通常是匆匆浏览,或是在完成其他任务的同时进行。因此,简单性和可读性非常重要,这样用户才能快速解析和理解 BUILD 文件。

当用户打开 BUILD 文件时,他们会很快想知道文件中的目标列表;或查看相应 C++ 库的来源列表;或从相应 Java 二进制文件中移除依赖项。每次添加抽象层,都会让用户更难完成这些任务。

BUILD 文件也会被许多不同的工具分析和更新。如果 BUILD 文件使用抽象,工具可能无法对其进行编辑。保持 BUILD 文件简单明了,有助于您获得更好的工具。随着代码库的增长,为了更新库或进行清理,越来越频繁地需要跨多个 BUILD 文件进行更改。

总体建议

样式

Python 样式

如有疑问,请尽可能遵循 PEP 8 样式指南。具体来说,请使用四个空格(而非两个)进行缩进,以遵循 Python 惯例。

由于 Starlark 不是 Python,因此 Python 风格的某些方面不适用。例如,PEP 8 建议使用 is 来比较单例,但 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 期间使用的 build 图不同 - 宏在 Bazel 进行任何 build 图分析之前即已展开。

因此,如果出现问题,用户需要了解宏的实现才能排查 build 问题。此外,bazel query结果可能难以解读,因为结果中显示的目标来自宏扩展。最后,方面不知道宏,因此依赖于方面的工具(IDE 等)可能会失败。

宏的一种安全用法是定义旨在直接在 Bazel CLI 或 BUILD 文件中引用的其他目标:在这种情况下,只有这些目标的最终用户需要了解它们,并且宏引入的任何 build 问题都不会离其使用位置太远。

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

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

如果相关规则的 Starlark API 无法满足工程师的特定使用情形,那么无论该规则是在 Bazel 中以原生代码定义,还是在 Starlark 中定义,工程师通常都会编写宏。如果您遇到此问题,请咨询规则作者,看看他们是否可以扩展 API 来实现您的目标。

一般来说,宏越像规则越好。

另请参阅

规则

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