mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 17:00:35 +02:00
A Wayland touch screen's keyboard types text events, which the menus' fields do not read: text input is started only for Steam's keyboard (SDL_ENABLE_STEAM_SCREEN_KEYBOARD). The sysroot's extraction writes nothing through a link that leads out of it. The three SDL versions (Windows, Android, the Linux sysroot) name each other. #222's test stubs the screen keyboard and passes the text field's new password argument.
321 lines
17 KiB
Python
321 lines
17 KiB
Python
#!/usr/bin/env python3
|
|
"""The 32-bit x86 system root of the portable Linux build (configure.py
|
|
--portable), and the SDL3 source it builds libSDL3.so.0 from.
|
|
|
|
An executable built against the build machine's glibc asks for the symbols
|
|
of that version (GLIBC_2.43 on an up-to-date Arch Linux) and starts on no
|
|
older system. The portable build is therefore compiled and linked against
|
|
Debian 11's i386 glibc 2.31, the baseline of the Steam Runtime 3 ("sniper"),
|
|
which SteamOS 3, the Steam Deck and every distribution since 2020 have, and
|
|
against the headers of the libraries SDL loads when it starts (X11, Wayland
|
|
and libdecor, PipeWire, PulseAudio, ALSA, D-Bus, udev), which it opens itself
|
|
(dlopen) and does not link to.
|
|
|
|
Each package comes from the first of these that has it: archive.debian.org,
|
|
which keeps every Debian release, deb.debian.org, and last
|
|
snapshot.debian.org, whose URLs (the archive as it was at a moment when the
|
|
package was in it) do not change. SDL's source comes from its GitHub release,
|
|
or libsdl.org's. Each download is tried a few times, and checked against the
|
|
SHA-256 digests here.
|
|
|
|
python tools/linux_sysroot.py <folder>
|
|
|
|
makes <folder>/sysroot and <folder>/SDL3 once, and writes <folder>/stamp
|
|
(the list of what it holds) last; a folder whose stamp matches is left as it
|
|
is. Python alone: no dpkg, no root.
|
|
|
|
python tools/linux_sysroot.py --check-sdl <SDL_build_config.h>
|
|
|
|
stops the build if SDL was built without one of the drivers it needs.
|
|
"""
|
|
|
|
import hashlib
|
|
import http.client
|
|
import io
|
|
import os
|
|
import shutil
|
|
import sys
|
|
import tarfile
|
|
import time
|
|
import urllib.error
|
|
import urllib.request
|
|
from pathlib import Path
|
|
from typing import List, Tuple
|
|
|
|
DEBIAN_MIRRORS = [
|
|
"https://archive.debian.org/debian/",
|
|
"https://deb.debian.org/debian/",
|
|
]
|
|
|
|
# (and the last resort: the archive as snapshot.debian.org has it at the time
|
|
# given with each package, when the package was in it)
|
|
SNAPSHOT = "https://snapshot.debian.org/archive/debian/{}/"
|
|
|
|
# (path in the Debian archive, SHA-256, a snapshot.debian.org time that has
|
|
# it), all i386 from Debian 11 "bullseye" but PipeWire's (bullseye-backports':
|
|
# SDL needs 0.3.44 or later, bullseye has 0.3.19) and Wayland's and
|
|
# libdecor's (below). Only the C library's and libgcc's are linked to; the
|
|
# rest are SDL's headers, and the libraries it reads the names of.
|
|
PACKAGES: List[Tuple[str, str, str]] = [
|
|
# the C library and the compiler's runtime (crtbegin.o, libgcc)
|
|
("pool/main/g/glibc/libc6_2.31-13+deb11u11_i386.deb",
|
|
"8198abd817cbcd615ce3207fb924317b53cc0b522bfe57d689757de13fe00d66", "20240817T023924Z"),
|
|
("pool/main/g/glibc/libc6-dev_2.31-13+deb11u11_i386.deb",
|
|
"7d10b944894d2b6ccab08ccf51aae6cef6c5f5773f36696e6d9f34c21e0365b4", "20240817T023924Z"),
|
|
("pool/main/l/linux/linux-libc-dev_5.10.223-1_i386.deb",
|
|
"e57d6065a43e763e54a0a80afd66fa2371bcdc3b035f9f4463816e9c6fc26049", "20240815T030336Z"),
|
|
("pool/main/g/gcc-10/libgcc-s1_10.2.1-6_i386.deb",
|
|
"0a52edec5d626f8d9acf1998248d276760aff3950f44a308d27d4a2a488fe18d", "20210111T024320Z"),
|
|
("pool/main/g/gcc-10/libgcc-10-dev_10.2.1-6_i386.deb",
|
|
"64a544dcf5463225603cb025a05c7b79dea9876004c09d81af2bc31ffe0ccc1e", "20210111T024320Z"),
|
|
# SDL's X11 video driver
|
|
("pool/main/libx/libx11/libx11-6_1.7.2-1+deb11u2_i386.deb",
|
|
"a0307da50139370781beac6fe6d3b173493af11d0a35c43c053c539d5d67969c", "20231012T085604Z"),
|
|
("pool/main/libx/libx11/libx11-dev_1.7.2-1+deb11u2_i386.deb",
|
|
"6674c5517140f65addb055b2fcbab42e83c299555ecf516daf40758f938cf2d0", "20231012T085604Z"),
|
|
("pool/main/x/xorgproto/x11proto-dev_2020.1-1_all.deb",
|
|
"d5568d587d9ad2664c34c14b0ac538ccb3c567e126ee5291085a8de704a565f5", "20200415T024424Z"),
|
|
# (xcb/xcb.h, for SDL's Vulkan headers)
|
|
("pool/main/libx/libxcb/libxcb1-dev_1.14-3_i386.deb",
|
|
"b1d522ad25e5d4f208c73c2f20e6c918a869d11171835b7f792f487ff367d788", "20210201T212616Z"),
|
|
("pool/main/libx/libxext/libxext6_1.3.3-1.1_i386.deb",
|
|
"b93e2d194c4d0aa5ca0c056883787b1fac35941f5ce9ec4fa6baedfba4cdb5a9", "20201218T204051Z"),
|
|
("pool/main/libx/libxext/libxext-dev_1.3.3-1.1_i386.deb",
|
|
"0f2ca4c3c5e74e6ce6c99da60cbf1a66de1665a5703ddfb725a5855cc9ef7281", "20201218T204051Z"),
|
|
("pool/main/libx/libxcursor/libxcursor1_1.2.0-2_i386.deb",
|
|
"593d4ac35460cc397d1fa96d251b62c26eb3f807b2e0bfe21dc33abef68811eb", "20190714T093329Z"),
|
|
("pool/main/libx/libxcursor/libxcursor-dev_1.2.0-2_i386.deb",
|
|
"c3db4042a513952624d6f06485599f1c84d4095ebfaa753ddc243fa07f4bf81c", "20190714T093329Z"),
|
|
("pool/main/libx/libxi/libxi6_1.7.10-1_i386.deb",
|
|
"004182ad1167319b5153fa736a41b646806586739db93e6a06db857ff8553505", "20200603T030403Z"),
|
|
("pool/main/libx/libxi/libxi-dev_1.7.10-1_i386.deb",
|
|
"7a1b570d9bdf70d7e79e8b03a87c48cbb40a68ed672920f6170c67ec270edfc2", "20200603T030403Z"),
|
|
("pool/main/libx/libxrandr/libxrandr2_1.5.1-1_i386.deb",
|
|
"b9a2d67526ee1a6cd4eda834534a21bd4a5a3c795a4945214553933560d21ce2", "20161207T032837Z"),
|
|
("pool/main/libx/libxrandr/libxrandr-dev_1.5.1-1_i386.deb",
|
|
"bffbcd2b6eb5459aebada2adba44b32415cfdb8e27e2ee736b908e4c29f03360", "20161207T032837Z"),
|
|
("pool/main/libx/libxfixes/libxfixes3_5.0.3-2_i386.deb",
|
|
"2250bbc75dcf0d73fc84b69150dcaefefc071ad2a28985a5a69abfeac2290f11", "20200414T204951Z"),
|
|
("pool/main/libx/libxfixes/libxfixes-dev_5.0.3-2_i386.deb",
|
|
"3c81c546f61ed7af6a6a30ad3709b75dc74a983dff8f00dcce30eaff5a8c18e1", "20200414T204951Z"),
|
|
("pool/main/libx/libxrender/libxrender1_0.9.10-1_i386.deb",
|
|
"0b71b3f530a1b039b64f74c0223b433bd73fe0164916b60b25f9f3f11a6b9bfb", "20161205T212728Z"),
|
|
("pool/main/libx/libxrender/libxrender-dev_0.9.10-1_i386.deb",
|
|
"7a4a1cbc6e2004c520dc5882e0367c8ce5c7094e07922e792992d1d27a5829a4", "20161205T212728Z"),
|
|
("pool/main/libx/libxss/libxss1_1.2.3-1_i386.deb",
|
|
"eb50d23f8e849a7f86a6313f3e48c5c1fbf6cf82ca34ea5ef32bc99ae980c64a", "20180906T152804Z"),
|
|
("pool/main/libx/libxss/libxss-dev_1.2.3-1_i386.deb",
|
|
"e3f4268c8c5ca5a1e7a2b549470f9eca58cf9974b0176a929dd2185026895e7e", "20180906T152804Z"),
|
|
# SDL's OpenGL (GL/gl.h, GL/glx.h)
|
|
("pool/main/libg/libglvnd/libgl-dev_1.3.2-1_i386.deb",
|
|
"1603066c9e658adbd71296b9a5db08d771c92900ac90035b9e210b9d7e2b7a3d", "20200729T210030Z"),
|
|
("pool/main/libg/libglvnd/libglx-dev_1.3.2-1_i386.deb",
|
|
"97c1367f7ecf6c7e103a0eb85e119b3e22d9d882d52587a53733ff93daa94c1d", "20200729T210030Z"),
|
|
# SDL's Wayland video driver: Debian 12's Wayland (1.21), whose headers
|
|
# match the protocol code that a recent wayland-scanner writes
|
|
# (wl_proxy_marshal_flags, Wayland 1.20)
|
|
("pool/main/w/wayland/libwayland-client0_1.21.0-1_i386.deb",
|
|
"1454b74db0b175bd4ef2b14ca6e66269e5d05e1bafa4fdaa6efa15424872061a", "20220709T031205Z"),
|
|
("pool/main/w/wayland/libwayland-cursor0_1.21.0-1_i386.deb",
|
|
"5ea0a94c004d546fc41e426a47269b4251af8a0e691db57a011d72f43c62d3b7", "20220709T031205Z"),
|
|
("pool/main/w/wayland/libwayland-egl1_1.21.0-1_i386.deb",
|
|
"0b73ffd5ddd6b3c6fbbd74dc0b5d8259614515e89e791fafe3472168f6b0149c", "20220709T031205Z"),
|
|
("pool/main/w/wayland/libwayland-dev_1.21.0-1_i386.deb",
|
|
"c5051589b50623bc71a0a54e006571740b066ec61476b38808ef5f93fc0c95d3", "20220709T031205Z"),
|
|
# (libdecor, the window decorations GNOME's Wayland asks clients to draw:
|
|
# Debian 12's, as it needs Wayland 1.20's headers)
|
|
("pool/main/libd/libdecor-0/libdecor-0-0_0.1.1-2_i386.deb",
|
|
"74b39bf96b3870899883c8e3b6a16bca75e8c5828614a7e1b9d061439ee6bb35", "20230211T151657Z"),
|
|
("pool/main/libd/libdecor-0/libdecor-0-dev_0.1.1-2_i386.deb",
|
|
"31a307669c759629853ae06d7898a4e6355a9c155278b2a1a482d583ca3eac66", "20230211T151657Z"),
|
|
("pool/main/libf/libffi/libffi-dev_3.3-6_i386.deb",
|
|
"a699fc6b1dbeff5f3633be8b3f8d31526b69d11bf435c5865e50ac249b98f94c", "20210221T031914Z"),
|
|
("pool/main/libx/libxkbcommon/libxkbcommon0_1.0.3-2_i386.deb",
|
|
"b6d8929647c0c182f63d1c886b7fe8b23674383b2585401b2377ff61d2fe670e", "20201126T153155Z"),
|
|
("pool/main/libx/libxkbcommon/libxkbcommon-dev_1.0.3-2_i386.deb",
|
|
"1a88ce18baafa1404452b451b0b3ce1a8eebf6204eedd498e38b34c5d6f42c44", "20201126T153155Z"),
|
|
("pool/main/libg/libglvnd/libegl1_1.3.2-1_i386.deb",
|
|
"aba3025fe2becdcf9ed7d3822a52cd98ba6921fd8ccdd7825ec5a1f99b437fc8", "20200729T210030Z"),
|
|
("pool/main/libg/libglvnd/libegl-dev_1.3.2-1_i386.deb",
|
|
"5073d41e5be4bffad4b19a5ebf93c2ad43b420d00417a778e76d84f831a47a66", "20200729T210030Z"),
|
|
# SDL's audio drivers
|
|
("pool/main/a/alsa-lib/libasound2_1.2.4-1.1_i386.deb",
|
|
"1e7da3fa4ba22b7079bceb18e8ce3494d9dced9b1cffe5c6bb9aae8e64ecc9f7", "20210104T204018Z"),
|
|
("pool/main/a/alsa-lib/libasound2-dev_1.2.4-1.1_i386.deb",
|
|
"7ea775aaa87bc4d391b800c5cbdcacdc298bb52d9a82dff31073860dc9737309", "20210104T204018Z"),
|
|
("pool/main/p/pulseaudio/libpulse0_14.2-2_i386.deb",
|
|
"c84c1a3dbd17355cdf6eaf23c2313db18faf3581ca7ec95cf3a2177d1c70a577", "20210227T024018Z"),
|
|
("pool/main/p/pulseaudio/libpulse-dev_14.2-2_i386.deb",
|
|
"c530edf490a67c2ece2bf54563872693d9f02ce6069e73b2b9b5372d965ef8e8", "20210227T024018Z"),
|
|
("pool/main/p/pipewire/libpipewire-0.3-0_0.3.65-2~bpo11+1_i386.deb",
|
|
"1d032b638081704393bc847e2e1f7a00c6894fa262ed0272fb7282962e8915a9", "20230217T033139Z"),
|
|
("pool/main/p/pipewire/libpipewire-0.3-dev_0.3.65-2~bpo11+1_i386.deb",
|
|
"5f67d3e5e435251a34e55a10b4834a794e6b48b83d6da39aab267b35ce9aba33", "20230217T033139Z"),
|
|
("pool/main/p/pipewire/libspa-0.2-dev_0.3.65-2~bpo11+1_i386.deb",
|
|
"5128545520aa6444af8c3f61c72e5f8d39ced7556ce07dfdedcbec291c4a5048", "20230217T033139Z"),
|
|
# the desktop's D-Bus (the screen saver, the system's settings) and
|
|
# udev (gamepads plugged in and out)
|
|
("pool/main/d/dbus/libdbus-1-3_1.12.28-0+deb11u1_i386.deb",
|
|
"7d8d30ca9cfa726f61e5b71dc6294ca3266147208d13cdec1fbefb217b0a9d25", "20230625T033524Z"),
|
|
("pool/main/d/dbus/libdbus-1-dev_1.12.28-0+deb11u1_i386.deb",
|
|
"8ca893c7e096f70fd87745744a042507bd335bd6c13d98ba57bb6ecfa1170210", "20230625T033524Z"),
|
|
("pool/main/s/systemd/libudev1_247.3-7+deb11u5_i386.deb",
|
|
"07891a18db3527472eaafb26e07ba26d3bfb926dbe2c01d3282e6b5fe61454cc", "20240616T023746Z"),
|
|
("pool/main/s/systemd/libudev-dev_247.3-7+deb11u5_i386.deb",
|
|
"634a5cc9d7bc226d75ced0414750e32b07e82531e8b2b091e41708d42978db3d", "20240616T023746Z"),
|
|
]
|
|
|
|
# SDL3's source release (the version of the other ports' SDL), from GitHub
|
|
# or libsdl.org
|
|
# (as tools/windows_build.py's SDL_VERSION and tools/android_build.py's SDL_TAG)
|
|
SDL_VERSION = "3.4.16"
|
|
SDL_URLS = [
|
|
f"https://github.com/libsdl-org/SDL/releases/download/release-{SDL_VERSION}/SDL3-{SDL_VERSION}.tar.gz",
|
|
f"https://www.libsdl.org/release/SDL3-{SDL_VERSION}.tar.gz",
|
|
]
|
|
SDL_SHA256 = "7322236cd12090c3eb40b9728be4d49c76f66ad17d04369584d4ecad5cf77c68"
|
|
|
|
|
|
# what SDL must have been built with (its SDL_build_config.h): the drivers a
|
|
# desktop needs, each loaded when SDL starts rather than linked to (libdecor
|
|
# too: without its library's name, SDL would link to it, and the Steam Deck
|
|
# has no 32-bit libdecor)
|
|
SDL_REQUIRED = [
|
|
"SDL_VIDEO_DRIVER_X11_DYNAMIC", "SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC", "HAVE_LIBDECOR_H",
|
|
"SDL_VIDEO_DRIVER_WAYLAND_DYNAMIC_LIBDECOR", "SDL_VIDEO_OPENGL_GLX",
|
|
"SDL_VIDEO_OPENGL_EGL", "SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC", "SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC",
|
|
"SDL_AUDIO_DRIVER_ALSA_DYNAMIC", "SDL_UDEV_DYNAMIC", "HAVE_DBUS_DBUS_H",
|
|
]
|
|
|
|
|
|
def check_sdl(build_config: Path) -> None:
|
|
"""stops (exit status 1) if SDL was built without one of SDL_REQUIRED,
|
|
as when the system root lacks a header SDL looks for"""
|
|
defined = {line.split()[1] for line in build_config.read_text().splitlines()
|
|
if line.startswith("#define ") and len(line.split()) > 2}
|
|
missing = [name for name in SDL_REQUIRED if name not in defined]
|
|
if missing:
|
|
sys.exit(f"SDL3 was built without {', '.join(missing)}: refer to {build_config.parent.parent.parent}"
|
|
"/CMakeFiles/CMakeConfigureLog.yaml")
|
|
|
|
|
|
def stamp_text() -> str:
|
|
"""what a complete folder's stamp holds"""
|
|
return "".join(f"{path} {digest}\n" for path, digest, _ in PACKAGES) + f"{SDL_URLS[0]} {SDL_SHA256}\n"
|
|
|
|
|
|
# (each URL's tries, a moment apart, before the next URL)
|
|
TRIES = 3
|
|
RETRY_SECONDS = 2
|
|
|
|
|
|
def download(urls: List[str], digest: str) -> bytes:
|
|
"""the file at the first of urls that answers, checked against digest"""
|
|
errors = []
|
|
for url in urls:
|
|
for attempt in range(1, TRIES + 1):
|
|
if attempt > 1:
|
|
time.sleep(RETRY_SECONDS * (attempt - 1))
|
|
try:
|
|
with urllib.request.urlopen(url, timeout=60) as response:
|
|
data = response.read()
|
|
# (a connection cut short is http.client.IncompleteRead, not an
|
|
# OSError)
|
|
except (OSError, http.client.HTTPException) as error:
|
|
errors.append(f"{url}: {error!r}")
|
|
# (the mirror does not have it: no use asking again)
|
|
if isinstance(error, urllib.error.HTTPError) and error.code == 404:
|
|
break
|
|
continue
|
|
actual = hashlib.sha256(data).hexdigest()
|
|
if actual == digest:
|
|
return data
|
|
errors.append(f"{url}: SHA-256 {actual}, expected {digest}")
|
|
raise RuntimeError("cannot download " + "; ".join(errors))
|
|
|
|
|
|
def deb_data(deb: bytes) -> tarfile.TarFile:
|
|
"""the data.tar.* member of a .deb (an ar archive)"""
|
|
if not deb.startswith(b"!<arch>\n"):
|
|
raise RuntimeError("not a Debian package")
|
|
offset = 8
|
|
while offset + 60 <= len(deb):
|
|
header = deb[offset:offset + 60]
|
|
name = header[:16].decode().strip().rstrip("/")
|
|
size = int(header[48:58].decode())
|
|
body = deb[offset + 60:offset + 60 + size]
|
|
if name.startswith("data.tar"):
|
|
return tarfile.open(fileobj=io.BytesIO(body), mode="r:*")
|
|
offset += 60 + size + (size & 1)
|
|
raise RuntimeError("a Debian package without data.tar")
|
|
|
|
|
|
# what the packages hold that the build does not need
|
|
UNUSED = (("usr", "share", "doc"), ("usr", "share", "man"), ("usr", "share", "locale"), ("usr", "share", "lintian"),
|
|
("usr", "share", "bug"), ("usr", "bin"), ("sbin",), ("usr", "sbin"))
|
|
|
|
|
|
def extract(archive: tarfile.TarFile, destination: Path, strip: int = 0) -> None:
|
|
"""an archive's files, folders and links into destination, nothing
|
|
outside it (no device files, no absolute or upward names, nothing
|
|
through a link of its own that leads out of it)"""
|
|
root = destination.resolve()
|
|
for member in archive.getmembers():
|
|
parts = [part for part in Path(member.name).parts if part not in (".", "/")][strip:]
|
|
if not parts or ".." in parts or any(tuple(parts[:len(unused)]) == unused for unused in UNUSED):
|
|
continue
|
|
target = destination.joinpath(*parts)
|
|
if not target.parent.resolve().is_relative_to(root):
|
|
continue
|
|
if member.isdir():
|
|
target.mkdir(parents=True, exist_ok=True)
|
|
elif member.isfile() or member.islnk():
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
if target.is_symlink():
|
|
target.unlink()
|
|
with archive.extractfile(member) as source, open(target, "wb") as output:
|
|
shutil.copyfileobj(source, output)
|
|
os.chmod(target, member.mode & 0o755 | 0o600)
|
|
elif member.issym():
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
if target.is_symlink() or target.exists():
|
|
target.unlink()
|
|
link = member.linkname
|
|
# (an absolute link, /lib/i386-linux-gnu/libudev.so.1, points
|
|
# into the system root instead of the build machine's)
|
|
if link.startswith("/"):
|
|
link = os.path.relpath(destination / link.lstrip("/"), target.parent)
|
|
os.symlink(link, target)
|
|
|
|
|
|
def make(folder: Path) -> None:
|
|
stamp = folder / "stamp"
|
|
if stamp.is_file() and stamp.read_text() == stamp_text():
|
|
return
|
|
for name in ("stamp", "sysroot", "SDL3"):
|
|
path = folder / name
|
|
if path.is_dir():
|
|
shutil.rmtree(path)
|
|
elif path.exists():
|
|
path.unlink()
|
|
sysroot = folder / "sysroot"
|
|
sysroot.mkdir(parents=True)
|
|
for path, digest, snapshot in PACKAGES:
|
|
print(f"Downloading {path.rsplit('/', 1)[-1]}", flush=True)
|
|
mirrors = DEBIAN_MIRRORS + [SNAPSHOT.format(snapshot)]
|
|
with deb_data(download([mirror + path for mirror in mirrors], digest)) as archive:
|
|
extract(archive, sysroot)
|
|
print(f"Downloading SDL3-{SDL_VERSION}.tar.gz", flush=True)
|
|
with tarfile.open(fileobj=io.BytesIO(download(SDL_URLS, SDL_SHA256)), mode="r:gz") as archive:
|
|
extract(archive, folder / "SDL3", strip=1)
|
|
stamp.write_text(stamp_text())
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) == 3 and sys.argv[1] == "--check-sdl":
|
|
check_sdl(Path(sys.argv[2]))
|
|
elif len(sys.argv) == 2:
|
|
make(Path(sys.argv[1]))
|
|
else:
|
|
sys.exit(__doc__)
|