From c1dad54db743347e4aacb72dec5153028e41e76c Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 10 May 2026 11:29:34 -0700 Subject: [PATCH 01/10] update to raylib 6.0 --- raylib/build.sh | 64 ++++++++++++--------------------------- raylib/pyproject.toml | 4 +-- raylib/raylib/__init__.py | 9 ++---- raylib/raylib/build.py | 19 +++++------- raylib/raylib/version.py | 2 +- raylib/setup.py | 21 +++++++++---- setup.sh | 10 ++++-- 7 files changed, 57 insertions(+), 72 deletions(-) diff --git a/raylib/build.sh b/raylib/build.sh index d3c0c67..a22e950 100755 --- a/raylib/build.sh +++ b/raylib/build.sh @@ -14,6 +14,9 @@ RAYLIB_PLATFORM="${RAYLIB_PLATFORM:-PLATFORM_DESKTOP}" if [ -f /TICI ]; then RAYLIB_PLATFORM="PLATFORM_COMMA" fi +if [ "$RAYLIB_PLATFORM" = "PLATFORM_OFFSCREEN" ]; then + RAYLIB_PLATFORM="PLATFORM_MEMORY" +fi export RAYLIB_PLATFORM # Install build dependencies @@ -28,15 +31,9 @@ if [[ "$(uname)" == "Linux" ]]; then 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 - 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 - fi + elif [ "$RAYLIB_PLATFORM" = "PLATFORM_MEMORY" ]; then + # memory platform: software renderer, no window-system dev packages needed + true else # desktop: needs X11/GL dev packages if command -v dnf &>/dev/null; then @@ -52,20 +49,25 @@ if [[ "$(uname)" == "Linux" ]]; then fi # Clone and build raylib C library -RAYLIB_COMMIT="d9d7cc1353ec0f73c97e84ddf0973983d1ee25e2" +RAYLIB_COMMIT="dff603f4f122163900469e73d113deacd9ec9817" 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 remote set-url origin https://github.com/commaai/raylib.git 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}" +if [ "$RAYLIB_PLATFORM" = "PLATFORM_MEMORY" ]; then + make -j"$NJOBS" PLATFORM="$RAYLIB_PLATFORM" CC="${CC:-gcc}" CUSTOM_CFLAGS="${CUSTOM_CFLAGS:-} -fPIC" +else + make -j"$NJOBS" PLATFORM="$RAYLIB_PLATFORM" CC="${CC:-gcc}" +fi cd "$DIR" @@ -76,44 +78,18 @@ 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/" -# 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..." - - # 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 - fi - +# On x86_64 Linux, also build the memory variant for CI headless rendering +if [[ "$(uname)" == "Linux" && "$(uname -m)" == "x86_64" && "$RAYLIB_PLATFORM" != "PLATFORM_MEMORY" ]]; then + echo "Building memory variant..." cd raylib-src/src make clean - make -j"$NJOBS" PLATFORM=PLATFORM_OFFSCREEN CC="${CC:-gcc}" - cp libraylib.a "$INSTALL_DIR/lib/libraylib_offscreen.a" + make -j"$NJOBS" PLATFORM=PLATFORM_MEMORY CC="${CC:-gcc}" CUSTOM_CFLAGS="${CUSTOM_CFLAGS:-} -fPIC" + cp libraylib.a "$INSTALL_DIR/lib/libraylib_memory.a" cd "$DIR" - - # Bundle GLVND dispatchers so offscreen rendering works without extra system packages - MESA_DIR="$INSTALL_DIR/lib/mesa" - mkdir -p "$MESA_DIR" - ldconfig 2>/dev/null || true - for lib in libEGL.so.1 libOpenGL.so.0 libGLdispatch.so.0; do - 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" +RAYGUI_COMMIT="1e03efca48c50c5ea4b4a053d5bf04bad58d3e43" curl -fsSLo "$INSTALL_DIR/include/raygui.h" \ "https://raw.githubusercontent.com/raysan5/raygui/$RAYGUI_COMMIT/src/raygui.h" diff --git a/raylib/pyproject.toml b/raylib/pyproject.toml index 9bcfb1a..18204ae 100644 --- a/raylib/pyproject.toml +++ b/raylib/pyproject.toml @@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta" [project] name = "raylib" -version = "5.5.0.8" +version = "6.0.0.0" description = "raylib + pyray Python bindings (commaai fork)" requires-python = ">=3.8" -dependencies = ["cffi>=1.17.1"] +dependencies = ["cffi>=1.17.1", "setuptools>=64"] [tool.setuptools.packages.find] include = ["raylib*", "pyray*"] diff --git a/raylib/raylib/__init__.py b/raylib/raylib/__init__.py index cbcc4cc..794d79c 100644 --- a/raylib/raylib/__init__.py +++ b/raylib/raylib/__init__.py @@ -7,12 +7,12 @@ def _detect_platform(): - """Auto-detect the raylib platform. In CI on Linux x86_64, use offscreen EGL rendering.""" + """Auto-detect the raylib platform. In CI on Linux x86_64, use memory rendering.""" explicit = os.environ.get("RAYLIB_PLATFORM", "") if explicit: - return explicit + return "PLATFORM_MEMORY" if explicit == "PLATFORM_OFFSCREEN" else explicit if os.environ.get("CI") and _platform.system() == "Linux" and _platform.machine() == "x86_64": - return "PLATFORM_OFFSCREEN" + return "PLATFORM_MEMORY" return "" @@ -34,9 +34,6 @@ def _ensure_cffi_built(): # 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") cffi_files = glob.glob(os.path.join(pkg_dir, "_raylib_cffi*")) diff --git a/raylib/raylib/build.py b/raylib/raylib/build.py index 74590f3..684a59b 100644 --- a/raylib/raylib/build.py +++ b/raylib/raylib/build.py @@ -1,4 +1,4 @@ -# Based on commaai/raylib-python-cffi (commit ab0191f) +# Based on commaai/raylib-python-cffi (commit a32e910) # Modified to use local install paths and compile standalone (no cffi_modules) import re @@ -13,6 +13,8 @@ RAYLIB_INCLUDE_PATH = os.path.join(HERE, "install", "include") RAYLIB_LIB_PATH = os.path.join(HERE, "install", "lib") RAYLIB_PLATFORM = os.getenv("RAYLIB_PLATFORM", "") +if RAYLIB_PLATFORM == "PLATFORM_OFFSCREEN": + RAYLIB_PLATFORM = "PLATFORM_MEMORY" ffibuilder = FFI() @@ -104,17 +106,12 @@ def build_ffi(): if RAYLIB_PLATFORM == "PLATFORM_COMMA": 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' + elif RAYLIB_PLATFORM == "PLATFORM_MEMORY": + # Use memory/software variant if available, otherwise fall back to default + memory_lib = os.path.join(RAYLIB_LIB_PATH, 'libraylib_memory.a') + if os.path.isfile(memory_lib): + extra_link_args[extra_link_args.index('-lraylib')] = '-lraylib_memory' extra_link_args.remove('-lGL') - # Use bundled GLVND dispatchers if available, with RPATH for runtime - mesa_dir = os.path.join(RAYLIB_LIB_PATH, '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'] else: extra_link_args += ['-lX11'] extra_compile_args = ["-Wno-incompatible-pointer-types"] diff --git a/raylib/raylib/version.py b/raylib/raylib/version.py index fe59f63..2c91105 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__ = "6.0.0.0" diff --git a/raylib/setup.py b/raylib/setup.py index eb46389..c652781 100644 --- a/raylib/setup.py +++ b/raylib/setup.py @@ -1,6 +1,7 @@ import glob import os import platform +import shutil import subprocess import sys @@ -21,12 +22,20 @@ def run(self): 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 extension so it's included in the wheel. Always regenerate it + # after build.sh because the cached raylib source pin may have changed. + for old_cffi in glob.glob(os.path.join(pkg_dir, "raylib", "_raylib_cffi*")): + os.remove(old_cffi) + for modified_header in glob.glob(os.path.join(pkg_dir, "raylib", "*.modified")): + os.remove(modified_header) + 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) + + staged_pkg = os.path.join(self.build_lib, "raylib") + shutil.rmtree(os.path.join(staged_pkg, "install"), ignore_errors=True) + for old_cffi in glob.glob(os.path.join(staged_pkg, "_raylib_cffi*")): + os.remove(old_cffi) super().run() diff --git a/setup.sh b/setup.sh index ab72c06..bcd6c87 100755 --- a/setup.sh +++ b/setup.sh @@ -17,10 +17,16 @@ run_as_root() { if [ "$(uname)" = "Darwin" ]; then brew install nasm pkg-config ccache autoconf automake libtool elif command -v dnf &>/dev/null; then - dnf install -y nasm cmake gcc-c++ pkgconfig git perl-IPC-Cmd ccache autoconf automake libtool + dnf install -y \ + nasm cmake gcc-c++ pkgconfig git perl-IPC-Cmd ccache autoconf automake libtool \ + libX11-devel libXcursor-devel libXrandr-devel libXinerama-devel libXi-devel \ + mesa-libGL-devel libdrm-devel mesa-libgbm-devel mesa-libEGL-devel mesa-libGLES-devel elif command -v apt-get &>/dev/null; then run_as_root apt-get update - run_as_root apt-get install -y nasm cmake g++ pkg-config curl ccache autoconf automake libtool + run_as_root apt-get install -y \ + nasm cmake g++ pkg-config curl ccache autoconf automake libtool \ + libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libgl-dev \ + libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev fi if ! command -v uv &>/dev/null; then From a072f1e33363b48f2a8e97448be4bb7a7f7e5769 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 10 May 2026 11:31:35 -0700 Subject: [PATCH 02/10] rm offscreen --- raylib/build.sh | 3 --- raylib/raylib/__init__.py | 2 +- raylib/raylib/build.py | 2 -- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/raylib/build.sh b/raylib/build.sh index a22e950..30937f2 100755 --- a/raylib/build.sh +++ b/raylib/build.sh @@ -14,9 +14,6 @@ RAYLIB_PLATFORM="${RAYLIB_PLATFORM:-PLATFORM_DESKTOP}" if [ -f /TICI ]; then RAYLIB_PLATFORM="PLATFORM_COMMA" fi -if [ "$RAYLIB_PLATFORM" = "PLATFORM_OFFSCREEN" ]; then - RAYLIB_PLATFORM="PLATFORM_MEMORY" -fi export RAYLIB_PLATFORM # Install build dependencies diff --git a/raylib/raylib/__init__.py b/raylib/raylib/__init__.py index 794d79c..56880a8 100644 --- a/raylib/raylib/__init__.py +++ b/raylib/raylib/__init__.py @@ -10,7 +10,7 @@ def _detect_platform(): """Auto-detect the raylib platform. In CI on Linux x86_64, use memory rendering.""" explicit = os.environ.get("RAYLIB_PLATFORM", "") if explicit: - return "PLATFORM_MEMORY" if explicit == "PLATFORM_OFFSCREEN" else explicit + return explicit if os.environ.get("CI") and _platform.system() == "Linux" and _platform.machine() == "x86_64": return "PLATFORM_MEMORY" return "" diff --git a/raylib/raylib/build.py b/raylib/raylib/build.py index 684a59b..6a68a77 100644 --- a/raylib/raylib/build.py +++ b/raylib/raylib/build.py @@ -13,8 +13,6 @@ RAYLIB_INCLUDE_PATH = os.path.join(HERE, "install", "include") RAYLIB_LIB_PATH = os.path.join(HERE, "install", "lib") RAYLIB_PLATFORM = os.getenv("RAYLIB_PLATFORM", "") -if RAYLIB_PLATFORM == "PLATFORM_OFFSCREEN": - RAYLIB_PLATFORM = "PLATFORM_MEMORY" ffibuilder = FFI() From 4371ba4fe54c855c2f971fc99abbee687e9bb835 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 10 May 2026 11:39:29 -0700 Subject: [PATCH 03/10] dual backend --- raylib/build.sh | 63 ++++++++++++++++------ raylib/raylib/__init__.py | 106 ++++++++++++++++++++++++++------------ raylib/raylib/build.py | 42 +++++++++++---- raylib/setup.py | 24 +++++++-- 4 files changed, 173 insertions(+), 62 deletions(-) diff --git a/raylib/build.sh b/raylib/build.sh index 30937f2..bda9043 100755 --- a/raylib/build.sh +++ b/raylib/build.sh @@ -9,16 +9,34 @@ INSTALL_DIR="$DIR/raylib/install" NJOBS="$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 2)" CC="ccache ${CC:-cc}" +is_linux_aarch64() { + [[ "$(uname)" == "Linux" && ( "$(uname -m)" == "aarch64" || "$(uname -m)" == "arm64" ) ]] +} + # Detect platform: PLATFORM_COMMA for comma devices, PLATFORM_DESKTOP otherwise RAYLIB_PLATFORM="${RAYLIB_PLATFORM:-PLATFORM_DESKTOP}" -if [ -f /TICI ]; then +if [ -f /AGNOS ] || [ -f /TICI ]; then RAYLIB_PLATFORM="PLATFORM_COMMA" fi export RAYLIB_PLATFORM # Install build dependencies if [[ "$(uname)" == "Linux" ]]; then - if [ "$RAYLIB_PLATFORM" = "PLATFORM_COMMA" ]; then + if is_linux_aarch64; then + # Linux/aarch64 wheels include both desktop and comma backends. + if command -v dnf &>/dev/null; then + dnf install -y libX11-devel libXcursor-devel libXrandr-devel libXinerama-devel libXi-devel \ + mesa-libGL-devel libdrm-devel mesa-libgbm-devel mesa-libEGL-devel mesa-libGLES-devel + elif command -v apt-get &>/dev/null; then + if [ "$(id -u)" -eq 0 ]; then + apt-get update && apt-get install -y libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libgl-dev \ + libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev + else + sudo apt-get update && sudo apt-get install -y libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libgl-dev \ + libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev + fi + fi + elif [ "$RAYLIB_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 @@ -58,31 +76,44 @@ git remote set-url origin https://github.com/commaai/raylib.git git fetch --depth 1 origin "$RAYLIB_COMMIT" git reset --hard "$RAYLIB_COMMIT" -cd src -make clean -if [ "$RAYLIB_PLATFORM" = "PLATFORM_MEMORY" ]; then - make -j"$NJOBS" PLATFORM="$RAYLIB_PLATFORM" CC="${CC:-gcc}" CUSTOM_CFLAGS="${CUSTOM_CFLAGS:-} -fPIC" -else - make -j"$NJOBS" PLATFORM="$RAYLIB_PLATFORM" CC="${CC:-gcc}" -fi - 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/" +build_raylib() { + local platform="$1" + local output="$2" + + cd "$DIR/raylib-src/src" + make clean + if [ "$platform" = "PLATFORM_MEMORY" ]; then + make -j"$NJOBS" PLATFORM="$platform" CC="${CC:-gcc}" CUSTOM_CFLAGS="${CUSTOM_CFLAGS:-} -fPIC" + else + make -j"$NJOBS" PLATFORM="$platform" CC="${CC:-gcc}" + fi + cp libraylib.a "$INSTALL_DIR/lib/$output" + cd "$DIR" +} + +if is_linux_aarch64; then + echo "Building desktop backend..." + build_raylib PLATFORM_DESKTOP libraylib_desktop.a + cp "$INSTALL_DIR/lib/libraylib_desktop.a" "$INSTALL_DIR/lib/libraylib.a" + + echo "Building comma backend..." + build_raylib PLATFORM_COMMA libraylib_comma.a +else + build_raylib "$RAYLIB_PLATFORM" libraylib.a +fi + # On x86_64 Linux, also build the memory variant for CI headless rendering if [[ "$(uname)" == "Linux" && "$(uname -m)" == "x86_64" && "$RAYLIB_PLATFORM" != "PLATFORM_MEMORY" ]]; then echo "Building memory variant..." - cd raylib-src/src - make clean - make -j"$NJOBS" PLATFORM=PLATFORM_MEMORY CC="${CC:-gcc}" CUSTOM_CFLAGS="${CUSTOM_CFLAGS:-} -fPIC" - cp libraylib.a "$INSTALL_DIR/lib/libraylib_memory.a" - cd "$DIR" + build_raylib PLATFORM_MEMORY libraylib_memory.a fi # Download raygui header diff --git a/raylib/raylib/__init__.py b/raylib/raylib/__init__.py index 56880a8..d049eba 100644 --- a/raylib/raylib/__init__.py +++ b/raylib/raylib/__init__.py @@ -1,3 +1,5 @@ +import importlib +import importlib.machinery import os import platform as _platform @@ -6,19 +8,45 @@ INCLUDE_DIR = os.path.join(DIR, "include") -def _detect_platform(): - """Auto-detect the raylib platform. In CI on Linux x86_64, use memory rendering.""" - explicit = os.environ.get("RAYLIB_PLATFORM", "") +def _detect_backend(): + explicit = os.environ.get("RAYLIB_BACKEND", "").strip().lower() if explicit: + if explicit not in ("comma", "desktop"): + raise ValueError("RAYLIB_BACKEND must be 'comma' or 'desktop'") return explicit + if os.path.exists("/AGNOS") or os.path.exists("/TICI"): + return "comma" + platform_override = os.environ.get("RAYLIB_PLATFORM", "") + if platform_override == "PLATFORM_COMMA": + return "comma" + if platform_override == "PLATFORM_MEMORY": + return "memory" + if platform_override == "PLATFORM_DESKTOP": + return "desktop" if os.environ.get("CI") and _platform.system() == "Linux" and _platform.machine() == "x86_64": + return "memory" + return "desktop" + + +def _backend_platform(backend): + if backend == "comma": + return "PLATFORM_COMMA" + if backend == "memory": return "PLATFORM_MEMORY" - return "" + return "PLATFORM_DESKTOP" + + +def _has_extension(pkg_dir, module_name): + return any(os.path.isfile(os.path.join(pkg_dir, module_name + suffix)) + for suffix in importlib.machinery.EXTENSION_SUFFIXES) 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" + if _platform.system() == "Linux" and _platform.machine() in ("aarch64", "arm64"): + assert os.path.isfile(os.path.join(LIB_DIR, "libraylib_desktop.a")), "libraylib_desktop.a not found" + assert os.path.isfile(os.path.join(LIB_DIR, "libraylib_comma.a")), "libraylib_comma.a not found" # Build CFFI extension on first import if not already compiled, @@ -28,39 +56,53 @@ def _ensure_cffi_built(): import subprocess import sys pkg_dir = os.path.dirname(__file__) - platform_marker = os.path.join(pkg_dir, ".raylib_platform") - requested = _detect_platform() + backend_marker = os.path.join(pkg_dir, ".raylib_backend") + backend = _detect_backend() + + if backend in ("desktop", "comma"): + backend_module = f"_raylib_cffi_{backend}" + if _has_extension(pkg_dir, backend_module): + return backend_module # Export so build.py picks it up - if requested: - os.environ["RAYLIB_PLATFORM"] = requested - - cffi_files = glob.glob(os.path.join(pkg_dir, "_raylib_cffi*")) - - # 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 = [] - - 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() + os.environ["RAYLIB_PLATFORM"] = _backend_platform(backend) + if backend in ("desktop", "comma"): + os.environ["RAYLIB_BACKEND"] = backend + + default_module = "_raylib_cffi" + cffi_files = glob.glob(os.path.join(pkg_dir, "_raylib_cffi.*")) + default_exists = _has_extension(pkg_dir, default_module) + + if default_exists: + built_for = open(backend_marker).read().strip() if os.path.isfile(backend_marker) else "desktop" + if built_for == backend: + return default_module + + if cffi_files: + for f in cffi_files: + os.remove(f) + for modified_header in glob.glob(os.path.join(pkg_dir, "*.modified")): + os.remove(modified_header) + + 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(backend_marker, "w") as f: + f.write(backend) + except subprocess.CalledProcessError: + pass + return default_module + +_cffi_module = _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 = importlib.import_module(f".{_cffi_module}", __name__) + ffi, rl = _cffi.ffi, _cffi.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__ diff --git a/raylib/raylib/build.py b/raylib/raylib/build.py index 6a68a77..80df076 100644 --- a/raylib/raylib/build.py +++ b/raylib/raylib/build.py @@ -13,12 +13,37 @@ RAYLIB_INCLUDE_PATH = os.path.join(HERE, "install", "include") RAYLIB_LIB_PATH = os.path.join(HERE, "install", "lib") RAYLIB_PLATFORM = os.getenv("RAYLIB_PLATFORM", "") +RAYLIB_BACKEND = os.getenv("RAYLIB_BACKEND", "").lower() +RAYLIB_CFFI_MODULE = os.getenv("RAYLIB_CFFI_MODULE", "raylib._raylib_cffi") + +if RAYLIB_BACKEND: + if RAYLIB_BACKEND == "comma": + RAYLIB_PLATFORM = "PLATFORM_COMMA" + elif RAYLIB_BACKEND == "desktop": + RAYLIB_PLATFORM = "PLATFORM_DESKTOP" + else: + raise ValueError(f"unsupported RAYLIB_BACKEND: {RAYLIB_BACKEND}") ffibuilder = FFI() def check_raylib_installed(): - return os.path.isfile(os.path.join(RAYLIB_LIB_PATH, 'libraylib.a')) + return os.path.isfile(_raylib_archive()) + + +def _raylib_archive(): + if RAYLIB_PLATFORM == "PLATFORM_COMMA": + candidates = ("libraylib_comma.a", "libraylib.a") + elif RAYLIB_PLATFORM == "PLATFORM_MEMORY": + candidates = ("libraylib_memory.a", "libraylib.a") + else: + candidates = ("libraylib_desktop.a", "libraylib.a") + + for candidate in candidates: + path = os.path.join(RAYLIB_LIB_PATH, candidate) + if os.path.isfile(path): + return path + return os.path.join(RAYLIB_LIB_PATH, candidates[0]) def pre_process_header(filename, remove_function_bodies=False): @@ -97,26 +122,21 @@ def build_ffi(): else: print("BUILDING FOR LINUX") extra_link_args = [ - f'-L{RAYLIB_LIB_PATH}', '-lraylib', - '-lm', '-lpthread', '-lGL', + _raylib_archive(), + '-lm', '-lpthread', '-lrt', '-ldl', '-lpthread', '-latomic', ] if RAYLIB_PLATFORM == "PLATFORM_COMMA": - extra_link_args.remove('-lGL') extra_link_args += ['-lGLESv2', '-lEGL', '-lgbm', '-ldrm'] elif RAYLIB_PLATFORM == "PLATFORM_MEMORY": - # Use memory/software variant if available, otherwise fall back to default - memory_lib = os.path.join(RAYLIB_LIB_PATH, 'libraylib_memory.a') - if os.path.isfile(memory_lib): - extra_link_args[extra_link_args.index('-lraylib')] = '-lraylib_memory' - extra_link_args.remove('-lGL') + pass else: - extra_link_args += ['-lX11'] + extra_link_args += ['-lGL', '-lX11'] extra_compile_args = ["-Wno-incompatible-pointer-types"] 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/setup.py b/raylib/setup.py index c652781..3480a53 100644 --- a/raylib/setup.py +++ b/raylib/setup.py @@ -17,6 +17,22 @@ class BuildRaylib(build_py): """Run build.sh to compile the C library and CFFI extension before collecting package data.""" + @staticmethod + def _is_linux_aarch64(): + return platform.system() == "Linux" and platform.machine() in ("aarch64", "arm64") + + @staticmethod + def _build_cffi(pkg_dir, backend=None): + build_cffi = os.path.join(pkg_dir, "raylib", "build.py") + if not os.path.isfile(build_cffi): + return + + env = os.environ.copy() + if backend is not None: + env["RAYLIB_BACKEND"] = backend + env["RAYLIB_CFFI_MODULE"] = f"raylib._raylib_cffi_{backend}" + subprocess.check_call([sys.executable, build_cffi], cwd=pkg_dir, env=env) + def run(self): pkg_dir = os.path.dirname(os.path.abspath(__file__)) build_script = os.path.join(pkg_dir, "build.sh") @@ -28,9 +44,11 @@ def run(self): os.remove(old_cffi) for modified_header in glob.glob(os.path.join(pkg_dir, "raylib", "*.modified")): os.remove(modified_header) - 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) + if self._is_linux_aarch64(): + for backend in ("desktop", "comma"): + self._build_cffi(pkg_dir, backend) + else: + self._build_cffi(pkg_dir) staged_pkg = os.path.join(self.build_lib, "raylib") shutil.rmtree(os.path.join(staged_pkg, "install"), ignore_errors=True) From 22e282a34a767f5c1a8ffc59da87c8fa69998f18 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 10 May 2026 11:42:33 -0700 Subject: [PATCH 04/10] clean that up --- raylib/build.sh | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/raylib/build.sh b/raylib/build.sh index bda9043..4bad88d 100755 --- a/raylib/build.sh +++ b/raylib/build.sh @@ -20,49 +20,6 @@ if [ -f /AGNOS ] || [ -f /TICI ]; then fi export RAYLIB_PLATFORM -# Install build dependencies -if [[ "$(uname)" == "Linux" ]]; then - if is_linux_aarch64; then - # Linux/aarch64 wheels include both desktop and comma backends. - if command -v dnf &>/dev/null; then - dnf install -y libX11-devel libXcursor-devel libXrandr-devel libXinerama-devel libXi-devel \ - mesa-libGL-devel libdrm-devel mesa-libgbm-devel mesa-libEGL-devel mesa-libGLES-devel - elif command -v apt-get &>/dev/null; then - if [ "$(id -u)" -eq 0 ]; then - apt-get update && apt-get install -y libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libgl-dev \ - libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev - else - sudo apt-get update && sudo apt-get install -y libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libgl-dev \ - libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev - fi - fi - elif [ "$RAYLIB_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 - 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_MEMORY" ]; then - # memory platform: software renderer, no window-system dev packages needed - true - else - # desktop: needs X11/GL dev packages - if command -v dnf &>/dev/null; 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 - if [ "$(id -u)" -eq 0 ]; then - apt-get update && apt-get install -y libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libgl-dev - else - sudo apt-get update && sudo apt-get install -y libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libgl-dev - fi - fi - fi -fi - # Clone and build raylib C library RAYLIB_COMMIT="dff603f4f122163900469e73d113deacd9ec9817" From c3e5727b169d51e30161c139a7d0e0f1559e47a0 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 10 May 2026 11:50:50 -0700 Subject: [PATCH 05/10] cleanup --- raylib/.gitignore | 1 + raylib/build.sh | 20 +++++----- raylib/raylib/__init__.py | 44 ++++------------------ raylib/raylib/_backend.py | 78 +++++++++++++++++++++++++++++++++++++++ raylib/raylib/build.py | 34 +++++------------ raylib/raylib/defines.py | 8 ++-- raylib/setup.py | 20 +++++++--- 7 files changed, 124 insertions(+), 81 deletions(-) create mode 100644 raylib/raylib/_backend.py diff --git a/raylib/.gitignore b/raylib/.gitignore index 6ee9dcf..2360341 100644 --- a/raylib/.gitignore +++ b/raylib/.gitignore @@ -2,3 +2,4 @@ raylib-src/ raylib/install/ raylib/*.modified raylib/_raylib_cffi* +raylib/.raylib_backend diff --git a/raylib/build.sh b/raylib/build.sh index 4bad88d..6ca2d4f 100755 --- a/raylib/build.sh +++ b/raylib/build.sh @@ -18,6 +18,14 @@ RAYLIB_PLATFORM="${RAYLIB_PLATFORM:-PLATFORM_DESKTOP}" if [ -f /AGNOS ] || [ -f /TICI ]; then RAYLIB_PLATFORM="PLATFORM_COMMA" fi + +case "$RAYLIB_PLATFORM" in + PLATFORM_DESKTOP|PLATFORM_COMMA) ;; + *) + echo "Unsupported RAYLIB_PLATFORM=$RAYLIB_PLATFORM; expected PLATFORM_DESKTOP or PLATFORM_COMMA" >&2 + exit 1 + ;; +esac export RAYLIB_PLATFORM # Clone and build raylib C library @@ -47,11 +55,7 @@ build_raylib() { cd "$DIR/raylib-src/src" make clean - if [ "$platform" = "PLATFORM_MEMORY" ]; then - make -j"$NJOBS" PLATFORM="$platform" CC="${CC:-gcc}" CUSTOM_CFLAGS="${CUSTOM_CFLAGS:-} -fPIC" - else - make -j"$NJOBS" PLATFORM="$platform" CC="${CC:-gcc}" - fi + make -j"$NJOBS" PLATFORM="$platform" CC="${CC:-gcc}" cp libraylib.a "$INSTALL_DIR/lib/$output" cd "$DIR" } @@ -67,12 +71,6 @@ else build_raylib "$RAYLIB_PLATFORM" libraylib.a fi -# On x86_64 Linux, also build the memory variant for CI headless rendering -if [[ "$(uname)" == "Linux" && "$(uname -m)" == "x86_64" && "$RAYLIB_PLATFORM" != "PLATFORM_MEMORY" ]]; then - echo "Building memory variant..." - build_raylib PLATFORM_MEMORY libraylib_memory.a -fi - # Download raygui header RAYGUI_COMMIT="1e03efca48c50c5ea4b4a053d5bf04bad58d3e43" curl -fsSLo "$INSTALL_DIR/include/raygui.h" \ diff --git a/raylib/raylib/__init__.py b/raylib/raylib/__init__.py index d049eba..d8d029e 100644 --- a/raylib/raylib/__init__.py +++ b/raylib/raylib/__init__.py @@ -3,39 +3,13 @@ import os import platform as _platform +from ._backend import cffi_module_for_backend, detect_backend, platform_for_backend + DIR = os.path.join(os.path.dirname(__file__), "install") LIB_DIR = os.path.join(DIR, "lib") INCLUDE_DIR = os.path.join(DIR, "include") -def _detect_backend(): - explicit = os.environ.get("RAYLIB_BACKEND", "").strip().lower() - if explicit: - if explicit not in ("comma", "desktop"): - raise ValueError("RAYLIB_BACKEND must be 'comma' or 'desktop'") - return explicit - if os.path.exists("/AGNOS") or os.path.exists("/TICI"): - return "comma" - platform_override = os.environ.get("RAYLIB_PLATFORM", "") - if platform_override == "PLATFORM_COMMA": - return "comma" - if platform_override == "PLATFORM_MEMORY": - return "memory" - if platform_override == "PLATFORM_DESKTOP": - return "desktop" - if os.environ.get("CI") and _platform.system() == "Linux" and _platform.machine() == "x86_64": - return "memory" - return "desktop" - - -def _backend_platform(backend): - if backend == "comma": - return "PLATFORM_COMMA" - if backend == "memory": - return "PLATFORM_MEMORY" - return "PLATFORM_DESKTOP" - - def _has_extension(pkg_dir, module_name): return any(os.path.isfile(os.path.join(pkg_dir, module_name + suffix)) for suffix in importlib.machinery.EXTENSION_SUFFIXES) @@ -57,17 +31,15 @@ def _ensure_cffi_built(): import sys pkg_dir = os.path.dirname(__file__) backend_marker = os.path.join(pkg_dir, ".raylib_backend") - backend = _detect_backend() + backend = detect_backend() - if backend in ("desktop", "comma"): - backend_module = f"_raylib_cffi_{backend}" - if _has_extension(pkg_dir, backend_module): - return backend_module + backend_module = cffi_module_for_backend(backend) + if _has_extension(pkg_dir, backend_module): + return backend_module # Export so build.py picks it up - os.environ["RAYLIB_PLATFORM"] = _backend_platform(backend) - if backend in ("desktop", "comma"): - os.environ["RAYLIB_BACKEND"] = backend + os.environ["RAYLIB_PLATFORM"] = platform_for_backend(backend) + os.environ["RAYLIB_BACKEND"] = backend default_module = "_raylib_cffi" cffi_files = glob.glob(os.path.join(pkg_dir, "_raylib_cffi.*")) diff --git a/raylib/raylib/_backend.py b/raylib/raylib/_backend.py new file mode 100644 index 0000000..2d40bc6 --- /dev/null +++ b/raylib/raylib/_backend.py @@ -0,0 +1,78 @@ +import os + +DESKTOP = "desktop" +COMMA = "comma" +BACKENDS = (DESKTOP, COMMA) +DEFAULT_BACKEND = DESKTOP + +BACKEND_PLATFORMS = { + DESKTOP: "PLATFORM_DESKTOP", + COMMA: "PLATFORM_COMMA", +} +PLATFORM_BACKENDS = {platform: backend for backend, platform in BACKEND_PLATFORMS.items()} + +BACKEND_CFFI_MODULES = { + DESKTOP: "_raylib_cffi_desktop", + COMMA: "_raylib_cffi_comma", +} + +BACKEND_ARCHIVES = { + DESKTOP: ("libraylib_desktop.a", "libraylib.a"), + COMMA: ("libraylib_comma.a", "libraylib.a"), +} + +BACKEND_LINK_ARGS = { + DESKTOP: ("-lGL", "-lX11"), + COMMA: ("-lGLESv2", "-lEGL", "-lgbm", "-ldrm"), +} + +COMMA_DEVICE_MARKERS = ("/AGNOS", "/TICI") + + +def validate_backend(backend): + if backend not in BACKENDS: + raise ValueError("RAYLIB_BACKEND must be 'comma' or 'desktop'") + return backend + + +def backend_from_platform(platform): + if platform not in PLATFORM_BACKENDS: + raise ValueError("RAYLIB_PLATFORM must be 'PLATFORM_COMMA' or 'PLATFORM_DESKTOP'") + return PLATFORM_BACKENDS[platform] + + +def detect_backend(environ=None, exists=os.path.exists): + environ = os.environ if environ is None else environ + + explicit = environ.get("RAYLIB_BACKEND", "").strip().lower() + if explicit: + return validate_backend(explicit) + + if any(exists(marker) for marker in COMMA_DEVICE_MARKERS): + return COMMA + + platform = environ.get("RAYLIB_PLATFORM", "") + if platform: + return backend_from_platform(platform) + + return DEFAULT_BACKEND + + +def platform_for_backend(backend): + return BACKEND_PLATFORMS[validate_backend(backend)] + + +def cffi_module_for_backend(backend): + return BACKEND_CFFI_MODULES[validate_backend(backend)] + + +def qualified_cffi_module_for_backend(backend): + return f"raylib.{cffi_module_for_backend(backend)}" + + +def archive_candidates_for_backend(backend): + return BACKEND_ARCHIVES[validate_backend(backend)] + + +def link_args_for_backend(backend): + return BACKEND_LINK_ARGS[validate_backend(backend)] diff --git a/raylib/raylib/build.py b/raylib/raylib/build.py index 80df076..ee15244 100644 --- a/raylib/raylib/build.py +++ b/raylib/raylib/build.py @@ -9,21 +9,17 @@ from cffi import FFI +try: + from ._backend import archive_candidates_for_backend, detect_backend, link_args_for_backend +except ImportError: + from _backend import archive_candidates_for_backend, detect_backend, link_args_for_backend + 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_PLATFORM = os.getenv("RAYLIB_PLATFORM", "") -RAYLIB_BACKEND = os.getenv("RAYLIB_BACKEND", "").lower() +RAYLIB_BACKEND = detect_backend() RAYLIB_CFFI_MODULE = os.getenv("RAYLIB_CFFI_MODULE", "raylib._raylib_cffi") -if RAYLIB_BACKEND: - if RAYLIB_BACKEND == "comma": - RAYLIB_PLATFORM = "PLATFORM_COMMA" - elif RAYLIB_BACKEND == "desktop": - RAYLIB_PLATFORM = "PLATFORM_DESKTOP" - else: - raise ValueError(f"unsupported RAYLIB_BACKEND: {RAYLIB_BACKEND}") - ffibuilder = FFI() @@ -32,18 +28,11 @@ def check_raylib_installed(): def _raylib_archive(): - if RAYLIB_PLATFORM == "PLATFORM_COMMA": - candidates = ("libraylib_comma.a", "libraylib.a") - elif RAYLIB_PLATFORM == "PLATFORM_MEMORY": - candidates = ("libraylib_memory.a", "libraylib.a") - else: - candidates = ("libraylib_desktop.a", "libraylib.a") - - for candidate in candidates: + for candidate in archive_candidates_for_backend(RAYLIB_BACKEND): path = os.path.join(RAYLIB_LIB_PATH, candidate) if os.path.isfile(path): return path - return os.path.join(RAYLIB_LIB_PATH, candidates[0]) + return os.path.join(RAYLIB_LIB_PATH, archive_candidates_for_backend(RAYLIB_BACKEND)[0]) def pre_process_header(filename, remove_function_bodies=False): @@ -126,12 +115,7 @@ def build_ffi(): '-lm', '-lpthread', '-lrt', '-ldl', '-lpthread', '-latomic', ] - if RAYLIB_PLATFORM == "PLATFORM_COMMA": - extra_link_args += ['-lGLESv2', '-lEGL', '-lgbm', '-ldrm'] - elif RAYLIB_PLATFORM == "PLATFORM_MEMORY": - pass - else: - extra_link_args += ['-lGL', '-lX11'] + extra_link_args += link_args_for_backend(RAYLIB_BACKEND) extra_compile_args = ["-Wno-incompatible-pointer-types"] libraries = [] diff --git a/raylib/raylib/defines.py b/raylib/raylib/defines.py index a7e9902..b83bd58 100644 --- a/raylib/raylib/defines.py +++ b/raylib/raylib/defines.py @@ -1,9 +1,9 @@ import raylib -RAYLIB_VERSION_MAJOR: int = 5 -RAYLIB_VERSION_MINOR: int = 5 +RAYLIB_VERSION_MAJOR: int = 6 +RAYLIB_VERSION_MINOR: int = 0 RAYLIB_VERSION_PATCH: int = 0 -RAYLIB_VERSION: str = "5.5" +RAYLIB_VERSION: str = "6.0" PI: float = 3.141592653589793 DEG2RAD = PI / 180.0 RAD2DEG = 180.0 / PI @@ -15,7 +15,7 @@ SHADER_LOC_MAP_DIFFUSE = raylib.SHADER_LOC_MAP_ALBEDO SHADER_LOC_MAP_SPECULAR = raylib.SHADER_LOC_MAP_METALNESS EPSILON: float = 1e-06 -RLGL_VERSION: str = "5.0" +RLGL_VERSION: str = "6.0" RL_DEFAULT_BATCH_BUFFER_ELEMENTS: int = 8192 RL_DEFAULT_BATCH_BUFFERS: int = 1 RL_DEFAULT_BATCH_DRAWCALLS: int = 256 diff --git a/raylib/setup.py b/raylib/setup.py index 3480a53..c5fdf47 100644 --- a/raylib/setup.py +++ b/raylib/setup.py @@ -1,4 +1,5 @@ import glob +import importlib.util import os import platform import shutil @@ -17,12 +18,20 @@ class BuildRaylib(build_py): """Run build.sh to compile the C library and CFFI extension before collecting package data.""" + @staticmethod + def _backend_config(pkg_dir): + backend_config = os.path.join(pkg_dir, "raylib", "_backend.py") + spec = importlib.util.spec_from_file_location("_raylib_backend_config", backend_config) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + @staticmethod def _is_linux_aarch64(): return platform.system() == "Linux" and platform.machine() in ("aarch64", "arm64") @staticmethod - def _build_cffi(pkg_dir, backend=None): + def _build_cffi(pkg_dir, backend_config, backend=None): build_cffi = os.path.join(pkg_dir, "raylib", "build.py") if not os.path.isfile(build_cffi): return @@ -30,11 +39,12 @@ def _build_cffi(pkg_dir, backend=None): env = os.environ.copy() if backend is not None: env["RAYLIB_BACKEND"] = backend - env["RAYLIB_CFFI_MODULE"] = f"raylib._raylib_cffi_{backend}" + env["RAYLIB_CFFI_MODULE"] = backend_config.qualified_cffi_module_for_backend(backend) subprocess.check_call([sys.executable, build_cffi], cwd=pkg_dir, env=env) def run(self): pkg_dir = os.path.dirname(os.path.abspath(__file__)) + backend_config = self._backend_config(pkg_dir) build_script = os.path.join(pkg_dir, "build.sh") subprocess.check_call(["bash", build_script], cwd=pkg_dir) @@ -45,10 +55,10 @@ def run(self): for modified_header in glob.glob(os.path.join(pkg_dir, "raylib", "*.modified")): os.remove(modified_header) if self._is_linux_aarch64(): - for backend in ("desktop", "comma"): - self._build_cffi(pkg_dir, backend) + for backend in backend_config.BACKENDS: + self._build_cffi(pkg_dir, backend_config, backend) else: - self._build_cffi(pkg_dir) + self._build_cffi(pkg_dir, backend_config) staged_pkg = os.path.join(self.build_lib, "raylib") shutil.rmtree(os.path.join(staged_pkg, "install"), ignore_errors=True) From 532876640da9b63d08c2915bbbd7c6159503e492 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 10 May 2026 12:41:26 -0700 Subject: [PATCH 06/10] lil more --- raylib/.gitignore | 2 - raylib/build.sh | 37 ++++++---- raylib/build_cffi.py | 109 +++++++++++++++++++++++++++++ raylib/pyproject.toml | 9 ++- raylib/raylib/__init__.py | 93 +++++------------------- raylib/raylib/_backend.py | 42 ++--------- raylib/raylib/build.py | 136 ------------------------------------ raylib/raylib/enums.py | 16 ++++- raylib/setup.py | 51 +++++--------- raylib/validate_bindings.py | 111 +++++++++++++++++++++++++++++ 10 files changed, 304 insertions(+), 302 deletions(-) create mode 100644 raylib/build_cffi.py delete mode 100644 raylib/raylib/build.py create mode 100644 raylib/validate_bindings.py diff --git a/raylib/.gitignore b/raylib/.gitignore index 2360341..984172e 100644 --- a/raylib/.gitignore +++ b/raylib/.gitignore @@ -1,5 +1,3 @@ raylib-src/ raylib/install/ -raylib/*.modified raylib/_raylib_cffi* -raylib/.raylib_backend diff --git a/raylib/build.sh b/raylib/build.sh index 6ca2d4f..47bfea1 100755 --- a/raylib/build.sh +++ b/raylib/build.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -e +set -euo pipefail DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null && pwd)" cd "$DIR" @@ -7,26 +7,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}" +if command -v ccache &>/dev/null; then + CC="ccache ${CC:-cc}" +else + CC="${CC:-cc}" +fi is_linux_aarch64() { [[ "$(uname)" == "Linux" && ( "$(uname -m)" == "aarch64" || "$(uname -m)" == "arm64" ) ]] } -# Detect platform: PLATFORM_COMMA for comma devices, PLATFORM_DESKTOP otherwise -RAYLIB_PLATFORM="${RAYLIB_PLATFORM:-PLATFORM_DESKTOP}" -if [ -f /AGNOS ] || [ -f /TICI ]; then - RAYLIB_PLATFORM="PLATFORM_COMMA" +if [ -n "${RAYLIB_PLATFORM:-}" ]; then + echo "RAYLIB_PLATFORM is no longer supported; use RAYLIB_BACKEND=desktop or RAYLIB_BACKEND=comma" >&2 + exit 1 +fi + +RAYLIB_BACKEND="${RAYLIB_BACKEND:-}" +if [ -z "$RAYLIB_BACKEND" ]; then + RAYLIB_BACKEND="desktop" + if [ -f /AGNOS ] || [ -f /TICI ]; then + RAYLIB_BACKEND="comma" + fi fi -case "$RAYLIB_PLATFORM" in - PLATFORM_DESKTOP|PLATFORM_COMMA) ;; +case "$RAYLIB_BACKEND" in + desktop|comma) ;; *) - echo "Unsupported RAYLIB_PLATFORM=$RAYLIB_PLATFORM; expected PLATFORM_DESKTOP or PLATFORM_COMMA" >&2 + echo "Unsupported RAYLIB_BACKEND=$RAYLIB_BACKEND; expected desktop or comma" >&2 exit 1 ;; esac -export RAYLIB_PLATFORM # Clone and build raylib C library RAYLIB_COMMIT="dff603f4f122163900469e73d113deacd9ec9817" @@ -63,12 +73,15 @@ build_raylib() { if is_linux_aarch64; then echo "Building desktop backend..." build_raylib PLATFORM_DESKTOP libraylib_desktop.a - cp "$INSTALL_DIR/lib/libraylib_desktop.a" "$INSTALL_DIR/lib/libraylib.a" echo "Building comma backend..." build_raylib PLATFORM_COMMA libraylib_comma.a else - build_raylib "$RAYLIB_PLATFORM" libraylib.a + if [ "$RAYLIB_BACKEND" = "comma" ]; then + build_raylib PLATFORM_COMMA libraylib_comma.a + else + build_raylib PLATFORM_DESKTOP libraylib_desktop.a + fi fi # Download raygui header diff --git a/raylib/build_cffi.py b/raylib/build_cffi.py new file mode 100644 index 0000000..e0350f1 --- /dev/null +++ b/raylib/build_cffi.py @@ -0,0 +1,109 @@ +# Based on commaai/raylib-python-cffi (commit a32e910) +# Modified to use local install paths and compile standalone. + +import os +import platform +import re +import subprocess +import sys + +from cffi import FFI + +ROOT = os.path.dirname(os.path.abspath(__file__)) +PACKAGE_DIR = os.path.join(ROOT, "raylib") +RAYLIB_INCLUDE_PATH = os.path.join(PACKAGE_DIR, "install", "include") +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 validate_bindings import validate_static_bindings # noqa: E402 + +RAYLIB_BACKEND = detect_backend() +RAYLIB_CFFI_MODULE = os.getenv("RAYLIB_CFFI_MODULE", f"raylib.{BACKEND_CFFI_MODULES[RAYLIB_BACKEND]}") + +ffibuilder = FFI() + + +def _raylib_archive(): + archive = os.path.join(RAYLIB_LIB_PATH, BACKEND_ARCHIVES[RAYLIB_BACKEND]) + if not os.path.isfile(archive): + raise FileNotFoundError(f"{archive} not found. Please run build.sh first.") + return archive + + +def pre_process_header(filename, remove_function_bodies=False): + print("Pre-processing " + filename) + with open(filename, "r") as f: + filetext = "".join([line for line in f if '#include' not in line]) + command = ['gcc', '-CC', '-P', '-undef', '-nostdinc', '-DRL_MATRIX_TYPE', + '-DRL_QUATERNION_TYPE', '-DRL_VECTOR4_TYPE', '-DRL_VECTOR3_TYPE', '-DRL_VECTOR2_TYPE', + '-DRLAPI=', '-DPHYSACDEF=', '-DRAYGUIDEF=', '-DRMAPI=', + '-dDI', '-E', '-'] + filetext = subprocess.run(command, text=True, input=filetext, stdout=subprocess.PIPE, check=True).stdout + filetext = filetext.replace("va_list", "void *") + if remove_function_bodies: + filetext = re.sub('\n{\n(.|\n)*?\n}\n', ';', filetext) + return "\n".join([line for line in filetext.splitlines() if not line.startswith("#")]) + + +def build_ffi(): + raylib_archive = _raylib_archive() + + raylib_h = os.path.join(RAYLIB_INCLUDE_PATH, "raylib.h") + rlgl_h = os.path.join(RAYLIB_INCLUDE_PATH, "rlgl.h") + raymath_h = os.path.join(RAYLIB_INCLUDE_PATH, "raymath.h") + raygui_h = os.path.join(RAYLIB_INCLUDE_PATH, "raygui.h") + + for header in (raylib_h, rlgl_h, raymath_h, raygui_h): + if not os.path.isfile(header): + raise FileNotFoundError(f"{header} not found. Please run build.sh first.") + + ffi_includes = """ + #include "raylib.h" + #include "rlgl.h" + #include "raymath.h" + #define RAYGUI_IMPLEMENTATION + #define RAYGUI_SUPPORT_RICONS + #include "raygui.h" + """ + + ffibuilder.cdef(pre_process_header(raylib_h)) + ffibuilder.cdef(pre_process_header(rlgl_h)) + ffibuilder.cdef(pre_process_header(raymath_h, True)) + ffibuilder.cdef(pre_process_header(raygui_h)) + + validate_static_bindings(PACKAGE_DIR, RAYLIB_INCLUDE_PATH) + + if platform.system() == "Darwin": + print("BUILDING FOR MAC") + extra_link_args = [ + raylib_archive, + '-framework', 'OpenGL', + '-framework', 'Cocoa', + '-framework', 'IOKit', + '-framework', 'CoreFoundation', + '-framework', 'CoreVideo', + ] + extra_compile_args = ["-Wno-error=incompatible-function-pointer-types"] + else: + print("BUILDING FOR LINUX") + extra_link_args = [ + raylib_archive, + '-lm', '-lpthread', '-lrt', '-ldl', '-latomic', + *BACKEND_LINK_ARGS[RAYLIB_BACKEND], + ] + extra_compile_args = ["-Wno-incompatible-pointer-types"] + + print("extra_link_args: " + str(extra_link_args)) + ffibuilder.set_source(RAYLIB_CFFI_MODULE, + ffi_includes, + py_limited_api=True, + include_dirs=[RAYLIB_INCLUDE_PATH], + extra_link_args=extra_link_args, + extra_compile_args=extra_compile_args, + libraries=[]) + + +if __name__ == "__main__": + build_ffi() + ffibuilder.compile(verbose=True, tmpdir=ROOT) diff --git a/raylib/pyproject.toml b/raylib/pyproject.toml index 18204ae..0c35c07 100644 --- a/raylib/pyproject.toml +++ b/raylib/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=64", "wheel", "cffi>=1.17.1"] +requires = ["setuptools>=70.1", "cffi>=1.17.1"] build-backend = "setuptools.build_meta" [project] @@ -7,10 +7,13 @@ name = "raylib" version = "6.0.0.0" description = "raylib + pyray Python bindings (commaai fork)" requires-python = ">=3.8" -dependencies = ["cffi>=1.17.1", "setuptools>=64"] +dependencies = ["cffi>=1.17.1"] + +[tool.setuptools] +include-package-data = false [tool.setuptools.packages.find] include = ["raylib*", "pyray*"] [tool.setuptools.package-data] -raylib = ["install/**/*", "_raylib_cffi*.so"] +raylib = ["_raylib_cffi*.so"] diff --git a/raylib/raylib/__init__.py b/raylib/raylib/__init__.py index d8d029e..7c45952 100644 --- a/raylib/raylib/__init__.py +++ b/raylib/raylib/__init__.py @@ -1,82 +1,27 @@ import importlib -import importlib.machinery -import os -import platform as _platform -from ._backend import cffi_module_for_backend, detect_backend, platform_for_backend - -DIR = os.path.join(os.path.dirname(__file__), "install") -LIB_DIR = os.path.join(DIR, "lib") -INCLUDE_DIR = os.path.join(DIR, "include") - - -def _has_extension(pkg_dir, module_name): - return any(os.path.isfile(os.path.join(pkg_dir, module_name + suffix)) - for suffix in importlib.machinery.EXTENSION_SUFFIXES) +from ._backend import BACKEND_CFFI_MODULES, detect_backend +from .version import __version__ 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" - if _platform.system() == "Linux" and _platform.machine() in ("aarch64", "arm64"): - assert os.path.isfile(os.path.join(LIB_DIR, "libraylib_desktop.a")), "libraylib_desktop.a not found" - assert os.path.isfile(os.path.join(LIB_DIR, "libraylib_comma.a")), "libraylib_comma.a not found" + assert ffi is not None + assert rl is not None -# 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__) - backend_marker = os.path.join(pkg_dir, ".raylib_backend") +def _load_cffi(): backend = detect_backend() - - backend_module = cffi_module_for_backend(backend) - if _has_extension(pkg_dir, backend_module): - return backend_module - - # Export so build.py picks it up - os.environ["RAYLIB_PLATFORM"] = platform_for_backend(backend) - os.environ["RAYLIB_BACKEND"] = backend - - default_module = "_raylib_cffi" - cffi_files = glob.glob(os.path.join(pkg_dir, "_raylib_cffi.*")) - default_exists = _has_extension(pkg_dir, default_module) - - if default_exists: - built_for = open(backend_marker).read().strip() if os.path.isfile(backend_marker) else "desktop" - if built_for == backend: - return default_module - - if cffi_files: - for f in cffi_files: - os.remove(f) - for modified_header in glob.glob(os.path.join(pkg_dir, "*.modified")): - os.remove(modified_header) - - 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(backend_marker, "w") as f: - f.write(backend) - except subprocess.CalledProcessError: - pass - return default_module - -_cffi_module = _ensure_cffi_built() - -# CFFI bindings (available when graphics libraries are present) -try: - _cffi = importlib.import_module(f".{_cffi_module}", __name__) - ffi, rl = _cffi.ffi, _cffi.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 + backend_module = BACKEND_CFFI_MODULES[backend] + try: + return importlib.import_module(f".{backend_module}", __name__) + except (ImportError, OSError) as e: + raise ImportError(f"failed to load raylib {backend} backend extension {backend_module}") from e + + +_cffi = _load_cffi() +ffi, rl = _cffi.ffi, _cffi.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 diff --git a/raylib/raylib/_backend.py b/raylib/raylib/_backend.py index 2d40bc6..a7b9e65 100644 --- a/raylib/raylib/_backend.py +++ b/raylib/raylib/_backend.py @@ -5,20 +5,14 @@ BACKENDS = (DESKTOP, COMMA) DEFAULT_BACKEND = DESKTOP -BACKEND_PLATFORMS = { - DESKTOP: "PLATFORM_DESKTOP", - COMMA: "PLATFORM_COMMA", -} -PLATFORM_BACKENDS = {platform: backend for backend, platform in BACKEND_PLATFORMS.items()} - BACKEND_CFFI_MODULES = { DESKTOP: "_raylib_cffi_desktop", COMMA: "_raylib_cffi_comma", } BACKEND_ARCHIVES = { - DESKTOP: ("libraylib_desktop.a", "libraylib.a"), - COMMA: ("libraylib_comma.a", "libraylib.a"), + DESKTOP: "libraylib_desktop.a", + COMMA: "libraylib_comma.a", } BACKEND_LINK_ARGS = { @@ -35,14 +29,10 @@ def validate_backend(backend): return backend -def backend_from_platform(platform): - if platform not in PLATFORM_BACKENDS: - raise ValueError("RAYLIB_PLATFORM must be 'PLATFORM_COMMA' or 'PLATFORM_DESKTOP'") - return PLATFORM_BACKENDS[platform] - - def detect_backend(environ=None, exists=os.path.exists): environ = os.environ if environ is None else environ + if environ.get("RAYLIB_PLATFORM"): + raise ValueError("RAYLIB_PLATFORM is no longer supported; use RAYLIB_BACKEND=comma|desktop") explicit = environ.get("RAYLIB_BACKEND", "").strip().lower() if explicit: @@ -51,28 +41,4 @@ def detect_backend(environ=None, exists=os.path.exists): if any(exists(marker) for marker in COMMA_DEVICE_MARKERS): return COMMA - platform = environ.get("RAYLIB_PLATFORM", "") - if platform: - return backend_from_platform(platform) - return DEFAULT_BACKEND - - -def platform_for_backend(backend): - return BACKEND_PLATFORMS[validate_backend(backend)] - - -def cffi_module_for_backend(backend): - return BACKEND_CFFI_MODULES[validate_backend(backend)] - - -def qualified_cffi_module_for_backend(backend): - return f"raylib.{cffi_module_for_backend(backend)}" - - -def archive_candidates_for_backend(backend): - return BACKEND_ARCHIVES[validate_backend(backend)] - - -def link_args_for_backend(backend): - return BACKEND_LINK_ARGS[validate_backend(backend)] diff --git a/raylib/raylib/build.py b/raylib/raylib/build.py deleted file mode 100644 index ee15244..0000000 --- a/raylib/raylib/build.py +++ /dev/null @@ -1,136 +0,0 @@ -# Based on commaai/raylib-python-cffi (commit a32e910) -# Modified to use local install paths and compile standalone (no cffi_modules) - -import re -import os -import platform -import subprocess -import time - -from cffi import FFI - -try: - from ._backend import archive_candidates_for_backend, detect_backend, link_args_for_backend -except ImportError: - from _backend import archive_candidates_for_backend, detect_backend, link_args_for_backend - -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_BACKEND = detect_backend() -RAYLIB_CFFI_MODULE = os.getenv("RAYLIB_CFFI_MODULE", "raylib._raylib_cffi") - -ffibuilder = FFI() - - -def check_raylib_installed(): - return os.path.isfile(_raylib_archive()) - - -def _raylib_archive(): - for candidate in archive_candidates_for_backend(RAYLIB_BACKEND): - path = os.path.join(RAYLIB_LIB_PATH, candidate) - if os.path.isfile(path): - return path - return os.path.join(RAYLIB_LIB_PATH, archive_candidates_for_backend(RAYLIB_BACKEND)[0]) - - -def pre_process_header(filename, remove_function_bodies=False): - print("Pre-processing " + filename) - file = open(filename, "r") - filetext = "".join([line for line in file if '#include' not in line]) - command = ['gcc', '-CC', '-P', '-undef', '-nostdinc', '-DRL_MATRIX_TYPE', - '-DRL_QUATERNION_TYPE', '-DRL_VECTOR4_TYPE', '-DRL_VECTOR3_TYPE', '-DRL_VECTOR2_TYPE', - '-DRLAPI=', '-DPHYSACDEF=', '-DRAYGUIDEF=', '-DRMAPI=', - '-dDI', '-E', '-'] - filetext = subprocess.run(command, text=True, input=filetext, stdout=subprocess.PIPE).stdout - filetext = filetext.replace("va_list", "void *") - if remove_function_bodies: - filetext = re.sub('\n{\n(.|\n)*?\n}\n', ';', filetext) - filetext = "\n".join([line for line in filetext.splitlines() if not line.startswith("#")]) - modified_path = os.path.join(HERE, os.path.basename(filename) + ".modified") - with open(modified_path, "w") as f: - f.write(filetext) - return filetext - - -def check_header_exists(file): - if not os.path.isfile(file): - print(f"\nWARNING: {file} not found. Build will not contain these extra functions.\n") - time.sleep(1) - return False - return True - - -def build_ffi(): - """Set up the FFI builder. Must be called after libraylib.a and headers exist.""" - if not check_raylib_installed(): - raise Exception("ERROR: raylib not found. Please run build.sh first.") - - raylib_h = os.path.join(RAYLIB_INCLUDE_PATH, "raylib.h") - rlgl_h = os.path.join(RAYLIB_INCLUDE_PATH, "rlgl.h") - raymath_h = os.path.join(RAYLIB_INCLUDE_PATH, "raymath.h") - - for h in (raylib_h, rlgl_h, raymath_h): - if not os.path.isfile(h): - raise Exception(f"ERROR: {h} not found. Please run build.sh first.") - - ffi_includes = """ - #include "raylib.h" - #include "rlgl.h" - #include "raymath.h" - """ - - raygui_h = os.path.join(RAYLIB_INCLUDE_PATH, "raygui.h") - if check_header_exists(raygui_h): - ffi_includes += """ - #define RAYGUI_IMPLEMENTATION - #define RAYGUI_SUPPORT_RICONS - #include "raygui.h" - """ - - ffibuilder.cdef(pre_process_header(raylib_h)) - ffibuilder.cdef(pre_process_header(rlgl_h)) - ffibuilder.cdef(pre_process_header(raymath_h, True)) - - if os.path.isfile(raygui_h): - ffibuilder.cdef(pre_process_header(raygui_h)) - - if platform.system() == "Darwin": - print("BUILDING FOR MAC") - extra_link_args = [ - os.path.join(RAYLIB_LIB_PATH, 'libraylib.a'), - '-framework', 'OpenGL', - '-framework', 'Cocoa', - '-framework', 'IOKit', - '-framework', 'CoreFoundation', - '-framework', 'CoreVideo', - ] - libraries = [] - extra_compile_args = ["-Wno-error=incompatible-function-pointer-types"] - else: - print("BUILDING FOR LINUX") - extra_link_args = [ - _raylib_archive(), - '-lm', '-lpthread', - '-lrt', '-ldl', '-lpthread', '-latomic', - ] - extra_link_args += link_args_for_backend(RAYLIB_BACKEND) - extra_compile_args = ["-Wno-incompatible-pointer-types"] - libraries = [] - - print("extra_link_args: " + str(extra_link_args)) - ffibuilder.set_source(RAYLIB_CFFI_MODULE, - ffi_includes, - py_limited_api=True, - include_dirs=[RAYLIB_INCLUDE_PATH], - extra_link_args=extra_link_args, - extra_compile_args=extra_compile_args, - libraries=libraries) - - -if __name__ == "__main__": - build_ffi() - # compile with output going to the package root (parent of raylib/) - pkg_root = os.path.dirname(HERE) - ffibuilder.compile(verbose=True, tmpdir=pkg_root) diff --git a/raylib/raylib/enums.py b/raylib/raylib/enums.py index b397095..8802191 100644 --- a/raylib/raylib/enums.py +++ b/raylib/raylib/enums.py @@ -232,7 +232,8 @@ class ShaderLocationIndex(IntEnum): SHADER_LOC_MAP_BRDF = 25 SHADER_LOC_VERTEX_BONEIDS = 26 SHADER_LOC_VERTEX_BONEWEIGHTS = 27 - SHADER_LOC_BONE_MATRICES = 28 + SHADER_LOC_MATRIX_BONETRANSFORMS = 28 + SHADER_LOC_VERTEX_INSTANCETRANSFORM = 29 class ShaderUniformDataType(IntEnum): SHADER_UNIFORM_FLOAT = 0 @@ -243,7 +244,11 @@ class ShaderUniformDataType(IntEnum): SHADER_UNIFORM_IVEC2 = 5 SHADER_UNIFORM_IVEC3 = 6 SHADER_UNIFORM_IVEC4 = 7 - SHADER_UNIFORM_SAMPLER2D = 8 + SHADER_UNIFORM_UINT = 8 + SHADER_UNIFORM_UIVEC2 = 9 + SHADER_UNIFORM_UIVEC3 = 10 + SHADER_UNIFORM_UIVEC4 = 11 + SHADER_UNIFORM_SAMPLER2D = 12 class ShaderAttributeDataType(IntEnum): SHADER_ATTRIB_FLOAT = 0 @@ -348,6 +353,12 @@ class GuiState(IntEnum): STATE_PRESSED = 2 STATE_DISABLED = 3 +class GuiPropertyElement(IntEnum): + BORDER = 0 + BASE = 1 + TEXT = 2 + OTHER = 3 + class GuiTextAlignment(IntEnum): TEXT_ALIGN_LEFT = 0 TEXT_ALIGN_CENTER = 1 @@ -716,4 +727,3 @@ class GuiIconName(IntEnum): ICON_253 = 253 ICON_254 = 254 ICON_255 = 255 - diff --git a/raylib/setup.py b/raylib/setup.py index c5fdf47..f6fb8ec 100644 --- a/raylib/setup.py +++ b/raylib/setup.py @@ -8,11 +8,7 @@ from setuptools import setup from setuptools.command.build_py import build_py - -try: - from wheel.bdist_wheel import bdist_wheel -except ImportError: - bdist_wheel = None +from setuptools.command.bdist_wheel import bdist_wheel class BuildRaylib(build_py): @@ -31,15 +27,14 @@ def _is_linux_aarch64(): return platform.system() == "Linux" and platform.machine() in ("aarch64", "arm64") @staticmethod - def _build_cffi(pkg_dir, backend_config, backend=None): - build_cffi = os.path.join(pkg_dir, "raylib", "build.py") + def _build_cffi(pkg_dir, backend_config, backend): + build_cffi = os.path.join(pkg_dir, "build_cffi.py") if not os.path.isfile(build_cffi): return env = os.environ.copy() - if backend is not None: - env["RAYLIB_BACKEND"] = backend - env["RAYLIB_CFFI_MODULE"] = backend_config.qualified_cffi_module_for_backend(backend) + env["RAYLIB_BACKEND"] = backend + env["RAYLIB_CFFI_MODULE"] = f"raylib.{backend_config.BACKEND_CFFI_MODULES[backend]}" subprocess.check_call([sys.executable, build_cffi], cwd=pkg_dir, env=env) def run(self): @@ -52,13 +47,11 @@ def run(self): # after build.sh because the cached raylib source pin may have changed. for old_cffi in glob.glob(os.path.join(pkg_dir, "raylib", "_raylib_cffi*")): os.remove(old_cffi) - for modified_header in glob.glob(os.path.join(pkg_dir, "raylib", "*.modified")): - os.remove(modified_header) if self._is_linux_aarch64(): for backend in backend_config.BACKENDS: self._build_cffi(pkg_dir, backend_config, backend) else: - self._build_cffi(pkg_dir, backend_config) + self._build_cffi(pkg_dir, backend_config, backend_config.detect_backend()) staged_pkg = os.path.join(self.build_lib, "raylib") shutil.rmtree(os.path.join(staged_pkg, "install"), ignore_errors=True) @@ -68,31 +61,21 @@ def run(self): super().run() -cmdclass = {"build_py": BuildRaylib} - -if bdist_wheel is not None: - - class PlatformWheel(bdist_wheel): - """Produce a platform-specific wheel (contains native .a library).""" - - def finalize_options(self): - super().finalize_options() - self.root_is_pure = False +class PlatformWheel(bdist_wheel): + """Produce a platform-specific wheel (contains native .a library).""" - def get_tag(self): - system = platform.system() - machine = platform.machine() + def finalize_options(self): + super().finalize_options() + self.root_is_pure = False - if system == "Linux": - plat = f"linux_{machine}" - elif system == "Darwin": - plat = "macosx_11_0_arm64" - else: - plat = f"{system.lower()}_{machine}" + def get_tag(self): + _, _, plat_tag = super().get_tag() + if platform.system() == "Linux": + plat_tag = f"linux_{platform.machine()}" + return "py3", "none", plat_tag - return "py3", "none", plat - cmdclass["bdist_wheel"] = PlatformWheel +cmdclass = {"build_py": BuildRaylib, "bdist_wheel": PlatformWheel} setup(cmdclass=cmdclass) diff --git a/raylib/validate_bindings.py b/raylib/validate_bindings.py new file mode 100644 index 0000000..fe08e75 --- /dev/null +++ b/raylib/validate_bindings.py @@ -0,0 +1,111 @@ +import ast +import os +import re + + +def _read_python_assignments(path): + assignments = {} + with open(path, "r") as f: + tree = ast.parse(f.read()) + for node in tree.body: + if isinstance(node, ast.Assign) and len(node.targets) == 1 and isinstance(node.targets[0], ast.Name): + try: + assignments[node.targets[0].id] = ast.literal_eval(node.value) + except ValueError: + pass + elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name) and node.value is not None: + try: + assignments[node.target.id] = ast.literal_eval(node.value) + except ValueError: + pass + return assignments + + +def _read_python_enums(path): + enums = {} + with open(path, "r") as f: + tree = ast.parse(f.read()) + for node in tree.body: + if not isinstance(node, ast.ClassDef): + continue + values = {} + for stmt in node.body: + if isinstance(stmt, ast.Assign) and len(stmt.targets) == 1 and isinstance(stmt.targets[0], ast.Name): + values[stmt.targets[0].id] = ast.literal_eval(stmt.value) + enums[node.name] = values + return enums + + +def _strip_c_comments(text): + text = re.sub(r'/\*.*?\*/', '', text, flags=re.S) + return re.sub(r'//.*', '', text) + + +def _eval_c_enum_expr(expr, symbols): + if not re.fullmatch(r'[A-Za-z0-9_ xXa-fA-F|&~!<>()+\-*/]+', expr): + raise ValueError(expr) + return int(eval(expr.replace("!", " not "), {"__builtins__": {}}, symbols)) + + +def _read_c_enums(paths): + symbols = {} + enums = {} + for path in paths: + with open(path, "r") as f: + text = _strip_c_comments(f.read()) + for match in re.finditer(r'typedef\s+enum(?:\s+\w+)?\s*\{(.*?)\}\s*(\w+)\s*;', text, re.S): + body, enum_name = match.groups() + value = -1 + values = {} + for raw_item in body.split(','): + item = raw_item.strip() + if not item: + continue + if '=' in item: + name, expr = [part.strip() for part in item.split('=', 1)] + value = _eval_c_enum_expr(expr, symbols) + else: + name = item + value += 1 + if re.fullmatch(r'[A-Za-z_][A-Za-z0-9_]*', name): + values[name] = value + symbols[name] = value + enums[enum_name] = values + return enums + + +def validate_static_bindings(package_dir, include_dir): + raylib_h = os.path.join(include_dir, "raylib.h") + rlgl_h = os.path.join(include_dir, "rlgl.h") + raygui_h = os.path.join(include_dir, "raygui.h") + + defines = _read_python_assignments(os.path.join(package_dir, "defines.py")) + for header, names in ( + (raylib_h, ("RAYLIB_VERSION_MAJOR", "RAYLIB_VERSION_MINOR", "RAYLIB_VERSION_PATCH", "RAYLIB_VERSION")), + (rlgl_h, ("RLGL_VERSION",)), + ): + with open(header, "r") as f: + text = f.read() + for name in names: + match = re.search(rf"#define\s+{name}\s+(.+)", text) + if not match: + continue + expected = match.group(1).split("//", 1)[0].strip().strip('"') + actual = str(defines[name]) + if actual != expected: + raise ValueError(f"{name} in defines.py is {actual}, expected {expected}") + + py_enums = _read_python_enums(os.path.join(package_dir, "enums.py")) + c_enums = _read_c_enums((raylib_h, raygui_h)) + missing_classes = sorted(set(c_enums) - set(py_enums) - {"bool"}) + extra_classes = sorted(set(py_enums) - set(c_enums)) + if missing_classes or extra_classes: + raise ValueError(f"enums.py drift: missing={missing_classes}, extra={extra_classes}") + for enum_name in sorted(set(c_enums) & set(py_enums)): + if py_enums[enum_name] != c_enums[enum_name]: + raise ValueError(f"enums.py drift in {enum_name}") + + +if __name__ == "__main__": + root = os.path.dirname(os.path.abspath(__file__)) + validate_static_bindings(os.path.join(root, "raylib"), os.path.join(root, "raylib", "install", "include")) From 40e144f47193c802b5f5d321433f5d30afaedb72 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 10 May 2026 12:46:11 -0700 Subject: [PATCH 07/10] fix macOS --- raylib/pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/raylib/pyproject.toml b/raylib/pyproject.toml index 0c35c07..753ef8a 100644 --- a/raylib/pyproject.toml +++ b/raylib/pyproject.toml @@ -13,7 +13,8 @@ dependencies = ["cffi>=1.17.1"] include-package-data = false [tool.setuptools.packages.find] -include = ["raylib*", "pyray*"] +include = ["raylib", "pyray"] +namespaces = false [tool.setuptools.package-data] raylib = ["_raylib_cffi*.so"] From 4c210ecc28908046655497a09584f8d4aa1f5256 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 10 May 2026 12:58:44 -0700 Subject: [PATCH 08/10] fix raylib wheel smoke tests and dev files --- build.sh | 13 +++++++++++++ raylib/pyproject.toml | 2 +- raylib/raylib/__init__.py | 36 ++++++++++++++++++++++++++++++++---- raylib/setup.py | 12 +++++------- 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/build.sh b/build.sh index a2825ed..d67387f 100755 --- a/build.sh +++ b/build.sh @@ -68,6 +68,19 @@ echo echo "Running smoketests" uv venv --allow-existing --quiet "$VENV_DIR" +runtime_deps="$(mktemp)" +python3 - <<'PY' > "$runtime_deps" +import pathlib +import tomllib + +for toml in sorted(pathlib.Path(".").glob("*/pyproject.toml")): + for dep in tomllib.load(toml.open("rb")).get("project", {}).get("dependencies", []): + print(dep) +PY +if [[ -s "$runtime_deps" ]]; then + uv pip install --python "$VENV_DIR/bin/python" --quiet -r "$runtime_deps" >/dev/null +fi +rm -f "$runtime_deps" uv pip install --python "$VENV_DIR/bin/python" --reinstall --no-deps --quiet dist/*.whl >/dev/null for toml in */pyproject.toml; do diff --git a/raylib/pyproject.toml b/raylib/pyproject.toml index 753ef8a..2a7f4b9 100644 --- a/raylib/pyproject.toml +++ b/raylib/pyproject.toml @@ -17,4 +17,4 @@ include = ["raylib", "pyray"] namespaces = false [tool.setuptools.package-data] -raylib = ["_raylib_cffi*.so"] +raylib = ["_raylib_cffi*.so", "install/include/*.h", "install/lib/*.a"] diff --git a/raylib/raylib/__init__.py b/raylib/raylib/__init__.py index 7c45952..c2aac2f 100644 --- a/raylib/raylib/__init__.py +++ b/raylib/raylib/__init__.py @@ -1,21 +1,49 @@ import importlib +import os +import platform -from ._backend import BACKEND_CFFI_MODULES, detect_backend +from ._backend import BACKEND_ARCHIVES, BACKEND_CFFI_MODULES, COMMA, DESKTOP, detect_backend, validate_backend from .version import __version__ +DIR = os.path.join(os.path.dirname(__file__), "install") +INCLUDE_DIR = os.path.join(DIR, "include") +LIB_DIR = os.path.join(DIR, "lib") +BACKEND_LIBRARY_NAMES = { + DESKTOP: "raylib_desktop", + COMMA: "raylib_comma", +} + +_BACKEND = detect_backend() +LIBRARY_NAME = BACKEND_LIBRARY_NAMES[_BACKEND] +ARCHIVE = os.path.join(LIB_DIR, BACKEND_ARCHIVES[_BACKEND]) + + +def backend_archive(backend=None): + backend = _BACKEND if backend is None else validate_backend(backend) + return os.path.join(LIB_DIR, BACKEND_ARCHIVES[backend]) + + +def _expected_archives(): + if platform.system() == "Linux" and platform.machine() in ("aarch64", "arm64"): + return BACKEND_ARCHIVES.values() + return (BACKEND_ARCHIVES[_BACKEND],) + def smoketest(): assert ffi is not None assert rl is not None + for header in ("raylib.h", "raymath.h", "rlgl.h", "raygui.h"): + assert os.path.isfile(os.path.join(INCLUDE_DIR, header)), f"{header} not found" + for archive in _expected_archives(): + assert os.path.isfile(os.path.join(LIB_DIR, archive)), f"{archive} not found" def _load_cffi(): - backend = detect_backend() - backend_module = BACKEND_CFFI_MODULES[backend] + backend_module = BACKEND_CFFI_MODULES[_BACKEND] try: return importlib.import_module(f".{backend_module}", __name__) except (ImportError, OSError) as e: - raise ImportError(f"failed to load raylib {backend} backend extension {backend_module}") from e + raise ImportError(f"failed to load raylib {_BACKEND} backend extension {backend_module}") from e _cffi = _load_cffi() diff --git a/raylib/setup.py b/raylib/setup.py index f6fb8ec..1f32c4d 100644 --- a/raylib/setup.py +++ b/raylib/setup.py @@ -12,7 +12,7 @@ class BuildRaylib(build_py): - """Run build.sh to compile the C library and CFFI extension before collecting package data.""" + """Run build.sh to compile raylib artifacts before collecting package data.""" @staticmethod def _backend_config(pkg_dir): @@ -30,7 +30,7 @@ def _is_linux_aarch64(): def _build_cffi(pkg_dir, backend_config, backend): build_cffi = os.path.join(pkg_dir, "build_cffi.py") if not os.path.isfile(build_cffi): - return + raise FileNotFoundError(build_cffi) env = os.environ.copy() env["RAYLIB_BACKEND"] = backend @@ -53,16 +53,14 @@ def run(self): else: self._build_cffi(pkg_dir, backend_config, backend_config.detect_backend()) - staged_pkg = os.path.join(self.build_lib, "raylib") - shutil.rmtree(os.path.join(staged_pkg, "install"), ignore_errors=True) - for old_cffi in glob.glob(os.path.join(staged_pkg, "_raylib_cffi*")): - os.remove(old_cffi) + for package in ("raylib", "pyray"): + shutil.rmtree(os.path.join(self.build_lib, package), ignore_errors=True) super().run() class PlatformWheel(bdist_wheel): - """Produce a platform-specific wheel (contains native .a library).""" + """Produce a platform-specific wheel with native raylib artifacts.""" def finalize_options(self): super().finalize_options() From 92f6ad83dfe55ce785a43e6a7255a323e36abb92 Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 10 May 2026 13:10:15 -0700 Subject: [PATCH 09/10] limit smoke test bootstrap deps --- build.sh | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/build.sh b/build.sh index d67387f..629d2c0 100755 --- a/build.sh +++ b/build.sh @@ -68,19 +68,7 @@ echo echo "Running smoketests" uv venv --allow-existing --quiet "$VENV_DIR" -runtime_deps="$(mktemp)" -python3 - <<'PY' > "$runtime_deps" -import pathlib -import tomllib - -for toml in sorted(pathlib.Path(".").glob("*/pyproject.toml")): - for dep in tomllib.load(toml.open("rb")).get("project", {}).get("dependencies", []): - print(dep) -PY -if [[ -s "$runtime_deps" ]]; then - uv pip install --python "$VENV_DIR/bin/python" --quiet -r "$runtime_deps" >/dev/null -fi -rm -f "$runtime_deps" +uv pip install --python "$VENV_DIR/bin/python" --quiet "cffi>=1.17.1" >/dev/null uv pip install --python "$VENV_DIR/bin/python" --reinstall --no-deps --quiet dist/*.whl >/dev/null for toml in */pyproject.toml; do From f6e93d608640eac12ede0d33ae01d333ca82282c Mon Sep 17 00:00:00 2001 From: Adeeb Shihadeh Date: Sun, 10 May 2026 14:32:09 -0700 Subject: [PATCH 10/10] cleanup: --- raylib/raylib/__init__.py | 22 +++++----------- raylib/raylib/_backend.py | 11 ++++---- raylib/setup.py | 52 ++++++++++++------------------------- raylib/validate_bindings.py | 26 +++++++++---------- 4 files changed, 40 insertions(+), 71 deletions(-) diff --git a/raylib/raylib/__init__.py b/raylib/raylib/__init__.py index c2aac2f..6ea6e43 100644 --- a/raylib/raylib/__init__.py +++ b/raylib/raylib/__init__.py @@ -1,30 +1,18 @@ import importlib import os -import platform -from ._backend import BACKEND_ARCHIVES, BACKEND_CFFI_MODULES, COMMA, DESKTOP, detect_backend, validate_backend +from ._backend import BACKEND_ARCHIVES, BACKEND_CFFI_MODULES, detect_backend, is_dual_backend_host from .version import __version__ DIR = os.path.join(os.path.dirname(__file__), "install") INCLUDE_DIR = os.path.join(DIR, "include") LIB_DIR = os.path.join(DIR, "lib") -BACKEND_LIBRARY_NAMES = { - DESKTOP: "raylib_desktop", - COMMA: "raylib_comma", -} _BACKEND = detect_backend() -LIBRARY_NAME = BACKEND_LIBRARY_NAMES[_BACKEND] -ARCHIVE = os.path.join(LIB_DIR, BACKEND_ARCHIVES[_BACKEND]) - - -def backend_archive(backend=None): - backend = _BACKEND if backend is None else validate_backend(backend) - return os.path.join(LIB_DIR, BACKEND_ARCHIVES[backend]) def _expected_archives(): - if platform.system() == "Linux" and platform.machine() in ("aarch64", "arm64"): + if is_dual_backend_host(): return BACKEND_ARCHIVES.values() return (BACKEND_ARCHIVES[_BACKEND],) @@ -48,8 +36,10 @@ def _load_cffi(): _cffi = _load_cffi() ffi, rl = _cffi.ffi, _cffi.lib +# Module name is dynamic per backend, so we can't use `from ._raylib_cffi_X.lib import *`. 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 +del _name +from raylib.colors import * # noqa: F403, E402 +from raylib.defines import * # noqa: F403, E402 diff --git a/raylib/raylib/_backend.py b/raylib/raylib/_backend.py index a7b9e65..b1e52c1 100644 --- a/raylib/raylib/_backend.py +++ b/raylib/raylib/_backend.py @@ -1,4 +1,5 @@ import os +import platform DESKTOP = "desktop" COMMA = "comma" @@ -23,10 +24,8 @@ COMMA_DEVICE_MARKERS = ("/AGNOS", "/TICI") -def validate_backend(backend): - if backend not in BACKENDS: - raise ValueError("RAYLIB_BACKEND must be 'comma' or 'desktop'") - return backend +def is_dual_backend_host(): + return platform.system() == "Linux" and platform.machine() in ("aarch64", "arm64") def detect_backend(environ=None, exists=os.path.exists): @@ -36,7 +35,9 @@ def detect_backend(environ=None, exists=os.path.exists): explicit = environ.get("RAYLIB_BACKEND", "").strip().lower() if explicit: - return validate_backend(explicit) + if explicit not in BACKENDS: + raise ValueError("RAYLIB_BACKEND must be 'comma' or 'desktop'") + return explicit if any(exists(marker) for marker in COMMA_DEVICE_MARKERS): return COMMA diff --git a/raylib/setup.py b/raylib/setup.py index 1f32c4d..c132a99 100644 --- a/raylib/setup.py +++ b/raylib/setup.py @@ -1,5 +1,4 @@ import glob -import importlib.util import os import platform import shutil @@ -10,49 +9,33 @@ from setuptools.command.build_py import build_py from setuptools.command.bdist_wheel import bdist_wheel +PKG_DIR = os.path.dirname(os.path.abspath(__file__)) +sys.path.insert(0, os.path.join(PKG_DIR, "raylib")) +from _backend import BACKENDS, BACKEND_CFFI_MODULES, detect_backend, is_dual_backend_host # noqa: E402 + class BuildRaylib(build_py): """Run build.sh to compile raylib artifacts before collecting package data.""" @staticmethod - def _backend_config(pkg_dir): - backend_config = os.path.join(pkg_dir, "raylib", "_backend.py") - spec = importlib.util.spec_from_file_location("_raylib_backend_config", backend_config) - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - return module - - @staticmethod - def _is_linux_aarch64(): - return platform.system() == "Linux" and platform.machine() in ("aarch64", "arm64") - - @staticmethod - def _build_cffi(pkg_dir, backend_config, backend): - build_cffi = os.path.join(pkg_dir, "build_cffi.py") - if not os.path.isfile(build_cffi): - raise FileNotFoundError(build_cffi) - + def _build_cffi(backend): env = os.environ.copy() env["RAYLIB_BACKEND"] = backend - env["RAYLIB_CFFI_MODULE"] = f"raylib.{backend_config.BACKEND_CFFI_MODULES[backend]}" - subprocess.check_call([sys.executable, build_cffi], cwd=pkg_dir, env=env) + env["RAYLIB_CFFI_MODULE"] = f"raylib.{BACKEND_CFFI_MODULES[backend]}" + subprocess.check_call([sys.executable, "build_cffi.py"], cwd=PKG_DIR, env=env) def run(self): - pkg_dir = os.path.dirname(os.path.abspath(__file__)) - backend_config = self._backend_config(pkg_dir) - build_script = os.path.join(pkg_dir, "build.sh") - subprocess.check_call(["bash", build_script], cwd=pkg_dir) + subprocess.check_call(["bash", "build.sh"], cwd=PKG_DIR) - # Build CFFI extension so it's included in the wheel. Always regenerate it - # after build.sh because the cached raylib source pin may have changed. - for old_cffi in glob.glob(os.path.join(pkg_dir, "raylib", "_raylib_cffi*")): + # Always regenerate CFFI extensions: the cached raylib source pin may have changed. + for old_cffi in glob.glob(os.path.join(PKG_DIR, "raylib", "_raylib_cffi*")): os.remove(old_cffi) - if self._is_linux_aarch64(): - for backend in backend_config.BACKENDS: - self._build_cffi(pkg_dir, backend_config, backend) - else: - self._build_cffi(pkg_dir, backend_config, backend_config.detect_backend()) + backends = BACKENDS if is_dual_backend_host() else (detect_backend(),) + for backend in backends: + self._build_cffi(backend) + # Wipe build_lib package dirs so deleted/renamed files (e.g. old _raylib_cffi* + # backends, removed modules) don't get packaged from the cached build/ tree. for package in ("raylib", "pyray"): shutil.rmtree(os.path.join(self.build_lib, package), ignore_errors=True) @@ -73,7 +56,4 @@ def get_tag(self): return "py3", "none", plat_tag -cmdclass = {"build_py": BuildRaylib, "bdist_wheel": PlatformWheel} - - -setup(cmdclass=cmdclass) +setup(cmdclass={"build_py": BuildRaylib, "bdist_wheel": PlatformWheel}) diff --git a/raylib/validate_bindings.py b/raylib/validate_bindings.py index fe08e75..d062713 100644 --- a/raylib/validate_bindings.py +++ b/raylib/validate_bindings.py @@ -47,12 +47,10 @@ def _eval_c_enum_expr(expr, symbols): return int(eval(expr.replace("!", " not "), {"__builtins__": {}}, symbols)) -def _read_c_enums(paths): +def _read_c_enums(header_texts): symbols = {} enums = {} - for path in paths: - with open(path, "r") as f: - text = _strip_c_comments(f.read()) + for text in header_texts: for match in re.finditer(r'typedef\s+enum(?:\s+\w+)?\s*\{(.*?)\}\s*(\w+)\s*;', text, re.S): body, enum_name = match.groups() value = -1 @@ -75,28 +73,28 @@ def _read_c_enums(paths): def validate_static_bindings(package_dir, include_dir): - raylib_h = os.path.join(include_dir, "raylib.h") - rlgl_h = os.path.join(include_dir, "rlgl.h") - raygui_h = os.path.join(include_dir, "raygui.h") + headers = {} + for name in ("raylib.h", "rlgl.h", "raygui.h"): + with open(os.path.join(include_dir, name), "r") as f: + headers[name] = _strip_c_comments(f.read()) defines = _read_python_assignments(os.path.join(package_dir, "defines.py")) - for header, names in ( - (raylib_h, ("RAYLIB_VERSION_MAJOR", "RAYLIB_VERSION_MINOR", "RAYLIB_VERSION_PATCH", "RAYLIB_VERSION")), - (rlgl_h, ("RLGL_VERSION",)), + for header_name, names in ( + ("raylib.h", ("RAYLIB_VERSION_MAJOR", "RAYLIB_VERSION_MINOR", "RAYLIB_VERSION_PATCH", "RAYLIB_VERSION")), + ("rlgl.h", ("RLGL_VERSION",)), ): - with open(header, "r") as f: - text = f.read() + text = headers[header_name] for name in names: match = re.search(rf"#define\s+{name}\s+(.+)", text) if not match: continue - expected = match.group(1).split("//", 1)[0].strip().strip('"') + expected = match.group(1).strip().strip('"') actual = str(defines[name]) if actual != expected: raise ValueError(f"{name} in defines.py is {actual}, expected {expected}") py_enums = _read_python_enums(os.path.join(package_dir, "enums.py")) - c_enums = _read_c_enums((raylib_h, raygui_h)) + c_enums = _read_c_enums((headers["raylib.h"], headers["raygui.h"])) missing_classes = sorted(set(c_enums) - set(py_enums) - {"bool"}) extra_classes = sorted(set(py_enums) - set(c_enums)) if missing_classes or extra_classes: