本页介绍了 Starlark 的基本样式准则,还包含有关宏和规则的信息。
Starlark 是一种用于定义软件构建方式的语言,因此它既是编程语言,也是配置语言。
您将使用 Starlark 编写 BUILD
文件、宏和 build 规则。宏和规则本质上是元语言,用于定义 BUILD
文件的写入方式。BUILD
文件应尽量简单且重复。
所有软件的读取次数都比写入次数多。对于 Starlark 来说尤其如此,因为工程师会读取 BUILD
文件来了解其目标的依赖项及其 build 的详细信息。这些读取操作通常是在完成其他一些任务的同时或快速发生,或同时进行。因此,简单性和可读性非常重要,以便用户能够快速解析和理解 BUILD
文件。
当用户打开 BUILD
文件时,他们很快会想要了解文件中的目标列表;或者想要查看该 C++ 库的源代码列表;或者从该 Java 二进制文件中移除依赖项。每次添加一层抽象层,都会使用户更难完成这些任务。
BUILD
文件还会由许多不同的工具进行分析和更新。如果 BUILD
文件使用抽象,工具可能无法对其进行修改。保持 BUILD
文件的简单性有助于您获得更好的工具。随着代码库不断扩大,为了更新库或进行清理,需要越来越频繁地更改许多 BUILD
文件。
总体建议
- 将 Buildifier 用作格式设置工具和 lint 工具。
- 遵循测试准则。
样式
Python 风格
如有疑问,请尽可能遵循 PEP 8 样式指南。具体而言,请使用四个空格(而非两个空格)进行缩进,以遵循 Python 惯例。
由于 Starlark 不是 Python,因此 Python 样式的某些方面不适用。例如,PEP 8 建议使用 is
与单例进行比较,但 is
在 Starlark 中不是运算符。
文档字符串
使用 docstring 记录文件和函数。
在每个 .bzl
文件的顶部使用 docstring,并为每个公共函数使用 docstring。
文档规则和方面
应使用 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()
的代码。例如,如果 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 以实现您的目标。
一般来说,宏越像规则越好。
另请参阅宏。
规则
- 规则、方面及其属性应使用小写名称(“蛇形命名法”)。
- 规则名称是名词,用于从其依赖项的角度(对于叶规则,则是用户)来描述规则生成的主要工件类型。不一定是文件后缀。例如,生成要用作 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
。 - 使用明确定义的提供程序接口在您的规则之间传递信息。声明和记录提供程序字段。
- 设计规则时应考虑可扩展性。请注意,其他规则可能需要与您的规则交互、访问您的提供程序,以及重复使用您创建的操作。
- 在规则中遵循效果指南。