#!/usr/bin/env bash set -euo pipefail ROOT_DIR=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd) DIST_DIR="${DIST_DIR:-$ROOT_DIR/dist}" APPDIR="${APPDIR:-$DIST_DIR/AppDir}" APP_NAME="Mice" APP_ID="mice" ARCH_EXPECTED="aarch64" APPIMAGETOOL_BIN="${APPIMAGETOOL_BIN:-appimagetool}" PYTHON_BIN="${PYTHON_BIN:-python3}" OUTPUT_APPIMAGE="${OUTPUT_APPIMAGE:-$DIST_DIR/${APP_NAME}-${ARCH_EXPECTED}.AppImage}" PYTHON_DIR="$APPDIR/usr/opt/python" GAME_DIR="$APPDIR/usr/share/mice" LIB_DIR="$APPDIR/usr/lib" require_command() { if ! command -v "$1" >/dev/null 2>&1; then printf 'Missing required command: %s\n' "$1" >&2 exit 1 fi } find_system_library() { local soname="$1" ldconfig -p | awk -v target="$soname" '$1 == target { print $NF; exit }' } should_bundle_soname() { case "$1" in linux-vdso.so.*|libc.so.*|libm.so.*|libpthread.so.*|libdl.so.*|librt.so.*|libutil.so.*|libresolv.so.*|ld-linux*.so.*) return 1 ;; *) return 0 ;; esac } copy_dependency_tree() { local binary="$1" local dep local soname while IFS= read -r dep; do [ -n "$dep" ] || continue [ -f "$dep" ] || continue soname=$(basename "$dep") if ! should_bundle_soname "$soname"; then continue fi if [ ! -e "$LIB_DIR/$soname" ]; then cp -a "$dep" "$LIB_DIR/$soname" chmod 755 "$LIB_DIR/$soname" || true copy_dependency_tree "$dep" fi done < <( ldd "$binary" 2>/dev/null | awk ' /=>/ && $3 ~ /^\// { print $3 } $1 ~ /^\// { print $1 } ' | sort -u ) } copy_system_library() { local soname="$1" local path path=$(find_system_library "$soname") if [ -z "$path" ]; then printf 'Unable to locate required system library: %s\n' "$soname" >&2 exit 1 fi cp -a "$path" "$LIB_DIR/$(basename "$path")" chmod 755 "$LIB_DIR/$(basename "$path")" || true copy_dependency_tree "$path" } printf '==> Checking build prerequisites\n' require_command rsync require_command "$PYTHON_BIN" require_command "$APPIMAGETOOL_BIN" require_command ldconfig require_command ldd if [ "$(uname -m)" != "$ARCH_EXPECTED" ]; then printf 'This builder must run on %s. Current architecture: %s\n' "$ARCH_EXPECTED" "$(uname -m)" >&2 exit 1 fi printf '==> Creating AppDir at %s\n' "$APPDIR" rm -rf "$APPDIR" mkdir -p "$DIST_DIR" "$LIB_DIR" "$GAME_DIR" printf '==> Building bundled Python environment with %s\n' "$PYTHON_BIN" "$PYTHON_BIN" -m venv --copies "$PYTHON_DIR" "$PYTHON_DIR/bin/pip" install --upgrade pip setuptools wheel "$PYTHON_DIR/bin/pip" install -r "$ROOT_DIR/requirements.txt" printf '==> Syncing game files\n' rsync -a \ --delete \ --exclude '.git' \ --exclude '.github' \ --exclude '.venv' \ --exclude '__pycache__' \ --exclude '*.pyc' \ --exclude '.mypy_cache' \ --exclude '.pytest_cache' \ --exclude 'build' \ --exclude 'dist' \ --exclude 'packaging' \ "$ROOT_DIR/" "$GAME_DIR/" rm -f "$GAME_DIR/user_profiles.json" "$GAME_DIR/scores.txt" printf '==> Installing AppImage metadata\n' install -Dm755 "$ROOT_DIR/packaging/appimage/AppRun" "$APPDIR/AppRun" install -Dm644 "$ROOT_DIR/packaging/appimage/mice.desktop" "$APPDIR/$APP_ID.desktop" install -Dm644 "$ROOT_DIR/packaging/appimage/mice.desktop" "$APPDIR/usr/share/applications/$APP_ID.desktop" install -Dm644 "$ROOT_DIR/assets/Rat/BMP_WEWIN.png" "$APPDIR/$APP_ID.png" install -Dm644 "$ROOT_DIR/assets/Rat/BMP_WEWIN.png" "$APPDIR/usr/share/icons/hicolor/256x256/apps/$APP_ID.png" printf '==> Bundling native dependencies\n' copy_system_library libSDL2-2.0.so.0 copy_system_library libSDL2_ttf-2.0.so.0 copy_dependency_tree "$PYTHON_DIR/bin/python" while IFS= read -r -d '' candidate; do copy_dependency_tree "$candidate" done < <(find "$PYTHON_DIR" -type f \( -name '*.so' -o -name '*.so.*' -o -perm -u+x \) -print0) printf '==> Building AppImage %s\n' "$OUTPUT_APPIMAGE" ARCH="$ARCH_EXPECTED" "$APPIMAGETOOL_BIN" --appimage-extract-and-run "$APPDIR" "$OUTPUT_APPIMAGE" printf 'AppImage created at %s\n' "$OUTPUT_APPIMAGE"