Merge pull request #2852 from Nexhas28/Nexhas28/cranky-wozniak-d7ca80

This commit is contained in:
bryanthaboi
2026-10-10 15:15:50 -04:00
committed by GitHub
21 changed files with 868 additions and 113 deletions
+51 -1
View File
@@ -283,7 +283,7 @@ jobs:
echo "changed=true" >> "$GITHUB_OUTPUT"
exit 0
fi
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep -Eq '^(scripts/build_linux_arm64\.sh$|scripts/linux-arm64/|scripts/pack_love\.sh$|docs/linux-arm64-build\.md$|\.github/workflows/(ci|release)\.yml$)'; then
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep -Eq '^(scripts/build_linux_arm64\.sh$|scripts/linux-arm64/|scripts/luajit/|scripts/lib/|scripts/pack_love\.sh$|docs/linux-arm64-build\.md$|\.github/workflows/(ci|release)\.yml$)'; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "changed=false" >> "$GITHUB_OUTPUT"
@@ -302,6 +302,8 @@ jobs:
# so the slow native job below only ever starts on a sane tree.
- name: Linux arm64 offline selftest
run: bash scripts/linux-arm64/selftest_build_linux_arm64.sh
- name: PortMaster LuaJIT helpers offline selftest
run: bash scripts/luajit/selftest_portmaster_runtime.sh
linux-arm64-build:
name: Linux arm64 AppImage build
@@ -486,12 +488,53 @@ jobs:
if-no-files-found: error
retention-days: 7
luajit-linux-arm64:
name: build pinned LuaJIT (linux arm64)
# Native arm64 runner: emulated aarch64 builds have miscompiled LuaJIT.
# Built once here and shared by both PortMaster zip jobs below.
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v7
# The zip scripts depend on these helpers; fail fast before the build.
- name: PortMaster LuaJIT helpers offline selftest
run: bash scripts/luajit/selftest_portmaster_runtime.sh
- name: Cache pinned LuaJIT
uses: actions/cache@v6
with:
path: .bazinga/cache/luajit
key: luajit-linux-arm64-${{ hashFiles('scripts/luajit/**', 'scripts/lib/fetch.sh') }}
- name: Build pinned LuaJIT
run: |
set -euo pipefail
lib="$(scripts/luajit/build_luajit.sh)"
mkdir -p dist/luajit
cp "$lib" "$(dirname "$lib")/COPYRIGHT" dist/luajit/
- name: Verify LuaJIT
run: scripts/luajit/verify_luajit.sh dist/luajit/libluajit-5.1.so.2
- name: Upload LuaJIT
uses: actions/upload-artifact@v7
with:
name: luajit-linux-arm64
path: |
dist/luajit/libluajit-5.1.so.2
dist/luajit/COPYRIGHT
if-no-files-found: error
retention-days: 7
rg34xxsp-build:
name: RG34XXSP PortMaster build
needs: luajit-linux-arm64
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Download pinned LuaJIT
uses: actions/download-artifact@v8
with:
name: luajit-linux-arm64
path: dist/native/luajit-linux-arm64
- name: build RG34XXSP package
env:
GEN1RECOMP_LUAJIT_LIB: ${{ github.workspace }}/dist/native/luajit-linux-arm64/libluajit-5.1.so.2
run: ./build-rg34xxsp.sh --version 0.0.0
- name: upload RG34XXSP artifact
uses: actions/upload-artifact@v7
@@ -503,12 +546,19 @@ jobs:
linux-arm-sbc-build:
name: Linux ARM SBC PortMaster build
needs: luajit-linux-arm64
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Download pinned LuaJIT
uses: actions/download-artifact@v8
with:
name: luajit-linux-arm64
path: dist/native/luajit-linux-arm64
- name: build Linux ARM SBC package
env:
GEN1RECOMP_SOURCE_DIR: ${{ github.workspace }}
GEN1RECOMP_LUAJIT_LIB: ${{ github.workspace }}/dist/native/luajit-linux-arm64/libluajit-5.1.so.2
run: ./build-linux-arm-sbc.sh --version 0.0.0
- name: upload Linux ARM SBC artifact
uses: actions/upload-artifact@v7
+47 -3
View File
@@ -165,7 +165,7 @@ jobs:
uses: actions/cache@v6
with:
path: .bazinga/cache/linux-arm64
key: linux-arm64-deps-${{ hashFiles('scripts/linux-arm64/**', 'scripts/build_linux_arm64.sh') }}
key: linux-arm64-deps-${{ hashFiles('scripts/linux-arm64/**', 'scripts/luajit/**', 'scripts/build_linux_arm64.sh') }}
- name: Build Linux arm64 AppImage
env:
@@ -767,9 +767,39 @@ jobs:
if-no-files-found: error
retention-days: 7
luajit-linux-arm64:
name: build pinned LuaJIT (linux arm64)
needs: version
# Native arm64 runner: emulated aarch64 builds have miscompiled LuaJIT.
runs-on: ubuntu-24.04-arm
steps:
- uses: actions/checkout@v7
- name: Cache pinned LuaJIT
uses: actions/cache@v6
with:
path: .bazinga/cache/luajit
key: luajit-linux-arm64-${{ hashFiles('scripts/luajit/**', 'scripts/lib/fetch.sh') }}
- name: Build pinned LuaJIT
run: |
set -euo pipefail
lib="$(scripts/luajit/build_luajit.sh)"
mkdir -p dist/luajit
cp "$lib" "$(dirname "$lib")/COPYRIGHT" dist/luajit/
- name: Verify LuaJIT
run: scripts/luajit/verify_luajit.sh dist/luajit/libluajit-5.1.so.2
- name: Upload LuaJIT
uses: actions/upload-artifact@v7
with:
name: luajit-linux-arm64
path: |
dist/luajit/libluajit-5.1.so.2
dist/luajit/COPYRIGHT
if-no-files-found: error
retention-days: 7
rg34xxsp:
name: build Anbernic RG34XXSP port
needs: [version, shaderfx-bridge]
needs: [version, shaderfx-bridge, luajit-linux-arm64]
runs-on: ${{ fromJSON(github.repository == 'bryanthaboi/gen1recomp' && '["self-hosted", "macOS"]' || '"macos-latest"') }}
steps:
# The self-hosted runner lives under the machine owner's home
@@ -787,9 +817,16 @@ jobs:
name: shaderfx-bridge-linux-arm64
path: dist/native/linux-arm64
- name: Download the pinned LuaJIT
uses: actions/download-artifact@v8
with:
name: luajit-linux-arm64
path: dist/native/luajit-linux-arm64
- name: Build Anbernic RG34XXSP port
env:
SHADERFX_BRIDGE_REQUIRED: "1"
GEN1RECOMP_LUAJIT_LIB: ${{ github.workspace }}/dist/native/luajit-linux-arm64/libluajit-5.1.so.2
run: |
set -euo pipefail
# Self-contained aarch64 PortMaster-style pack; pulls the LÖVE 11.5
@@ -807,7 +844,7 @@ jobs:
linux-arm-sbc:
name: build Linux ARM SBC PortMaster port
needs: [version, shaderfx-bridge]
needs: [version, shaderfx-bridge, luajit-linux-arm64]
runs-on: ${{ fromJSON(github.repository == 'bryanthaboi/gen1recomp' && '["self-hosted", "macOS"]' || '"macos-latest"') }}
steps:
# The self-hosted runner lives under the machine owner's home
@@ -825,6 +862,12 @@ jobs:
name: shaderfx-bridge-linux-arm64
path: dist/native/linux-arm64
- name: Download the pinned LuaJIT
uses: actions/download-artifact@v8
with:
name: luajit-linux-arm64
path: dist/native/luajit-linux-arm64
- name: Build Linux ARM SBC PortMaster port
env:
# The release workflow must package the commit being released. The
@@ -833,6 +876,7 @@ jobs:
GEN1RECOMP_SOURCE_DIR: ${{ github.workspace }}
GEN1RECOMP_RELEASE_TAG: v${{ needs.version.outputs.version }}
SHADERFX_BRIDGE_REQUIRED: "1"
GEN1RECOMP_LUAJIT_LIB: ${{ github.workspace }}/dist/native/luajit-linux-arm64/libluajit-5.1.so.2
run: |
set -euo pipefail
# Same aarch64 PortMaster-style pack for Linux ARM SBC PortMaster. The build
+28 -13
View File
@@ -11,6 +11,11 @@
# GEN1RECOMP_SOURCE_DIR="$PWD" ./build-linux-arm-sbc.sh --version X.Y.Z
# ./build-linux-arm-sbc.sh --source /path/to/gen1recomp --version X.Y.Z
#
# LuaJIT: the zip ships a pinned LuaJIT, compiled during the build, so building
# needs an aarch64 host with docker or podman. Alternatively set
# GEN1RECOMP_LUAJIT_LIB=/path/libluajit-5.1.so.2 with its COPYRIGHT beside it
# (or GEN1RECOMP_LUAJIT_COPYRIGHT=/path/to/COPYRIGHT).
#
# Output:
# dist/linux-arm-sbc/gen1recomp-sbc-portmaster.zip
#
@@ -45,8 +50,6 @@ SOURCE_DIR_OVERRIDE="${GEN1RECOMP_SOURCE_DIR:-}"
SOURCE_TAG_OVERRIDE="${GEN1RECOMP_RELEASE_TAG:-}"
VERSION="${GEN1RECOMP_VERSION:-}"
# Official PortMaster LÖVE 11.5 aarch64 runtime (small love stub + liblove).
PM_RUNTIME_BASE="https://raw.githubusercontent.com/PortsMaster/PortMaster-GUI/main/PortMaster/runtimes/love_${LOVE_VERSION}"
RELEASES_LATEST_URL="https://github.com/bryanthaboi/gen1recomp/releases/latest"
RELEASE_TARBALL_BASE="https://github.com/bryanthaboi/gen1recomp/archive/refs/tags"
@@ -60,7 +63,7 @@ while [ $# -gt 0 ]; do
--source) [ $# -ge 2 ] || fail "--source needs a directory"; SOURCE_DIR_OVERRIDE="$2"; shift ;;
--release-tag) [ $# -ge 2 ] || fail "--release-tag needs a tag"; SOURCE_TAG_OVERRIDE="$2"; shift ;;
-h|--help)
sed -n '2,24p' "$0"
sed -n '2,29p' "$0"
exit 0
;;
*) fail "unknown argument: $1" ;;
@@ -86,6 +89,10 @@ download() {
mv "$dest.tmp" "$dest"
}
# PortMaster pins + LuaJIT pin (LUAJIT_COMMIT, ...) + download/verify helpers.
# shellcheck source=scripts/luajit/portmaster_runtime.sh
. "$ROOT/scripts/luajit/portmaster_runtime.sh"
# --------------------------------------------------------------- source + game tree
# Release builds use the latest published source archive. A local checkout is
# an explicit override for development and for CI's just-built release source.
@@ -171,15 +178,12 @@ fi
say "fetching LÖVE $LOVE_VERSION aarch64 runtime"
LOVE_BIN="$CACHE/love.aarch64"
LOVE_LIB="$CACHE/liblove-11.5.so"
LUAJIT_LIB="$CACHE/libluajit-5.1.so.2"
MODPLUG_LIB="$CACHE/libmodplug.so.1"
OGG_LIB="$CACHE/libogg.so.0"
download "$PM_RUNTIME_BASE/love.aarch64" "$LOVE_BIN"
download "$PM_RUNTIME_BASE/libs.aarch64/liblove-11.5.so" "$LOVE_LIB"
download "$PM_RUNTIME_BASE/libs.aarch64/libluajit-5.1.so.2" "$LUAJIT_LIB"
download "$PM_RUNTIME_BASE/libs.aarch64/libmodplug.so.1" "$MODPLUG_LIB"
download "$PM_RUNTIME_BASE/libs.aarch64/libogg.so.0" "$OGG_LIB"
pm_fetch_runtime "$CACHE"
pm_select_luajit
# Sanity: love stub must be an aarch64 ELF.
file "$LOVE_BIN" | grep -qi 'aarch64\|ARM aarch64' \
@@ -197,8 +201,9 @@ mkdir -p "$PORT_ROOT/$PORT_DIR_NAME/bin" \
cp -R "$GAME_SRC" "$PORT_ROOT/$PORT_DIR_NAME/lovegame"
cp "$LOVE_BIN" "$PORT_ROOT/$PORT_DIR_NAME/bin/love.aarch64"
chmod +x "$PORT_ROOT/$PORT_DIR_NAME/bin/love.aarch64"
cp "$LOVE_LIB" "$LUAJIT_LIB" "$MODPLUG_LIB" "$OGG_LIB" \
cp "$LOVE_LIB" "$MODPLUG_LIB" "$OGG_LIB" \
"$PORT_ROOT/$PORT_DIR_NAME/libs.aarch64/"
pm_stage_luajit "$PORT_ROOT/$PORT_DIR_NAME/libs.aarch64" "$PORT_ROOT/$PORT_DIR_NAME/licenses"
BRIDGE_LIB="liblibrashader_bridge.so"
BRIDGE_SRC="${SHADERFX_BRIDGE_LINUX_ARM64:-}"
@@ -215,10 +220,14 @@ else
fi
# Drop a short license pointer for the bundled LÖVE bits.
cat > "$PORT_ROOT/$PORT_DIR_NAME/licenses/LICENSE.love2d.txt" <<'EOF'
cat > "$PORT_ROOT/$PORT_DIR_NAME/licenses/LICENSE.love2d.txt" <<EOF
This port bundles the LÖVE 11.5 aarch64 runtime from PortMaster
(https://github.com/PortsMaster/PortMaster-GUI). LÖVE is zlib-licensed;
see https://love2d.org/ for full terms.
(https://github.com/PortsMaster/PortMaster-GUI, commit $PM_RUNTIME_COMMIT),
except for LuaJIT. LÖVE is zlib-licensed; see https://love2d.org/ for full terms.
libs.aarch64/libluajit-5.1.so.2 is LuaJIT, built from source at LuaJIT commit
$LUAJIT_COMMIT (https://github.com/LuaJIT/LuaJIT, MIT license; see
LuaJIT-COPYRIGHT). It replaces PortMaster's LuaJIT 2.1.0-beta3.
EOF
# --------------------------------------------------------------- launcher
@@ -500,6 +509,10 @@ Put the `.gb` in `lovegame/`, then press **Choose ROM**. After import, the ROM-d
LÖVE runtime binaries from [PortMaster](https://portmaster.games/). PortMaster device support and runtime integration are maintained by the PortMaster team.
EOF
cat >> "$PORT_ROOT/README.md" <<EOF
LuaJIT (MIT license) is built from source at LuaJIT commit \`$LUAJIT_COMMIT\` instead of using PortMaster's older LuaJIT 2.1.0-beta3. The JIT stays off by default on this build.
EOF
sed -i.bak "s/__SOURCE_TAG__/$SOURCE_TAG/g" "$PORT_ROOT/README.md"
rm -f "$PORT_ROOT/README.md.bak"
@@ -510,6 +523,8 @@ say "packing $ZIP_OUT"
(cd "$PORT_ROOT" && zip -q -9 -r "$ZIP_OUT" \
"$LAUNCHER_NAME" "$PORT_DIR_NAME" port.json gameinfo.xml README.md)
pm_verify_zip "$ZIP_OUT" "$PORT_DIR_NAME" "$WORK"
say "done."
say "artifact: $ZIP_OUT ($(du -h "$ZIP_OUT" | cut -f1))"
say "copy into the device PortMaster ports folder, then drop your .gb into gen1recomp-sbc/lovegame/"
+27 -24
View File
@@ -11,6 +11,11 @@
# Usage:
# ./build-rg34xxsp.sh [--version X.Y.Z]
#
# LuaJIT: the zip ships a pinned LuaJIT, compiled during the build, so building
# needs an aarch64 host with docker or podman. Alternatively set
# GEN1RECOMP_LUAJIT_LIB=/path/libluajit-5.1.so.2 with its COPYRIGHT beside it
# (or GEN1RECOMP_LUAJIT_COPYRIGHT=/path/to/COPYRIGHT).
#
# Output:
# dist/rg34xxsp/gen1recomp-rg34xxsp-stockos64-mod.zip
#
@@ -41,9 +46,6 @@ LAUNCHER_NAME="Gen1recomp.sh"
LOVE_VERSION="11.5"
VERSION="$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo dev)"
# Official PortMaster LÖVE 11.5 aarch64 runtime (small love stub + liblove).
PM_RUNTIME_BASE="https://raw.githubusercontent.com/PortsMaster/PortMaster-GUI/main/PortMaster/runtimes/love_${LOVE_VERSION}"
say() { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33mwarn:\033[0m %s\n' "$*" >&2; }
fail() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
@@ -52,7 +54,7 @@ while [ $# -gt 0 ]; do
case "$1" in
--version) VERSION="$2"; shift ;;
-h|--help)
sed -n '2,24p' "$0"
sed -n '2,29p' "$0"
exit 0
;;
*) fail "unknown argument: $1" ;;
@@ -66,16 +68,9 @@ command -v unzip >/dev/null || fail "unzip is required"
mkdir -p "$CACHE" "$WORK" "$DIST"
download() {
local url="$1" dest="$2"
if [ -f "$dest" ] && [ -s "$dest" ]; then
return 0
fi
say "downloading $(basename "$dest")"
curl -fL --progress-bar "$url" -o "$dest.tmp" \
|| fail "download failed: $url"
mv "$dest.tmp" "$dest"
}
# PortMaster pins + LuaJIT pin (LUAJIT_COMMIT, ...) + download/verify helpers.
# shellcheck source=scripts/luajit/portmaster_runtime.sh
. "$ROOT/scripts/luajit/portmaster_runtime.sh"
# --------------------------------------------------------------- game tree
# Unpacked directory (not a .love zip) so the player can drop a .gb next to
@@ -129,15 +124,12 @@ fi
say "fetching LÖVE $LOVE_VERSION aarch64 runtime"
LOVE_BIN="$CACHE/love.aarch64"
LOVE_LIB="$CACHE/liblove-11.5.so"
LUAJIT_LIB="$CACHE/libluajit-5.1.so.2"
MODPLUG_LIB="$CACHE/libmodplug.so.1"
OGG_LIB="$CACHE/libogg.so.0"
download "$PM_RUNTIME_BASE/love.aarch64" "$LOVE_BIN"
download "$PM_RUNTIME_BASE/libs.aarch64/liblove-11.5.so" "$LOVE_LIB"
download "$PM_RUNTIME_BASE/libs.aarch64/libluajit-5.1.so.2" "$LUAJIT_LIB"
download "$PM_RUNTIME_BASE/libs.aarch64/libmodplug.so.1" "$MODPLUG_LIB"
download "$PM_RUNTIME_BASE/libs.aarch64/libogg.so.0" "$OGG_LIB"
pm_fetch_runtime "$CACHE"
pm_select_luajit
# Sanity: love stub must be an aarch64 ELF.
file "$LOVE_BIN" | grep -qi 'aarch64\|ARM aarch64' \
@@ -155,8 +147,9 @@ mkdir -p "$PORT_ROOT/$PORT_DIR_NAME/bin" \
cp -R "$GAME_SRC" "$PORT_ROOT/$PORT_DIR_NAME/lovegame"
cp "$LOVE_BIN" "$PORT_ROOT/$PORT_DIR_NAME/bin/love.aarch64"
chmod +x "$PORT_ROOT/$PORT_DIR_NAME/bin/love.aarch64"
cp "$LOVE_LIB" "$LUAJIT_LIB" "$MODPLUG_LIB" "$OGG_LIB" \
cp "$LOVE_LIB" "$MODPLUG_LIB" "$OGG_LIB" \
"$PORT_ROOT/$PORT_DIR_NAME/libs.aarch64/"
pm_stage_luajit "$PORT_ROOT/$PORT_DIR_NAME/libs.aarch64" "$PORT_ROOT/$PORT_DIR_NAME/licenses"
BRIDGE_LIB="liblibrashader_bridge.so"
BRIDGE_SRC="${SHADERFX_BRIDGE_LINUX_ARM64:-}"
@@ -173,10 +166,14 @@ else
fi
# Drop a short license pointer for the bundled LÖVE bits.
cat > "$PORT_ROOT/$PORT_DIR_NAME/licenses/LICENSE.love2d.txt" <<'EOF'
cat > "$PORT_ROOT/$PORT_DIR_NAME/licenses/LICENSE.love2d.txt" <<EOF
This port bundles the LÖVE 11.5 aarch64 runtime from PortMaster
(https://github.com/PortsMaster/PortMaster-GUI). LÖVE is zlib-licensed;
see https://love2d.org/ for full terms.
(https://github.com/PortsMaster/PortMaster-GUI, commit $PM_RUNTIME_COMMIT),
except for LuaJIT. LÖVE is zlib-licensed; see https://love2d.org/ for full terms.
libs.aarch64/libluajit-5.1.so.2 is LuaJIT, built from source at LuaJIT commit
$LUAJIT_COMMIT (https://github.com/LuaJIT/LuaJIT, MIT license; see
LuaJIT-COPYRIGHT). It replaces PortMaster's LuaJIT 2.1.0-beta3.
EOF
# --------------------------------------------------------------- launcher
@@ -355,6 +352,10 @@ Canonical SHA-1 (1 MiB US carts only):
LÖVE runtime binaries from [PortMaster](https://portmaster.games/).
Stock OS 64-bit MOD: [cbepx-me](https://github.com/cbepx-me/Anbernic-H700-RG-xx-StockOS-Modification).
EOF
cat >> "$PORT_ROOT/README.md" <<EOF
LuaJIT (MIT license) is built from source at LuaJIT commit \`$LUAJIT_COMMIT\` instead of using PortMaster's older LuaJIT 2.1.0-beta3. The JIT stays off by default on this build.
EOF
# --------------------------------------------------------------- zip
ZIP_OUT="$DIST/$APP_NAME-$ARTIFACT_SUFFIX.zip"
@@ -363,6 +364,8 @@ say "packing $ZIP_OUT"
(cd "$PORT_ROOT" && zip -q -9 -r "$ZIP_OUT" \
"$LAUNCHER_NAME" "$PORT_DIR_NAME" port.json gameinfo.xml README.md)
pm_verify_zip "$ZIP_OUT" "$PORT_DIR_NAME" "$WORK"
say "done."
say "artifact: $ZIP_OUT ($(du -h "$ZIP_OUT" | cut -f1))"
say "copy onto the RG34XXSP SD card under roms/PORTS/ (Stock OS 64-bit MOD), then drop your .gb into gen1recomp/lovegame/"
+26
View File
@@ -98,6 +98,32 @@ casing and mount points.
If a launch fails, `Roms/PORTS/gen1recomp/log.txt` holds the output of the
last run.
## Bundled LuaJIT
The pack does not use PortMaster's `libluajit-5.1.so.2`. That one is LuaJIT
2.1.0-beta3 (2017), which segfaulted 3 of 3 runs with the JIT on (TrimUI
Brick). The pack ships a pinned LuaJIT 2.1 build instead (commit
`c6ffc141a8762b41703f9287d63d93622a13dd8f`, MIT, license in
`licenses/LuaJIT-COPYRIGHT`). It is compiled from source in a Debian bullseye
aarch64 container (glibc 2.31, GC64, `make amalg`) by
`scripts/luajit/build_luajit.sh`, and the build checks the version string and
the glibc requirement before and after zipping. On the TrimUI Brick it ran a
26-minute JIT-on soak with no crash.
The JIT still stays **off by default** on these builds: LÖVE turns it off on
arm64, and nothing here sets `POKEPORT_JIT`. It has only been validated on one
device (TrimUI Brick, A133P). Enabling `POKEPORT_JIT=1` by default needs
stability testing on H700, RK3566 and A133P devices first.
Existing installs keep their old LuaJIT until the zip is re-extracted: in-app
updates replace only the game's code, not the runtime libraries.
Building the port now needs an aarch64 host with docker or podman (non-aarch64
hosts are refused), or a prebuilt library passed with
`GEN1RECOMP_LUAJIT_LIB=/path/to/libluajit-5.1.so.2` (with its `COPYRIGHT` next
to it, or `GEN1RECOMP_LUAJIT_COPYRIGHT`). The remaining PortMaster runtime
files are pinned to a PortMaster-GUI commit and sha256-checked.
## Building the port
Release runs build this automatically (see `.github/workflows/release.yml`).
+38
View File
@@ -79,6 +79,44 @@ Environment variables for fine-tuning (configured in `gen1recomp-sbc.sh`):
| `POKEPORT_CPU_GOVERNOR` | `schedutil` | CPU scaling governor (`schedutil`, `performance`, `ondemand`) |
## Bundled LuaJIT
The pack does not use PortMaster's `libluajit-5.1.so.2`. That one is LuaJIT
2.1.0-beta3 (2017), which segfaulted 3 of 3 runs with the JIT on (TrimUI
Brick). The pack ships a pinned LuaJIT 2.1 build instead (commit
`c6ffc141a8762b41703f9287d63d93622a13dd8f`, MIT, license in
`licenses/LuaJIT-COPYRIGHT`). It is compiled from source in a Debian bullseye
aarch64 container (glibc 2.31, GC64, `make amalg`) by
`scripts/luajit/build_luajit.sh`, and the build checks the version string and
the glibc requirement before and after zipping. On the TrimUI Brick it ran a
26-minute JIT-on soak with no crash.
The JIT still stays **off by default** on these builds: LÖVE turns it off on
arm64, and nothing here sets `POKEPORT_JIT`. It has only been validated on one
device (TrimUI Brick, A133P). Enabling `POKEPORT_JIT=1` by default needs
stability testing on H700, RK3566 and A133P devices first.
The PortMaster catalogue package (`scripts/portmaster/`, see
[portmaster-submission.md](portmaster-submission.md)) is different: it runs on
the device's own PortMaster `love_11.5` runtime, so it still uses that
runtime's LuaJIT 2.1.0-beta3. Keep the JIT off there.
Existing installs keep their old LuaJIT until the zip is re-extracted: in-app
updates replace only the game's code, not the runtime libraries.
Building the port now needs an aarch64 host with docker or podman (non-aarch64
hosts are refused), or a prebuilt library passed with
`GEN1RECOMP_LUAJIT_LIB=/path/to/libluajit-5.1.so.2` (with its `COPYRIGHT` next
to it, or `GEN1RECOMP_LUAJIT_COPYRIGHT`). The remaining PortMaster runtime
files are pinned to a PortMaster-GUI commit and sha256-checked.
For scripts that wrap `build-linux-arm-sbc.sh`: the library in
`GEN1RECOMP_LUAJIT_LIB` must have a `COPYRIGHT` beside it (or set
`GEN1RECOMP_LUAJIT_COPYRIGHT`) and must pass `scripts/luajit/verify_luajit.sh`
(the pinned LuaJIT version, built with `PREFIX=/usr`). There is no longer any
way to ship PortMaster's stock LuaJIT; a wrapper's "stock LuaJIT" mode should
be removed, and the wrapper pointed at `scripts/luajit/build_luajit.sh` instead.
## Building
Release workflows build this automatically. Standalone builds resolve the latest published Gen1Recomp release by default:
+20
View File
@@ -201,6 +201,26 @@ Both pins live in `scripts/linux-arm64/common.sh`:
source-built libraries. Bumping these is usually safe and occasionally
necessary: `libmpg123` in particular must stay at least as new as what a
target host's `libsndfile` expects, which is asserted for `mpg123_info2`.
- `LUAJIT_*` in `scripts/luajit/pins.sh` (shared with the PortMaster builds) —
LuaJIT is compiled from a pinned `v2.1`-branch commit inside the builder
container instead of using bullseye's `libluajit-5.1-dev` (2.1.0-beta3, 2017,
which segfaulted with the JIT on). The commit and a hash of
`scripts/luajit/compile_luajit.sh` are part of the cached prefix
name, and the build fails if the bundled library lacks
`LUAJIT_VERSION_STRING` or if the system LuaJIT is present in the image.
LuaJIT bakes its compile-time `PREFIX` into the default module paths, so it
is compiled with `PREFIX=/usr` and only installed into the cached prefix;
the build fails unless the library's default path is
`/usr/share/luajit-2.1/?.lua` with no build prefix embedded. The install goes
to a staging root first and is copied into the prefix only after
`scripts/luajit/verify_luajit.sh` passes. `LUAJIT_BUILD_IMAGE` (digest-pinned)
and `LUAJIT_APT_SOURCES` in `pins.sh` must match the builder Dockerfile's
`FROM` and apt sources (see the re-pin notes in `pins.sh`). To
bump: change `LUAJIT_COMMIT` and `LUAJIT_SHA256` (of the GitHub archive
tarball) and set `LUAJIT_VERSION_STRING` to `LuaJIT 2.1.<contents of .relver
at that commit>`. The license ships as `LuaJIT-COPYRIGHT` in the AppImage.
The JIT stays off by default: LÖVE's `jitsetup` disables it on arm64, and
enabling it by default needs stability testing on several devices first.
- `APPIMAGE_RUNTIME_TAG` / `APPIMAGE_RUNTIME_SHA256` — always a dated tag
from [AppImage/type2-runtime](https://github.com/AppImage/type2-runtime/releases).
The selftest fails the build if this ever points at `continuous`.
+3 -1
View File
@@ -62,7 +62,9 @@ cache are included. The project owner has explicitly permitted PortMaster
distribution of the launcher, recorded in the included license.
I tested this catalogue candidate on my TrimUI Brick and confirmed it works.
It uses PortMaster's LÖVE 11.5 runtime. I'm looking
It uses PortMaster's LÖVE 11.5 runtime, including that runtime's LuaJIT
2.1.0-beta3 (unlike the release zips, which bundle a pinned modern LuaJIT; see
https://github.com/bryanthaboi/gen1recomp/blob/dev/docs/linux-arm-sbc.md#bundled-luajit), so the JIT must stay off. I'm looking
for testing on the firmware and resolutions listed above, especially import,
save/reload, controller exit and suspend/resume. Please include device,
firmware, package checksum, result and `gen1recomp/log.txt` when reporting a
+7
View File
@@ -125,6 +125,7 @@ download_pinned "$THEORA_URL" "$CACHE/$THEORA_TARBALL" "$THEORA_SHA256"
download_pinned "$OGG_URL" "$CACHE/$OGG_TARBALL" "$OGG_SHA256"
download_pinned "$VORBIS_URL" "$CACHE/$VORBIS_TARBALL" "$VORBIS_SHA256"
download_pinned "$MPG123_URL" "$CACHE/$MPG123_TARBALL" "$MPG123_SHA256"
download_pinned "$LUAJIT_URL" "$CACHE/$LUAJIT_TARBALL" "$LUAJIT_SHA256"
download_pinned "$APPIMAGE_RUNTIME_URL" "$CACHE/$APPIMAGE_RUNTIME_NAME" \
"$APPIMAGE_RUNTIME_SHA256"
@@ -162,12 +163,18 @@ say "compiling and packaging inside $BUILDER_BASE_IMAGE"
-e VORBIS_TARBALL="$VORBIS_TARBALL" \
-e MPG123_VERSION="$MPG123_VERSION" \
-e MPG123_TARBALL="$MPG123_TARBALL" \
-e LUAJIT_COMMIT="$LUAJIT_COMMIT" \
-e LUAJIT_TARBALL="$LUAJIT_TARBALL" \
-e LUAJIT_VERSION_STRING="$LUAJIT_VERSION_STRING" \
-e LUAJIT_MAX_GLIBC="$LUAJIT_MAX_GLIBC" \
-e BUILDER_HASH="$BUILDER_HASH" \
-e APP_NAME="$APP_NAME" \
-e VERSION="$VERSION" \
-v "$CACHE:/cache" \
-v "$IN_DIR:/in:ro" \
-v "$OUT_DIR:/out" \
-v "$ROOT/scripts/linux-arm64:/scripts:ro" \
-v "$ROOT/scripts/luajit:/luajit-scripts:ro" \
"$BUILDER_IMAGE" bash /scripts/build_appimage.sh
# --------------------------------------------------------------- publish
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Sourceable download/hash/container helpers. No side effects on source.
# The caller must define say, warn and fail.
# Print SHA-256 hex digest of PATH. Prefers sha256sum, falls back to shasum
# (same order-agnostic pair scripts/switch/common.sh uses).
sha256_file() {
local path="$1"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$path" | awk '{print $1}'
elif command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$path" | awk '{print $1}'
else
fail "need sha256sum or shasum (install coreutils)"
fi
}
# download_pinned URL DEST EXPECTED_SHA256
#
# A cache hit is only trusted if it still hashes to the pin: a download
# truncated by a network drop would otherwise be reused forever, which is the
# same trap scripts/build.sh guards for the win64 zip and the x86_64 AppImage.
download_pinned() {
local url="$1" dest="$2" want="$3" got=""
if [ -f "$dest" ]; then
got="$(sha256_file "$dest")"
if [ "$got" = "$want" ]; then
return 0
fi
warn "cached $(basename "$dest") has the wrong digest, re-downloading"
rm -f "$dest"
fi
say "downloading $(basename "$dest")"
# --retry-all-errors because plain --retry skips TLS handshake failures,
# which is how xiph.org drops these tarballs; the pin below still gates it.
# curl < 7.71 lacks the flag and rejects it, so it is only passed if offered.
local retry_all=()
# No grep -q: it would exit early and SIGPIPE curl, which pipefail reports.
if curl --help all 2>/dev/null | grep -- --retry-all-errors >/dev/null; then
retry_all=(--retry-all-errors)
fi
curl -fL --retry 5 --retry-delay 2 ${retry_all[@]+"${retry_all[@]}"} --progress-bar \
"$url" -o "$dest.tmp" || fail "download failed: $url"
got="$(sha256_file "$dest.tmp")"
[ "$got" = "$want" ] || fail "$(printf '%s\n expected %s\n got %s' \
"checksum mismatch for $(basename "$dest")" "$want" "$got")"
mv "$dest.tmp" "$dest"
}
# Echo the container runtime to use: docker, else podman.
container_runtime() {
if [ -n "${GEN1_CONTAINER_RUNTIME:-}" ]; then
printf '%s' "$GEN1_CONTAINER_RUNTIME"
return 0
fi
if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
printf 'docker'
elif command -v podman >/dev/null 2>&1; then
printf 'podman'
else
return 1
fi
}
+6 -3
View File
@@ -8,7 +8,8 @@
#
# This image is arch-native: build it on an aarch64 host (Raspberry Pi 5,
# ubuntu-24.04-arm runner, Apple Silicon Docker) — no qemu emulation.
FROM debian:bullseye
# Must match LUAJIT_BUILD_IMAGE in scripts/luajit/pins.sh (checked by the selftest).
FROM debian:bullseye@sha256:6f519a81440354a85eb592c5f32109ab80605f6b892455983a6f618bf87fabe9
ENV DEBIAN_FRONTEND=noninteractive
@@ -18,7 +19,9 @@ ENV DEBIAN_FRONTEND=noninteractive
# Note what is deliberately ABSENT: libsdl2-dev, libtheora-dev and
# libopenal-dev. All three are built from source instead (see common.sh for
# why), and having Debian's copies installed would let pkg-config hand LÖVE's
# configure the system ones and silently undo it.
# configure the system ones and silently undo it. libluajit-5.1-dev is absent
# for the same reason: LuaJIT is built from a pinned commit (scripts/luajit/),
# because bullseye ships 2.1.0-beta3, which segfaulted with the JIT on.
#
# The remaining lib*-dev set is LÖVE's optional-module surface. A missing one
# does not fail configure, it silently drops a module (love.sound decoders,
@@ -37,7 +40,7 @@ RUN printf 'deb http://archive.debian.org/debian bullseye main\ndeb http://archi
build-essential pkg-config autoconf automake libtool cmake \
ca-certificates curl file xz-utils bzip2 zip unzip squashfs-tools \
libogg-dev libvorbis-dev \
libmodplug-dev libmpg123-dev libfreetype6-dev libluajit-5.1-dev \
libmodplug-dev libmpg123-dev libfreetype6-dev \
zlib1g-dev libgl1-mesa-dev libgles2-mesa-dev libegl1-mesa-dev \
libasound2-dev libpulse-dev libudev-dev libdbus-1-dev \
libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxi-dev \
+72 -3
View File
@@ -27,6 +27,11 @@ VORBIS_VERSION="${VORBIS_VERSION:?}"
VORBIS_TARBALL="${VORBIS_TARBALL:?}"
MPG123_VERSION="${MPG123_VERSION:?}"
MPG123_TARBALL="${MPG123_TARBALL:?}"
LUAJIT_COMMIT="${LUAJIT_COMMIT:?}"
LUAJIT_TARBALL="${LUAJIT_TARBALL:?}"
LUAJIT_VERSION_STRING="${LUAJIT_VERSION_STRING:?}"
LUAJIT_MAX_GLIBC="${LUAJIT_MAX_GLIBC:?}"
BUILDER_HASH="${BUILDER_HASH:?}"
APP_NAME="${APP_NAME:?}"
VERSION="${VERSION:?}"
JOBS="${JOBS:-$(nproc)}"
@@ -46,12 +51,40 @@ mkdir -p "$WORK"
# the only slow part (~5 min cold on a Pi 5) and is identical for every game
# version. The key includes every source version, so bumping any of them
# invalidates the cache instead of silently reusing a stale mix.
PREFIX="$CACHE/prefix-love$LOVE_VERSION-sdl$SDL2_VERSION-al$OPENAL_VERSION-theora$THEORA_VERSION-ogg$OGG_VERSION-vorbis$VORBIS_VERSION-mpg$MPG123_VERSION"
# The key also carries a hash of the LuaJIT build recipe, so changing it
# rebuilds LuaJIT instead of reusing a prefix built the old way. It also carries
# the builder image's Dockerfile hash, so a toolchain/package change rebuilds
# everything instead of reusing objects compiled by the old image.
LUAJIT_RECIPE="$(sha256sum /luajit-scripts/compile_luajit.sh | cut -c1-12)"
PREFIX="$CACHE/prefix-love$LOVE_VERSION-sdl$SDL2_VERSION-al$OPENAL_VERSION-theora$THEORA_VERSION-ogg$OGG_VERSION-vorbis$VORBIS_VERSION-mpg$MPG123_VERSION-luajit${LUAJIT_COMMIT:0:12}-${LUAJIT_RECIPE}-img${BUILDER_HASH}"
export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig"
# Our own libraries must win over the system ones during LÖVE's configure and
# link, or the whole point of building them is lost.
export LD_LIBRARY_PATH="$PREFIX/lib"
# ----------------------------------------------------------- compile LuaJIT
# Built from the pinned commit, not bullseye's 2.1.0-beta3 (which segfaulted
# with the JIT on). The cache key above carries the commit, so a prefix built
# against any other LuaJIT is never reused.
# COPYRIGHT is installed last by compile_luajit.sh, so it marks a finished install.
if [ -f "$PREFIX/lib/libluajit-5.1.so.2" ] && [ -f "$PREFIX/lib/pkgconfig/luajit.pc" ] \
&& [ -s "$PREFIX/share/doc/luajit/COPYRIGHT" ]; then
say "reusing cached LuaJIT ${LUAJIT_COMMIT:0:12}"
bash /luajit-scripts/verify_luajit.sh "$PREFIX/lib/libluajit-5.1.so.2" \
|| fail "the cached LuaJIT in $PREFIX failed verification; re-run with --clean-cache"
else
say "compiling LuaJIT ${LUAJIT_COMMIT:0:12}"
bash /luajit-scripts/compile_luajit.sh "$CACHE/$LUAJIT_TARBALL" "$PREFIX"
fi
# Debian's copy must not be able to win: a stale builder image that still has
# libluajit-5.1-dev would let pkg-config hand LOVE the 2017 beta3.
if ls /usr/lib/aarch64-linux-gnu/libluajit-5.1.so* >/dev/null 2>&1; then
fail "the system libluajit-5.1 is installed in the builder image; rebuild it with --rebuild-image"
fi
luajit_prefix="$(pkg-config --variable=prefix luajit)"
[ "$luajit_prefix" = "$PREFIX" ] \
|| fail "pkg-config resolves luajit to '$luajit_prefix', not $PREFIX"
# ------------------------------------------------------ compile audio codecs
# Ordered by dependency: vorbis needs ogg, and theora needs ogg too. All three
# are small, plain autotools builds -- well under a minute each.
@@ -250,6 +283,38 @@ for soname in libSDL2-2.0.so.0 libopenal.so.1 libfreetype.so.6 \
|| fail "liblove is not linked against $soname (a -dev package went missing)"
done
# liblove must really be linked against OUR LuaJIT. LD_LIBRARY_PATH points at
# $PREFIX/lib for this whole script, so a plain ldd would resolve there no
# matter how liblove was linked. Prove it without that crutch instead:
# (a) every RPATH/RUNPATH entry stays inside $PREFIX (luajit.pc adds none;
# libtool adds $PREFIX/lib because liblove links the .la libraries built
# there -- SDL2, ogg, vorbis, theora, mpg123 -- and (b) relies on that),
# (b) with LD_LIBRARY_PATH unset, the loader still finds libluajit-5.1.so.2,
# and only at $PREFIX/lib -- via the checked rpath or the system search
# path, so no other LuaJIT on the system could have satisfied the link, and
# (c) when compiled in this run, configure recorded our include dir.
love_rpath="$(objdump -p "$love_lib" | awk '/RPATH|RUNPATH/ {print $2}')"
if [ -n "$love_rpath" ]; then
IFS=: read -ra rpath_dirs <<<"$love_rpath"
for rpath_dir in "${rpath_dirs[@]}"; do
case "$rpath_dir" in
"$PREFIX"|"$PREFIX"/*) ;;
*) fail "liblove has an RPATH/RUNPATH entry outside $PREFIX: $rpath_dir" ;;
esac
done
fi
bare_ldd="$(env -u LD_LIBRARY_PATH ldd "$love_lib")"
luajit_resolved="$(awk '$1 == "libluajit-5.1.so.2" {print $3}' <<<"$bare_ldd")"
[ -n "$luajit_resolved" ] && [ "$luajit_resolved" != "not" ] \
&& [ "$(readlink -f "$luajit_resolved")" = "$(readlink -f "$PREFIX/lib/libluajit-5.1.so.2")" ] \
|| fail "without LD_LIBRARY_PATH liblove resolves libluajit-5.1.so.2 to '${luajit_resolved:-nothing}', not the pinned build in $PREFIX/lib"
say "liblove -> libluajit-5.1.so.2 (no LD_LIBRARY_PATH): $luajit_resolved"
if [ -f "$WORK/love-src/config.log" ]; then
grep -qF -- "-I$PREFIX/include/luajit-2.1" "$WORK/love-src/config.log" \
|| fail "LÖVE's configure did not record -I$PREFIX/include/luajit-2.1"
say "config.log: $(grep -F -m1 -- "-I$PREFIX/include/luajit-2.1" "$WORK/love-src/config.log")"
fi
# --------------------------------------------------------------- AppDir
# Layout mirrors LÖVE's own x86_64 AppImage exactly (bin/ lib/ share/ at the
# AppDir root, not usr/-prefixed), so the AppRun contract below -- and the
@@ -324,6 +389,8 @@ chmod 0644 "$APPDIR/lib/liblove-$LOVE_VERSION.so"
BUNDLED["liblove-$LOVE_VERSION.so"]=1
bundle_needed "$APPDIR/bin/love"
bundle_needed "$APPDIR/lib/liblove-$LOVE_VERSION.so"
bash /luajit-scripts/verify_luajit.sh "$APPDIR/lib/libluajit-5.1.so.2" \
|| fail "bundled libluajit-5.1.so.2 is not the pinned build"
say "bundled $(ls "$APPDIR/lib" | wc -l) libraries: $(ls "$APPDIR/lib" | tr '\n' ' ')"
# ------------------------------------------------- host dependency contract
@@ -363,8 +430,8 @@ say "host dependency contract holds (glibc, libstdc++ and the font stack only)"
# LÖVE loads jit.* (jit.status, the profiler) through LUA_PATH; without these
# the modules are simply absent, so ship them the way upstream's image does.
jit_share="$(ls -d /usr/share/luajit-* 2>/dev/null | head -1)"
[ -n "$jit_share" ] || fail "luajit jit/*.lua modules not found under /usr/share"
jit_share="$(ls -d "$PREFIX"/share/luajit-* 2>/dev/null | head -1)"
[ -n "$jit_share" ] || fail "luajit jit/*.lua modules not found under $PREFIX/share"
LUAJIT_SHARE_DIR="$(basename "$jit_share")"
mkdir -p "$APPDIR/share/$LUAJIT_SHARE_DIR" "$APPDIR/share/lua/5.1" "$APPDIR/lib/lua/5.1"
cp -R "$jit_share/jit" "$APPDIR/share/$LUAJIT_SHARE_DIR/"
@@ -445,6 +512,8 @@ chmod +x "$APPDIR/AppRun"
[ -f "$PREFIX/license.txt" ] || fail "LÖVE license.txt missing from the build prefix"
cp "$PREFIX/license.txt" "$APPDIR/license.love2d.txt"
[ -s "$PREFIX/share/doc/luajit/COPYRIGHT" ] || fail "LuaJIT COPYRIGHT missing from the build prefix"
cp "$PREFIX/share/doc/luajit/COPYRIGHT" "$APPDIR/LuaJIT-COPYRIGHT"
# --------------------------------------------------------------- fuse image
# An AppImage is just <runtime ELF><squashfs>. gzip at 128K blocks matches what
+19 -60
View File
@@ -8,6 +8,19 @@ if [ -z "${ROOT:-}" ]; then
fi
export ROOT
# Pinned LuaJIT (shared with the PortMaster builds). Built from source in the
# builder container rather than taken from bullseye's libluajit-5.1-dev.
# shellcheck source=../luajit/pins.sh
. "$ROOT/scripts/luajit/pins.sh"
say() { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33mwarn:\033[0m %s\n' "$*" >&2; }
fail() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
# sha256_file, download_pinned, container_runtime (shared with scripts/luajit).
# shellcheck source=../lib/fetch.sh
. "$ROOT/scripts/lib/fetch.sh"
# ---------------------------------------------------------------- pins
# LÖVE ships no aarch64 binary of any kind -- the 11.5 release has win32/win64,
# macOS, Android, iOS and an x86_64 AppImage, and that is the whole list. So
@@ -104,69 +117,15 @@ APPIMAGE_RUNTIME_SHA256="00cbdfcf917cc6c0ff6d3347d59e0ca1f7f45a6df1a428a0d6d8a78
# AppImage cover Raspberry Pi OS bullseye/bookworm/trixie, Ubuntu 20.04+ and
# the aarch64 handheld distros. Building on a newer base would silently
# restrict the artifact to that base and newer.
BUILDER_BASE_IMAGE="debian:bullseye"
BUILDER_IMAGE="${GEN1_LINUX_ARM64_IMAGE:-gen1recomp-linux-arm64-builder}"
BUILDER_BASE_IMAGE="$LUAJIT_BUILD_IMAGE"
# The tag is derived from the Dockerfile content, so any change to the image
# (packages, apt sources, base digest) yields a new tag and a stale local image
# (e.g. one still carrying libluajit-5.1-dev) is never reused.
BUILDER_HASH="$(sha256_file "$ROOT/scripts/linux-arm64/Dockerfile" | cut -c1-12)"
BUILDER_IMAGE="${GEN1_LINUX_ARM64_IMAGE:-gen1recomp-linux-arm64-builder:$BUILDER_HASH}"
APP_NAME="gen1recomp"
say() { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33mwarn:\033[0m %s\n' "$*" >&2; }
fail() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
# Print SHA-256 hex digest of PATH. Prefers sha256sum, falls back to shasum
# (same order-agnostic pair scripts/switch/common.sh uses).
sha256_file() {
local path="$1"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$path" | awk '{print $1}'
elif command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$path" | awk '{print $1}'
else
fail "need sha256sum or shasum (install coreutils)"
fi
}
# download_pinned URL DEST EXPECTED_SHA256
#
# A cache hit is only trusted if it still hashes to the pin: a download
# truncated by a network drop would otherwise be reused forever, which is the
# same trap scripts/build.sh guards for the win64 zip and the x86_64 AppImage.
download_pinned() {
local url="$1" dest="$2" want="$3" got=""
if [ -f "$dest" ]; then
got="$(sha256_file "$dest")"
if [ "$got" = "$want" ]; then
return 0
fi
warn "cached $(basename "$dest") has the wrong digest, re-downloading"
rm -f "$dest"
fi
say "downloading $(basename "$dest")"
# --retry-all-errors because plain --retry skips TLS handshake failures,
# which is how xiph.org drops these tarballs; the pin below still gates it.
curl -fL --retry 5 --retry-delay 2 --retry-all-errors --progress-bar \
"$url" -o "$dest.tmp" || fail "download failed: $url"
got="$(sha256_file "$dest.tmp")"
[ "$got" = "$want" ] || fail "$(printf '%s\n expected %s\n got %s' \
"checksum mismatch for $(basename "$dest")" "$want" "$got")"
mv "$dest.tmp" "$dest"
}
# Echo the container runtime to use: docker, else podman.
container_runtime() {
if [ -n "${GEN1_CONTAINER_RUNTIME:-}" ]; then
printf '%s' "$GEN1_CONTAINER_RUNTIME"
return 0
fi
if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then
printf 'docker'
elif command -v podman >/dev/null 2>&1; then
printf 'podman'
else
return 1
fi
}
fail_need_container() {
fail "$(cat <<'EOF'
the aarch64 AppImage is compiled inside a Debian bullseye container and needs
@@ -60,11 +60,46 @@ printf '%s' "$LOVE_SRC_URL" | grep -q "/$LOVE_VERSION/$LOVE_SRC_TARBALL\$" \
say "checking the builder base image"
# Building on anything newer than bullseye silently raises the glibc floor and
# strands every user on an older distro, with no symptom until they run it.
grep -q '^FROM debian:bullseye$' "$SCRIPT_DIR/Dockerfile" \
|| fail "Dockerfile no longer builds on debian:bullseye (that raises the glibc floor)"
[ "$BUILDER_BASE_IMAGE" = "debian:bullseye" ] \
grep -qxF "FROM $LUAJIT_BUILD_IMAGE" "$SCRIPT_DIR/Dockerfile" \
|| fail "Dockerfile FROM is not LUAJIT_BUILD_IMAGE ($LUAJIT_BUILD_IMAGE)"
printf '%s' "$LUAJIT_BUILD_IMAGE" | grep -Eq '^debian:bullseye@sha256:[0-9a-f]{64}$' \
|| fail "LUAJIT_BUILD_IMAGE is not a digest-pinned debian:bullseye (that raises the glibc floor)"
[ "$BUILDER_BASE_IMAGE" = "$LUAJIT_BUILD_IMAGE" ] \
|| fail "BUILDER_BASE_IMAGE disagrees with the Dockerfile"
while IFS= read -r apt_line; do
[ -n "$apt_line" ] || continue
grep -qF -- "$apt_line" "$SCRIPT_DIR/Dockerfile" \
|| fail "build_luajit.sh and the builder image must use the same apt sources (Dockerfile lacks: $apt_line)"
done <<<"$LUAJIT_APT_SOURCES"
say "checking verify_luajit.sh rejection paths"
# Synthetic files only: header = magic, 64-bit (02), little-endian (01), zeros,
# e_machine at offset 18-19.
lj_dir="$(mktemp -d "${TMPDIR:-/tmp}/gen1recomp-verify-luajit.XXXXXX")"
temp_dir=""
trap 'rm -rf "$lj_dir" ${temp_dir:+"$temp_dir"}' EXIT
lj_hdr() { # $1 = e_machine as two hex bytes, e.g. b7 00
printf '\177ELF\002\001'; head -c 12 /dev/zero; printf "\\x$1\\x$2"
}
lj_make() { # $1 file, $2 e_machine lo, $3 hi, $4 version, $5 path, $6 glibc
{ lj_hdr "$2" "$3"; printf '\0%s\0%s\0%s\0' "$4" "$5" "$6"; } > "$1"
}
lj_path='/usr/share/luajit-2.1/?.lua'
lj_verify() { bash "$ROOT/scripts/luajit/verify_luajit.sh" "$1" >/dev/null 2>&1; }
lj_make "$lj_dir/ok" b7 00 "$LUAJIT_VERSION_STRING" "$lj_path" GLIBC_2.17
lj_verify "$lj_dir/ok" || fail "verify_luajit.sh rejects a valid synthetic aarch64 library"
printf '%s %s GLIBC_2.17' "$LUAJIT_VERSION_STRING" "$lj_path" > "$lj_dir/text"
lj_make "$lj_dir/x86" 3e 00 "$LUAJIT_VERSION_STRING" "$lj_path" GLIBC_2.17
lj_make "$lj_dir/nopath" b7 00 "$LUAJIT_VERSION_STRING" "/opt/build/share/luajit-2.1/?.lua" GLIBC_2.17
lj_make "$lj_dir/glibc" b7 00 "$LUAJIT_VERSION_STRING" "$lj_path" GLIBC_2.34
lj_make "$lj_dir/beta" b7 00 "$LUAJIT_VERSION_STRING LuaJIT 2.1.0-beta3" "$lj_path" GLIBC_2.17
for bad in text x86 nopath glibc beta; do
if lj_verify "$lj_dir/$bad"; then
fail "verify_luajit.sh accepted the bad '$bad' library"
fi
done
say "checking the dependency exclude list"
# Extract the live regex from the build script and classify known sonames
# through it, so a future edit cannot quietly start bundling glibc or stop
@@ -121,11 +156,17 @@ say "checking the dlopen guarantees"
# 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
for forbidden_pkg in libsdl2-dev libtheora-dev libopenal-dev libluajit-5.1-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 'compile_luajit.sh' "$SCRIPT_DIR/build_appimage.sh" \
|| fail "build_appimage.sh no longer builds the pinned LuaJIT"
grep -qF 'luajit${LUAJIT_COMMIT:0:12}-${LUAJIT_RECIPE}-img${BUILDER_HASH}' "$SCRIPT_DIR/build_appimage.sh" \
|| fail "the PREFIX cache key does not include the LuaJIT commit, recipe hash and builder image hash"
printf '%s' "$LUAJIT_SHA256" | grep -Eq '^[0-9a-f]{64}$' \
|| fail "LUAJIT_SHA256 is not a sha256 digest: $LUAJIT_SHA256"
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" \
@@ -158,7 +199,6 @@ 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
"$ROOT/scripts/pack_love.sh" \
--output "$temp_dir/game.love" \
--listing "$temp_dir/love-listing.txt" \
+10
View File
@@ -8,6 +8,8 @@ image="${1:?usage: verify_appimage.sh <AppImage>}"
[ -f "$image" ] || { echo "::error::no such AppImage: $image"; exit 1; }
image="$(cd "$(dirname "$image")" && pwd)/$(basename "$image")"
verify_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../luajit" && pwd)"
workdir="$(mktemp -d)"
trap 'rm -rf "$workdir"' EXIT
cd "$workdir"
@@ -20,6 +22,14 @@ for required in AppRun bin/love game.love lib/liblove-11.5.so; do
|| { echo "::error::AppImage is missing $required"; exit 1; }
done
# The pinned LuaJIT, not bullseye's 2.1.0-beta3 (segfaults with the JIT on).
[ -f "squashfs-root/lib/libluajit-5.1.so.2" ] \
|| { echo "::error::AppImage is missing lib/libluajit-5.1.so.2"; exit 1; }
bash "$verify_dir/verify_luajit.sh" squashfs-root/lib/libluajit-5.1.so.2 \
|| { echo "::error::bundled LuaJIT is not the pinned build"; exit 1; }
[ -s "squashfs-root/LuaJIT-COPYRIGHT" ] \
|| { echo "::error::AppImage is missing LuaJIT-COPYRIGHT"; exit 1; }
bridge="squashfs-root/liblibrashader_bridge.so"
if [ "${SHADERFX_BRIDGE_REQUIRED:-}" = "1" ] && [ ! -e "$bridge" ]; then
echo "::error::AppImage is missing liblibrashader_bridge.so"
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
# Build the pinned LuaJIT for aarch64 Linux inside a debian:bullseye container.
#
# Usage: scripts/luajit/build_luajit.sh
# Prints the path of the library on stdout; progress goes to stderr.
# Output: .bazinga/cache/luajit/<commit>-<recipe hash>-<image digest>/{libluajit-5.1.so.2,COPYRIGHT}
#
# Must run on an aarch64 host (docker or podman). Emulated (qemu) aarch64
# builds have miscompiled LuaJIT before, so other hosts are refused.
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$HERE/../.." && pwd)"
# shellcheck source=pins.sh
. "$HERE/pins.sh"
say() { printf '\033[1;32m==>\033[0m %s\n' "$*" >&2; }
warn() { printf '\033[1;33mwarn:\033[0m %s\n' "$*" >&2; }
fail() { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }
# shellcheck source=../lib/fetch.sh
. "$ROOT/scripts/lib/fetch.sh"
case "$(uname -m)" in
aarch64|arm64) ;;
*) fail "LuaJIT must be built on an aarch64 host (got $(uname -m)); emulated builds are unreliable. Build on arm64 or pass GEN1RECOMP_LUAJIT_LIB to the zip scripts." ;;
esac
CACHE="$ROOT/.bazinga/cache/luajit"
# Keyed by the build recipe and the pinned image digest, so changing either rebuilds.
RECIPE_HASH="$(sha256_file "$HERE/compile_luajit.sh")"
IMAGE_DIGEST="${LUAJIT_BUILD_IMAGE##*sha256:}"
OUT_DIR="$CACHE/$LUAJIT_COMMIT-${RECIPE_HASH:0:12}-${IMAGE_DIGEST:0:12}"
OUT="$OUT_DIR/libluajit-5.1.so.2"
mkdir -p "$CACHE" "$OUT_DIR"
if [ -f "$OUT" ] && [ -s "$OUT_DIR/COPYRIGHT" ] \
&& "$HERE/verify_luajit.sh" "$OUT" >/dev/null 2>&1; then
say "LuaJIT $LUAJIT_COMMIT already built"
echo "$OUT"
exit 0
fi
# Pinned tarball (a cache hit must still match the pin). curl's progress goes
# to stderr, but keep stdout clean regardless: it carries only the lib path.
TARBALL="$CACHE/$LUAJIT_TARBALL"
download_pinned "$LUAJIT_URL" "$TARBALL" "$LUAJIT_SHA256" >&2
RT="$(container_runtime)" \
|| fail "docker or podman is required to build LuaJIT (or set GEN1RECOMP_LUAJIT_LIB for the zip scripts)"
STAGE="$(mktemp -d "$CACHE/stage.XXXXXX")"
trap 'rm -rf "$STAGE"' EXIT
mkdir -p "$STAGE/out"
say "building LuaJIT $LUAJIT_COMMIT in $RT ($LUAJIT_BUILD_IMAGE, arm64)"
"$RT" run --rm --platform linux/arm64 \
-e LUAJIT_VERSION_STRING="$LUAJIT_VERSION_STRING" \
-e LUAJIT_MAX_GLIBC="$LUAJIT_MAX_GLIBC" \
-e LUAJIT_APT_SOURCES="$LUAJIT_APT_SOURCES" \
-v "$HERE:/luajit-scripts:ro" \
-v "$TARBALL:/in/$LUAJIT_TARBALL:ro" \
-v "$STAGE/out:/out" \
"$LUAJIT_BUILD_IMAGE" bash -euo pipefail -c '
# Bullseye is EOL; same sources/apt.conf as scripts/linux-arm64/Dockerfile.
printf "%s\n" "$LUAJIT_APT_SOURCES" > /etc/apt/sources.list
rm -f /etc/apt/sources.list.d/*
printf "Acquire::Check-Valid-Until \"false\";\nAcquire::Retries \"5\";\nAcquire::http::Pipeline-Depth \"0\";\nAcquire::http::No-Cache \"true\";\n" > /etc/apt/apt.conf.d/99builder
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq build-essential binutils ca-certificates >/dev/null
bash /luajit-scripts/compile_luajit.sh "/in/'"$LUAJIT_TARBALL"'" /tmp/prefix
cp -L /tmp/prefix/lib/libluajit-5.1.so.2 /out/libluajit-5.1.so.2
cp /tmp/prefix/share/doc/luajit/COPYRIGHT /out/COPYRIGHT
' >&2 || fail "LuaJIT container build failed"
"$HERE/verify_luajit.sh" "$STAGE/out/libluajit-5.1.so.2" || fail "built library failed verification"
[ -s "$STAGE/out/COPYRIGHT" ] || fail "built COPYRIGHT is missing or empty"
# Install: COPYRIGHT first, library last, so the cache-hit test is atomic.
mv -f "$STAGE/out/COPYRIGHT" "$OUT_DIR/COPYRIGHT"
mv -f "$STAGE/out/libluajit-5.1.so.2" "$OUT"
say "built $OUT"
echo "$OUT"
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# Compile the pinned LuaJIT. Runs INSIDE an aarch64 Linux container
# (debian:bullseye + build-essential; binutils for readelf).
#
# Usage: compile_luajit.sh <tarball> <prefix>
# Env: LUAJIT_VERSION_STRING string the built library must contain (required)
# LUAJIT_MAX_GLIBC highest GLIBC_x.y symbol allowed (required)
#
# No -mcpu flag: the validated recipe is plain `make amalg` with GC64.
set -euo pipefail
fail() { printf 'error: %s\n' "$*" >&2; exit 1; }
[ $# -eq 2 ] || fail "usage: compile_luajit.sh <tarball> <prefix>"
TARBALL="$1"
PREFIX="$2"
[ -f "$TARBALL" ] || fail "tarball not found: $TARBALL"
: "${LUAJIT_VERSION_STRING:?LUAJIT_VERSION_STRING must be set}"
: "${LUAJIT_MAX_GLIBC:?LUAJIT_MAX_GLIBC must be set}"
SRC_TMP="$(mktemp -d)"
trap 'rm -rf "$SRC_TMP"' EXIT
tar -xzf "$TARBALL" -C "$SRC_TMP"
SRC="$(find "$SRC_TMP" -mindepth 1 -maxdepth 1 -type d -print -quit)"
[ -n "$SRC" ] || fail "tarball had no top-level directory"
cd "$SRC"
# Compile with PREFIX=/usr, install with the real prefix. LuaJIT bakes
# LUA_ROOT=$(PREFIX) into its default package.path/cpath at compile time, so
# compiling with the install prefix would leak a build-time directory
# (/tmp/prefix, /cache/prefix-...) into the shipped library. The top-level
# install only depends on src/luajit, so it does not recompile.
make -j"$(nproc)" amalg PREFIX=/usr XCFLAGS=-DLUAJIT_ENABLE_GC64 >&2
# Install into a staging root first (luajit.pc still says prefix=$PREFIX), so a
# failed check below never leaves a half-trusted tree in $PREFIX.
STAGE="$(mktemp -d)"
trap 'rm -rf "$SRC_TMP" "$STAGE"' EXIT
make install PREFIX="$PREFIX" DESTDIR="$STAGE" >&2
mkdir -p "$STAGE$PREFIX/share/doc/luajit"
cp COPYRIGHT "$STAGE$PREFIX/share/doc/luajit/COPYRIGHT"
LIB="$(readlink -f "$STAGE$PREFIX/lib/libluajit-5.1.so.2")"
[ -f "$LIB" ] || fail "missing $STAGE$PREFIX/lib/libluajit-5.1.so.2"
soname="$(readelf -d "$LIB" | sed -n 's/.*SONAME.*\[\(.*\)\]/\1/p')"
[ "$soname" = "libluajit-5.1.so.2" ] || fail "unexpected SONAME: $soname"
if grep -aqF "$PREFIX" "$LIB"; then
fail "library contains the build prefix '$PREFIX' (baked-in module path)"
fi
# Version, module path, ELF class/arch and glibc ceiling: the same checks the
# host-side gate runs (verify_luajit.sh needs only od, grep and awk).
bash "$(dirname "$0")/verify_luajit.sh" "$LIB" >&2
# COPYRIGHT goes in last: callers treat it as the "install finished" marker, so
# an interrupted copy never looks like a complete cached prefix.
mv "$STAGE$PREFIX/share/doc/luajit/COPYRIGHT" "$STAGE/COPYRIGHT"
mkdir -p "$PREFIX/share/doc/luajit"
cp -a "$STAGE$PREFIX/." "$PREFIX/"
cp "$STAGE/COPYRIGHT" "$PREFIX/share/doc/luajit/COPYRIGHT"
echo "luajit ok: staged and installed to $PREFIX" >&2
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Pinned LuaJIT for the aarch64 Linux builds (PortMaster zips, AppImage).
# Source this file; it only sets variables and has no side effects.
#
# Why: PortMaster's LOVE 11.5 runtime ships LuaJIT 2.1.0-beta3 (2017), which
# segfaulted 3/3 runs with the JIT on (TrimUI Brick). This pinned v2.1-branch
# commit ran a 26-minute JIT-on soak without a crash. See docs/linux-arm-sbc.md.
#
# The commit's .relver is already expanded, so the built library contains the
# string "LuaJIT 2.1.1788856981" (beta3 contains "LuaJIT 2.1.0-beta3").
# shellcheck disable=SC2034
LUAJIT_COMMIT="c6ffc141a8762b41703f9287d63d93622a13dd8f"
LUAJIT_URL="https://github.com/LuaJIT/LuaJIT/archive/$LUAJIT_COMMIT.tar.gz"
LUAJIT_SHA256="6e5fec07750add912e7c3eae0c194d24cd6d023714e1f04a0298a5b4819e4457"
LUAJIT_TARBALL="LuaJIT-$LUAJIT_COMMIT.tar.gz"
LUAJIT_VERSION_STRING="LuaJIT 2.1.1788856981"
# Built on debian:bullseye (glibc 2.31); the library must not need newer.
# The standalone build (build_luajit.sh) pins the image by digest so a re-tag
# cannot silently change the toolchain; the digest is part of its cache key.
# To re-pin: `docker buildx imagetools inspect debian:bullseye` -> the top-level
# "Digest:" line; put it here AND in the FROM line of
# scripts/linux-arm64/Dockerfile (the selftest checks the two are equal).
LUAJIT_BUILD_IMAGE="debian:bullseye@sha256:6f519a81440354a85eb592c5f32109ab80605f6b892455983a6f618bf87fabe9"
LUAJIT_MAX_GLIBC="2.31"
# apt sources for the build container (bullseye is EOL; security comes from a
# dated snapshot). Newline-separated; must match scripts/linux-arm64/Dockerfile
# so both LuaJIT builds see the same package state.
LUAJIT_APT_SOURCES="deb http://archive.debian.org/debian bullseye main
deb http://archive.debian.org/debian bullseye-updates main
deb http://snapshot.debian.org/archive/debian-security/20260831T000000Z bullseye-security main"
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# Shared PortMaster zip helpers for build-linux-arm-sbc.sh and build-rg34xxsp.sh.
# Source this file; it has no side effects at source time.
# The caller must define ROOT, say, warn and fail.
# shellcheck source=scripts/luajit/pins.sh
. "$ROOT/scripts/luajit/pins.sh"
# shellcheck source=scripts/lib/fetch.sh
. "$ROOT/scripts/lib/fetch.sh"
# shellcheck disable=SC2034
LOVE_VERSION="${LOVE_VERSION:-11.5}"
# Official PortMaster LÖVE 11.5 aarch64 runtime (small love stub + liblove).
# Pinned to the last PortMaster-GUI commit that touched runtimes/love_11.5, and
# every file is sha256-checked. libluajit is deliberately NOT taken from
# here: that one is LuaJIT 2.1.0-beta3 (2017), which crashes with the JIT on.
PM_RUNTIME_COMMIT="715f50fd277febf942a66d59ac31e5a9c36c2f69"
PM_RUNTIME_BASE="https://raw.githubusercontent.com/PortsMaster/PortMaster-GUI/$PM_RUNTIME_COMMIT/PortMaster/runtimes/love_${LOVE_VERSION}"
PM_SHA_LOVE="c6c64453ed6163c8aeb14d303bad1e4d9ecc4d628097b3c6420ada2fa6a8690f"
PM_SHA_LIBLOVE="a50c83729cb0e1c4d8389eaaa827f0a6e7f3362814d0504ca84074dd0e47c379"
PM_SHA_MODPLUG="026b2287c9d68f920cf541b68b94f1a691168e4bbf301a8793f4114808aa8f95"
PM_SHA_OGG="66015f344b53895affd3ff153f402097ebaa2add33246bea2d7fca27b682e07a"
# pm_fetch_runtime CACHE_DIR: download + sha256-check the 4 runtime files.
pm_fetch_runtime() {
local dir="$1"
download_pinned "$PM_RUNTIME_BASE/love.aarch64" "$dir/love.aarch64" "$PM_SHA_LOVE"
download_pinned "$PM_RUNTIME_BASE/libs.aarch64/liblove-11.5.so" "$dir/liblove-11.5.so" "$PM_SHA_LIBLOVE"
download_pinned "$PM_RUNTIME_BASE/libs.aarch64/libmodplug.so.1" "$dir/libmodplug.so.1" "$PM_SHA_MODPLUG"
download_pinned "$PM_RUNTIME_BASE/libs.aarch64/libogg.so.0" "$dir/libogg.so.0" "$PM_SHA_OGG"
}
# pm_select_luajit: a pinned modern build, never PortMaster's beta3. Either
# supplied via GEN1RECOMP_LUAJIT_LIB (CI release jobs) or built in a container.
# Sets LUAJIT_LIB and LUAJIT_COPYRIGHT.
pm_select_luajit() {
if [ -n "${GEN1RECOMP_LUAJIT_LIB:-}" ]; then
LUAJIT_LIB="$GEN1RECOMP_LUAJIT_LIB"
[ -f "$LUAJIT_LIB" ] || fail "GEN1RECOMP_LUAJIT_LIB does not exist: $LUAJIT_LIB"
LUAJIT_COPYRIGHT="$(dirname "$LUAJIT_LIB")/COPYRIGHT"
[ -s "$LUAJIT_COPYRIGHT" ] || LUAJIT_COPYRIGHT="${GEN1RECOMP_LUAJIT_COPYRIGHT:-}"
[ -s "$LUAJIT_COPYRIGHT" ] \
|| fail "LuaJIT COPYRIGHT not found next to $LUAJIT_LIB; set GEN1RECOMP_LUAJIT_COPYRIGHT"
else
say "building pinned LuaJIT $LUAJIT_COMMIT"
LUAJIT_LIB="$("$ROOT/scripts/luajit/build_luajit.sh")" \
|| fail "LuaJIT build failed (needs an aarch64 host with docker/podman, or set GEN1RECOMP_LUAJIT_LIB)"
LUAJIT_COPYRIGHT="$(dirname "$LUAJIT_LIB")/COPYRIGHT"
fi
[ -f "$LUAJIT_LIB" ] && [ -s "$LUAJIT_COPYRIGHT" ] || fail "LuaJIT library or COPYRIGHT missing"
"$ROOT/scripts/luajit/verify_luajit.sh" "$LUAJIT_LIB" || fail "LuaJIT library failed verification: $LUAJIT_LIB"
}
# pm_stage_luajit LIBS_DIR LICENSES_DIR
pm_stage_luajit() {
: "${LUAJIT_LIB:?call pm_select_luajit before pm_stage_luajit}"
: "${LUAJIT_COPYRIGHT:?call pm_select_luajit before pm_stage_luajit}"
cp "$LUAJIT_LIB" "$1/libluajit-5.1.so.2"
cp "$LUAJIT_COPYRIGHT" "$2/LuaJIT-COPYRIGHT"
}
# pm_verify_zip ZIP PORT_DIR_NAME WORK_DIR: verify the LuaJIT that actually
# landed in the zip, not just the input file.
pm_verify_zip() {
local zip="$1" port="$2" work="$3" vdir
vdir="$(mktemp -d "$work/verify.XXXXXX")"
unzip -q "$zip" -d "$vdir"
"$ROOT/scripts/luajit/verify_luajit.sh" "$vdir/$port/libs.aarch64/libluajit-5.1.so.2" \
|| { rm -rf "$vdir"; fail "zip LuaJIT verification failed"; }
[ -s "$vdir/$port/licenses/LuaJIT-COPYRIGHT" ] \
|| { rm -rf "$vdir"; fail "zip is missing licenses/LuaJIT-COPYRIGHT"; }
rm -rf "$vdir"
}
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env bash
# Offline test of scripts/luajit/portmaster_runtime.sh (no docker, no network).
# Usage: bash scripts/luajit/selftest_portmaster_runtime.sh
# Each case runs in a subshell with a stub fail() that exits that subshell, so
# failures can be asserted.
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$HERE/../.." && pwd)"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT
# shellcheck source=pins.sh
. "$HERE/pins.sh"
say() { :; }
warn() { :; }
fail() { printf 'stub fail: %s\n' "$*" >&2; exit 1; }
# shellcheck source=portmaster_runtime.sh
. "$HERE/portmaster_runtime.sh"
bad() { printf 'FAIL: %s\n' "$*" >&2; exit 1; }
# expect_ok NAME CMD... / expect_fail NAME CMD...: run CMD in a subshell.
expect_ok() { local n="$1"; shift; ( "$@" ) >/dev/null 2>&1 || bad "$n: expected success"; }
expect_fail() { local n="$1"; shift; if ( "$@" ) >/dev/null 2>&1; then bad "$n: expected failure"; fi; }
# Synthetic aarch64 "library" that passes verify_luajit.sh.
lj_make() {
{ printf '\177ELF\002\001'; head -c 12 /dev/zero; printf '\xb7\x00'
printf '\0%s\0%s\0%s\0' "$LUAJIT_VERSION_STRING" '/usr/share/luajit-2.1/?.lua' GLIBC_2.17
} > "$1"
}
mkdir -p "$TMP/good" "$TMP/nocopy" "$TMP/emptycopy"
lj_make "$TMP/good/libluajit-5.1.so.2"
echo "LuaJIT license" > "$TMP/good/COPYRIGHT"
lj_make "$TMP/nocopy/libluajit-5.1.so.2"
lj_make "$TMP/emptycopy/libluajit-5.1.so.2"
: > "$TMP/emptycopy/COPYRIGHT"
echo "other license" > "$TMP/alt-COPYRIGHT"
case_stage_without_select() { unset LUAJIT_LIB LUAJIT_COPYRIGHT; pm_stage_luajit "$TMP" "$TMP"; }
expect_fail "stage without select" case_stage_without_select
case_missing_lib() { GEN1RECOMP_LUAJIT_LIB="$TMP/nope.so" pm_select_luajit; }
expect_fail "missing GEN1RECOMP_LUAJIT_LIB" case_missing_lib
case_empty_copyright() {
GEN1RECOMP_LUAJIT_LIB="$TMP/emptycopy/libluajit-5.1.so.2" GEN1RECOMP_LUAJIT_COPYRIGHT= pm_select_luajit
}
expect_fail "empty COPYRIGHT" case_empty_copyright
case_no_copyright_anywhere() {
GEN1RECOMP_LUAJIT_LIB="$TMP/nocopy/libluajit-5.1.so.2" GEN1RECOMP_LUAJIT_COPYRIGHT= pm_select_luajit
}
expect_fail "no COPYRIGHT anywhere" case_no_copyright_anywhere
# Verification is really reached: a bad library is rejected.
printf 'not a library' > "$TMP/emptycopy/bad.so"
echo x > "$TMP/emptycopy/COPYRIGHT"
case_bad_lib() { GEN1RECOMP_LUAJIT_LIB="$TMP/emptycopy/bad.so" pm_select_luajit; }
expect_fail "bad library" case_bad_lib
case_env_copyright() {
export GEN1RECOMP_LUAJIT_LIB="$TMP/nocopy/libluajit-5.1.so.2"
export GEN1RECOMP_LUAJIT_COPYRIGHT="$TMP/alt-COPYRIGHT"
pm_select_luajit
[ "$LUAJIT_LIB" = "$GEN1RECOMP_LUAJIT_LIB" ] || exit 1
[ "$LUAJIT_COPYRIGHT" = "$TMP/alt-COPYRIGHT" ] || exit 1
}
expect_ok "COPYRIGHT via env" case_env_copyright
# Stage + zip round trip.
build_tree() { # $1 = tree root
mkdir -p "$1/port/libs.aarch64" "$1/port/licenses"
GEN1RECOMP_LUAJIT_LIB="$TMP/good/libluajit-5.1.so.2" pm_select_luajit
pm_stage_luajit "$1/port/libs.aarch64" "$1/port/licenses"
}
case_stage() {
build_tree "$TMP/tree"
cmp "$TMP/good/libluajit-5.1.so.2" "$TMP/tree/port/libs.aarch64/libluajit-5.1.so.2"
cmp "$TMP/good/COPYRIGHT" "$TMP/tree/port/licenses/LuaJIT-COPYRIGHT"
}
expect_ok "stage copies both files" case_stage
command -v zip >/dev/null && command -v unzip >/dev/null || { echo "zip/unzip missing; skipping zip cases" >&2; echo "selftest_portmaster_runtime: ok (zip cases skipped)"; exit 0; }
( cd "$TMP/tree" && zip -q -r "$TMP/good.zip" port )
rm "$TMP/tree/port/licenses/LuaJIT-COPYRIGHT"
( cd "$TMP/tree" && zip -q -r "$TMP/nocopy.zip" port )
mkdir -p "$TMP/work"
expect_ok "verify good zip" pm_verify_zip "$TMP/good.zip" port "$TMP/work"
expect_fail "verify zip without COPYRIGHT" pm_verify_zip "$TMP/nocopy.zip" port "$TMP/work"
echo "selftest_portmaster_runtime: ok"
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
# Host-side check of a built libluajit-5.1.so.2 (works on macOS and Linux, no
# readelf needed). Fails if the library is not the pinned LuaJIT, is the old
# 2.1.0-beta3, or needs a glibc newer than the pin allows.
#
# Usage: scripts/luajit/verify_luajit.sh <libluajit-5.1.so.2>
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
# shellcheck source=pins.sh
. "$HERE/pins.sh"
fail() { printf 'error: %s\n' "$*" >&2; exit 1; }
[ $# -eq 1 ] || fail "usage: verify_luajit.sh <libluajit-5.1.so.2>"
LIB="$1"
[ -f "$LIB" ] || fail "not a file: $LIB"
# 64-bit (byte 4 = 02), little-endian (byte 5 = 01), e_machine 0xb7 = aarch64.
hdr="$(od -An -tx1 -N20 "$LIB" | tr -d ' \n')"
[ "${hdr:0:8}" = "7f454c46" ] && [ "${hdr:8:2}" = "02" ] && [ "${hdr:10:2}" = "01" ] \
&& [ "${hdr:36:4}" = "b700" ] \
|| fail "$LIB is not a 64-bit little-endian aarch64 ELF"
grep -aqF "$LUAJIT_VERSION_STRING" "$LIB" \
|| fail "$LIB does not contain '$LUAJIT_VERSION_STRING'"
if grep -aq '2\.1\.0-beta' "$LIB"; then
fail "$LIB contains '2.1.0-beta' (old LuaJIT)"
fi
# Built with PREFIX=/usr; a build-prefix path here means a stale or wrong build.
grep -aqF '/usr/share/luajit-2.1/?.lua' "$LIB" \
|| fail "$LIB does not contain the default module path /usr/share/luajit-2.1/?.lua"
# Compare dotted versions numerically without relying on sort -V.
ver_gt() {
awk -v a="$1" -v b="$2" 'BEGIN {
na = split(a, x, "."); nb = split(b, y, ".")
n = na > nb ? na : nb
for (i = 1; i <= n; i++) {
if ((x[i] + 0) > (y[i] + 0)) exit 0
if ((x[i] + 0) < (y[i] + 0)) exit 1
}
exit 1
}'
}
maxg=""
while IFS= read -r v; do
v="${v#GLIBC_}"
[ -n "$v" ] || continue
if [ -z "$maxg" ] || ver_gt "$v" "$maxg"; then maxg="$v"; fi
done < <(grep -ao 'GLIBC_[0-9][0-9.]*' "$LIB" | sort -u)
[ -n "$maxg" ] || fail "$LIB has no GLIBC_ version strings"
if ver_gt "$maxg" "$LUAJIT_MAX_GLIBC"; then
fail "$LIB requires GLIBC_$maxg > $LUAJIT_MAX_GLIBC"
fi
echo "verify_luajit ok: '$LUAJIT_VERSION_STRING', max GLIBC=$maxg (limit $LUAJIT_MAX_GLIBC)" >&2