blob: de0cb55fcaabce143d82bc5309607c0affc96525 [file] [log] [blame] [view]
# ChromeOS Bazel Design / Architecture
This document will explain the overall design of the ChromeOS bazel migration and the rational
for why certain things are architected the way that they are.
## Introduction
TODO
## Configuration
ChromeOS has three main sources of configuration data:
1) [ebuild repositories](https://chromium.googlesource.com/chromiumos/docs/+/HEAD/portage/overlay_faq.md) (overlays) - This was the initial configuration format used by ChromeOS. Overlays are organized into various categories that logically group specific traits. These overlays can define parent overlays which forms a graph of configuration nodes where child nodes can override configuration data of parent nodes.
i.e.,
> eclass-overlay -> portage-stable -> chromiumos-overlay -> chipset-XXX -> baseboard-YYY -> board-ZZZ.
As of Jan 2023 we have almost 500 public and private overlays. The primary mechanism of controlling feature enablement is via [USE flags](https://wiki.gentoo.org/wiki/USE_flag).
[ebuilds](https://devmanual.gentoo.org/ebuild-writing/index.html) are also a wealth of [configuration](https://devmanual.gentoo.org/ebuild-writing/variables/index.html) data. They define all the `USE` flags supported by the package, the valid combinations of those `USE` flags, the different types of dependencies, the constraints that need to be imposed on its dependencies, variables to control how `eclasses` get configured (i.e., [cros-workon](https://source.chromium.org/chromium/chromiumos/overlays/chromiumos-overlay/+/main:eclass/cros-workon.eclass;l=65;drc=bb94461990266fcd3368bbbe9f907497339902f3), [cros-rust](https://source.chromium.org/chromium/chromiumos/overlays/chromiumos-overlay/+/main:eclass/cros-rust.eclass;l=27;drc=bb94461990266fcd3368bbbe9f907497339902f3), etc).
1) [chromeos-config](https://chromium.googlesource.com/chromiumos/platform2/+/HEAD/chromeos-config/README.md) - This configuration system was introduced to help ChromeOS scale. Previously every OEM device that derived from a reference design required its own `board-` overlay. This meant that each model would have its own OS build which is very expensive to maintain. With the introduction of `chromeos-config` (i.e., `unibuild`) it was now possible to have a single `board-` overlay that supported multiple models. `chromeos-config` does runtime probing of the device and exposes a directory at `/run/chromeos-config` that contains all the various runtime configuration for the device. There are some ebuilds that need to generate per-model artifacts. These ebuilds consume the `chromeos config` at build time and iterate over all the defined models. Since a single build is used for all models, the `USE` flags set by the `board-` overlay must be compatible with all the models.
1) [boxster](go/cros-boxster-site) - Boxster was a reenvisioning of `chromeos-config`. It uses `proto` and `starlark` as the configuration language and provides a lot more structure to how configuration is defined. The boxster configuration gets transformed into the same output format as `chromeos-config`. This insulates the ebuilds and devices from having to learn about a new configuration system.
With the migration to Bazel, we have an option for another configuration model (insert `xkcd` link here), [bazel platforms](https://bazel.build/extending/platforms). Bazel platforms can be used to tell bazel how to the target should be compiled, which features to enable, what libraries to link in, etc.
### USE Flags
Due to the massive amounts of portage configuration used by ChromeOS and the constant churn of that configuration, it doesn't make sense to take on a massive configuration conversion effort as part of the bazel migration. Instead we will embrace `USE` flags and the portage configuration model. Due to the [expressiveness](https://dev.gentoo.org/~ulm/pms/head/pms.html#section-8.2) of the `USE` flag expressions, and the complexity of how `USE` flags get computed it's practically impossible to convert the `USE` flag model into `bazel` [constraint_settings](https://bazel.build/reference/be/platform#constraint_setting) and [select](https://bazel.build/reference/be/functions#select) clauses. Instead we will preprocesses all of the portage configuration settings and bake their final values into the generated `BUILD.bazel` files.
```
ebuild(
name = "8.1_p1-r1",
ebuild = "readline-8.1_p1-r1.ebuild",
distfiles = {
"@portage-dist_readline-8.1.tar.gz//file": "readline-8.1.tar.gz",
"@portage-dist_readline81-001//file": "readline81-001",
},
build_deps = [
"//internal/overlays/third_party/portage-stable/sys-libs/ncurses:5.9-r99",
],
runtime_deps = [
"//internal/overlays/third_party/portage-stable/sys-libs/ncurses:5.9-r99",
],
files = glob(["files/**", "*.bashrc"]),
use = ["-cros_host", "-static-libs", "unicode", "utils"],
eclasses = [
"//internal/overlays/third_party/portage-stable/eclass:flag-o-matic",
"//internal/overlays/third_party/chromiumos-overlay/eclass:toolchain-funcs",
"//internal/overlays/third_party/portage-stable/eclass:usr-ldscript",
],
sdk = "//internal/sdk",
visibility = ["//visibility:public"],
)
```
This removes bazel from having to understand anything about `USE` flags, portages dependency resolution logic, and keeps the generated `BUILD` files readable.
#### Flag Overrides
Portage supports setting the `USE` environment variable when invoking `emerge`:
USE="debug" emerge-$BOARD sys-kernel/chromeos-kernel-upstream
This invocation overrides the `USE` flags defined in the `overlays`, but it only override the `USE`
flag for packages installed by the `emerge` invocation. It doesn't apply globally to all packages
that are already installed unless you specify `--deep` and `--newuse`. We want to support something
similar.
When invoking `bazel` with the `USE` environment variable it will be taken into account when
`alchemist` does the `USE` flag calculations. It will be applied globally though, so it will affect
ALL packages that declare that `USE` flag.
BOARD=arm64-generic USE=debug bazel build @portage//sys-kernel/chromeos-kernel-upstream
If the user wanted to limit a USE flag override to a specific package, they could use the user
provided [package.use](https://wiki.gentoo.org/wiki//etc/portage/package.use) file. This file
can be used to override the individual `USE` flags for the packages they care about.
src/package.use.user:
```
sys-kernel/chromeos-kernel-upstream debug
```
`alchemist` will consume the file when calculating the `USE` flags and apply it as an override source.
The benefits of this approach are that it's well documented, and well understood. It also removes
the burden of having to remember which debug `USE` flags you normally set on a package.
TODO: Should we support a `host` `package.use` and a `target` `package.use`?
### Host Tools
Currently we don't support `BDEPEND`, but we would like to. We will generate a new `ebuild` target with the `host`'s portage configuration baked in.
```
ebuild(
name = "4.2.1-r4_host",
ebuild = "make-4.2.1-r4.ebuild",
distfiles = {
"@portage-dist_make-4.2.1.tar.bz2//file": "make-4.2.1.tar.bz2",
},
files = glob(["files/**", "*.bashrc"]),
use = ["guile", "nls", "-static"],
eclasses = [
"//internal/overlays/third_party/portage-stable/eclass:flag-o-matic",
],
sdk = "@//bazel/portage/sdk",
visibility = ["//visibility:public"],
)
```
The `target` version of the ebuild will then include the `_host` targets as `tool_deps`.
```
ebuild(
name = "8.1_p1-r1",
ebuild = "readline-8.1_p1-r1.ebuild",
...
tool_deps = [
"//internal/overlays/third_party/portage-stable/sys-devel/make:4.2.1-r4_host",
],
)
```
TODO: Instead of the `_host` suffix, evaluate putting `target` and `host` packages in their own sub-directories instead. i.e., `@portage//host/...` and `@portage//target/...`.
TODO: Add `target_compatible_with` to `host` and `target` ebuilds once we have the attributes defined.
To make it easy for users to build `host` packages, they will use the same generated aliases in the `@portage` repo as they use for the `target` packages. The difference will be they will pass in `--platforms=@portage//:host`. The generated aliases will use this in a `select` statement to choose the correct package to build.
@portage//sys-devel/make/BUILD.bazel
```
alias(
name = "4.2.1-r4",
actual = select({
"//:host": ["//internal/overlays/third_party/portage-stable/sys-devel/make:4.2.1-r4_host"],
"//conditions:default": ["//internal/overlays/third_party/portage-stable/sys-devel/make:4.2.1-r4]
}),
visibility = ["//visibility:public"],
)
```
An example invocation looks like so:
```
bazel build --platforms=@portage//:host @portage//sys-devel/make
```