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>
82 lines
4.9 KiB
Docker
82 lines
4.9 KiB
Docker
# Cross-compiling CI builder: amd64 host toolchain + an arm64 multiarch sysroot, for the
|
|
# aarch64 Linux CLIENT artifacts (punktfunk-client + punktfunk-session).
|
|
#
|
|
# docker build -f ci/rust-ci-arm64cross.Dockerfile -t punktfunk-rust-ci-arm64cross .
|
|
#
|
|
# Derived from punktfunk-rust-ci so the Rust toolchain, clang, and CMake are byte-identical
|
|
# to the amd64 legs — this image only adds the target side. Kept as a SEPARATE image rather
|
|
# than folded into the base because the :arm64 dev libs are ~1 GB that every other CI job
|
|
# would otherwise pull for nothing.
|
|
#
|
|
# Client only: the Linux HOST stays amd64 (its encode stack is NVENC/QSV/AMF), so none of the
|
|
# host's CUDA/GBM link deps are mirrored here.
|
|
#
|
|
# Ubuntu splits archives by architecture: amd64 lives on archive.ubuntu.com, every port
|
|
# (arm64 included) on ports.ubuntu.com. Both stanzas therefore have to be pinned with an
|
|
# explicit `Architectures:` or apt tries to fetch arm64 from the amd64 mirror and 404s.
|
|
#
|
|
# Built from the REPO ROOT context (not ci/) — see the rust-toolchain.toml copy below.
|
|
FROM git.unom.io/unom/punktfunk-rust-ci:latest
|
|
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
|
|
# 1. Pin the stock sources to amd64, add ports.ubuntu.com for arm64.
|
|
RUN sed -i 's|^Types: deb$|Types: deb\nArchitectures: amd64|' /etc/apt/sources.list.d/ubuntu.sources \
|
|
&& . /etc/os-release \
|
|
&& printf 'Types: deb\nArchitectures: arm64\nURIs: http://ports.ubuntu.com/ubuntu-ports/\nSuites: %s %s-updates %s-backports %s-security\nComponents: main universe restricted multiverse\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\n' \
|
|
"$VERSION_CODENAME" "$VERSION_CODENAME" "$VERSION_CODENAME" "$VERSION_CODENAME" \
|
|
> /etc/apt/sources.list.d/ubuntu-ports-arm64.sources \
|
|
&& dpkg --add-architecture arm64
|
|
|
|
# 2. The cross toolchain + every arm64 dev lib the client links. Mirrors the client half of
|
|
# rust-ci.Dockerfile's list (FFmpeg, PipeWire, Opus, SDL3, GTK4/libadwaita, xkbcommon,
|
|
# Vulkan headers for pf-ffvk's bindgen over hwcontext_vulkan.h).
|
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
crossbuild-essential-arm64 \
|
|
libavcodec-dev:arm64 libavformat-dev:arm64 libavutil-dev:arm64 libswscale-dev:arm64 \
|
|
libavfilter-dev:arm64 libavdevice-dev:arm64 \
|
|
libpipewire-0.3-dev:arm64 libopus-dev:arm64 \
|
|
libsdl3-dev:arm64 libgtk-4-dev:arm64 libadwaita-1-dev:arm64 \
|
|
libwayland-dev:arm64 libxkbcommon-dev:arm64 libvulkan-dev:arm64 \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# 3. The Rust target — installed against the toolchain the WORKSPACE pins, not the image's
|
|
# default. The base image bakes whatever `stable` was at its build time, while every build
|
|
# in the repo switches to the exact channel in rust-toolchain.toml; adding the target to
|
|
# the default toolchain instead leaves the pinned one without an aarch64 std, and the build
|
|
# dies on `can't find crate for core` a few hundred crates in. Running rustup from a
|
|
# directory that contains the pin file resolves the right toolchain (and pre-downloads it,
|
|
# which every workspace job would otherwise pay for on first use).
|
|
COPY rust-toolchain.toml /opt/pf-toolchain/
|
|
WORKDIR /opt/pf-toolchain
|
|
RUN rustup target add aarch64-unknown-linux-gnu && rustup show
|
|
WORKDIR /
|
|
|
|
# 4. Cross wiring. Everything in this image is a cross build, so the plain (un-suffixed)
|
|
# variables are safe and cover the crates that roll their own pkg-config/bindgen calls
|
|
# instead of going through the target-scoped lookups.
|
|
# * PKG_CONFIG uses Debian's multiarch wrapper, which resolves the arm64 .pc files and
|
|
# rewrites -I/-L into the sysroot without per-crate cooperation.
|
|
# * BINDGEN_EXTRA_CLANG_ARGS: clang defaults to the host triple, so bindgen would parse
|
|
# arm64 headers with amd64 type layouts (silently wrong, not a build error) — the
|
|
# explicit --target plus the multiarch include dir is what keeps the layouts honest.
|
|
# * CC_x86_64_unknown_linux_gnu routes HOST-targeted compiles through a wrapper that
|
|
# strips the arm64 include dirs — see ci/pf-host-cc for the ffmpeg-sys-next probe it
|
|
# exists for.
|
|
COPY ci/pf-host-cc /usr/local/bin/pf-host-cc
|
|
RUN chmod 0755 /usr/local/bin/pf-host-cc
|
|
|
|
ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
|
|
CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc \
|
|
CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ \
|
|
AR_aarch64_unknown_linux_gnu=aarch64-linux-gnu-ar \
|
|
CC_x86_64_unknown_linux_gnu=/usr/local/bin/pf-host-cc \
|
|
PKG_CONFIG=aarch64-linux-gnu-pkg-config \
|
|
PKG_CONFIG_ALLOW_CROSS=1 \
|
|
BINDGEN_EXTRA_CLANG_ARGS="--target=aarch64-unknown-linux-gnu -I/usr/include/aarch64-linux-gnu"
|
|
|
|
# Fail the BUILD, not some later CI job, if the wrapper or a sysroot .pc is missing.
|
|
RUN command -v aarch64-linux-gnu-pkg-config \
|
|
&& aarch64-linux-gnu-pkg-config --cflags libavcodec sdl3 gtk4 libpipewire-0.3 \
|
|
&& aarch64-linux-gnu-gcc -dumpmachine | grep -q aarch64
|