Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 20 additions & 12 deletions linux/rocknix/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,9 @@ Confirmed on-device (ROCKNIX `next`, `OS_NAME="ROCKNIX"` in `/etc/os-release`):

### pygame

ROCKNIX ships Python 3.13 but **no `pip` and no `pygame`**. The bundle vendors a self-contained
`pygame-ce` (Community Edition) 2.5.7 cp313 aarch64 manylinux wheel (its own SDL 2.32.10 in
`pygame_ce.libs`, built with both the wayland and kmsdrm video drivers — no reliance on system
ROCKNIX ships a system Python but **no `pip` and no `pygame`**. The bundle vendors a
self-contained `pygame-ce` (Community Edition) 2.5.7 aarch64 manylinux wheel (its own SDL 2.32.10
in `pygame_ce.libs`, built with both the wayland and kmsdrm video drivers — no reliance on system
SDL). The package still imports as `pygame` (pygame-ce is API-compatible).

The vanilla `pygame` 2.6.1 wheel used before segfaulted inside SDL's native wayland/EGL init on
Expand All @@ -154,20 +154,28 @@ RK3326 devices with the `libmali` GPU blob (issue #55, and an earlier Discord re
device class run fine using `pygame-ce`/`pygame_sdl2` builds instead of the vanilla wheel, so we
switched the vendored build rather than adding more launch-script workarounds.

Which CPython the wheel has to match is not fixed: stable ROCKNIX ships 3.13, nightlies since
20260822 ship 3.14, and a bundle carrying only one of them dies at startup on the other with
`No module named 'pygame.base'` (issue #127), because the `.so` files are ABI-tagged and an
interpreter never sees extension modules built for another minor version. The vendor directory
therefore holds **every supported ABI at once**: `base.cpython-313-aarch64-linux-gnu.so` and
`base.cpython-314-aarch64-linux-gnu.so` sit side by side and each interpreter picks its own. The
wheels' pure-Python files and their `pygame_ce.libs` payload are byte-identical across ABIs of the
same release, so only the extra `.so` files add to the installer.

The vendored payload is **not** checked in (see `.gitignore`). Populate it before building:

```bash
# downloads a prebuilt cp313 aarch64 manylinux pygame-ce wheel, no build toolchain needed
cd linux/rocknix/vendor
pip download pygame-ce --platform manylinux_2_17_aarch64 --python-version 313 \
--implementation cp --abi cp313 --only-binary=:all: --no-deps -d .
unzip pygame_ce-*-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
# leaves vendor/pygame and vendor/pygame_ce.libs in place
./linux/rocknix/fetch_vendor.sh
```

> Note: the vendored `.so` files are cp313-specific, and the `pygame_ce.libs` directory name is
> baked into each `.so`'s RPATH (`$ORIGIN/../pygame_ce.libs`) — don't rename it. If ROCKNIX moves
> to a different Python minor version, re-vendor a matching wheel.
That downloads the prebuilt cp313 and cp314 wheels (no build toolchain needed) and merges them
into `vendor/pygame` + `vendor/pygame_ce.libs`. To support a further Python version, add it to
`PY_VERSIONS` in `fetch_vendor.sh` and `REQUIRED_PY_VERSIONS` in `build_bundle.sh`; the build
refuses to run if a required ABI is missing from the vendor directory.

> Note: the `pygame_ce.libs` directory name is baked into each `.so`'s RPATH
> (`$ORIGIN/../pygame_ce.libs`), so don't rename it.

## Build

Expand Down
16 changes: 14 additions & 2 deletions linux/rocknix/build_bundle.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,24 @@ VERSION="${RAOFFLINEPROXY_APP_VERSION:-1.11.1-alpha1}"
INSTALLER_PATH="${DIST_DIR}/RAOfflineProxy-Rocknix-v${VERSION}-Install.sh"
TEMP_TARBALL="${DIST_DIR}/.raofflineproxy-rocknix-bundle.tar.gz"

# ROCKNIX builds ship different CPython minor versions (3.13 through nightly's
# 3.14), and pygame's extension modules only import into the interpreter whose
# ABI tag they carry, so the vendored pygame must cover every one of them.
REQUIRED_PY_VERSIONS=(313 314)

if [ ! -d "${SCRIPT_DIR}/vendor/pygame" ] || [ ! -d "${SCRIPT_DIR}/vendor/pygame_ce.libs" ]; then
echo "Missing vendored pygame-ce. Populate linux/rocknix/vendor/{pygame,pygame_ce.libs}" >&2
echo "from a pygame-ce cp313 aarch64 manylinux wheel before building." >&2
echo "Missing vendored pygame-ce. Run linux/rocknix/fetch_vendor.sh before building." >&2
exit 1
fi

for py_version in "${REQUIRED_PY_VERSIONS[@]}"; do
if [ ! -f "${SCRIPT_DIR}/vendor/pygame/base.cpython-${py_version}-aarch64-linux-gnu.so" ]; then
echo "Vendored pygame-ce has no cpython-${py_version} extension modules." >&2
echo "Re-run linux/rocknix/fetch_vendor.sh before building." >&2
exit 1
fi
done

TARGET="aarch64-linux-gnu.2.17" OUT_DIR="${SCRIPT_DIR}/native" \
"${LINUX_DIR}/build_rchash.sh"

Expand Down
49 changes: 49 additions & 0 deletions linux/rocknix/fetch_vendor.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash
# Downloads the ROCKNIX pygame vendor directory: prebuilt pygame-ce aarch64
# manylinux wheels, one per CPython ABI a ROCKNIX build may ship.
#
# ROCKNIX has no pip and no pygame, so the bundle carries its own. The wheels'
# extension modules are ABI-tagged (base.cpython-313-... vs
# base.cpython-314-...), so several ABIs live side by side in one pygame
# directory: each interpreter only imports the .so carrying its own tag. The
# pure-Python files and the pygame_ce.libs payload are byte-identical across
# ABIs of the same release, so the merge costs only the extra .so files.
#
# Usage:
# linux/rocknix/fetch_vendor.sh

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VENDOR_DIR="${SCRIPT_DIR}/vendor"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "${TMP_DIR}"' EXIT

PYGAME_CE_VERSION="2.5.7"
# Keep in sync with build_bundle.sh's REQUIRED_PY_VERSIONS.
PY_VERSIONS=(313 314)

rm -rf "${VENDOR_DIR}/pygame" "${VENDOR_DIR}/pygame_ce.libs"
mkdir -p "${VENDOR_DIR}"

for py_version in "${PY_VERSIONS[@]}"; do
abi="cp${py_version}"
echo "Downloading pygame-ce ${PYGAME_CE_VERSION} ${abi} aarch64..."
python3 -m pip download "pygame-ce==${PYGAME_CE_VERSION}" \
--platform manylinux_2_17_aarch64 --python-version "${py_version}" \
--implementation cp --abi "${abi}" --only-binary=:all: --no-deps \
-d "${TMP_DIR}" >/dev/null

# Each .so's RPATH is $ORIGIN/../pygame_ce.libs, so both directories have to
# land next to each other under vendor/ under those exact names.
unzip -oq "${TMP_DIR}/pygame_ce-${PYGAME_CE_VERSION}-${abi}-${abi}-"*aarch64.whl \
"pygame/*" "pygame_ce.libs/*" -d "${VENDOR_DIR}"
done

find "${VENDOR_DIR}/pygame" -name "__pycache__" -type d -prune -exec rm -rf {} +

echo ""
for py_version in "${PY_VERSIONS[@]}"; do
count="$(find "${VENDOR_DIR}/pygame" -name "*.cpython-${py_version}-aarch64-linux-gnu.so" | wc -l | tr -d ' ')"
echo "vendor/pygame contains ${count} cpython-${py_version} extension modules."
done