mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-17 19:24:01 +02:00
Build SDL2, OpenAL and the codecs from source for the arm64 AppImage
CI on a headless ubuntu-24.04-arm runner caught what a desktop Pi could not: the AppImage only started on a machine that already had a full desktop stack installed. Three distinct causes, all from bundling Debian's builds of libraries that Debian builds for a co-versioned system, which is the opposite of an AppImage's situation. 1. Hard-linked backends. Debian's libSDL2 lists libpulse, libasound, libX11 and libwayland-client as DT_NEEDED rather than dlopening them, so the loader demanded all four at startup; the CI job failed with "libpulse.so.0 => not found". Debian's OpenAL does the same through libsndio, which itself hard-links libasound. Built from source with --enable-*-shared and ALSOFT_DLOPEN, both dlopen their backends, so the image now runs on a Wayland-only session, a KMSDRM handheld with no X server, or a box with ALSA and no PulseAudio. 2. A stray link. Debian's libtheoradec is linked against libcairo, which drags in X11, xcb, fontconfig and freetype for a video decoder. --disable-examples leaves it needing only libogg. 3. SONAME collision with the host. OpenAL dlopens ALSA, ALSA's config loads its PulseAudio hook plugin, and that plugin pulls the host's libsndfile into the process. libsndfile links libogg, libvorbis and libmpg123 -- the same three we bundle -- and since the loader resolves a SONAME once per process it bound to our bullseye copies. A bullseye libmpg123 has no mpg123_info2 (added in 1.32), so the plugin failed to relocate, ALSA config collapsed, and the game ran with no audio device at all. Building them current means our copies satisfy the host's libsndfile instead of starving it. The general rule, now stated as an assertion instead of a comment: never bundle a library the host's own stack may also load unless ours is at least as new as theirs. build_appimage.sh fails if any shipped object hard-requires anything beyond glibc, libstdc++ and the font stack, and CI re-checks it on the extracted artifact. Host requirements drop from "a working desktop" to glibc 2.29+, libstdc++, libfreetype6 and zlib. Bundled libraries drop from 13 to 10: libcairo, libpixman and libsndio are gone entirely. Verified on a Raspberry Pi 5 (trixie, Wayland): boots, imports, plays, and audio works -- SDL 2.30 now picks the native Wayland backend rather than falling back to XWayland as bullseye's 2.0.14 did. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -43,7 +43,8 @@ printf '%s' "$guard_out" | grep -q 'aarch64 host' \
|
||||
say "checking pinned inputs"
|
||||
# Pins must be real digests, and the AppImage runtime must come from a dated
|
||||
# tag: "continuous" is a moving target and would make rebuilds unreproducible.
|
||||
for pin_name in LOVE_SRC_SHA256 APPIMAGE_RUNTIME_SHA256; do
|
||||
for pin_name in LOVE_SRC_SHA256 SDL2_SHA256 OPENAL_SHA256 THEORA_SHA256 \
|
||||
OGG_SHA256 VORBIS_SHA256 MPG123_SHA256 APPIMAGE_RUNTIME_SHA256; do
|
||||
pin_value="${!pin_name}"
|
||||
printf '%s' "$pin_value" | grep -Eq '^[0-9a-f]{64}$' \
|
||||
|| fail "$pin_name is not a sha256 digest: $pin_value"
|
||||
@@ -113,6 +114,48 @@ for soname in libSDL2-2.0.so.0 libopenal.so.1 libfreetype.so.6 libmodplug.so.1 \
|
||||
|| fail "build_appimage.sh no longer asserts liblove links $soname"
|
||||
done
|
||||
|
||||
say "checking the dlopen guarantees"
|
||||
# SDL2, OpenAL and libtheora are compiled from source for correctness, not for
|
||||
# a newer version number: Debian's builds hard-link libpulse/libasound/libX11/
|
||||
# libwayland (SDL2), libsndio (OpenAL) and libcairo (libtheora), each of which
|
||||
# turns an optional runtime capability into a mandatory startup dependency.
|
||||
# If a future edit drops the source build and reaches for the -dev package
|
||||
# again, the AppImage silently stops starting on lean systems.
|
||||
for forbidden_pkg in libsdl2-dev libtheora-dev libopenal-dev; do
|
||||
if grep -qE "^ +.*\b$forbidden_pkg\b" "$SCRIPT_DIR/Dockerfile"; then
|
||||
fail "Dockerfile installs $forbidden_pkg; that library is built from source on purpose"
|
||||
fi
|
||||
done
|
||||
grep -qF -- '--enable-alsa-shared' "$SCRIPT_DIR/build_appimage.sh" \
|
||||
|| fail "SDL2 is no longer configured to dlopen its audio backends"
|
||||
grep -qF -- '--enable-x11-shared' "$SCRIPT_DIR/build_appimage.sh" \
|
||||
|| fail "SDL2 is no longer configured to dlopen its video backends"
|
||||
grep -qF 'ALSOFT_DLOPEN=ON' "$SCRIPT_DIR/build_appimage.sh" \
|
||||
|| fail "openal-soft is no longer configured to dlopen its backends"
|
||||
grep -qF -- '--disable-examples' "$SCRIPT_DIR/build_appimage.sh" \
|
||||
|| fail "libtheora is no longer built with --disable-examples (it regains the libcairo link)"
|
||||
|
||||
say "checking the host dependency contract"
|
||||
# The shipped objects may require nothing from the host beyond glibc,
|
||||
# libstdc++ and the font stack. Everything driver-, session- or audio-related
|
||||
# has to be dlopened. This is the invariant a headless CI runner proved was
|
||||
# broken the first time round.
|
||||
HOST_ALLOWED_RE="$(
|
||||
grep -m1 "^HOST_ALLOWED_RE=" "$SCRIPT_DIR/build_appimage.sh" \
|
||||
| sed "s/^HOST_ALLOWED_RE='//; s/'\$//"
|
||||
)"
|
||||
[ -n "$HOST_ALLOWED_RE" ] || fail "could not read HOST_ALLOWED_RE out of build_appimage.sh"
|
||||
for soname in libpulse.so.0 libasound.so.2 libX11.so.6 libwayland-client.so.0 \
|
||||
libGL.so.1 libcairo.so.2 libsndio.so.7.0 libdbus-1.so.3; do
|
||||
if [[ "$soname" =~ $HOST_ALLOWED_RE ]]; then
|
||||
fail "$soname is allowed as a hard host dependency; it must be dlopened"
|
||||
fi
|
||||
done
|
||||
for soname in libc.so.6 libstdc++.so.6 libfreetype.so.6 libz.so.1; do
|
||||
[[ "$soname" =~ $HOST_ALLOWED_RE ]] \
|
||||
|| fail "$soname must be allowed as a host dependency but the contract rejects it"
|
||||
done
|
||||
|
||||
say "checking the shared game.love payload"
|
||||
temp_dir="$(mktemp -d "${TMPDIR:-/tmp}/gen1recomp-linux-arm64-selftest.XXXXXX")"
|
||||
trap 'rm -rf "$temp_dir"' EXIT
|
||||
|
||||
Reference in New Issue
Block a user