build(ci): cross-compile and publish an aarch64 Linux client

Adds punktfunk-rust-ci-arm64cross (the rust-ci toolchain + an Ubuntu ports
arm64 multiarch sysroot) and a deb.yml job that cross-builds the client
package on the ordinary amd64 runner — no arm64 runner in the fleet and
none needed. Client only: the Linux host encodes with NVENC/QSV/AMF, all
x86. The apt registry keys pool entries by the .deb's own arch, so an arm64
box picks the package up with no client-side configuration.

Three things this had to get right, each of which cost a failed build:

  * rustup target add must run against the toolchain rust-toolchain.toml
    PINS, not the image's default `stable` — otherwise the pinned toolchain
    has no aarch64 std and the build dies ~230 crates in on "can't find
    crate for core". Copying the pin file in also pre-downloads the
    toolchain every CI job currently re-fetches on first use.

  * ffmpeg-sys-next compiles a probe it intends to RUN, so it forces
    .target(HOST) while still passing the TARGET's include paths; the arm64
    dir then shadows the host's own libc headers and the x86 compiler dies
    in bits/math-vector.h on NEON/SVE types. Prepending the host dir does
    NOT help — GCC drops a -I that duplicates a system directory, keeping
    its original later position — so ci/pf-host-cc strips target include
    dirs from host compiles instead.

  * the job hard-fails on a non-AArch64 session binary, rather than
    shipping an amd64 payload under an arm64 package name.

skia needs no special handling: the aarch64-linux-gnu textlayout+vulkan
prebuilt resolves, so arm64 ships the FULL client, OSD included — unlike
Windows ARM64, which still builds --no-default-features.

The cross image builds in its own docker.yml job (needs: build-push) since
it is FROM punktfunk-rust-ci:latest and must not race the entry that
publishes that base. rpm/Arch/Flatpak stay x86_64 for now — their builders
have no arm64 sysroot, which is its own piece of work.

Plan: punktfunk-planning design/embedded-arm64-client.md

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-25 14:32:45 +02:00
co-authored by Claude Opus 5
parent 915fc3ef9e
commit 000f8b85b0
6 changed files with 285 additions and 9 deletions
+21 -5
View File
@@ -7,24 +7,40 @@
# the package names the target boxes ship. The client links no NVIDIA libs — no filter
# needed.
#
# Usage: VERSION=0.0.1~ci42.gdeadbee [ARCH=amd64] bash packaging/debian/build-client-deb.sh
# Usage: VERSION=0.0.1~ci42.gdeadbee [ARCH=amd64] [TARGET=<rust triple>] \
# bash packaging/debian/build-client-deb.sh
# Output: dist/punktfunk-client_<version>_<arch>.deb
#
# TARGET cross-compiles (and moves the binaries under target/<triple>/release). Set it
# together with ARCH — e.g. ARCH=arm64 TARGET=aarch64-unknown-linux-gnu inside the
# ci/rust-ci-arm64cross.Dockerfile image, which carries the matching :arm64 sysroot.
set -euo pipefail
VERSION="${VERSION:?set VERSION (e.g. 0.0.1 or 0.0.1~ci42.gdeadbee)}"
ARCH="${ARCH:-amd64}"
TARGET="${TARGET:-}"
PKG="punktfunk-client"
CRATE="punktfunk-client-linux"
ROOTDIR="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$ROOTDIR"
BIN="target/release/$PKG"
# Cargo drops a cross build under target/<triple>/release, a native one straight in
# target/release.
if [ -n "$TARGET" ]; then
OUTDIR="target/$TARGET/release"
CARGO_TARGET_ARGS=(--target "$TARGET")
else
OUTDIR="target/release"
CARGO_TARGET_ARGS=()
fi
BIN="$OUTDIR/$PKG"
# The Vulkan/Skia session streamer the shell execs for a connect — shipped alongside the shell
# (the shell resolves it as its /usr/bin sibling), or desktop streaming breaks.
SESSION_BIN="target/release/punktfunk-session"
SESSION_BIN="$OUTDIR/punktfunk-session"
if [ ! -x "$BIN" ] || [ ! -x "$SESSION_BIN" ]; then
echo "==> building $CRATE + punktfunk-client-session (release)"
cargo build --release --locked -p "$CRATE" -p punktfunk-client-session
echo "==> building $CRATE + punktfunk-client-session (release${TARGET:+ for $TARGET})"
cargo build --release --locked "${CARGO_TARGET_ARGS[@]}" -p "$CRATE" -p punktfunk-client-session
fi
STAGE="$(mktemp -d)"