本页介绍了 Starlark 的基本样式指南,还包含 有关宏和规则的信息。
Starlark 是一种 用于定义软件构建方式的语言,因此它既是 编程语言,也是配置语言。
您将使用 Starlark 编写 BUILD 文件、宏和构建规则。宏和
规则本质上是元语言,它们定义了 BUILD 文件的编写方式。
BUILD 文件旨在简单且重复。
所有软件的读取次数都比写入次数多。对于
Starlark 来说尤其如此,因为工程师会读取 BUILD 文件,以了解其
目标依赖项和构建详细信息。这种读取通常会发生在匆忙之间,
或者与其他任务并行进行。因此,
简单性和可读性非常重要,这样用户才能快速解析和
理解 BUILD 文件。
当用户打开 BUILD 文件时,他们希望快速了解文件中的目标列表;或查看该 C++ 库的来源列表;或从该 Java 二进制文件中移除依赖项。每次添加一层抽象,都会让用户更难完成这些任务。
BUILD 文件也会被许多不同的工具分析和更新。如果 BUILD 文件使用抽象,工具可能无法对其进行修改。保持 BUILD
文件的简单性将有助于您获得更好的工具。随着代码库的增长,它
为了更新库或进行清理,跨多个 BUILD 文件进行更改的情况会越来越频繁。
总体建议
- 使用 Buildifier 作为格式设置工具和 lint 工具。
- 遵循测试指南。
样式
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,
)
布尔值
对于布尔值
(例如在规则中使用布尔属性时),最好使用值 True 和 False(而不是 1 和 0)。
仅将 print 用于调试
请勿在生产代码中使用 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只能用于派生 宏定义的目标的名称,而不能用于其他任何用途。例如,请勿使用该名称来派生 不是由宏本身生成的依赖项或输入文件。- 在宏中创建的所有目标都应以某种方式与 主要目标耦合。
- 按照惯例,
name应是定义宏时的第一个实参。 - 保持宏中的形参名称一致。如果形参作为属性值传递给主要目标,请保持其名称不变。如果宏
形参与常见规则属性(例如
deps)的用途相同,请按照属性的命名方式进行命名(请参阅下文)。 - 调用宏时,仅使用关键字实参。这与 规则一致,并且大大提高了可读性。
当相关规则的 Starlark API 不足以满足其特定用例时,工程师通常会编写宏,无论该规则是 在 Bazel 中以原生代码定义,还是在 Starlark 中定义。如果您遇到此 问题,请询问规则作者是否可以扩展 API 以实现您的 目标。
根据经验,宏越像规则越好。
另请参阅宏。
规则
- 规则、方面及其属性应使用 lower_case 名称(“蛇形 命名法”)。
- 规则名称是名词,用于从其依赖项(或对于叶规则,
用户)的角度描述
规则生成的主要工件类型。这不一定是文件后缀。例如,生成旨在用作 Python 扩展的 C++ 工件的规则可以称为
py_extension。对于大多数语言,典型规则包括:*_library- 编译单元或“模块”。*_binary- 生成可执行文件或部署单元的目标。*_test- 测试目标。这可以包括多个测试。预计*_test目标中的所有 测试都是同一主题的变体,例如测试单个库。*_import:封装预编译工件(例如.jar或在编译期间使用的.dll)的目标。
- 为属性使用一致的名称和类型。一些普遍适用的
属性包括:
srcs:label_list,允许文件:源文件,通常由 人工编写。deps:label_list,通常不允许文件:编译 依赖项。data:label_list,允许文件:数据文件,例如测试数据等。runtime_deps:label_list:编译不需要的运行时依赖项。
- 对于任何行为不明显的属性(例如,具有特殊替换的字符串模板
,或使用特定
要求调用的工具),请使用
doc关键字实参向 属性的声明(attr.label_list()或类似项)提供文档。 - 规则实现函数几乎总是私有函数
(以开头下划线命名)。一种常见的样式是为
myrule的实现函数命名为_myrule_impl。 - 使用明确定义的 提供程序接口在规则之间传递信息。声明和记录提供程序 字段。
- 设计规则时请考虑可扩展性。请考虑其他规则可能 希望与您的规则互动、访问您的提供程序以及重复使用您创建的 操作。
- 在规则中遵循性能指南。