111 lines
3.9 KiB
Bash
Executable File
111 lines
3.9 KiB
Bash
Executable File
#!/bin/bash
|
|
# Bundles a fully self-contained Python + GTK4 + gtk4-layer-shell + pycairo
|
|
# runtime into $1, closing over the *entire* shared-library dependency graph
|
|
# via ldd (not just evdev/python-xlib like the original build). This is for
|
|
# targets like NixOS that have no FHS /usr/bin/python3 or system GTK4 on
|
|
# PATH at all, so relying on "the host probably has this" doesn't hold.
|
|
#
|
|
# Includes the target's own ld.so + libc so it doesn't depend on the host
|
|
# glibc version either - AppRun invokes that ld.so explicitly with
|
|
# --library-path instead of a normal dynamic-linked exec.
|
|
set -euo pipefail
|
|
|
|
RT="$1"
|
|
mkdir -p "${RT}/lib" "${RT}/bin" "${RT}/typelibs"
|
|
|
|
PYBIN="$(python3 -c 'import sys; print(sys.executable)')"
|
|
STDLIB="$(python3 -c 'import sysconfig; print(sysconfig.get_path("stdlib"))')"
|
|
|
|
echo "Bundling python3 (${PYBIN}) + stdlib (${STDLIB})..."
|
|
cp "${PYBIN}" "${RT}/bin/python3"
|
|
mkdir -p "${RT}/python-lib"
|
|
cp -r "${STDLIB}" "${RT}/python-lib/"
|
|
|
|
DIST_PKGS="/usr/lib/python3/dist-packages"
|
|
SEEDS=(
|
|
"${PYBIN}"
|
|
"${DIST_PKGS}/gi/_gi.cpython-313-x86_64-linux-gnu.so"
|
|
"${DIST_PKGS}/gi/_gi_cairo.cpython-313-x86_64-linux-gnu.so"
|
|
"${DIST_PKGS}/cairo/_cairo.cpython-313-x86_64-linux-gnu.so"
|
|
"/usr/lib/x86_64-linux-gnu/libgtk-4.so.1"
|
|
"/usr/lib/x86_64-linux-gnu/libgtk4-layer-shell.so.0"
|
|
)
|
|
|
|
echo "Copying gi, cairo, evdev, Xlib Python packages..."
|
|
cp -r "${DIST_PKGS}/gi" "${RT}/"
|
|
cp -r "${DIST_PKGS}/cairo" "${RT}/"
|
|
cp -r "${DIST_PKGS}/evdev" "${RT}/"
|
|
cp -r "${DIST_PKGS}/Xlib" "${RT}/"
|
|
for so in "${DIST_PKGS}"/evdev/*.so; do
|
|
SEEDS+=("$so")
|
|
done
|
|
|
|
echo "Resolving full shared-library closure via ldd..."
|
|
declare -A SEEN
|
|
QUEUE=("${SEEDS[@]}")
|
|
while [ "${#QUEUE[@]}" -gt 0 ]; do
|
|
f="${QUEUE[0]}"
|
|
QUEUE=("${QUEUE[@]:1}")
|
|
realf="$(readlink -f "$f")"
|
|
fname="$(basename "$f")"
|
|
realname="$(basename "$realf")"
|
|
|
|
# ldd/DT_NEEDED reference libraries by their SONAME (e.g.
|
|
# libgirepository-1.0.so.1), which on Debian is often just a symlink to a
|
|
# fully-versioned real file (libgirepository-1.0.so.1.0.0). readlink -f
|
|
# collapses that symlink, so without this alias the dynamic linker can't
|
|
# find the file under the name it's actually asked for at runtime.
|
|
if [ "$fname" != "$realname" ] && [ ! -e "${RT}/lib/${fname}" ]; then
|
|
ln -s "$realname" "${RT}/lib/${fname}"
|
|
fi
|
|
|
|
[ -n "${SEEN[$realf]:-}" ] && continue
|
|
SEEN["$realf"]=1
|
|
cp -n "$realf" "${RT}/lib/${realname}" 2>/dev/null || true
|
|
|
|
for tok in $(ldd "$realf" 2>/dev/null | grep -o '/[^ ]*'); do
|
|
QUEUE+=("$tok")
|
|
done
|
|
done
|
|
echo "Bundled ${#SEEN[@]} shared libraries."
|
|
|
|
echo "Resolving full typelib closure via GIRepository dependency graph..."
|
|
TYPELIB_DIR="/usr/lib/x86_64-linux-gnu/girepository-1.0"
|
|
# Hardcoding the namespace list by hand previously missed transitive deps
|
|
# (Graphene-1.0, freetype2-2.0, xlib-2.0 were absent, breaking `import gi`
|
|
# on Gtk.init() with "Typelib file for namespace 'Graphene' not found").
|
|
# Instead ask GIRepository itself what Gtk/GdkX11/GdkWayland/Gtk4LayerShell
|
|
# transitively require, so the list can't silently drift from reality.
|
|
NAMESPACES="$(python3 -c '
|
|
import gi
|
|
from gi.repository import GIRepository
|
|
r = GIRepository.Repository.get_default()
|
|
roots = [("Gtk", "4.0"), ("GdkX11", "4.0"), ("GdkWayland", "4.0"), ("Gtk4LayerShell", "1.0")]
|
|
seen = {}
|
|
stack = []
|
|
for ns, ver in roots:
|
|
r.require(ns, ver, 0)
|
|
seen[ns] = ver
|
|
stack.append(ns)
|
|
i = 0
|
|
while i < len(stack):
|
|
ns = stack[i]
|
|
i += 1
|
|
for d in r.get_immediate_dependencies(ns):
|
|
dep_ns, dep_ver = d.rsplit("-", 1)
|
|
if dep_ns not in seen:
|
|
r.require(dep_ns, dep_ver, 0)
|
|
seen[dep_ns] = dep_ver
|
|
stack.append(dep_ns)
|
|
for ns, ver in sorted(seen.items()):
|
|
print(f"{ns}-{ver}")
|
|
' 2>/dev/null)"
|
|
|
|
for name in ${NAMESPACES}; do
|
|
src="${TYPELIB_DIR}/${name}.typelib"
|
|
[ -f "$src" ] && cp "$src" "${RT}/typelibs/"
|
|
done
|
|
echo "Bundled typelibs: ${NAMESPACES}"
|
|
|
|
echo "Runtime bundle assembled in ${RT}"
|