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
11 changes: 10 additions & 1 deletion build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ if [[ -n "${BUILD_SH_IN_MANYLINUX:-}" ]]; then
if command -v dnf &>/dev/null; then
dnf install -y -q \
libX11-devel libXcursor-devel libXrandr-devel libXinerama-devel libXi-devel \
mesa-libGL-devel mesa-libEGL-devel mesa-libGLES-devel \
mesa-libGL-devel mesa-libEGL-devel mesa-libGLES-devel mesa-dri-drivers \
wayland-devel wayland-protocols-devel libxkbcommon-devel
fi
fi
Expand Down Expand Up @@ -83,6 +83,15 @@ python3 build_versions.py apply "$POST_N" */pyproject.toml
uv build --all-packages --wheel --out-dir dist --no-create-gitignore
restore_build_versions

if [[ -n "${BUILD_SH_IN_MANYLINUX:-}" ]]; then
mkdir -p dist/unrepaired/
mv dist/comma_deps_raylib-*.whl dist/unrepaired/
auditwheel repair \
--ldpaths "$ROOT_DIR/raylib/raylib/install/lib:/usr/lib64:/lib64" \
--wheel-dir dist/ dist/unrepaired/comma_deps_raylib-*.whl
bash "$ROOT_DIR/raylib/vendor_headless_gl.sh" dist/comma_deps_raylib-*.whl
fi

if [[ -n "${BUILD_SH_IN_MANYLINUX:-}" ]]; then
VENV_DIR="$ROOT_DIR/.venv-manylinux"
else
Expand Down
32 changes: 29 additions & 3 deletions raylib/build_cffi.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import os
import platform
import re
import shutil
import subprocess
import sys

Expand All @@ -15,7 +16,7 @@
RAYLIB_LIB_PATH = os.path.join(PACKAGE_DIR, "install", "lib")

sys.path.insert(0, PACKAGE_DIR)
from _backend import BACKEND_ARCHIVES, BACKEND_CFFI_MODULES, BACKEND_LINK_ARGS, detect_backend # noqa: E402
from _backend import BACKEND_ARCHIVES, BACKEND_CFFI_MODULES, BACKEND_LINK_ARGS, DESKTOP, detect_backend # noqa: E402
from validate_bindings import validate_static_bindings # noqa: E402

RAYLIB_BACKEND = detect_backend()
Expand All @@ -31,6 +32,26 @@ def _raylib_archive():
return archive


def _private_desktop_libs():
private_libs = {}
for soname in ("libGL.so.1", "libGLX.so.0", "libX11.so.6", "libXext.so.6"):
source = subprocess.check_output(["gcc", f"-print-file-name={soname}"], text=True).strip()
if source == soname or not os.path.isfile(source):
raise FileNotFoundError(f"{soname} not found")

private_soname = soname.replace(".so", "-comma.so", 1)
target = os.path.join(RAYLIB_LIB_PATH, private_soname)
shutil.copy2(source, target)
subprocess.check_call(["patchelf", "--set-soname", private_soname, target])
private_libs[soname] = target

for target in private_libs.values():
for soname, dependency in private_libs.items():
subprocess.check_call(["patchelf", "--replace-needed", soname, os.path.basename(dependency), target])

return private_libs["libGL.so.1"], private_libs["libX11.so.6"]


def pre_process_header(filename, remove_function_bodies=False):
print("Pre-processing " + filename)
with open(filename, "r") as f:
Expand Down Expand Up @@ -87,10 +108,15 @@ def build_ffi():
extra_compile_args = ["-Wno-error=incompatible-function-pointer-types"]
else:
print("BUILDING FOR LINUX")
backend_link_args = BACKEND_LINK_ARGS[RAYLIB_BACKEND]
if RAYLIB_BACKEND == DESKTOP and os.environ.get("AUDITWHEEL_PLAT"):
backend_link_args = _private_desktop_libs()

extra_link_args = [
raylib_archive,
'-lm', '-lpthread', '-lrt', '-ldl', '-latomic',
*BACKEND_LINK_ARGS[RAYLIB_BACKEND],
'-lm', '-lpthread', '-lrt', '-ldl',
'-Wl,-Bstatic', '-latomic', '-Wl,-Bdynamic',
*backend_link_args,
]
extra_compile_args = ["-Wno-incompatible-pointer-types"]

Expand Down
18 changes: 18 additions & 0 deletions raylib/raylib/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
import importlib
import json
import os


