From 857ed0f73ff2c6e4661338c0965b54fbd593413b Mon Sep 17 00:00:00 2001 From: Daniel Rossier Date: Thu, 16 Jul 2026 15:24:51 +0200 Subject: [PATCH 1/2] build: auto-build QEMU on a virt BSP build when its binary is missing A fresh tree needed a separate `build.sh qemu` before `st.sh` would run; forgetting it produced a bare "qemu-system-* not found". Make a BSP build bootstrap the emulator instead. build.sh now, before building bsp-so3/bsp-linux on virt32/virt64, checks for qemu/build/qemu-system- and runs `bitbake qemu` first when it is absent. It is a bootstrap only: an already-built emulator is left untouched, so the inner build loop pays nothing and the qemu-hacking loop stays the explicit `build.sh -x qemu`. Hardware platforms (verdin, rpi4) never run under QEMU and are skipped. Opt out with IB_BUILD_QEMU="0". The check lives in build.sh, not a bitbake dependency: a parse-time os.path.exists() is not part of bitbake's parse-cache key (so it goes stale), and qemu's do_build is nostamp (so a hard dependency would reconfigure+rebuild qemu on every BSP build). A wrapper check reads the real FS state each run. CI sets IB_BUILD_QEMU="0" (it only builds+deploys, never runs the emulator); the lvperf Dockerfiles drop the now-redundant `build.sh -x qemu` step. Docs and local.conf document the knob. --- .github/workflows/build.yml | 4 +++ build/conf/local.conf | 7 ++++++ doc/source/user_guide.rst | 8 ++++++ docker/Dockerfile.lvperf_32b | 7 +++--- docker/Dockerfile.lvperf_64b | 9 ++++--- docker/README.md | 13 +++++----- scripts/build.sh | 49 +++++++++++++++++++++++++++++++++++- 7 files changed, 83 insertions(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8abb14c93e..d8aa3b9a84 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,6 +57,10 @@ jobs: # local.conf uses IB_BOOT_CHAIN="full" (ATF + OP-TEE + AVZ); AVZ is # fetched from a local dev path, so "full" cannot build in CI. echo "IB_BOOT_CHAIN = \"uboot\"" >> build/conf/local.conf + # CI only builds + deploys the BSP; it never runs the emulator, so + # skip the (multi-minute) QEMU compile that build.sh bsp-so3 would + # otherwise bootstrap on the virt platforms. + echo "IB_BUILD_QEMU = \"0\"" >> build/conf/local.conf . ./env.sh build.sh bsp-so3 # Create the (empty) SD-card image — privileged losetup/mkfs/parted — diff --git a/build/conf/local.conf b/build/conf/local.conf index 8b82911707..10fbe96d3a 100644 --- a/build/conf/local.conf +++ b/build/conf/local.conf @@ -9,6 +9,13 @@ IB_PLATFORM ?= "virt64" # IB_PLATFORM ?= "rpi4_64" # IB_PLATFORM ?= "verdin-imx8mp" +# On the QEMU-emulated platforms (virt32/virt64) a full BSP build +# (build.sh bsp-so3 / bsp-linux) also builds the QEMU emulator when its binary +# is still missing, so a fresh tree is runnable (st.sh) without a separate +# `build.sh qemu`. Set to "0" to skip that (e.g. CI that only builds+deploys and +# never runs the emulator). Ignored on hardware platforms (verdin, rpi4). +IB_BUILD_QEMU ?= "1" + # ============================================================================ # SO3 # ============================================================================ diff --git a/doc/source/user_guide.rst b/doc/source/user_guide.rst index e62eb2c43b..4fa697a2d2 100644 --- a/doc/source/user_guide.rst +++ b/doc/source/user_guide.rst @@ -64,6 +64,14 @@ the FIT image): build.sh bsp-so3 +On the emulated platforms (``virt32``/``virt64``) this also builds the patched +QEMU the first time (when ``qemu/build/qemu-system-*`` is still missing), so the +tree is runnable at step 6 without a separate ``build.sh qemu``. Once built, QEMU +is left alone on later BSP builds; set ``IB_BUILD_QEMU = "0"`` in +``build/conf/local.conf`` to opt out (e.g. a build-only CI that never runs the +emulator). Rebuilding QEMU after editing its sources stays the explicit +``build.sh -x qemu`` (see :ref:`build_system`). + **4. Create the SD-card image.** ``build.sh bsp-so3`` only *compiles* — the empty SD-card image (``filesystem/sdcard.img.``) is produced by the separate, privileged ``filesystem`` recipe (``losetup``/``mkfs``/``parted``, escalated with diff --git a/docker/Dockerfile.lvperf_32b b/docker/Dockerfile.lvperf_32b index 4c305ea19e..d619d344e4 100644 --- a/docker/Dockerfile.lvperf_32b +++ b/docker/Dockerfile.lvperf_32b @@ -30,9 +30,10 @@ RUN { \ echo 'EXTRA_OVERRIDES .= ":lvgl"'; \ } >> build/conf/local.conf -# Non-privileged: build the emulator + the full BSP (compile only; the -# privileged rootfs loop-mount is deferred to deploy.sh at run time). -RUN bash -c 'set -e; . ./env.sh; build.sh -x qemu; build.sh bsp-so3' +# Non-privileged: build the full BSP (compile only; the privileged rootfs +# loop-mount is deferred to deploy.sh at run time). On virt32 the BSP build +# also pulls in the emulator (meta-qemu) automatically, as its binary is absent. +RUN bash -c 'set -e; . ./env.sh; build.sh bsp-so3' RUN printf '#!/bin/sh\n' > install_dependencies.sh && chmod +x install_dependencies.sh diff --git a/docker/Dockerfile.lvperf_64b b/docker/Dockerfile.lvperf_64b index cfac8723f9..c4da4159f5 100644 --- a/docker/Dockerfile.lvperf_64b +++ b/docker/Dockerfile.lvperf_64b @@ -43,11 +43,12 @@ RUN { \ echo 'EXTRA_OVERRIDES .= ":lvgl"'; \ } >> build/conf/local.conf -# Build everything non-privileged: the emulator (meta-qemu) plus the full BSP. +# Build everything non-privileged: the full BSP, which on virt64 also pulls in +# the emulator (meta-qemu) automatically since its binary is absent here. # `build.sh bsp-so3` is privilege-free — it only compiles (usr, u-boot, the -# kernel, the MUSL toolchain) and creates an empty rootfs.fat; the privileged -# rootfs loop-mount happens later, in deploy.sh. -RUN bash -c 'set -e; . ./env.sh; build.sh -x qemu; build.sh bsp-so3' +# kernel, the MUSL toolchain, qemu) and creates an empty rootfs.fat; the +# privileged rootfs loop-mount happens later, in deploy.sh. +RUN bash -c 'set -e; . ./env.sh; build.sh bsp-so3' # Hook to add extra dependencies at run time without rebuilding the image. RUN printf '#!/bin/sh\n' > install_dependencies.sh && chmod +x install_dependencies.sh diff --git a/docker/README.md b/docker/README.md index f6b70e5fc6..705c544d5e 100644 --- a/docker/README.md +++ b/docker/README.md @@ -47,13 +47,14 @@ docker run -it --privileged --network=host -v /dev:/dev so3-lvperf64b # or so3 ## Technical Details -- **At image-build time** (non-privileged), Infrabase builds the emulator and the - full BSP. `build.sh bsp-so3` is privilege-free: it only compiles (the MUSL - toolchain via `meta-toolchain`, the kernel, the user space, U-Boot) and creates - an empty `rootfs.fat`. The privileged rootfs loop-mount is deferred to - `deploy.sh`. +- **At image-build time** (non-privileged), Infrabase builds the full BSP. On the + virt platforms `build.sh bsp-so3` also builds the emulator (`meta-qemu`) + automatically when its binary is still missing, so no separate `build.sh qemu` + step is needed. It is privilege-free: it only compiles (the MUSL toolchain via + `meta-toolchain`, the kernel, the user space, U-Boot, QEMU) and creates an empty + `rootfs.fat`. The privileged rootfs loop-mount is deferred to `deploy.sh`. ``` - build.sh -x qemu && build.sh bsp-so3 + build.sh bsp-so3 ``` - **At container-run time** (privileged), the entrypoint rebuilds the user space (picking up a mounted LVGL), creates+formats the SD-card image (`build.sh -x filesystem` — diff --git a/scripts/build.sh b/scripts/build.sh index 777eae7f4a..a32aaa5385 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -43,7 +43,10 @@ pr_help() printf "$progname uboot Build u-boot\n" printf "$progname -x usr-so3 Build the SO3 user space\n" printf "$progname bsp-so3 Build the full SO3 BSP\n" - printf "$progname -v -c bsp-so3 Clean and rebuild the BSP, verbose\n" + printf "$progname -v -c bsp-so3 Clean and rebuild the BSP, verbose\n\n" + printf "On virt32/virt64, a BSP build (bsp-so3/bsp-linux) also builds QEMU when its\n" + printf "binary is still missing, so the tree is runnable with st.sh right away.\n" + printf "Set IB_BUILD_QEMU=\"0\" in build/conf/local.conf to skip that.\n" } if test $# -eq 0 @@ -153,8 +156,52 @@ then bitbake $recipename -c clean $IB_BB_OPTS fi +# Read the last uncommented assignment of a variable from local.conf. Handles +# `VAR = "x"`, `VAR ?= "x"`, `VAR := "x"`; a later line (e.g. a CI append) wins. +localconf_get() +{ + grep -E "^[[:space:]]*$1[[:space:]]*[?:]?=" build/conf/local.conf 2>/dev/null \ + | tail -1 | awk -F'"' '{print $2}' +} + +# On the QEMU-emulated platforms (virt32/virt64), a full BSP build should yield +# a runnable emulator without a separate `build.sh qemu` step. Build QEMU here +# when its binary is still missing — a bootstrap only: an already-built emulator +# is left untouched, so the inner build loop pays nothing and the qemu-hacking +# loop stays the explicit `build.sh -x qemu`. Hardware platforms (verdin, rpi4) +# never run under QEMU, so they are skipped. Opt out with IB_BUILD_QEMU="0" +# (e.g. CI that only builds+deploys and never runs the emulator). +maybe_bootstrap_qemu() +{ + case "$1" in + bsp-so3|bsp-linux) ;; + *) return 0 ;; + esac + + if test "$(localconf_get IB_BUILD_QEMU)" = "0" + then + return 0 + fi + + case "$(localconf_get IB_PLATFORM)" in + virt64) _qbin="qemu-system-aarch64" ;; + virt32) _qbin="qemu-system-arm" ;; + *) return 0 ;; + esac + + if test ! -x "$IB_ROOT_DIR/qemu/build/$_qbin" + then + printf "\n[infrabase] %s: the QEMU emulator (%s) is not built yet — building it first.\n" \ + "$1" "$_qbin" + printf " (set IB_BUILD_QEMU=\"0\" in build/conf/local.conf to skip this.)\n\n" + bitbake qemu $IB_BB_OPTS || exit 1 + fi +} + if test $dobuild -eq 1 then + maybe_bootstrap_qemu "$recipename" + if test $rootprivs -eq 1 then printf "\n *** NOTE: *** '$recipename' invokes privileged tools\n" From 288e511052f4d7fd0390bc77791f9425926f324a Mon Sep 17 00:00:00 2001 From: Daniel Rossier Date: Thu, 16 Jul 2026 15:27:07 +0200 Subject: [PATCH 2/2] build: fix do_attach_infrabase abort using undefined bbwarn/bbfatal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The attach guard's "local modifications not captured" branch called bbwarn and bbfatal, which are not defined as shell functions in the task environment, so the branch exited 127 (command not found) — turning the intended diagnostic into a hard task failure with no message printed. Use echo to stderr for the warning and echo + exit 1 for the abort, matching the codebase convention (bb* shell logging helpers are unavailable here). --- build/meta/classes/base.bbclass | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/meta/classes/base.bbclass b/build/meta/classes/base.bbclass index fc1d65e847..9602a7d6a1 100644 --- a/build/meta/classes/base.bbclass +++ b/build/meta/classes/base.bbclass @@ -205,9 +205,10 @@ do_attach_infrabase () { if [ -d "${IB_TARGET}" ] && [ -f "$ib_manifest" ] && [ "${IB_FORCE_ATTACH}" != "1" ]; then ib_dirty=$(cd "${IB_TARGET}" && LC_ALL=C sha256sum -c --quiet "$ib_manifest" 2>/dev/null | sed -n 's/: FAILED.*$//p') if [ -n "$ib_dirty" ]; then - bbwarn "Local modifications detected in ${IB_TARGET}, not captured in the ${PN} patch set:" + echo "WARNING: Local modifications detected in ${IB_TARGET}, not captured in the ${PN} patch set:" >&2 echo "$ib_dirty" | sed 's|^\./| |' >&2 - bbfatal "Refusing to re-attach ${PN} (would overwrite the files above). Run 'bitbake ${PN} -c updiff' to fold them into the patch set, or set IB_FORCE_ATTACH=1 to discard them and re-attach (prior tree is kept in ${IB_TARGET}.back)." + echo "ERROR: Refusing to re-attach ${PN} (would overwrite the files above). Run 'bitbake ${PN} -c updiff' to fold them into the patch set, or set IB_FORCE_ATTACH=1 to discard them and re-attach (prior tree is kept in ${IB_TARGET}.back)." >&2 + exit 1 fi fi