|author||Shuhei Takahashi <firstname.lastname@example.org>||Thu Oct 26 00:58:48 2023 +0900|
|committer||Chromeos LUCI <email@example.com>||Mon Oct 30 01:00:37 2023 +0000|
alchemist: Introduce MaybePackageDetails This patch follows the same pattern as MaybeEBuildMetadata: introduces an enum that allows interleaving success/failure cases of package loading and differentiating them statically at the same time. While this new enum allows us cleaning up a few logic, this patch focuses on introducing it with minimal changes. This is a pure refactoring and not supposed to change any behavior. BUG=b:303400631 TEST=bazel test //bazel/portage/bin/alchemist:all Change-Id: Ie0d1396c7a068b697b3e404aa8dcd287465a0b3e Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/bazel/+/4982815 Reviewed-by: Maksim Ivanov <firstname.lastname@example.org> Tested-by: Shuhei Takahashi <email@example.com> Commit-Queue: Shuhei Takahashi <firstname.lastname@example.org>
This repository provides the implementation to build ChromeOS with Bazel.
For building ChromeOS with Bazel, use the following
repo command to check out with a few additional repositories.
$ mkdir ~/chromiumos $ cd ~/chromiumos # If you have access to the internal manifests: $ repo init -u https://chrome-internal.googlesource.com/chromeos/manifest-internal -g default,bazel -b snapshot # Otherwise: $ repo init -u https://chromium.googlesource.com/chromiumos/manifest -g default,bazel -b snapshot $ repo sync -c -j 16 $ cd src
-g default,bazelis important to check out some additional repositories needed to build with Bazel.
snapshotbranch rather than
mainbecause Bazel‘s caching logic requires all inputs to match exactly, so you’re better off working from the
snapshotbranch that was already built by the Snapshot/CQ builders rather than working from
mainand picking up whatever commit happened to be at ToT at the time you ran
repo sync. You‘ll be at most 40 minutes behind ToT, and you’ll have the best chance of getting cache hits to speed your builds.
repo initcommand atop an existing checkout.
Unless otherwise specified, examples in this doc assume that your current directory is
We support two configurations to build ChromeOS with Bazel:
/etc/portagein the chroot, including
cros workonstates of packages. Also, this is the only way you’re going to get remote cache hits right now. On the other hand, you still need to set up a CrOS SDK chroot.
First, enter the CrOS SDK chroot with
cros_sdk command, which will create a new one if you haven't already. Then run the following command to create the
(cr) $ /mnt/host/source/src/scripts/create_sdk_board_root --board amd64-host --profile sdk/bootstrap
This will create
/build/amd64-host. This sysroot contains the Portage configuration that is used when building host tool packages. i.e., CBUILD.
You can then proceed to create the target board's sysroot:
(cr) $ setup_board --board amd64-generic --public --skip-chroot-upgrade --skip-toolchain-update
If you're attempting to build a public image while using an internal manifest, the
--public flag allows you to do that, which is useful when attempting to recreate a CQ/Snapshot failure for build targets that use public manifests on the CI bots, such as amd64-generic. Omit the flag if you do want to build internal packages.
Now that you have configured your chroot, you can invoke a build with the standard
cros build-packages command, except that you need to pass the extra option
--bazel to build with Bazel.
To build a single Portage package, e.g.
$ cros build-packages --board=amd64-generic --bazel sys-apps/attr
To build all packages included in the ChromeOS test image:
$ cros build-packages --board=amd64-generic --bazel
Upon successful completion, packages are installed to the sysroot inside the CrOS SDK chroot.
/usr/bin/bazel. Read the following section to see how to specify build targets.
cros workonstates in the CrOS SDK chroot are ignored even if they exist, and the live (9999) version of packages (if they exist and are not marked as
CROS_WORKON_MANUAL_UPREV) will be chosen by default. This means you can edit your source code and feel confident that the correct packages are getting rebuilt, though it might cause some build issues when live ebuilds are not prepared for Bazel builds. Note that you'll get no remote cache hits from CI builds today.
Before you start building a package you need to ensure that
which bazel prints a path under your depot_tools checkout. The wrapper script provided by
depot_tools performs additional tasks besides running the real
The syntax for specifying a Portage package is:
Now you're ready to start building. To build a single Portage package, e.g.
$ BOARD=amd64-generic bazel build @portage//target/sys-apps/attr
To build all packages included in the ChromeOS base image:
$ BOARD=amd64-generic bazel build @portage//target/virtual/target-os:package_set
package_set is a special target that also includes the target's PDEPENDs.
To build a package for the host, use the
$ BOARD=amd64-generic bazel build @portage//host/app-shells/bash
To build all packages included in the ChromeOS test image:
$ BOARD=amd64-generic bazel build @portage//target/virtual/target-os:package_set @portage//target/virtual/target-os-dev:package_set @portage//target/virtual/target-os-test:package_set
When you run Bazel inside the CrOS SDK chroot, you can simply use the standard
cros build-image command to build ChromeOS images.
The rest of the section describes the very experimental way to build ChromeOS images under Bazel outside the CrOS SDK chroot.
We have the following targets to build images:
@portage//images:chromiumos_minimal_image: Minimal image that contains
@portage//images:chromiumos_base_image: Base image.
@portage//images:chromiumos_dev_image: Dev image.
@portage//images:chromiumos_test_image: Test image.
Building a ChromeOS image takes several hours. Most packages build in a few minutes, but there are several known heavy packages, such as
chromeos-base/chromeos-chrome that takes 2-3 hours. You can inject prebuilt binary packages to bypass building those packages. See Injecting prebuilt binary packages for more details. To make
chromeos-base/chromeos-chrome build faster, you can also use Goma.
After building an image, you can use
cros_vm command available in CrOS SDK to run a VM locally. Make sure to copy an image out from
bazel-bin as it's not writable by default.
$ cp bazel-bin/external/_main~portage~portage/images/chromiumos_base_image.bin /tmp/ $ chmod +w /tmp/chromiumos_base_image.bin $ chromite/bin/cros_vm --start --board=amd64-generic --image-path /tmp/chromiumos_base_image.bin
You can use VNC viewer to view the VM.
$ vncviewer localhost:5900
You can also use
cros_vm command to stop the VM.
$ chromite/bin/cros_vm --stop
By default you can‘t tab complete the
@portage// repository. This is because bazel doesn’t provide support for tab completing external repositories. By setting
export ENABLE_PORTAGE_TAB_COMPLETION=1 in your
bazel will create a
@portage symlink in the workspace root (i.e.,
~/chromiumos/src). This allows the bazel tab completion to work, but comes with one caveat. You can no longer run
bazel build //... because it will generate analysis errors. This is why this flag is not enabled by default.
@portage symlink has another added benefit, you can easily browse the generated
run_tests.sh script runs currently available tests:
Optionally, you can skip running some tests by specifying some of the following environment variables when running
Bazel is able to correctly reuse content from the cache when all inputs are identified to it so it can detect when they change. Since our toolchain and our host tools (e.g. gsutil) are not yet fully hermetic, it‘s possible that you’ll run into problems when tools not yet tracked by Bazel are updated. In these situations we've found it useful to run
bazel clean --expunge to clear cached artifacts that seem not to be cleared without the
If you find you need the
--expunge flag, please file a bug to let the Bazelification team know about the non-hermeticity so we can fix the problem.
portage/... for building Portage packages (aka Alchemy)
common/... common Rust/Go libraries
build_defs/... build rule definitions in Starlark
repo_defs/... additional repository definitions
prebuilts/... defines prebuilt binaries
sdk/... defines the base SDK
tools/... misc small tools for development
workspace_root/... contains various files to be symlinked to the workspace root, including
You can speed up the build by enabling remote Bazel caching with RBE. To do this, follow this instruction to authenticate.
After authentication, make sure that you restart the Bazel instance by running
Sometimes you want to enter an ephemeral CrOS chroot where a package build is failing to inspect the environment interactively.
To enter an ephemeral CrOS chroot, run the following command:
$ BOARD=arm64-generic bazel run @portage//target/sys-apps/attr:debug -- --login=after
This command will give you an interactive shell after building a package. You can also specify other values to
--login to choose the timing to enter an interactive console:
--login=before: before building the package
--login=after: after building the package (default)
--login=after-fail: after failing to build the package
chromeos-base/chromeos-chrome takes 2-3 hours, but you can use Goma to make it as fast as less than 1 hour.
To use Goma, please follow Goma for Chromium contributors (or go/chrome-linux-build if you‘re a Googler) and run
goma_auth login for authentication. Please make sure that you perform authentication inside the chroot if you’re going to run
bazel build inside the chroot, and do that outside the chroot if you're going to run it outside the chroot.
After authentication, you can just run
bazel build with
USE_GOMA=true to enable Goma.
$ USE_GOMA=true BOARD=amd64-generic bazel build @portage//chromeos-base/chromeos-chrome
You can also run
--run-goma to run it with Goma.
$ build_packages --bazel --board=amd64-generic --run-goma
In the case your work is blocked by some package build failures, you can workaround them by injecting prebuilt binary packages via command line flags.
ebuild target under
@portage//internal/packages/..., an associated string flag target is defined. You can set a
gs:// URL of a prebuilt binary package to inject it.
For example, to inject a prebuilt binary packages for
chromeos-chrome, you can set this option:
You can run generate_chrome_prebuilt_config.py to generate the prebuilt config for the current version of chromeos-chrome.
% BOARD=amd64-generic portage/tools/generate_chrome_prebuilt_config.py
When performing changes to
chromite or other things that cache bust large parts of the graph, it might be beneficial to pin the binary packages for already built packages so you don't need to rebuild them when iterating on your changes. You can use the generate-stage2-prebuilts script to do this:
$ BOARD=amd64-generic ./bazel/portage/tools/generate-stage2-prebuilts
This will scan your
bazel-bin directory for any existing binpkgs and copy them to
~/.cache/binpkgs. It will then generate a
prebuilts.bazelrc that contains various
--config options. The
prebuilts.bazelrc is invalid after you
repo sync since it contains package version numbers. Just re-run the script after a
repo sync to regenerate the
prebuilts.bazelrc and it will pin the packages with versions that still exist in your
Running a build with pinned packages:
$ BOARD=amd64-generic bazel build --config=prebuilts/stage2-board-sdk @portage//target/sys-apps/attr
In case you need to extract the contents of a binary package so you can easily inspect it, you can use the
xpak split CLI.
bazel run //bazel/portage/bin/xpak:xpak -- split --extract libffi-3.1-r8.tbz2 libusb-0-r2.tbz2
If you'd like to run the tests every time you commit, add the following. You can skip it with
git commit --no-verify.
cd ~/chromiumos/src/bazel ln -s ../../../../../src/bazel/portage/tools/run_tests.sh .git/hooks/pre-commit