Skip to content

Debugging a failing package

Debugging a failing package

An action fails, or the build passes and the manifest differs. Find out why.

Read the failure

br2 dev prints every failed action with the tail of its output. The whole log of an action is in Buck2:

tools/buck2 log what-failed
tools/buck2 log what-ran --show-std-err | less

Watch a long action while it runs

The wrapped actions work in a scratch directory. While make <pkg> runs, its log is being written to make.log in it:

ls -dt /var/tmp/buckroot-work/br2-* | head
tail -f /var/tmp/buckroot-work/br2-host-cmake-*/make.log

The base directory is BR2_WORK_DIR (default /var/tmp/buckroot-work). It is deleted when the action ends; to keep it, and the whole Buildroot output tree with it, set BR2_KEEP_WORK=1 in the environment of the run.

Re-run one action by hand

Every action is python3 br2/pkg_action.py <mode> --spec SPEC --out OUT, and the spec is a JSON file Buck2 wrote:

SPEC=$(ls -t buck-out/v2/art/root/br2/generated/__rootfs__/*/rootfs.spec.json | head -1)
BR2_KEEP_WORK=1 python3 br2/pkg_action.py rootfs --spec "$SPEC" --out /tmp/rootfs.tar

This runs exactly what the action ran, with a work directory you can go and look at. It is how a missing cross strip was found (every binary in the image was larger than in the golden): out/host/bin/ lacked the cross tools because the rootfs action had merged only some of the packages' host trees.

Failure catalogue

Symptom Likely cause Where to go
No rule to make target 'package/x/x.mk' a package outside this one's dependency closure extra_view
Patches missing, build fails late a patch directory outside the view (a group directory such as package/gcc/) br2 preflight, then extra_view
Killed / OOM two heavy actions at once heavy
Kconfig / syncconfig error inside a package action a symbol's slice lacked a PROVIDES/HAS option scripts/show-inputs.py PKG --config
hash file / download errors a dead URL or a SHA-512-only hash br2 fetch
Build passes, a lot of files differ in size the strip step did not run, or a config the golden had compare with br2 golden
Same digest, different results across machines an ambient host tool that differs Wrapped and native
You must install 'git' in a remote action only the worker image lacks a host tool the local machine has add it to toolkit/infra/buildbarn/runner/Dockerfile and recreate the runner
make exits with -11 (SIGSEGV) on an old Buildroot GNU make 4.3 overflows the 8 MiB stack in printvars/show-info already handled: the namespace raises the stack limit; keep br2-ns.sh current
you should not run configure as root old host-tar, host-m4 refuse root already handled: the namespace sets FORCE_UNSAFE_CONFIGURE=1
No rule to make target 'show-vars' Buildroot older than 2023 already handled: extract falls back to printvars
unexpected local source '/mnt/...' at extract a local package site outside common/ in-tree sites (/mnt/external/..., /mnt/src/...) are accepted; anything else belongs in common/
make: *** No rule to make target for a package that builds locally, in a remote action only a view entry is not a declared input at its real path (a copied export_file), so the worker has nothing there br2 viewcheck --mode remote; see ADR-0014
br2-ns: view entry missing: ... a declared entry is absent from this action's inputs declare it (a links or extra_view entry), then re-run br2 viewcheck
ld: cannot find crti.o in a native build a native recipe hard-coded a toolchain tuple or the sysroot's directory name native rules read TARGET_TUPLE and STAGING_SUBDIR from br2/generated/target.bzl (ADR-0012)
Kernel: kernelrelease probe fails in the rootfs action the kernel's build tree is not there handled by a stub; if you see it, the toolkit is stale

Reading a manifest difference

scripts/fs-manifest.py scan <image> -o /tmp/mine.json
scripts/fs-manifest.py diff golden/rootfs.manifest.json /tmp/mine.json

differs: /bin/busybox (size: 813512 -> 997264) with a larger result is nearly always "not stripped". only in A is a file the golden had and Buck2 lost - commonly a dangling symlink Buck2 cannot see as an input (/var/log, /etc/mtab), which the renderer records as data. A file that differs on every run of the golden build too (random salts) belongs in manifest_ignore.

To compare one package's artifact between wrapped and native:

scripts/compare-pkg.py wrapped.tar native.tar