本页介绍了 Starlark 的基本样式指南,还包含有关宏和规则的信息。
Starlark 是一种定义软件构建方式的语言,因此既是编程语言,又是配置语言。
您将使用 Starlark 编写 BUILD
文件、宏和构建规则。宏和规则本质上是元语言,用于定义 BUILD
文件的编写方式。BUILD
文件应简洁且重复。
所有软件的读取频率都高于写入的速度。对于 Starlark 来说尤其如此,因为工程师会读取 BUILD
文件以了解其目标的依赖项及其 build 的详细信息。这种读数通常发生在通过时、时间紧迫或并行完成其他任务时。因此,简洁性和可读性非常重要,可让用户快速解析和理解 BUILD
文件。
当用户打开 BUILD
文件时,他们希望快速了解文件中的目标列表,或者查看该 C++ 库的源代码列表,或者从该 Java 二进制文件中移除依赖项。每次您添加抽象层后,用户较难执行这些任务。
许多不同的工具也会分析和更新 BUILD
文件。如果 BUILD
文件使用了抽象,工具可能无法修改该文件。尽量简化 BUILD
文件,您将能够获得更好的工具。随着代码库的规模不断扩大,为了更新库或进行清理,更改多个 BUILD
文件的频率会越来越高。
总体建议
- 使用 Builderifier 作为格式化程序和 linter。
- 遵循测试指南。
要风格
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()
函数;该函数仅用于调试,并且会向您的 .bzl
文件的所有直接和间接用户发送垃圾内容。唯一的例外是,您可以提交使用 print()
的代码,前提是这些代码在默认情况下处于停用状态,只能通过修改源代码来启用。例如,如果 print()
的所有使用都受 if DEBUG:
保护(其中 DEBUG
硬编码为 False
)。请注意这些语句是否足够有用,可以说明它们对可读性的影响。
宏
宏是一个函数,用于在加载阶段实例化一个或多个规则。一般而言,请尽可能使用规则,而不是宏。用户看到的 build 图与 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
。
- 请为属性使用一致的名称和类型。一些通常适用的属性包括:
srcs
:label_list
,允许以下文件:源文件(通常由人工编写)。deps
:label_list
,通常不允许上传文件:编译依赖项。data
:label_list
,允许存储文件:数据文件,例如测试数据等。runtime_deps
:label_list
:编译不需要的运行时依赖项。
- 对于任何不明显的行为(例如,具有特殊替换的字符串模板,或根据特定要求调用的工具),请提供属性声明(
attr.label_list()
或类似属性)的doc
关键字参数。 - 规则实现函数应始终是私有函数(以前导下划线命名)。一种常见样式是为
myrule
的实现函数指定名称_myrule_impl
。 - 使用明确定义的 provider 接口在您的规则之间传递信息。声明并记录提供程序字段。
- 在设计规则时要考虑到可扩展性。请注意,其他规则可能希望与您的规则进行交互、访问您的提供程序并重复使用您创建的操作。
- 遵守规则中的性能准则。