#!/usr/bin/env bash # build-sdl2.sh — Compile and run native-hello on macOS using the SDL2 backend. # # Usage: # ./build-sdl2.sh # compile and run # ./build-sdl2.sh compile # compile only # ./build-sdl2.sh clean # remove build artefacts # # Requirements: # - elc in PATH (or ../../../lang/dist/platform/elc) # - SDL2, SDL2_ttf, SDL2_image via Homebrew (brew install sdl2 sdl2_ttf sdl2_image) # - clang (Xcode Command Line Tools) # - macOS 12+ # # This is the SDL2 counterpart to build.sh (AppKit). The pipeline is identical; # only the platform bridge object and compile flags differ: # - el_sdl2.c instead of el_appkit.m # - -DEL_TARGET_SDL2 instead of -DEL_TARGET_MACOS # - sdl2-config --libs -lSDL2_ttf -lSDL2_image -ldl instead of -framework Cocoa set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" EL_LANG_ROOT="${SCRIPT_DIR}/../../../lang" EL_UI_ROOT="${SCRIPT_DIR}/../.." EL_RUNTIME="${EL_LANG_ROOT}/el-compiler/runtime" EL_NATIVE_VESSEL="${EL_UI_ROOT}/vessels/el-native/src/main.el" BUILD_DIR="${SCRIPT_DIR}/build" # Locate elc if command -v elc &>/dev/null; then ELC="elc" elif [ -x "${EL_LANG_ROOT}/dist/platform/elc" ]; then ELC="${EL_LANG_ROOT}/dist/platform/elc" else echo "Error: elc not found. Add it to PATH or place it at:" echo " ${EL_LANG_ROOT}/dist/platform/elc" exit 1 fi # Verify SDL2 is available if ! command -v sdl2-config &>/dev/null; then echo "Error: sdl2-config not found. Install SDL2 via:" echo " brew install sdl2 sdl2_ttf sdl2_image" exit 1 fi SDL2_CFLAGS=$(sdl2-config --cflags) SDL2_LIBS=$(sdl2-config --libs) CLANG_FLAGS_COMMON=( -std=c11 -DEL_TARGET_SDL2 -I "${EL_RUNTIME}" ) clean() { rm -rf "${BUILD_DIR}" echo "Cleaned." } compile() { mkdir -p "${BUILD_DIR}" # ── 1. Vessel forward-declarations header ───────────────────────────────── echo "==> Generating el-native vessel header (declarations only)..." "${ELC}" --emit-header "${EL_NATIVE_VESSEL}" \ | awk 'BEGIN{in_body=0} /^[a-zA-Z_].+\)[[:space:]]*\{/ {in_body=1} !in_body{print}' \ > "${BUILD_DIR}/el_native_vessel.h" # ── 2. Compile el-native vessel to C ────────────────────────────────────── echo "==> Compiling el-native vessel to C..." "${ELC}" "${EL_NATIVE_VESSEL}" \ > "${BUILD_DIR}/el_native_vessel.c" # ── 3. Compile app entry to C ───────────────────────────────────────────── echo "==> Compiling app entry to C..." EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \ "${ELC}" "${SCRIPT_DIR}/src/main.el" \ > "${BUILD_DIR}/native_hello.c" # ── 4. Compile el_sdl2.c (SDL2 bridge — pure C) ─────────────────────────── # sdl2-config --cflags gives -I/opt/homebrew/include/SDL2 which handles # #include but el_sdl2.c uses #include — also add # the parent directory so both forms resolve. echo "==> Compiling el_sdl2.c (SDL2 bridge)..." clang \ -std=c11 \ -DEL_TARGET_SDL2 \ ${SDL2_CFLAGS} \ -I/opt/homebrew/include \ -I "${EL_RUNTIME}" \ -c "${EL_RUNTIME}/el_sdl2.c" \ -o "${BUILD_DIR}/el_sdl2.o" # ── 5a. Compile el_seed.c ───────────────────────────────────────────────── echo "==> Compiling el_seed.c..." clang "${CLANG_FLAGS_COMMON[@]}" \ -c "${EL_RUNTIME}/el_seed.c" \ -o "${BUILD_DIR}/el_seed.o" # ── 5b. Compile el_runtime.c and patch out duplicate __-prefixed symbols ── echo "==> Compiling el_runtime.c..." clang "${CLANG_FLAGS_COMMON[@]}" \ -c "${EL_RUNTIME}/el_runtime.c" \ -o "${BUILD_DIR}/el_runtime.o" echo "==> Patching el_runtime.o (hiding __-prefixed symbols duplicated in el_seed.o)..." nm "${BUILD_DIR}/el_seed.o" 2>/dev/null \ | awk '/^[0-9a-f]+ T _/{print $3}' | sort > "${BUILD_DIR}/.seed_T.txt" nm "${BUILD_DIR}/el_runtime.o" 2>/dev/null \ | awk '/^[0-9a-f]+ T _/{print $3}' | sort > "${BUILD_DIR}/.rt_T.txt" comm -23 "${BUILD_DIR}/.rt_T.txt" "${BUILD_DIR}/.seed_T.txt" \ > "${BUILD_DIR}/.rt_keep.txt" nmedit -s "${BUILD_DIR}/.rt_keep.txt" "${BUILD_DIR}/el_runtime.o" # ── 6. Compile el-native vessel object ──────────────────────────────────── echo "==> Compiling el-native vessel object..." clang "${CLANG_FLAGS_COMMON[@]}" \ -include "${EL_RUNTIME}/el_native_target.h" \ -c "${BUILD_DIR}/el_native_vessel.c" \ -o "${BUILD_DIR}/el_native_vessel.o" echo "==> Patching el_native_vessel.o (hiding vessel main)..." nm "${BUILD_DIR}/el_native_vessel.o" 2>/dev/null \ | awk '/^[0-9a-f]+ T _/{print $3}' \ | grep -v '^_main$' \ > "${BUILD_DIR}/.vessel_keep.txt" nmedit -s "${BUILD_DIR}/.vessel_keep.txt" "${BUILD_DIR}/el_native_vessel.o" # ── 7. Compile app object ───────────────────────────────────────────────── echo "==> Compiling app object..." clang "${CLANG_FLAGS_COMMON[@]}" \ -include "${EL_RUNTIME}/el_native_target.h" \ -include "${BUILD_DIR}/el_native_vessel.h" \ -c "${BUILD_DIR}/native_hello.c" \ -o "${BUILD_DIR}/native_hello.o" # ── 8. Link ─────────────────────────────────────────────────────────────── echo "==> Linking native-hello-sdl2..." clang "${CLANG_FLAGS_COMMON[@]}" \ ${SDL2_LIBS} \ -lSDL2_ttf \ -lSDL2_image \ -lcurl \ -ldl \ -lpthread \ "${BUILD_DIR}/native_hello.o" \ "${BUILD_DIR}/el_native_vessel.o" \ "${BUILD_DIR}/el_sdl2.o" \ "${BUILD_DIR}/el_runtime.o" \ "${BUILD_DIR}/el_seed.o" \ -o "${BUILD_DIR}/native-hello-sdl2" echo "==> Build complete: ${BUILD_DIR}/native-hello-sdl2" } run() { compile echo "==> Launching native-hello-sdl2..." EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \ "${BUILD_DIR}/native-hello-sdl2" } case "${1:-run}" in clean) clean ;; compile) compile ;; run|*) run ;; esac