构建系统政策

本文档详细介绍了与 Fuchsia build 的运作方式相关的设计原则和具体技术决策。 这些原则适用于使用 Fuchsia build 的所有模式,例如通过交互式工程工作流或通过 CI/CQ 等自动化系统。

build 的目标和优先级

与任何系统一样,build 通常会受到多个相互冲突的要求的制约。如果存在冲突,我们通常会按重要性顺序满足以下优先级:

  1. 满足客户要求,具体由 Fuchsia 技术领导层确定。
  2. 确保正确性:生成所需的输出。
  3. 提高可维护性:文档、合理的工程流程。
  4. 提升性能:以更低的成本执行相同的 build。

build 的理想属性

以下属性被认为是 build 的良好属性:

  • 密封性 - build 是自成一体的,既不会影响外部软件和配置,也不会受到外部软件和配置的影响。
  • 可重复性和可重现性 - 来自同一源代码树的两个 build 会确定性地生成相同的输出或相同的结果。 可重现性有助于提高安全性、进行审核和简化问题排查。
  • 高效性 - build 应仅花费时间执行与 build 相关的工作,并且必须尽可能减少对人工和基础架构成本的影响。
  • 可移植性 - build 应在所有受支持的主机平台上生成一致的结果。

这些都是理想状态。 我们的目标是实现这些理想状态,并根据这些衡量标准来衡量我们的进展。

将 Python 脚本用作 build 操作

Python 脚本可用作 build 操作。

请遵循 Google Python 样式指南

Fuchsia 目前使用 Python 3.8。所有 Python 源代码都应以以下内容开头:

#!/usr/bin/env fuchsia-vendored-python

将 Shell 脚本用作 build 操作

Shell 脚本可用作 build 操作。

对于可以使用一些简单的 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 列表。这为自动化分析打开了大门,并使违反许可名单的更改能够快速失败其 build。

在将目标列入许可名单以使用您要迁移的旧版行为时,请通过将基本目录(而非单个目标)列入许可名单,让这些目标的所有者能够轻松进行简单的重构,例如重命名其目录中的单个目标。

记录重新生成和修剪任何许可名单的步骤,以便任何人都可以执行这些步骤。

请参见下面的示例:

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:
  # scripts/gn/trim_visibility.py --target="//path/to:foo_allowlist"
  visibility = [
    "//src/project1/*",
    "//src/project2/*",
    ...
  ]
}

然后在其他位置,自动添加对列入许可名单的目标的依赖项。

# 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/*",
    ...
  ]
}

根据更改的性质和所涉及的第三方代码,您可能可以在上游进行更改。请自行慎重判断。