#!/usr/bin/env bash # build-gtk4.sh — Compile and run native-hello using the GTK4 bridge on macOS. # # Usage: # ./build-gtk4.sh # compile and run # ./build-gtk4.sh compile # compile only # ./build-gtk4.sh clean # remove build artefacts # # Requirements: # - elc in PATH (or ../../../lang/dist/platform/elc) # - clang (Xcode Command Line Tools) # - GTK4 via Homebrew: brew install gtk4 # - macOS 12+ # # This is the same pipeline as build.sh but targeting the GTK4 backend: # - Links with $(pkg-config --libs gtk4) instead of -framework Cocoa # - Compiles el_gtk4.c (plain C) instead of el_appkit.m (ObjC) # - Defines EL_TARGET_LINUX (the GTK4 target flag) # # Multi-file compilation strategy: see build.sh comments — identical rationale. 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-gtk4" # 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 # GTK4 pkg-config flags GTK4_CFLAGS=$(pkg-config --cflags gtk4) GTK4_LIBS=$(pkg-config --libs gtk4) CLANG_FLAGS_COMMON=( -std=c11 -DEL_TARGET_LINUX -I "${EL_RUNTIME}" ) clean() { rm -rf "${BUILD_DIR}" echo "Cleaned." } compile() { mkdir -p "${BUILD_DIR}" # ── 1. Vessel forward-declarations header ───────────────────────────────── # elc --emit-header emits full function bodies, not just prototypes. # Extract only the forward-declaration lines: stop at the first function # definition (a line that ends with ') {' opening a body block). # macOS awk does not support \s — use POSIX [[:space:]] instead. 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_gtk4.c (plain C, GTK4 backend) ───────────────────────── echo "==> Compiling el_gtk4.c (GTK4 bridge)..." # shellcheck disable=SC2086 clang \ -std=c11 \ -DEL_TARGET_LINUX \ -I "${EL_RUNTIME}" \ ${GTK4_CFLAGS} \ -c "${EL_RUNTIME}/el_gtk4.c" \ -o "${BUILD_DIR}/el_gtk4.o" # ── 5a. Compile el_seed.c ───────────────────────────────────────────────── # el_seed.c provides the OS-boundary __-prefixed primitives + thin wrappers. # With -DEL_TARGET_LINUX, the EL_TARGET_LINUX section in el_seed.c is # compiled in (GTK4 forwarders) and the EL_TARGET_MACOS section is excluded. echo "==> Compiling el_seed.c (EL_TARGET_LINUX)..." # shellcheck disable=SC2086 clang "${CLANG_FLAGS_COMMON[@]}" \ ${GTK4_CFLAGS} \ -c "${EL_RUNTIME}/el_seed.c" \ -o "${BUILD_DIR}/el_seed.o" # ── 5b. Compile el_runtime.c and patch out duplicate __-prefixed symbols ── # Same strategy as build.sh: el_runtime.c has duplicates of the __-prefixed # OS wrappers that el_seed.c defines. Hide el_runtime.o's copies with nmedit. 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 ──────────────────────────────────── # Vessel code calls __-prefixed builtins. el_native_target.h provides the # EL_TARGET_LINUX block of declarations (same function signatures as MACOS). 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 ───────────────────────────────────────────────── # Inject el_native_target.h (float ops + EL_TARGET_LINUX native decls) and # the vessel's forward-declaration header. 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 ─────────────────────────────────────────────────────────────── # el_seed.o = OS-boundary primitives (__-prefixed) + GTK4 forwarders # el_runtime.o = high-level builtins (str_len, json_*, etc.), patched # el_native_vessel.o = el-level widget API, patched # el_gtk4.o = GTK4 C bridge # GTK4_LIBS = $(pkg-config --libs gtk4) # -ldl = dlsym for callback dispatch # -lpthread = threading echo "==> Linking native-hello-gtk4..." # shellcheck disable=SC2086 clang "${CLANG_FLAGS_COMMON[@]}" \ "${BUILD_DIR}/native_hello.o" \ "${BUILD_DIR}/el_native_vessel.o" \ "${BUILD_DIR}/el_gtk4.o" \ "${BUILD_DIR}/el_runtime.o" \ "${BUILD_DIR}/el_seed.o" \ ${GTK4_LIBS} \ -ldl \ -lpthread \ -lcurl \ -o "${BUILD_DIR}/native-hello-gtk4" echo "==> Build complete: ${BUILD_DIR}/native-hello-gtk4" } run() { compile echo "==> Launching native-hello-gtk4..." EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \ "${BUILD_DIR}/native-hello-gtk4" } case "${1:-run}" in clean) clean ;; compile) compile ;; run|*) run ;; esac