6.1 KiB
09 — QEMU build & distribution blueprint
How a Phase-2 patch to third-party/qemu-lcgamboa/ actually reaches a
running container in production. Read this before writing any C in
hw/misc/.
Where the binary comes from today
Production gets libqemu-xtensa.so from a GitHub Release, not from
the source tree. The download is wired in Dockerfile.standalone:11-23:
ARG QEMU_RELEASE_URL=https://github.com/davidmonterocrespo24/velxio/releases/download/qemu-prebuilt
…
curl -fSL -o "libqemu-xtensa.so" \
"${QEMU_RELEASE_URL}/libqemu-xtensa-${TARGETARCH}.so"
So whoever publishes the binaries (currently David, GitHub release tag
qemu-prebuilt) is the gatekeeper. The Docker build itself never
compiles QEMU.
The asset names already encode arch: libqemu-xtensa-amd64.so,
libqemu-xtensa-arm64.so, …
Build scripts that exist
third-party/qemu-lcgamboa/
├── build_libqemu-esp32.sh # Linux/macOS — host nproc, bash
└── build_libqemu-esp32-win.sh # MSYS2 MINGW64 — produces .dll
Both scripts:
./configure --target-list=xtensa-softmmu,riscv32-softmmuwith--enable-tcg --enable-system --enable-slirp --enable-gcrypt.make -j$(nproc)to build everything QEMU normally builds.- Hack the Ninja link command to drop
softmmu_main.c.oand add-shared, producing a.so(Linux) /.dll(Win) instead of the executable. - The resulting library is copied next to the backend.
The Linux build builds the architecture you're on. Cross-compile matrix:
| Target arch | How |
|---|---|
| linux/amd64 | Run build_libqemu-esp32.sh on x86_64 Linux |
| linux/arm64 | Run on arm64 Linux (or qemu-user-static) |
| windows/amd64 | Run build_libqemu-esp32-win.sh in MSYS2 |
| darwin/amd64,arm64 | Optional — Mac host, same Linux script |
In practice we only need amd64 + arm64 Linux (Docker production) plus Windows for the desktop tier.
Where Phase-2 changes go
A new C source file, drop-in:
third-party/qemu-lcgamboa/
├── hw/i2c/
│ └── esp32_ov2640.c # NEW — SCCB device, 7-bit addr 0x30
├── hw/misc/
│ └── esp32_i2s_cam.c # NEW — replaces esp32_unimp at I2S0
├── hw/xtensa/
│ └── esp32_picsimlab.c # MODIFIED — wires the two devices
└── include/hw/i2c/
└── esp32_ov2640.h # NEW — public header
Plus updates to:
hw/i2c/meson.build— addesp32_ov2640.cto the i2c group.hw/misc/meson.build— addesp32_i2s_cam.c.hw/xtensa/meson.build— already references esp32_picsimlab.c.
Touch only new files where possible. The picsimlab.c diff is the single existing-file change and should be a 5-10 line addition near where I2S0 is currently mapped as unimp (line 750).
Build smoke test (does the library still load?)
After every rebuild:
# Linux
python -c "
import ctypes
lib = ctypes.CDLL('./build/libqemu-xtensa.so')
print('symbols:', sum(1 for _ in iter(lib.__getattr__, None)))
"
# A more useful smoke test — does picsimlab init succeed?
file build/libqemu-xtensa.so # must say 'shared object, dynamically linked'
nm -D build/libqemu-xtensa.so | grep esp32_ # should list the new devices' init fns
The CI matrix needs:
- A Linux runner with
mingw-w64-x86_64-*packages OR a separate Windows runner. ~3 GBdisk,~10 minper arch on a 4-core runner.
Distribution flow proposal
Once the library is rebuilt:
- Tag the
third-party/qemu-lcgamboa/commit (in a fork, or a submodule pointer in this repo). - CI workflow
.github/workflows/qemu-publish.yml(NEW) builds the matrix and uploads to the existingqemu-prebuiltGitHub Release under new asset names:libqemu-xtensa-camera-vN-${TARGETARCH}.so. - Bump
QEMU_RELEASE_URL(or the explicit asset names) inDockerfile.standalone. - Next
docker compose build velxiopulls the new artifact. - Smoke test: a fresh container runs the camera_init.ino sketch and
the live test reports
got frame: N bytes.
Local dev cycle (no Docker)
For iteration speed during Phase 2:
# 1. one-time
cd third-party/qemu-lcgamboa
./configure --target-list=xtensa-softmmu --extra-cflags="-fPIC -DESP32_PICSIMLAB_SOFT_CACHE=1" --disable-werror --enable-tcg --enable-system --enable-debug
make -j$(nproc)
# 2. iterate (fast, <30 s)
# … edit hw/i2c/esp32_ov2640.c …
ninja -C build qemu-system-xtensa
bash -c "$(./repackage_dynlib.sh)" # the same ninja-rsp dance
# 3. point the backend at the local build
ln -sf $(pwd)/build/libqemu-xtensa.so /path/to/backend/app/services/libqemu-xtensa.so
# 4. restart backend, re-run live tests
We want to ship a repackage_dynlib.sh helper that wraps the ninja
.rsp manipulation from the existing build scripts (steps 4-5). Today
that logic is duplicated inline; extracting it cuts the iteration
loop in half.
Risks
- Missing sources for QEMU configure. The local fork ships with
sources for the chosen
--target-listonly. If a contributor accidentally needsarm-softmmuetc., reconfigure is needed. - MSYS2 environment fragility. The Windows build script has a
meson.buildpatch step (line 35-40) that's specific to a pre-existing meson-build text. If lcgamboa rebases meson, this sed will silently no-op. Add a strict assertion. - Per-arch artifact size. ~30 MB each, three artifacts, GH Release size limit is 2 GB → fine for years.
- TCG vs KVM. We use TCG (interpreter). KVM doesn't help us because the Xtensa target wouldn't use it anyway.
What this autosearch concludes
The build infrastructure is already in place; we don't need to invent anything new for Phase 2 distribution. We just have to:
- Add three files (
esp32_ov2640.{c,h},esp32_i2s_cam.c). - Modify one file (
esp32_picsimlab.c) by ~10 lines. - Run one of two existing scripts.
- Upload to GitHub Releases under new asset names.
- Bump the URL in Dockerfile.
Total Phase-4 (build/CI) effort: ~1 week, mostly CI plumbing.