Pi:dev build environment

From SpecNext Wiki
Revision as of 18:14, 5 May 2026 by SirCliveSinclairbot (talk | contribs) (via /pi-wiki-publish)
Jump to: navigation, search

Early draft. Procedure validated 2026-05-05 on Pi OS Bookworm / Pi 5. Corrections welcome on the talk page.


Reproducible procedure for setting up a build host that produces stretch-armhf binaries (compatible with the Raspbian-9 base used by NextPi) on any modern aarch64 Raspberry Pi running Pi OS Bookworm.

Why

NextPi releases ship as Raspbian 9 (stretch) armhf binaries. This procedure builds those binaries inside a stretch-armhf chroot hosted on a modern aarch64 Pi. aarch64 hardware can execute armhf userspace natively via aarch32 compat, so the build runs at host CPU speed without QEMU emulation, while the toolchain and libraries remain pinned to stretch for ABI compatibility with the deployment target. Apt sources point at the project's stretch mirror at http://zx.xalior.com/nextpi_dev, which keeps package versions deterministic across build hosts.

Measured speedup, libspectrum 1.6.0 build:

Build host Time Ratio
Pi 3, 4× Cortex-A53 armv7l, SD card (native stretch) 65 s 1.0×
Pi 5, 4× Cortex-A76 aarch64, NVMe (chroot) 11 s 5.9×

Prerequisites

  • Vanilla Pi OS Bookworm on a Pi 4 or Pi 5 with ~5 GB free disk.
  • SSH access as a sudo-capable user.
  • Network reach to zx.xalior.com.

Procedure

1. Install host tooling

sudo apt-get update
sudo apt-get install -y debootstrap qemu-user-static binfmt-support

qemu-user-static + binfmt-support register handlers so debootstrap can run armhf binaries during second-stage configure. (On Pi 5 the host kernel can also execute armhf natively via aarch32 compat; binfmt-misc is the fallback.)

Verify:

dpkg --print-foreign-architectures   # should list: armhf
ls /proc/sys/fs/binfmt_misc/ | grep qemu-arm

2. Add the apt signing key

The mirror is signed with the canonical Raspbian archive key (Mike Thompson, fingerprint A0DA 38D0 D76E 8B5D 6388 7281 9165 938D 90FD DD2E). Modern Pi OS Bookworm doesn't ship it, so add it explicitly:

curl -sL https://zx.xalior.com/nextpi_dev/debian_nextpi.key \
  | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/debian_nextpi.gpg

Confirm:

sudo gpg --no-default-keyring --keyring /etc/apt/trusted.gpg.d/debian_nextpi.gpg --list-keys
# pub rsa2048 2012-04-01 [SC] A0DA38D0D76E8B5D638872819165938D90FDDD2E
# uid Mike Thompson (Raspberry Pi Debian armhf ARMv6+VFP)

3. Debootstrap the chroot

sudo debootstrap --arch=armhf --variant=buildd \
    --keyring=/etc/apt/trusted.gpg.d/debian_nextpi.gpg \
    stretch /srv/nextpi-buildroot/ \
    http://zx.xalior.com/nextpi_dev

debootstrap downloads ~50 MB of .debs, extracts them, and ends with this warning:

W: Failure while configuring required packages.
W: See /srv/nextpi-buildroot/debootstrap/debootstrap.log for details (possibly the package systemd is at fault)

This is expected — stretch's systemd postinst segfaults under Pi 5's kernel via aarch32 compat. The next step works around it; systemd is never actually run inside a build chroot.

4. Force-install the unconfigured packages

The systemd configure failure halts dpkg's pending-configure pass and leaves several packages (including apt) unpacked-but-not-installed. Force them through:

sudo chroot /srv/nextpi-buildroot \
    /usr/bin/env -i PATH=/usr/sbin:/usr/bin:/sbin:/bin LANG=C \
    /bin/bash -c "dpkg --install --force-depends --force-confdef --force-confold --force-overwrite --force-bad-version --recursive /var/cache/apt/archives/"

Errors on systemd, plymouth, mountall are expected and harmless.

Verify:

sudo chroot /srv/nextpi-buildroot \
    /usr/bin/env -i PATH=/usr/sbin:/usr/bin:/sbin:/bin LANG=C \
    /bin/bash -c "type apt-get && type gcc"

5. Bind-mount runtime filesystems

for m in proc sys dev dev/pts; do
  sudo mkdir -p /srv/nextpi-buildroot/$m
  sudo mount --bind /$m /srv/nextpi-buildroot/$m
done
sudo cp /etc/resolv.conf /srv/nextpi-buildroot/etc/

These are session-only; see Persistent mounts below for fstab.

6. Hold the broken packages

So future apt-get install runs don't re-extract systemd and re-trigger the postinst:

sudo chroot /srv/nextpi-buildroot \
    /usr/bin/env -i PATH=/usr/sbin:/usr/bin:/sbin:/bin LANG=C \
    /bin/bash -c "apt-mark hold systemd plymouth mountall"

7. Install build dependencies

sudo chroot /srv/nextpi-buildroot \
    /usr/bin/env -i PATH=/usr/sbin:/usr/bin:/sbin:/bin LANG=C HOME=/root \
    /bin/bash -c "apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
        autoconf automake libtool libtool-bin pkg-config \
        libbz2-dev libaudiofile-dev \
        wget git ca-certificates"

libtool-bin is required separately on stretch — the libtool umbrella package no longer ships the /usr/bin/libtool script.

A W: APT had planned for dpkg to do more than it reported back warning about systemd:armhf will appear on every apt-get run; that's the hold doing its job.

Toolchain check:

sudo chroot /srv/nextpi-buildroot /usr/bin/env -i PATH=/usr/sbin:/usr/bin:/sbin:/bin /bin/bash -c \
    "gcc --version | head -1; automake --version | head -1; libtool --version | head -1; pkg-config --version"

Expect: gcc 6.3.0, automake 1.15, libtool 2.4.6, pkg-config 0.29.

8. Smoke test: build libspectrum 1.6.0

sudo chroot /srv/nextpi-buildroot \
    /usr/bin/env -i PATH=/usr/sbin:/usr/bin:/sbin:/bin LANG=C HOME=/root \
    /bin/bash -c '
        cd /tmp && \
        wget -q https://sourceforge.net/projects/fuse-emulator/files/libspectrum/1.6.0/libspectrum-1.6.0.tar.gz/download -O libspectrum-1.6.0.tar.gz && \
        tar xzf libspectrum-1.6.0.tar.gz && \
        cd libspectrum-1.6.0 && \
        ./configure --with-fake-glib --quiet && \
        time make -s
    '

Wall-clock ~10–15 s on Pi 5 / NVMe. Verify:

file /srv/nextpi-buildroot/tmp/libspectrum-1.6.0/.libs/libspectrum.so.8.10.0
# ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked

Chroot is ready.

Persistent mounts

The bind mounts in step 5 are session-only. For a long-lived build host, add to /etc/fstab:

/proc        /srv/nextpi-buildroot/proc      none  bind  0 0
/sys         /srv/nextpi-buildroot/sys       none  bind  0 0
/dev         /srv/nextpi-buildroot/dev       none  bind  0 0
/dev/pts     /srv/nextpi-buildroot/dev/pts   none  bind  0 0

(/etc/resolv.conf will need re-copying after host reboot regardless.)