You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

136 lines
4.1 KiB

#!/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"