From f51559030ad13d34ea62078a9dcacddf9cb15d05 Mon Sep 17 00:00:00 2001 From: Joseph Ferano Date: Sat, 12 Sep 2026 10:45:27 +0700 Subject: [PATCH] raylib for the browser is built rather than installed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No emscripten port provides raylib — emcc --show-ports offers contrib.glfw3 and nothing else nearby — so build-web.sh clones raylib at the 5.5 tag and compiles its seven modules with -DPLATFORM_WEB -DGRAPHICS_API_OPENGL_ES2 into one archive under vendor/raylib/web, which is gitignored along with the checkout it came from. 5.5 because that is the tag whose .so.550 the host links. raylib.flan carries raylib's struct layouts and enum values, and two targets built from different raylibs would disagree about them without saying so. rglfw.c is not among the modules: the web platform uses emscripten's own GLFW port, which is why link carries @web -sUSE_GLFW=3. No headers are installed, for the same reason the host build needs none — the generated shim declares the prototypes it uses. link now names the host library under @native and the archive under @web, through ${FLAN_RAYLIB_WEB}, so a web build with the variable unset is refused with the name of the variable rather than a page of undefined GLFW symbols. The one thing this costs: a wasi build that reaches raylib now fails on undefined symbols instead of on the missing -l:libraylib.so.550. --- .gitignore | 5 +++ vendor/raylib/build-web.sh | 76 ++++++++++++++++++++++++++++++++++++++ vendor/raylib/link | 33 ++++++++++++++++- 3 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 vendor/raylib/build-web.sh diff --git a/.gitignore b/.gitignore index 67b9111..634e8d8 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,8 @@ old-ocaml/ .claude/ probe probe.c + +# raylib built for the browser: a 1.6MB archive and the pinned checkout it +# came from. vendor/raylib/build-web.sh makes both, and the path is named to a +# build through FLAN_RAYLIB_WEB, not committed. +vendor/raylib/web/ diff --git a/vendor/raylib/build-web.sh b/vendor/raylib/build-web.sh new file mode 100644 index 0000000..1cc7388 --- /dev/null +++ b/vendor/raylib/build-web.sh @@ -0,0 +1,76 @@ +#!/bin/sh +# raylib, built for the browser. +# +# The host half of this package needs no build: Fedora ships libraylib.so.550 +# and `link` names it. The browser has no such thing, so the archive has to be +# made here, once, out of raylib's own sources with emscripten's clang. +# +# Pinned to the tag whose shared library the host links — 5.5 against +# libraylib.so.550 — because `raylib.flan` carries struct layouts and enum +# values that are raylib's, not ours, and a build where the two targets are +# different raylibs would disagree about them silently. +# +# The output is a plain static archive plus nothing else: no headers are +# installed, because the generated FFI shim declares the prototypes it uses +# (see BUILT.md, "No raylib headers are needed"). +# +# sh vendor/raylib/build-web.sh +# +# It prints the line to export. `vendor/raylib/link` points at the archive +# through ${FLAN_RAYLIB_WEB}, and a web build that names raylib with the +# variable unset is refused by name rather than met at the linker. +# +# Environment: +# FLAN_RAYLIB_TAG the raylib tag to build (default 5.5) +# FLAN_RAYLIB_WEB_DIR where to put it (default vendor/raylib/web) +# FLAN_RAYLIB_SRC an existing raylib checkout to build instead of cloning +set -eu + +tag=${FLAN_RAYLIB_TAG:-5.5} +here=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) +out=${FLAN_RAYLIB_WEB_DIR:-$here/web} +archive=$out/libraylib-$tag.a + +command -v emcc > /dev/null 2>&1 || { + echo "build-web.sh: no emcc on PATH. Source an emsdk's emsdk_env.sh." >&2 + exit 1 +} + +if [ -f "$archive" ]; then + echo "already built: $archive" >&2 +else + mkdir -p "$out" + if [ -n "${FLAN_RAYLIB_SRC:-}" ]; then + src=$FLAN_RAYLIB_SRC + else + src=$out/raylib-$tag + [ -d "$src" ] || git clone --depth 1 --branch "$tag" \ + https://github.com/raysan5/raylib "$src" + fi + + objs=$out/obj-$tag + rm -rf "$objs" + mkdir -p "$objs" + + # GRAPHICS_API_OPENGL_ES2 is what WebGL is. PLATFORM_WEB makes rcore.c + # include platforms/rcore_web.c, whose WindowShouldClose() is an + # emscripten_sleep that returns false — see BUILT.md on why that is the whole + # reason a Flan `until` loop needs no rewriting for the browser. + # + # rglfw.c is not in the list: the web platform uses emscripten's own GLFW + # (-sUSE_GLFW=3, in `link`), not a compiled-in one. + for m in rcore rshapes rtextures rtext rmodels raudio utils; do + [ -f "$src/src/$m.c" ] || continue + echo " emcc $m.c" >&2 + (cd "$src/src" && emcc -c -O2 -std=gnu99 \ + -DPLATFORM_WEB -DGRAPHICS_API_OPENGL_ES2 \ + -I. -Iexternal/glfw/include \ + "$m.c" -o "$objs/$m.o") + done + + emar rcs "$archive" "$objs"/*.o + rm -rf "$objs" +fi + +echo +echo "export FLAN_RAYLIB_WEB=$archive" diff --git a/vendor/raylib/link b/vendor/raylib/link index 21fde4d..b8317b5 100644 --- a/vendor/raylib/link +++ b/vendor/raylib/link @@ -1,2 +1,31 @@ --l:libraylib.so.550 --lm +# Extra linker arguments for this package, one per line. A line may be +# addressed to one target — @native, @wasi, @web — and an untagged line +# applies to all of them. ${NAME} expands from the environment. The selection +# and the expansion happen in Build, which is the only place that knows which +# target is being built; Load reads these lines and passes them through. + +# The host. Fedora's package installs the versioned soname and no unversioned +# symlink, so -lraylib finds nothing and the file has to be named. +@native -l:libraylib.so.550 +@native -lm + +# The browser. No shared library exists for wasm, so this is a static archive +# built out of raylib's own sources by vendor/raylib/build-web.sh, pinned to +# the 5.5 tag that matches the host's .so.550 — raylib.flan carries raylib's +# struct layouts and enum values, and two targets built from different raylibs +# would disagree about them without saying so. +# +# FLAN_RAYLIB_WEB is where that archive is. Unset, a web build that reaches +# raylib is refused by name here rather than met as a page of undefined GLFW +# symbols; build-web.sh prints the line to export. +@web ${FLAN_RAYLIB_WEB} + +# raylib's web platform is GLFW on emscripten's own port, not the rglfw.c it +# compiles in natively, and WebGL is GLES2. GL_ENABLE_GET_PROC_ADDRESS because +# rlgl asks for extension pointers by name. +@web -sUSE_GLFW=3 +@web -sGL_ENABLE_GET_PROC_ADDRESS + +# -sASYNCIFY is not here: Build adds it to every web link, because the reason +# for it is the browser's event loop and not raylib. See Build's comment on the +# main loop.