#!/usr/bin/env bash # docker-build-linux.sh — Build native-hello for Linux using Docker. # # Usage: # ./docker-build-linux.sh # build ARM64 (native on Apple Silicon) # ./docker-build-linux.sh --x86 # also build x86_64 via QEMU # ./docker-build-linux.sh --x86-only # only build x86_64 # # Output binaries are placed in ./build/: # native-hello-linux-arm64 — Linux ARM64 (Raspberry Pi 3/4/5, Apple Silicon VMs) # native-hello-linux-x86_64 — Linux x86_64 (desktop Linux, CI servers) # # Prerequisites: # - Docker (Rancher Desktop / Docker Desktop) # - elc at ../../../lang/dist/platform/elc or in PATH # # How it works: # 1. elc generates C sources from the el program (macOS, ARM64 native elc) # 2. Docker builds those C sources inside Ubuntu 22.04 + GTK4 # 3. The resulting ELF binary is extracted from the image # # Linux toolchain notes: # - GNU ld rejects duplicate symbol definitions that macOS ld accepts silently. # The Dockerfile uses objcopy --localize-symbol to hide duplicates between # el_seed.o / el_runtime.o and el_native_vessel.o / native_hello.o. # - el_gtk4.c requires _GNU_SOURCE for RTLD_DEFAULT (dlfcn.h). # - G_APPLICATION_DEFAULT_FLAGS was added in GLib 2.74; Ubuntu 22.04 ships # GLib 2.72, so the Dockerfile adds a compatibility define. 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" DOCKER_CTX="${SCRIPT_DIR}/build-docker" BUILD_X86=0 BUILD_ARM64=1 for arg in "$@"; do case "${arg}" in --x86) BUILD_X86=1 ;; --x86-only) BUILD_X86=1; BUILD_ARM64=0 ;; esac done # ── 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 at:" echo " ${EL_LANG_ROOT}/dist/platform/elc" exit 1 fi # ── Step 1: Generate C sources ──────────────────────────────────────────────── echo "==> Generating C sources with elc..." mkdir -p "${DOCKER_CTX}/runtime" # App EL_MANIFEST="${SCRIPT_DIR}/manifest.el" \ "${ELC}" "${SCRIPT_DIR}/src/main.el" > "${DOCKER_CTX}/native_hello.c" # Vessel "${ELC}" "${EL_NATIVE_VESSEL}" > "${DOCKER_CTX}/el_native_vessel.c" # Vessel header (declarations only — stop before first function body) "${ELC}" --emit-header "${EL_NATIVE_VESSEL}" \ | awk 'BEGIN{in_body=0} /^[a-zA-Z_].+\)[[:space:]]*\{/ {in_body=1} !in_body{print}' \ > "${DOCKER_CTX}/el_native_vessel.h" # Runtime C sources cp "${EL_RUNTIME}/el_gtk4.c" \ "${EL_RUNTIME}/el_seed.c" \ "${EL_RUNTIME}/el_seed.h" \ "${EL_RUNTIME}/el_runtime.c" \ "${EL_RUNTIME}/el_runtime.h" \ "${EL_RUNTIME}/el_native_target.h" \ "${DOCKER_CTX}/runtime/" echo "==> C sources ready in ${DOCKER_CTX}/" mkdir -p "${BUILD_DIR}" # ── Step 2: Docker build (ARM64) ───────────────────────────────────────────── if [ "${BUILD_ARM64}" -eq 1 ]; then echo "==> Building Linux ARM64 image..." docker build \ --platform linux/arm64 \ -f "${SCRIPT_DIR}/Dockerfile.linux-gtk4" \ --tag el-native-linux-gtk4-arm64 \ "${DOCKER_CTX}/" echo "==> Extracting ARM64 binary..." docker create --name el-extract-arm64 el-native-linux-gtk4-arm64 docker cp el-extract-arm64:/build/native-hello-linux "${BUILD_DIR}/native-hello-linux-arm64" docker rm el-extract-arm64 echo "==> ARM64 binary: ${BUILD_DIR}/native-hello-linux-arm64" file "${BUILD_DIR}/native-hello-linux-arm64" fi # ── Step 3: Docker build (x86_64) ──────────────────────────────────────────── if [ "${BUILD_X86}" -eq 1 ]; then echo "==> Building Linux x86_64 image (QEMU emulation — slower)..." docker build \ --platform linux/amd64 \ -f "${SCRIPT_DIR}/Dockerfile.linux-gtk4" \ --tag el-native-linux-gtk4-x86_64 \ "${DOCKER_CTX}/" echo "==> Extracting x86_64 binary..." docker create --name el-extract-x86 el-native-linux-gtk4-x86_64 docker cp el-extract-x86:/build/native-hello-linux "${BUILD_DIR}/native-hello-linux-x86_64" docker rm el-extract-x86 echo "==> x86_64 binary: ${BUILD_DIR}/native-hello-linux-x86_64" file "${BUILD_DIR}/native-hello-linux-x86_64" fi echo "" echo "==> Done. Binaries in ${BUILD_DIR}/" ls -lh "${BUILD_DIR}"/native-hello-linux-* 2>/dev/null || true