本文档详细介绍了与 Fuchsia build 的工作方式相关的设计原则和具体技术决策。 这些原则适用于使用 Fuchsia build 的所有模式,例如通过交互式工程工作流或通过 CI/CQ 等自动化系统。
构建的目标和优先级
与任何系统一样,构建系统通常会受到多项相互冲突的要求的约束。如果发生冲突,我们通常会按重要性顺序满足这些优先事项:
- 满足客户要求(由 Fuchsia 技术领导层确定)。
- 确保正确性:生成所需的输出。
- 提高可维护性:文档、完善的工程流程。
- 提升性能:以更低的费用执行相同的构建。
构建所需的属性
以下属性被视为 build 的良好属性:
- 封闭性 - 构建是自包含的,既不会影响外部软件和配置,也不会受外部软件和配置的影响。
- 可重复性和可再现性 - 同一源代码树中的两个 build 会确定性地产生相同的输出或相同的结果。 可重现性可提高安全性和审核能力,并简化问题排查。
- 高效 - 构建应该只花时间执行与构建相关的工作,并且必须旨在最大限度地减少对人力和基础设施成本的影响。
- 可移植性 - build 应在所有受支持的主机平台上产生一致的结果。
这些都是理想情况。 我们致力于实现这些理想,并根据这些措施衡量我们的进展。
作为构建操作的 Python 脚本
Python 脚本可用作构建操作。
请遵循 Google Python 样式指南。
Fuchsia 使用供应商提供的 Python 解释器(fuchsia-vendored-python,目前为 Python 3.11 或更高版本)。Python 源文件必须以以下内容开头:
#!/usr/bin/env fuchsia-vendored-python
作为构建操作的 Shell 脚本
Shell 脚本可用作构建操作。
对于可以通过几个简单的 shell 命令表达的任务,建议使用 shell 脚本。对于复杂的操作,最好使用其他语言。
请遵循 Google shell 脚本编写样式指南。 请使用 shellcheck 查找并更正常见的 shell 编程错误。
我们更倾向于使用 POSIX(也称为 Bourne)shell 脚本,以便在各种主机平台上实现可移植性。 如果您要维护现有的 Bash 脚本,请将使用的功能限制为 3.2 版,或考虑将脚本重写为 POSIX shell 脚本。如需检查脚本是否符合 POSIX 标准,您可以使用:
shellcheck --shell=sh在 POSIX shell 上运行的脚本应以以下内容开头:
#!/bin/sh
明确要求使用 Bash 的脚本应以以下内容开头:
#!/bin/bash
迁移
构建系统可以协助执行迁移,例如编译器功能、新工具或各种最佳实践的普及。旧版不良行为通常可以表示为对应用此行为的 config() 的依赖。对 group() 目标的依赖项可以捕获要替换的旧版工具或模板的使用情况。
承诺采用方案
我们始终欢迎您努力改善代码运行状况,但您应该在开始之前制定明确的计划来完成您开始的工作。半途而废的迁移可能比不迁移更糟糕。
建立回归停止
假设代码库每 8 个月翻一番,请尽早采取行动,防止引入旧版行为的新实例。通过建立回归停止,您可以根据代码库的翻倍率“被动”清理代码库,即在每个翻倍期内,您将“被动”清理一半的代码库。
确保许可清单由 OWNERS 文件保护,并且迁移的 POC 被列为所有者。由于所有者是按文件定义的,因此最好将许可清单细分为不同的 BUILD.gn 文件。例如,与 Rust 相关的 config() 目标已提取到 //build/config/rust,以便更好地管理 OWNERS 分配。
文档迁移/清理步骤
发布一份清晰的文档,说明迁移的性质、如何参与以及如何执行相关的维护工作。这样,您的迁移工作就可以扩缩,并防止任何个人成为持续迁移工作的障碍,例如当他们因支持请求而不堪重负或无法回答问题时。
查看 C++ 隐式转换,这是一个正面示例。
简化并自动执行许可清单维护
许可清单很容易表示为 GN 目标的 visibility 列表。这为自动分析打开了大门,并使违反许可清单的更改无法快速构建。
在将目标列入许可清单以使用您要迁移的旧版行为时,请将基本目录(而非单个目标)列入许可清单,以便这些目标的所有者轻松进行简单的重构,例如重命名其目录中的单个目标。
记录重新生成和删减任何许可清单的步骤,以便任何人都可以执行这些步骤。
请参见下面的示例:
group("foo_allowlist") {
# ________ _________ ________ ________
# |\ ____\|\___ ___\\ __ \|\ __ \
# \ \ \___|\|___ \ \_\ \ \|\ \ \ \|\ \
# \ \_____ \ \ \ \ \ \ \\\ \ \ ____\
# \|____|\ \ \ \ \ \ \ \\\ \ \ \___|
# ____\_\ \ \ \__\ \ \_______\ \__\
# |\_________\ \|__| \|_______|\|__|
# \|_________|
# This is an allowlist of targets that use the deprecated "foo" tool.
# As of April 2021 we no longer use "foo". Users should migrate to the new
# "bar" tool as described in this guide:
# https://fuchsia.dev/...
#
# To regenerate:
# fx gn refs $(fx get-build-dir) //path/to:foo_allowlist | sed 's|\(.*\):.*|"\1/*",|' | sort | uniq
#
# To trim:
# build/bazel/scripts/trim_visibility.py --target="//path/to:foo_allowlist"
visibility = [
"//src/project1/*",
"//src/project2/*",
...
]
}
build/bazel/scripts/trim_visibility.py 会使用 fx gn refs 和 fx bazel query 查找目标的消费者,并以其所在文件的语法输出经过修剪的列表。如果许可清单中的目标是在 BUILD.bazel 中定义的(包括 bazel2gn 同步到 BUILD.gn 的目标),请使用 Bazel 语法编写许可清单;脚本会根据 Bazel 和 GN 图中的使用方检查该清单。如需了解详情,请参阅 bazel2gn 文档。
然后,在其他位置自动添加对许可清单目标的依赖项。
# Invoke the legacy foo tool.
# For new usage, please consider using the new bar tool instead!
# See:
# https://fuchsia.dev/...
# ...
template("foo") {
action(target_name) {
...
deps += [ "//build/foo:foo_allowlist" ]
}
}
第三方可能不在范围内
Fuchsia 使用了大量第三方代码,即 Fuchsia 项目范围之外的代码。根据经验,通常可以为所有第三方代码输入一个一揽子许可清单,以便进行主观更改或违规认定。
group("bar_allowlist") {
...
visibility = [
"//third_party/*",
...
]
}
根据更改的性质和相关第三方代码,您或许可以在上游进行更改。请自行斟酌。