4. Containerised build environment
The build environment is available as a container image, driven by the
dbuild.sh standard script. It is the recommended way to build: the
image carries the cross toolchains, the host packages and Python, so nothing has
to be installed on your machine, and every developer builds against the same,
pinned environment.
The image contains no project source. The repository stays on the host and is
bind-mounted at its own absolute path, which is what makes the container and
the host interchangeable — BitBake stamps, the buildroot host tree, CMake caches
and the *.attach.sha256 manifests all embed absolute paths, so a tree built
inside the container can be built further outside it, and the other way round.
4.1. Usage
dbuild.sh --build Build (or rebuild) the image
dbuild.sh Interactive shell inside the container
dbuild.sh build.sh bsp-linux Run a build
dbuild.sh deploy.sh bsp-linux Deploy it
dbuild.sh -h Print help
$ scripts/dbuild.sh --build # once, or after changing the image
$ scripts/dbuild.sh build.sh bsp-linux
$ scripts/dbuild.sh deploy.sh bsp-linux
Any command is run with env.sh already sourced and with the current directory
preserved, so dbuild.sh in front of a command you already know is all there is
to it. With no command you get an interactive shell instead, with the environment
sourced and a [ib-build] prompt to make it obvious where you are — the tree
looks identical to the host one, same absolute paths.
Two environment variables adjust the invocation:
Variable |
Meaning |
|---|---|
|
Image name:tag, default |
|
Extra options passed to |
4.2. Running as you, not as root
Everything the recipes write lands in the bind-mounted repository — attached
source trees, build/tmp, the storage images. If the container ran as root,
all of it would come back root-owned on the host, reintroducing exactly the
ownership churn the unprivileged-bitbake model removes.
The UID/GID cannot be baked into the image (they differ per host), so
dbuild.sh passes HOST_UID/HOST_GID and the image’s entrypoint starts
as root, materialises a matching account, then drops to it with setpriv —
no extra process in the signal path, so Ctrl-C reaches the build and exit codes
propagate.
Inside a throw-away container, the privileged build steps take the simple route:
a blanket NOPASSWD sudoers rule, plus Defaults !authenticate because the
scripts open their session with sudo -v, which validates the user rather
than a command. This also means the container is the answer when the host’s own
sudo gets in the way.
4.3. What the container is given
--privilegedand/dev— the storage steps need loop devices,mountandmkfs. Loop devices are a host-kernel resource, so concurrent builds on one machine contend for them exactly as they do outside a container.--network host— keepsst.sh’s slirp port forwards and the GDB stub reachable from the host without publishing ports, and lets the recipes fetch through the host resolver.The ssh-agent socket, when present, so a fetch over SSH works from inside. Nothing is copied into the image.
DISPLAYand the X11 socket with the X cookie — whatst.sh -dneeds to open its window — mounted at its own path withXAUTHORITYpointing at it — the container user has a differentHOME, and under Wayland the cookie lives outsideHOMEanyway.stdin, always (
-i): without it Docker hands the container/dev/nulland piping into a containerised command would silently deliver nothing. A tty (-t) is allocated only for a real terminal, so CI logs stay clean.
4.4. Caveats
A mount lives only as long as the container that made it.
dbuild.sh mount.shmounts inside its own mount namespace, and the mount is gone once the command returns. Run the mount and whatever uses it in the same container — typically from an interactivedbuild.shshell.A server started inside dies with the command. The container runs with
--rm, sodbuild.shstarts the TEZI feed server on the host after the container exits instead (it shares the host network namespace, but not its lifetime).A snap-packaged Docker can only bind-mount paths under ``$HOME``: the confined daemon fails with “read-only file system” elsewhere. This only matters for a
site.confthat pointsIB_HTTP_DEPLOY_PATHoutside the tree — the default feed is inside the bind-mounted tree.dbuild.shdetects the snap both by client path and by asking the daemon, and skips such a mount with a one-line warning rather than failing every invocation; prefer Docker from the apt repository.
4.5. The image
The image is defined in docker/build-env/, and the build context is that
directory only — never the project root, which holds tens of gigabytes of build
output.
File |
Role |
|---|---|
|
Two stages: fetch the Arm cross toolchain, then build the environment proper. |
|
The host package list, one per line with comments; also what you install when building on the host. |
|
Materialises the host user and drops to it. |
|
rc file for the interactive shell (sources |
The toolchain version is pinned in the Dockerfile
(aarch64-none-linux-gnu 12.3.rel1): bump it there, rebuild the image and
re-validate — never by installing something else on a host. Adding a host
dependency means adding a line to packages.txt; the Dockerfile installs that
file verbatim.
Note
Sourcing env.sh happens in the interactive shell’s rc file rather than in
a parent process on purpose: env.sh defines shell functions (the
cd/pushd/popd wrappers and ib_autoswitch_*), which only exist
in the shell that sourced them. A parent that sources it and then execs bash
passes on the exported variables but loses the functions.