blob: fcd23fd853f701846dbf5b05f4100c357e7ba163 [file] [log] [blame] [view] [edit]
# Build system policies
This document details design principles and specific technical decisions made
that relate to how the Fuchsia build should work.
These principles apply to all modes of using the Fuchsia build, for instance
whether by interactive engineering workflows or via automated systems such as
CI/CQ.
## Goals and priorities of the build
Like any system, the build is often subject to multiple conflicting
requirements. When there is a conflict, we generally seek to satisfy these
priorities by order of importance:
1. Meet customer requirements, as determined by Fuchsia technical leadership.
2. Ensure correctness: produce the desired outputs.
3. Promote maintainability: documentation, sound engineering processes.
4. Improve performance: perform the same builds at a lower cost.
## Desired properties of the build
The following are considered to be good properties for the build:
* Hermeticity - the build is self-contained and neither influences external
software and configuration or is influenced by external software and
configuration.
* Repeatability and reproducibility - two builds from the same source tree
produce the same output or the same outcome deterministically.
Reproducibility promotes security and auditing, and simplifies
troubleshooting.
* Efficient - builds should only spend time doing work relevant to the build,
and must aim to minimize the impact on both human and infrastructure costs.
* Portability - builds should produce consistent results across all supported
host platforms.
These are ideals.
We aim to meet these ideals and measure our progress against these measures.
## Python scripts as build actions
Python scripts may be used as build actions.
Please follow the [Google style guide for Python][python-style].
Fuchsia currently uses Python 3.8. All Python sources are to begin with the
following:
```shell
#!/usr/bin/env fuchsia-vendored-python
```
## Shell scripts as build actions
Shell scripts may be used as build actions.
Shell scripts are encouraged for tasks that can be expressed with a few simple
shell commands. For complex operations, other languages are preferred.
Please follow the [Google style guide for shell scripting][bash-style].
Please use [shellcheck] to find and correct common shell programming errors.
We prefer POSIX (aka Bourne) shell scripts for portability across wide set of
host platforms.
If you're maintaining an existing Bash script, please restrict the features
used to version 3.2, or consider rewriting the script as POSIX shell script.
To check whether your script is POSIX compliant, you can use:
```posix-terminal
shellcheck --shell=sh
```
Scripts that run on POSIX shell should begin with the following:
```shell
#!/bin/sh
```
Scripts that specifically require Bash should begin with the following:
```shell
#!/bin/bash
```
## Migrations
The build system can assist in performing migrations for such things as
compiler features, new tools, or proliferation of various best practices.
A legacy undesired behavior can often be expressed in terms of a dependency
on a `config()` that applies this behavior. The use of a legacy tool or
template to be replaced can be captured by a dependency on a `group()`
target.
### Commit to a plan
Efforts to improve code health are always welcome, but you should have a clear
plan to finish what you started before you begin. A half-done migration that's
run out of momentum could be worse than no migration at all.
### Establish a regression stop
Assume that the codebase doubles every 8 months, and work as early as you can
to prevent new instances of the legacy behavior from being introduced. By
establishing a regression stop, you are "passively" cleaning up the codebase as
governed by its doubling rate, i.e. every doubling period you will have
passively cleaned up half of the codebase.
Ensure that allowlists are guarded by `OWNERS` files, and that POCs for the
migration are listed as owners. Since owners are defined by file, it may be
preferable to subdivide allowlists to different `BUILD.gn` files. For instance,
`config()` targets related to Rust were pulled out to `//build/config/rust` to
better manage the `OWNERS` assignments.
### Document migration / cleanup steps
Publish a clear document explaining the nature of the migration, how to
participate, and how to perform related maintenance work. This allows your
migration effort to scale, and keeps any individual from becoming a roadblock to
ongoing migration efforts such as when they're overwhelmed with support requests
or otherwise unavailable to attend to questions.
Review [C++ implicit conversions][wconversion-project] as a positive example.
### Simplify and automate allowlist maintenance
Allowlists are easy to express as `visibility` lists for GN targets. This opens
the door to automated analysis, and makes changes that violate the allowlist
fail their builds quickly.
When allowlisting targets to use the legacy behavior that you're migrating away
from, make it easy for owners of those targets to make simple refactors such as
renaming individual targets within their directories by allowlisting base
directories, not individual targets.
Document the steps to regenerate and trim any allowlist, such that they can be
conducted by anyone.
See the example below:
```gn
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/*",
...
]
}
```
Then elsewhere, automatically add a dependency on the allowlisted target.
```gn
# 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" ]
}
}
```
### Third party may be out of scope
Fuchsia uses a lot of third party code, that is code that is outside the scope
of the Fuchsia project. As a rule of thumb it's often fine to enter a blanket
allowlist for all third party code for opinionated changes or policy decisions.
```gn
group("bar_allowlist") {
...
visibility = [
"//third_party/*",
...
]
}
```
Depending on the nature of your change and the third party code in question,
it may be possible to make changes upstream. Use your best judgement.
[bash-style]: https://google.github.io/styleguide/shellguide.html
[python-style]: https://google.github.io/styleguide/pyguide.html
[shellcheck]: https://www.shellcheck.net/
[wconversion-project]: /docs/contribute/open_projects/cpp/wconversion.md