.bzl 样式指南

报告问题 查看源代码

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

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

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

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

当用户打开 BUILD 文件时,他们很快会想要知道文件中的目标列表;或者想要查看该 C++ 库的源代码列表;或者从该 Java 二进制文件中移除依赖项。每次添加抽象层都会增加用户执行这些任务的难度。

许多不同的工具也会分析和更新 BUILD 文件。如果 BUILD 文件使用了抽象,则工具可能无法修改该文件。尽量简化 BUILD 文件,以便获得更好的工具。随着代码库不断扩大,为更新库或清理库而对多个 BUILD 文件进行更改会变得越来越频繁。

总体建议

风格

Python 样式

如有疑问,请尽可能遵循 PEP 8 风格指南。具体而言,使用四个(而不是两个)空格进行缩进,以遵循 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。请注意这些语句是否足够实用,足以证明它们对可读性的影响。

宏是在加载阶段对一条或多条规则进行实例化的函数。一般来说,请尽可能使用规则,而不是宏。用户看到的构建图与 Bazel 在构建期间使用的构建图不同 - 宏会在 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 接口在您的规则之间传递信息。声明和文档提供程序字段。
  • 设计规则时请考虑可扩展性。要考虑到,其他规则可能希望与您的规则交互、访问您的提供程序并重复使用您创建的操作。
  • 在规则中遵循效果指南