def _setup_headless_gl():
if os.environ.get("RAYLIB_BACKEND", "").lower() != "headless":
return
libs_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), "comma_deps_raylib.libs")
mesa_egl = os.path.join(libs_dir, "libEGL_mesa.so.0")
if os.path.isdir(libs_dir) and os.path.isfile(mesa_egl):
vendor_json = os.path.join(libs_dir, "egl_vendor.json")
with open(vendor_json, "w") as f:
json.dump({"file_format_version": "1.0.0", "ICD": {"library_path": "libEGL_mesa.so.0"}}, f)
os.environ["__EGL_VENDOR_LIBRARY_FILENAMES"] = vendor_json
os.environ.setdefault("LIBGL_DRIVERS_PATH", libs_dir)
os.environ["GALLIUM_DRIVER"] = "llvmpipe"

@therepanic therepanic Aug 5, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first I thought we could launch using softpipe since the wheel itself would be lighter (around 18 MB compressed), but I was VERY mistaken.

openpilot test on the GitHub Actions runner ran for an hour and a half before finishing: https://github.com/commaai/openpilot/actions/runs/30970235965/job/92192765773.

With libLLVM the wheel for amd64 weighs in at around 55 MB and 5min test runtime. I also considered supporting the lightweight raylib rlsw but the performance clearly wouldn't be better than llvmpipe.



_setup_headless_gl()

from ._backend import BACKEND_ARCHIVES, BACKEND_CFFI_MODULES, detect_backend, host_backends
from .version import __version__

Expand Down
12 changes: 10 additions & 2 deletions raylib/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import subprocess
import sys

from setuptools import setup
from setuptools import Distribution, setup
from setuptools.command.build_py import build_py
from setuptools.command.bdist_wheel import bdist_wheel

Expand Down Expand Up @@ -61,4 +61,12 @@ def get_tag(self):
return "py3", "none", plat_tag


setup(cmdclass={"build_py": BuildRaylib, "bdist_wheel": PlatformWheel})
class BinaryDistribution(Distribution):
def has_ext_modules(self):
return True


setup(
cmdclass={"build_py": BuildRaylib, "bdist_wheel": PlatformWheel},
distclass=BinaryDistribution,
)
65 changes: 65 additions & 0 deletions raylib/vendor_headless_gl.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/bin/bash
set -euo pipefail
WHEEL="$(realpath "$1")"
TMPDIR="$(mktemp -d)"
OUTDIR="$(mktemp -d)"
trap 'rm -rf "$TMPDIR" "$OUTDIR"' EXIT

cd "$TMPDIR"
unzip -q "$WHEEL"
L=comma_deps_raylib.libs

resolve() { ldconfig -p | awk -v n="$1" '$1==n {print $NF; exit}'; }

ROOTS="libEGL_mesa.so.0 swrast_dri.so"
DRI_DIR=/usr/lib64/dri

declare -A DONE
todo="$ROOTS"
while [ -n "$todo" ]; do
next=""
for soname in $todo; do
[ -n "${DONE[$soname]:-}" ] && continue
DONE[$soname]=1
if [ "$soname" = "swrast_dri.so" ]; then
src="$DRI_DIR/swrast_dri.so"
else
src=$(resolve "$soname")
fi
[ -f "$src" ] || continue
cp -L "$src" "$L/$soname"
for dep in $(readelf -d "$src" 2>/dev/null | awk '/NEEDED/{print $5}' | tr -d '[]'); do
case "$dep" in
libc.so.6|libm.so.6|libpthread.so.0|librt.so.1|libdl.so.2|ld-linux-x86-64.so.2)
continue;;
esac
next="$next $dep"
done
done
todo="$next"
done

for f in "$L"/*; do
patchelf --force-rpath --set-rpath '$ORIGIN' "$f"
done

OUT="$OUTDIR/$(basename "$WHEEL")"
python3 - "$OUT" <<'PY'
import base64, hashlib, sys, zipfile
from pathlib import Path
root = Path(".")
record = next(root.glob("*.dist-info/RECORD"))
rows = []
for f in sorted(root.rglob("*")):
if not f.is_file() or f == record:
continue
h = hashlib.sha256(f.read_bytes()).digest()
rows.append(f"{f.as_posix()},sha256={base64.urlsafe_b64encode(h).rstrip(b'=').decode()},{f.stat().st_size}")
rows.append(f"{record.as_posix()},,")
record.write_text("\n".join(rows) + "\n")
with zipfile.ZipFile(sys.argv[1], "w", zipfile.ZIP_DEFLATED) as z:
for f in sorted(root.rglob("*")):
if f.is_file():
z.write(f, f.as_posix())
PY
cp "$OUT" "$WHEEL"
1 change: 1 addition & 0 deletions test_wheels_in_image.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ docker run --rm -v "$PWD:/work" -w /work wheeltest bash -lc '
module="$(basename "$(dirname "$toml")" | tr "-" "_")"
python -c "import $module; $module.smoketest()" && echo "$module: OK"
done
RAYLIB_BACKEND=headless python -c "import raylib; raylib.smoketest()"
'

echo
Expand Down
Loading