概览
本页包含特定于 Fuchsia 的样式和最佳实践,用于编写规则、宏和函数。这些内容始终在
.bzl文件中定义,并且适用于这些文件的指南也适用于此处
。此外,
BUILD.bazel 文件中与目标相关的指南也适用于由规则
和宏定义的目标。
本页是 Bazel 样式指南和最佳实践的一部分。
考虑是否适合使用新的宏和函数
与 Bazel 样式指南一样,Fuchsia
偏好使用 DAMP(描述性且有意义的短语)BUILD 文件,而不是 DRY
(不要重复)。
具体而言,与 GN 中使用此类模板相比,Fuchsia build 在 Bazel 中使用特定于区域的宏和函数的频率要低得多。
使用共享的静态列表(其中内容必须保持同步,例如常见依赖项),而不是编写特定于区域的宏。
虽然特定于区域的模板可以在初始创建目标时减少输入,但它们会成为维护和迁移问题,尤其是对于自动化工具,因为特定于区域的宏会隐藏各个目标。如果您认为使用特定于区域的宏和函数会带来很大的好处,请咨询 build 团队(根据以往的经验,门槛很高)。
尽可能使用现有宏和规则
此类宏或规则的潜在使用范围越大,这一点就越重要。
尤其是当您的需求仅限于以下情况时,请考虑是否可以使用现有宏和/或规则来满足这些需求:
生成元数据
强制执行属性值
最大限度地减少调用方必须指定的属性数量
在前两种情况下,尤其要考虑 Bazel 查询、 SHAC 规则、测试或其他机制是否可以 满足您的需求。Bazel 查询可以在依赖项树或目录树中定义的所有目标上运行。您可以编写一个运行查询并检查结果的测试吗?如果您的用例需要额外的属性,请考虑使用 标记。
使用现有规则和宏的原因包括:
更易于理解,开发人员的磨合期更短
熟悉 Bazel 的开发人员可以立即上手并了解目标。
团队中一部分的开发人员可以了解代码库其他部分的目标。
AI 更容易理解和生成使用通用规则和宏的 Bazel 文件
所需的
load()语句更少内置标识符不需要任何 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字符串。
- 对于私有属性(以下划线开头的属性),如果含义从
doc 字符串
在 Fuchsia 平台中,
doc字符串旨在在源文件中读取,而不是在某些生成的文档中读取。因此,偏好针对前者进行优化,而不是针对某些生成的文档的外观进行优化。长
doc字符串:在合理的情况下,偏好将顶级单句说明完全写在与
doc=同一行。Bazel 中没有 严格的行长度 限制。
如果需要多行,请使用三引号编写多行字符串。
避免附加常规字符串。
多行
doc字符串在与
doc实参同一行开始多行字符串 (doc = """Begin the comment...)。在
doc中的d下开始后续行,类似于 Python 注释在第一个"下开始下一行的方式。这针对一致性和可读性进行了优化,同时承认它对于生成的文本来说并不理想。
使用与
doc对齐的单独一行上的三引号 (""") 结束多行字符串。
规则和宏实现函数名称
使用前导下划线为 rule() 和 macro() 实例命名 implementation 函数,后跟规则或函数的名称,并以 _impl 结尾。
在宏中引用目标名称时使用变量
如果宏定义了一个目标,然后在另一个目标中依赖于该目标,请使用前一个目标的名称定义一个变量,并将其用于其 name 属性以及后一个目标的 deps。
这可以明确地链接这两个目标,尤其是在复杂情况下,其中存在基于其他目标名称的多个级别的目标名称,并且更容易突出显示和搜索。
除非可以提高可读性(例如,当所有目标名称都在一个位置定义时),否则请勿对内部未使用的目标使用变量。
封装内置规则、宏和函数
在编写宏以在 Fuchsia 平台代码库中替代常见的 Bazel 规则、宏或函数时,请在封装的名称前添加 fx_ 前缀。例如,应使用 fx_cc_library() 而不是 cc_library()。
“常见”包括 Bazel 中内置的符号(包括 native.*)以及常见代码库(例如 rules_cc)中的符号。对于其他
冲突(例如与 package 无关的 fx_package()),也请使用此前缀。请勿使用
a fuchsia_ 前缀,因为
fuchsia_... 文件和符号位于 Fuchsia Bazel
SDK 中。
避免配置转换
Fuchsia 平台目标应已在正确的配置中构建。如果您认为需要转换,则很可能不需要。请与 build 团队联系;只有 build 团队才能添加转换或新平台。
将工具作为可执行标签属性传递
运行工具的规则应将其作为
attr.label(executable = True, cfg = "exec"),并使用
ctx.executable 读取它。
反模式
通过
PATH查找工具。基础架构沙盒不会设置它,并且 Bazel 无法跟踪它不知道的工具。请改为将该工具作为可执行标签属性传递。将嵌入标签的 JSON 或字典 blob 传递给规则或宏。Bazel 仅在标签类型属性中看到标签,因此它不会构建或跟踪这些依赖项。请改为使用标签属性和提供程序。
通过
bazel-bin符号链接查找 build 输出。它指向一个配置,并且转换可以将您想要的输出放在不同的bazel-out目录中。通过提供程序传递输出,或明确报告其路径。依赖于上游规则集的实现详细信息,例如
rules_cc使用的_solib_目录前缀。当规则集更新时,它们会在不通知的情况下更改。
避免缓存或远程处理大型工件
请参阅避免缓存大型工件。