Add the cross-build sysroot recipe, verified on the device

packaging/make-sysroot.sh populates a minimal Alpine aarch64 rootfs with
apk-tools-static, so a cross build needs no root, no qemu and no phone. Adapted
from imsd's, plus glib-dev for the bus layer that is coming.

Building against a real Alpine sysroot means the binary links dynamically
against the phone's own musl and libc++ -- libc++, libc++abi, libunwind and
libgcc_s are all already on pmOS. The harness this daemon replaces had to be
built -static, but only because it was built with the host's glibc toolchain;
that constraint was never about the target.

The pipeline is proven end to end rather than assumed: the cross-built binary
runs on the phone, and all five core suites pass there too -- on the real
hardware, not just under qemu-aarch64. That matters for modules that are almost
entirely little-endian field packing and offset arithmetic.
This commit is contained in:
Jorijn van der Graaf 2026-09-02 17:34:27 +02:00
commit 93692c9505
2 changed files with 67 additions and 2 deletions

53
packaging/make-sysroot.sh Executable file
View file

@ -0,0 +1,53 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-3.0-only
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
# make-sysroot.sh — build an Alpine aarch64 sysroot for cross-compiling
# fingerprintd.
#
# Downloads apk-tools-static + the Alpine signing keys from the CDN and
# populates a minimal rootfs (musl, libc++, glib and their headers) that clang
# can use via --sysroot. No root, no qemu, no device needed:
#
# packaging/make-sysroot.sh [dir] # default: ~/.cache/fingerprintd/sysroot-aarch64-alpine
# crafter-build -- --target=aarch64-alpine-linux-musl --sysroot=<dir> \
# --march=armv8-a --mtune=generic
#
# Cross-building against a real Alpine sysroot is why the binary links
# DYNAMICALLY against the phone's own musl and libc++. The research harness
# this daemon replaces had to be built -static, but only because it was built
# with the host's glibc toolchain; that constraint does not apply here.
set -eu
SYSROOT="${1:-$HOME/.cache/fingerprintd/sysroot-aarch64-alpine}"
MIRROR="${MIRROR:-https://dl-cdn.alpinelinux.org/alpine/edge}"
ARCH=aarch64
# gcc is in the list for its crtbeginS.o/libgcc: clang's driver for
# *-alpine-linux-musl links against the GCC runtime it detects under
# <sysroot>/usr/lib/gcc/<triple>/.
# glib-dev is for the GDBus bus layer (net.reactivated.Fprint).
PKGS="musl-dev libc++ libc++-dev glib-dev compiler-rt llvm-libunwind-dev gcc"
work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT
fetch_pkg() { # $1 = repo (main/community), $2 = package name -> path on stdout
file=$(curl -fsSL "$MIRROR/$1/x86_64/" |
grep -o "href=\"$2-[0-9][^\"]*\.apk\"" | head -n1 | cut -d'"' -f2)
[ -n "$file" ] || { echo "cannot find $2 in $MIRROR/$1" >&2; exit 1; }
curl -fsSL -o "$work/$file" "$MIRROR/$1/x86_64/$file"
echo "$work/$file"
}
echo ">> fetching apk-tools-static + alpine-keys"
tar -xzf "$(fetch_pkg main apk-tools-static)" -C "$work" sbin/apk.static 2>/dev/null
tar -xzf "$(fetch_pkg main alpine-keys)" -C "$work" usr/share/apk/keys 2>/dev/null
echo ">> populating $SYSROOT ($ARCH: $PKGS)"
mkdir -p "$SYSROOT"
"$work/sbin/apk.static" \
--arch "$ARCH" --root "$SYSROOT" --initdb --no-scripts --no-cache \
--usermode --keys-dir "$work/usr/share/apk/keys/$ARCH" \
-X "$MIRROR/main" -X "$MIRROR/community" \
add $PKGS
echo ">> done: $SYSROOT"