From 47e6adeb3d3059df9cdf2c874cb4723564cf837a Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sat, 25 Apr 2026 11:49:19 -0700 Subject: [PATCH] Vendor comma raylib in dependency package --- raylib/build.sh | 143 +++++++++++++++++++++++--------------- raylib/pyproject.toml | 2 +- raylib/raylib/__init__.py | 124 ++++++++++++++++++++++----------- raylib/raylib/build.py | 13 ++-- raylib/raylib/version.py | 2 +- raylib/setup.py | 34 +++++++-- 6 files changed, 204 insertions(+), 114 deletions(-) diff --git a/raylib/build.sh b/raylib/build.sh index d3c0c67..4cc636f 100755 --- a/raylib/build.sh +++ b/raylib/build.sh @@ -1,5 +1,8 @@ #!/usr/bin/env bash -set -e +set -euo pipefail + +export SOURCE_DATE_EPOCH=0 +export ZERO_AR_DATE=1 DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" cd "$DIR" @@ -7,30 +10,36 @@ cd "$DIR" INSTALL_DIR="$DIR/raylib/install" NJOBS="$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 2)" -CC="ccache ${CC:-cc}" - -# Detect platform: PLATFORM_COMMA for comma devices, PLATFORM_DESKTOP otherwise -RAYLIB_PLATFORM="${RAYLIB_PLATFORM:-PLATFORM_DESKTOP}" -if [ -f /TICI ]; then - RAYLIB_PLATFORM="PLATFORM_COMMA" +if command -v ccache >/dev/null 2>&1; then + CC="${CC:-ccache cc}" +else + CC="${CC:-cc}" fi -export RAYLIB_PLATFORM -# Install build dependencies -if [[ "$(uname)" == "Linux" ]]; then - if [ "$RAYLIB_PLATFORM" = "PLATFORM_COMMA" ]; then +is_linux() { + [[ "$(uname)" == "Linux" ]] +} + +install_linux_deps() { + local platform="$1" + if ! is_linux; then + return + fi + + if [ "$platform" = "PLATFORM_COMMA" ]; then # comma device: needs DRM/EGL/GLES headers (usually already present on AGNOS) - # apt may fail on devices due to read-only rootfs or package conflicts — that's OK - if command -v apt-get &>/dev/null; then + # apt may fail on devices due to read-only rootfs or package conflicts; that's OK. + if command -v apt-get >/dev/null 2>&1; then if [ "$(id -u)" -eq 0 ]; then apt-get update && apt-get install -y libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev || true else sudo apt-get update && sudo apt-get install -y libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev || true fi fi - elif [ "$RAYLIB_PLATFORM" = "PLATFORM_OFFSCREEN" ]; then - # offscreen (CI): needs EGL/GL dev packages (no X11) - if command -v apt-get &>/dev/null; then + elif [ "$platform" = "PLATFORM_OFFSCREEN" ]; then + if command -v dnf >/dev/null 2>&1; then + dnf install -y mesa-libEGL-devel mesa-libGL-devel libglvnd-opengl libglvnd-core-devel 2>/dev/null || true + elif command -v apt-get >/dev/null 2>&1; then if [ "$(id -u)" -eq 0 ]; then apt-get update && apt-get install -y libegl-dev libgl-dev else @@ -38,10 +47,9 @@ if [[ "$(uname)" == "Linux" ]]; then fi fi else - # desktop: needs X11/GL dev packages - if command -v dnf &>/dev/null; then + if command -v dnf >/dev/null 2>&1; then dnf install -y libX11-devel libXcursor-devel libXrandr-devel libXinerama-devel libXi-devel mesa-libGL-devel - elif command -v apt-get &>/dev/null; then + elif command -v apt-get >/dev/null 2>&1; then if [ "$(id -u)" -eq 0 ]; then apt-get update && apt-get install -y libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libgl-dev else @@ -49,55 +57,72 @@ if [[ "$(uname)" == "Linux" ]]; then fi fi fi -fi +} -# Clone and build raylib C library -RAYLIB_COMMIT="d9d7cc1353ec0f73c97e84ddf0973983d1ee25e2" +# RAYLIB_COMMIT matches the openpilot third_party/raylib pin this package replaces. +RAYLIB_COMMIT="${RAYLIB_COMMIT:-3425bd9d1fb292ede4d80f97a1f4f258f614cffc}" +RAYLIB_OFFSCREEN_COMMIT="${RAYLIB_OFFSCREEN_COMMIT:-d9d7cc1353ec0f73c97e84ddf0973983d1ee25e2}" +RAYGUI_COMMIT="${RAYGUI_COMMIT:-76b36b597edb70ffaf96f046076adc20d67e7827}" if [ ! -d "raylib-src/.git" ]; then rm -rf raylib-src - git clone --depth 1 -b platform-offscreen --no-tags https://github.com/commaai/raylib.git raylib-src + git clone --depth 1 -b master --no-tags https://github.com/commaai/raylib.git raylib-src fi -cd raylib-src -git fetch --depth 1 origin "$RAYLIB_COMMIT" -git reset --hard "$RAYLIB_COMMIT" - -cd src -make clean -make -j"$NJOBS" PLATFORM="$RAYLIB_PLATFORM" CC="${CC:-gcc}" - -cd "$DIR" - -# Install lib + headers rm -rf "$INSTALL_DIR" mkdir -p "$INSTALL_DIR"/{lib,include} -cp raylib-src/src/libraylib.a "$INSTALL_DIR/lib/" -cp raylib-src/src/raylib.h raylib-src/src/raymath.h raylib-src/src/rlgl.h "$INSTALL_DIR/include/" +if [ -n "${RAYLIB_VARIANTS:-}" ]; then + read -r -a VARIANTS <<< "$RAYLIB_VARIANTS" +elif [[ -f /TICI || -f /AGNOS ]]; then + VARIANTS=(comma) +elif is_linux && [[ "$(uname -m)" == "x86_64" ]]; then + VARIANTS=(desktop offscreen) +elif is_linux && [[ "$(uname -m)" == "aarch64" ]]; then + VARIANTS=(desktop comma) +else + VARIANTS=(desktop) +fi -# On x86_64 Linux, also build the offscreen variant for CI headless rendering -if [[ "$(uname)" == "Linux" && "$(uname -m)" == "x86_64" && "$RAYLIB_PLATFORM" != "PLATFORM_OFFSCREEN" ]]; then - echo "Building offscreen variant..." +build_variant() { + local variant="$1" + local platform="PLATFORM_DESKTOP" + local commit="$RAYLIB_COMMIT" - # Install EGL/GL dev packages needed for offscreen build + bundling - if command -v dnf &>/dev/null; then - dnf install -y mesa-libEGL-devel mesa-libGL-devel libglvnd-opengl libglvnd-core-devel 2>/dev/null || true - elif command -v apt-get &>/dev/null; then - if [ "$(id -u)" -eq 0 ]; then - apt-get update && apt-get install -y libegl-dev libgl-dev - else - sudo apt-get update && sudo apt-get install -y libegl-dev libgl-dev - fi + if [ "$variant" = "comma" ]; then + platform="PLATFORM_COMMA" + elif [ "$variant" = "offscreen" ]; then + platform="PLATFORM_OFFSCREEN" + commit="$RAYLIB_OFFSCREEN_COMMIT" fi - cd raylib-src/src - make clean - make -j"$NJOBS" PLATFORM=PLATFORM_OFFSCREEN CC="${CC:-gcc}" - cp libraylib.a "$INSTALL_DIR/lib/libraylib_offscreen.a" - cd "$DIR" + echo "Building raylib $variant ($platform)" + install_linux_deps "$platform" - # Bundle GLVND dispatchers so offscreen rendering works without extra system packages + cd "$DIR/raylib-src" + git fetch --depth 1 origin "$commit" + git reset --hard "$commit" + git clean -xdff . + + cd src + make clean + make -j"$NJOBS" PLATFORM="$platform" CC="$CC" + + mkdir -p "$INSTALL_DIR/lib/$variant" + cp libraylib.a "$INSTALL_DIR/lib/$variant/libraylib.a" + if [ "$variant" = "${VARIANTS[0]}" ]; then + cp raylib.h raymath.h rlgl.h "$INSTALL_DIR/include/" + cp libraylib.a "$INSTALL_DIR/lib/libraylib.a" + fi +} + +for variant in "${VARIANTS[@]}"; do + build_variant "$variant" +done + +cd "$DIR" + +if is_linux && [[ "$(uname -m)" == "x86_64" && " ${VARIANTS[*]} " == *" offscreen "* ]]; then MESA_DIR="$INSTALL_DIR/lib/mesa" mkdir -p "$MESA_DIR" ldconfig 2>/dev/null || true @@ -105,17 +130,21 @@ if [[ "$(uname)" == "Linux" && "$(uname -m)" == "x86_64" && "$RAYLIB_PLATFORM" ! src="$(ldconfig -p 2>/dev/null | grep "$lib" | grep -E 'x86.64|libc6,' | awk '{print $NF}' | head -1)" if [ -n "$src" ] && [ -f "$src" ]; then cp -L "$src" "$MESA_DIR/" - # Create unversioned symlink for the linker base="${lib%%.so.*}" ln -sf "$lib" "$MESA_DIR/${base}.so" fi done fi -# Download raygui header -RAYGUI_COMMIT="76b36b597edb70ffaf96f046076adc20d67e7827" curl -fsSLo "$INSTALL_DIR/include/raygui.h" \ "https://raw.githubusercontent.com/raysan5/raygui/$RAYGUI_COMMIT/src/raygui.h" +cat > "$INSTALL_DIR/build-info.txt" <=1.17.1"] diff --git a/raylib/raylib/__init__.py b/raylib/raylib/__init__.py index cbcc4cc..5c856fb 100644 --- a/raylib/raylib/__init__.py +++ b/raylib/raylib/__init__.py @@ -1,71 +1,111 @@ +import importlib import os import platform as _platform DIR = os.path.join(os.path.dirname(__file__), "install") -LIB_DIR = os.path.join(DIR, "lib") +LIB_ROOT = os.path.join(DIR, "lib") INCLUDE_DIR = os.path.join(DIR, "include") +BUILD_INFO = os.path.join(DIR, "build-info.txt") + +_PLATFORM_BY_VARIANT = { + "comma": "PLATFORM_COMMA", + "desktop": "PLATFORM_DESKTOP", + "offscreen": "PLATFORM_OFFSCREEN", +} -def _detect_platform(): - """Auto-detect the raylib platform. In CI on Linux x86_64, use offscreen EGL rendering.""" - explicit = os.environ.get("RAYLIB_PLATFORM", "") +def _normalize_variant(value): + if value in ("PLATFORM_COMMA", "comma"): + return "comma" + if value in ("PLATFORM_OFFSCREEN", "offscreen"): + return "offscreen" + if value in ("PLATFORM_DESKTOP", "desktop", ""): + return "desktop" + return value + + +def _detect_variant(): + explicit = os.environ.get("RAYLIB_VARIANT") or os.environ.get("RAYLIB_PLATFORM", "") if explicit: - return explicit + return _normalize_variant(explicit) + if os.path.isfile("/TICI") or os.path.isfile("/AGNOS"): + return "comma" if os.environ.get("CI") and _platform.system() == "Linux" and _platform.machine() == "x86_64": - return "PLATFORM_OFFSCREEN" - return "" + return "offscreen" + return "desktop" + + +VARIANT = _detect_variant() +LIB_DIR = os.path.join(LIB_ROOT, VARIANT) +if not os.path.isfile(os.path.join(LIB_DIR, "libraylib.a")): + LIB_DIR = LIB_ROOT + + +def _module_name(variant): + return f"_raylib_cffi_{variant}" if variant else "_raylib_cffi" + + +def _import_cffi_module(): + names = [_module_name(VARIANT), "_raylib_cffi"] + seen = set() + for name in names: + if name in seen: + continue + seen.add(name) + try: + return importlib.import_module(f".{name}", __name__) + except ImportError: + continue + return None def smoketest(): assert os.path.isfile(os.path.join(LIB_DIR, "libraylib.a")), "libraylib.a not found" assert os.path.isfile(os.path.join(INCLUDE_DIR, "raylib.h")), "raylib.h not found" + assert os.path.isfile(os.path.join(INCLUDE_DIR, "raymath.h")), "raymath.h not found" + assert os.path.isfile(os.path.join(INCLUDE_DIR, "rlgl.h")), "rlgl.h not found" + assert os.path.isfile(os.path.join(INCLUDE_DIR, "raygui.h")), "raygui.h not found" + assert os.path.isfile(BUILD_INFO), "build-info.txt not found" + try: + import _cffi_backend # noqa: F401 + except ModuleNotFoundError: + return + assert _import_cffi_module() is not None, f"CFFI module for {VARIANT} not found" + import pyray # noqa: F401 -# Build CFFI extension on first import if not already compiled, -# or rebuild if the target platform changed since last build. def _ensure_cffi_built(): - import glob import subprocess import sys - pkg_dir = os.path.dirname(__file__) - platform_marker = os.path.join(pkg_dir, ".raylib_platform") - requested = _detect_platform() - # Export so build.py picks it up - if requested: - os.environ["RAYLIB_PLATFORM"] = requested - # Mesa llvmpipe for software rendering in headless CI - if requested == "PLATFORM_OFFSCREEN": - os.environ.setdefault("LIBGL_ALWAYS_SOFTWARE", "1") + if _import_cffi_module() is not None: + return - cffi_files = glob.glob(os.path.join(pkg_dir, "_raylib_cffi*")) + build_script = os.path.join(os.path.dirname(__file__), "build.py") + if not os.path.isfile(build_script) or not os.path.isfile(os.path.join(LIB_DIR, "libraylib.a")): + return - # Rebuild if platform changed - if cffi_files and requested: - built_for = open(platform_marker).read().strip() if os.path.isfile(platform_marker) else "" - if built_for != requested: - for f in cffi_files: - os.remove(f) - cffi_files = [] + env = os.environ.copy() + env["RAYLIB_VARIANT"] = VARIANT + env["RAYLIB_PLATFORM"] = _PLATFORM_BY_VARIANT.get(VARIANT, "PLATFORM_DESKTOP") + env["RAYLIB_CFFI_MODULE"] = f"raylib.{_module_name(VARIANT)}" + + try: + subprocess.check_call([sys.executable, build_script], cwd=os.path.dirname(os.path.dirname(__file__)), env=env) + except subprocess.CalledProcessError: + pass - if not cffi_files: - build_script = os.path.join(pkg_dir, "build.py") - if os.path.isfile(build_script) and os.path.isfile(os.path.join(LIB_DIR, "libraylib.a")): - try: - subprocess.check_call([sys.executable, build_script], cwd=os.path.dirname(pkg_dir)) - with open(platform_marker, "w") as f: - f.write(requested) - except subprocess.CalledProcessError: - pass _ensure_cffi_built() -# CFFI bindings (available when graphics libraries are present) -try: - from ._raylib_cffi import ffi, lib as rl - from raylib._raylib_cffi.lib import * # noqa: F403 +_cffi_module = _import_cffi_module() +if _cffi_module is not None: + ffi = _cffi_module.ffi + rl = _cffi_module.lib + for _name in dir(rl): + if not _name.startswith("_"): + globals()[_name] = getattr(rl, _name) + from raylib.colors import * # noqa: F403 from raylib.defines import * # noqa: F403 from .version import __version__ -except (ImportError, OSError): - pass diff --git a/raylib/raylib/build.py b/raylib/raylib/build.py index 74590f3..a37f675 100644 --- a/raylib/raylib/build.py +++ b/raylib/raylib/build.py @@ -11,8 +11,11 @@ from cffi import FFI HERE = os.path.dirname(os.path.abspath(__file__)) RAYLIB_INCLUDE_PATH = os.path.join(HERE, "install", "include") -RAYLIB_LIB_PATH = os.path.join(HERE, "install", "lib") +RAYLIB_VARIANT = os.getenv("RAYLIB_VARIANT", "") +RAYLIB_LIB_ROOT = os.path.join(HERE, "install", "lib") +RAYLIB_LIB_PATH = os.path.join(RAYLIB_LIB_ROOT, RAYLIB_VARIANT) if RAYLIB_VARIANT else RAYLIB_LIB_ROOT RAYLIB_PLATFORM = os.getenv("RAYLIB_PLATFORM", "") +RAYLIB_CFFI_MODULE = os.getenv("RAYLIB_CFFI_MODULE", "raylib._raylib_cffi") ffibuilder = FFI() @@ -105,13 +108,9 @@ def build_ffi(): extra_link_args.remove('-lGL') extra_link_args += ['-lGLESv2', '-lEGL', '-lgbm', '-ldrm'] elif RAYLIB_PLATFORM == "PLATFORM_OFFSCREEN": - # Use offscreen variant if available, otherwise fall back to default - offscreen_lib = os.path.join(RAYLIB_LIB_PATH, 'libraylib_offscreen.a') - if os.path.isfile(offscreen_lib): - extra_link_args[extra_link_args.index('-lraylib')] = '-lraylib_offscreen' extra_link_args.remove('-lGL') # Use bundled GLVND dispatchers if available, with RPATH for runtime - mesa_dir = os.path.join(RAYLIB_LIB_PATH, 'mesa') + mesa_dir = os.path.join(RAYLIB_LIB_ROOT, 'mesa') if os.path.isdir(mesa_dir): extra_link_args += [f'-L{mesa_dir}', f'-Wl,-rpath,$ORIGIN/install/lib/mesa'] extra_link_args += ['-lOpenGL', '-lEGL'] @@ -121,7 +120,7 @@ def build_ffi(): libraries = [] print("extra_link_args: " + str(extra_link_args)) - ffibuilder.set_source("raylib._raylib_cffi", + ffibuilder.set_source(RAYLIB_CFFI_MODULE, ffi_includes, py_limited_api=True, include_dirs=[RAYLIB_INCLUDE_PATH], diff --git a/raylib/raylib/version.py b/raylib/raylib/version.py index fe59f63..74e8e76 100644 --- a/raylib/raylib/version.py +++ b/raylib/raylib/version.py @@ -1 +1 @@ -__version__ = "5.5.0.8" \ No newline at end of file +__version__ = "5.5.0.10" diff --git a/raylib/setup.py b/raylib/setup.py index eb46389..c5c1eea 100644 --- a/raylib/setup.py +++ b/raylib/setup.py @@ -18,15 +18,37 @@ class BuildRaylib(build_py): def run(self): pkg_dir = os.path.dirname(os.path.abspath(__file__)) + raylib_pkg_dir = os.path.join(pkg_dir, "raylib") build_script = os.path.join(pkg_dir, "build.sh") subprocess.check_call(["bash", build_script], cwd=pkg_dir) - # Build CFFI extension so it's included in the wheel - cffi_so = glob.glob(os.path.join(pkg_dir, "raylib", "_raylib_cffi*")) - if not cffi_so: - build_cffi = os.path.join(pkg_dir, "raylib", "build.py") - if os.path.isfile(build_cffi): - subprocess.check_call([sys.executable, build_cffi], cwd=pkg_dir) + # Build CFFI extensions so they are included in the wheel, and make sure stale + # local extensions from another raylib platform do not leak into the wheel. + for path in glob.glob(os.path.join(raylib_pkg_dir, "_raylib_cffi*")): + os.remove(path) + + build_cffi = os.path.join(raylib_pkg_dir, "build.py") + if os.path.isfile(build_cffi): + variants = [ + os.path.basename(path) + for path in sorted(glob.glob(os.path.join(raylib_pkg_dir, "install", "lib", "*"))) + if os.path.isfile(os.path.join(path, "libraylib.a")) + ] + if not variants: + variants = [""] + + platform_by_variant = { + "comma": "PLATFORM_COMMA", + "desktop": "PLATFORM_DESKTOP", + "offscreen": "PLATFORM_OFFSCREEN", + "": os.environ.get("RAYLIB_PLATFORM", ""), + } + for variant in variants: + env = os.environ.copy() + env["RAYLIB_VARIANT"] = variant + env["RAYLIB_PLATFORM"] = platform_by_variant.get(variant, "PLATFORM_DESKTOP") + env["RAYLIB_CFFI_MODULE"] = f"raylib._raylib_cffi_{variant}" if variant else "raylib._raylib_cffi" + subprocess.check_call([sys.executable, build_cffi], cwd=pkg_dir, env=env) super().run()