概览
本页包含有关编写规则、宏和函数的 Fuchsia 特定样式和最佳实践。这些变量始终在 .bzl 文件中定义,并且相关指南也适用于此处。此外,与 BUILD.bazel 文件中的目标相关的指南也适用于规则和宏定义的目标。
本页是 Bazel 样式指南和最佳实践的一部分。
考虑新宏和函数是否合适
与 Bazel 样式指南一样,Fuchsia 偏好使用 DAMP(描述性且有意义的短语)BUILD 文件,而不是 DRY(不要重复)。
具体而言,Fuchsia build 在 Bazel 中使用特定于区域的宏和函数的频率远低于在 GN 中使用此类模板的频率。
使用共享的静态列表(如果内容必须保持同步,例如常见依赖项),而不是编写特定于区域的宏。
虽然特定于区域的模板可以在最初创建目标时减少输入量,但它们会成为维护和迁移问题,尤其是对于自动化工具,因为特定于区域的宏会隐藏各个目标。如果您认为自己会从使用特定于区域的宏和函数中获益匪浅,请咨询 Build 团队(根据以往的经验,门槛很高)。
尽可能优先使用现有宏和规则
此类宏或规则的潜在使用范围越大,此要求就越重要。
尤其是当您的需求仅限于以下情况时,请考虑是否可以使用现有宏和/或规则来实现这些需求:
生成元数据
强制执行属性值
例如,尽量减少调用者必须指定的属性数量
在前两种情况下,尤其要考虑 Bazel 查询、SHAC 规则、测试或其他机制是否能满足您的需求。Bazel 查询可以针对依赖树或目录树中定义的所有目标运行。您能编写一个运行查询并检查结果的测试吗?如果您的使用情形需要额外的属性,请考虑使用标记。
使用现有规则和宏的原因包括:
更易于理解,可缩短开发者的适应时间
熟悉 Bazel 的开发者可以立即上手并了解目标。
团队中一部分的开发者可以了解代码库其他部分的 build 目标。
AI 更容易理解和生成使用常见规则和宏的 Bazel 文件
所需声明数量减少至
load()个内置标识符不需要任何加载语句。
其他资源可能已针对文件中的其他目标加载。
减少了出现 bug 的机会
- 例如,只有当方面遍历所有相关目标时,它们才能正常运行。自定义宏或规则可用于引入不会被遵循的路径。请参阅使用标准属性名称。
首选符号宏,而不是旧版宏
建议编写使用 macro() 定义的符号宏,而不是旧版宏(使用 def 定义的、可创建目标的类 Python 函数)。符号宏可提供更清晰的属性及其类型文档,对属性执行类型检查,并确保在调用位置评估标签。它们还支持属性继承(通过 inherit_attrs)。如需了解更多背景信息,请参阅为什么不应使用旧版宏。
切勿在符号宏的实现函数中为实参指定默认值,因为默认值由 attrs 条目定义,并且系统会为每个属性提供一个值。
在某些情况下,必须在符号宏周围使用旧版宏封装容器,但对于大多数开发者来说,这种情况应该非常少见。
针对宏中定义的目标的可见性和访问权限检查
概览
为了便于理解,您可以将旧版宏视为在实例化时内联展开。在 BUILD.bazel 文件中使用时,旧版宏定义的目标实际上是在该文件中定义的。因此:
所定义目标的默认
visibility与实例化软件包相同,包括指定的软件包default_visibility。宏可以使用(例如,添加到
deps)实例化软件包可见的任何目标。- 定义旧版宏的
.bzl文件的位置无关紧要。
- 定义旧版宏的
不过,为了便于理解可见性,您可以将由符号宏定义的目标视为在与定义宏的 .bzl 文件相同的软件包(目录)中的 BUILD.bazel 文件中定义。因此:
已定义目标的默认
visibility是包含.bzl文件的软件包(目录)。宏可以使用(例如,添加到
deps)包含.bzl文件的软件包(目录)可见的任何目标。例如,这对于
fidl_library()添加的 FIDL 绑定支持库非常有用。但对于 HLCPP 支持库许可名单等内容,这会带来问题。
如需了解详情,请参阅问题 446911800。
为宏中定义的目标指定可见性
公开目标
将传递给宏的 visibility 属性转发给宏定义的主要目标(即 name 传递到的目标)。visibility 属性也可以根据需要转发到宏的 doc 字符串中提及的其他已定义的公开目标。
非公开目标
在符号宏中,所有其他已定义目标的可见性将默认为 ["//visibility:private"]。不过,对于旧版宏,您必须为旧版宏定义的所有目标指定可见性。
为旧版宏定义的所有目标指定可见性
对于不使用传递给宏的 visibility 属性的所有目标,请指定 visibility = ["//visibility:private"]。这是必需的,可防止它们在指定时默认使用软件包的 default_visibility。
使用标准属性名称
在宏和规则中使用标准属性名称。例如,使用 "deps"、"data" 甚至 "tools",而不是 "images" 或 "scripts"。请参阅一些普遍适用的属性。
原因包括:
为了提高可读性,以及出于与尽可能优先使用现有宏和规则中类似的其他原因。
只有当方面遍历所有相关目标时,它们才能正常工作,并且宏通常会配置为应用于
"deps"和其他适当的常见属性。不过,他们可能不知道"rust_deps"等属性,如果未能将这些属性应用到相关目标,可能会排除与该方面相关的目标。
需要具名实参
公共函数和旧版宏(一种特殊类别的函数)通常应声明仅限关键字的实参(所有实参均在 *, 之后声明)。对于实参数量不超过几个的函数,如果实参可从符号名称中清晰看出,并且实参不会因重构/重新排序而被错误地使用在错误的位置,则可以例外处理。布尔值实参和具有默认值的实参应始终出现在 * 之后。
虽然这对于旨在供代码库其他部分使用的符号最为重要,但也适用于所有 .bzl 文件中的公共符号。
这有助于强制执行 Bazel .bzl 样式指南的指导,即“调用宏时,仅使用关键字实参。这与规则一致,并大大提高了可读性。”
声明宏使用的所有实参
如果宏(可选)使用实参,请在宏实现的形参列表中明确声明该实参,而不是从 kwargs 中提取该实参。例如,请避免以下情况:
# Do NOT do this:
testonly = kwargs.get("testonly", False),
声明实参可更清楚地表明哪些实参与宏实现相关(与它调用的宏相比),并提供一个查看默认值的统一位置。对于旧版宏,它还可确保记录参数。(这主要适用于 Fuchsia Bazel SDK。)
与其他任何实参一样,但尤其是所有 build 规则共有的属性和所有测试规则共有的属性 (_test),您必须确保将实参传递给所有支持它的宏和规则,因为这些实参不会位于 **kwargs 中。
评论
为所有公共函数(不以下划线开头的函数)提供函数级注释。
为所有规则和宏提供
doc字符串。为所有规则和宏属性提供
doc字符串。- 对于私有属性(以英文下划线开头),如果含义可从
default值中明显看出,则可以省略doc字符串。
- 对于私有属性(以英文下划线开头),如果含义可从
文档字符串
在 Fuchsia 平台中,
doc字符串应在源文件中读取,而不是在某些生成的文档中读取。因此,最好优化该方面,而不是某些生成的文档的外观。长
doc字符串:在合理的情况下,最好将顶级单句说明与
doc=写在同一行。Bazel 中没有严格的行长度限制。
如果需要多行,请使用三引号编写多行字符串。
避免附加常规字符串。
多行
doc字符串在与
doc实参 (doc = """Begin the comment...) 相同的行上开始多行字符串。在
doc中,后续行从d下方开始,类似于 Python 注释从第一个"下方开始下一行。这可优化一致性和可读性,但生成的文本效果可能不太理想。
使用三引号 (
""") 结束多行字符串,并要单独占一行且与doc对齐。
规则和宏实现函数名称
使用下划线开头,后跟规则或函数的名称,并以 _impl 结尾,为 rule() 和 macro() 实例命名 implementation 函数。
在宏中引用目标名称时使用变量
如果某个宏定义了一个目标,然后在另一个目标中依赖于该目标,请定义一个以第一个目标的名称命名的变量,并将其用于第二个目标的 name 属性和 deps 中。
这样可以明确关联这两个目标,尤其是在复杂情况下(其中存在基于其他目标名称的多个级别的目标名称),并且更容易突出显示和搜索。
除非能提高可读性(例如,所有目标名称都定义在一个位置),否则请勿对内部未使用的目标使用变量。
封装内置规则、常用规则、宏和函数
在编写用于替代 Fuchsia 平台代码库中常见 Bazel 规则、宏或函数的宏时,请在封装的名称前添加 fx_ 前缀。例如,应使用 fx_cc_library() 而不是 cc_library()。
“通用”包括 Bazel 内置的符号(包括 native.*)以及常见代码库(例如 rules_cc)中的符号。此问题也适用于其他冲突,例如与 package 无关的 fx_package()。请勿使用 fuchsia_ 前缀,因为 fuchsia_... 文件和符号位于 Fuchsia Bazel SDK 中。
避免缓存或远程处理大型制品
请参阅避免缓存大型制品。