Cross-compile jerbuild for Linux + FreeBSD

ober

ea29e9c73c6b162663a62a99fac8689b68955423

diff --git a/Makefile b/Makefile
index 6921db8..ec9538c 100644
--- a/Makefile
+++ b/Makefile
@@ -179,6 +179,26 @@ jerbuild-smoke: jerbuild
 	    exit 1; \
 	fi
 
+# Cross-compiled jerbuild. Drives support/build-jerbuild.sh with TARGET_* env,
+# producing ./jerbuild-<machine> alongside the host ./jerbuild.
+#
+# Usage:
+#   make jerbuild-cross CHEZ_TARGET_MACHINE=ta6le    CROSS_CC=x86_64-linux-musl-gcc
+#   make jerbuild-cross CHEZ_TARGET_MACHINE=tarm64le CROSS_CC=aarch64-linux-musl-gcc
+#   make jerbuild-cross CHEZ_TARGET_MACHINE=ta6fb    CROSS_CC=$$PWD/support/cross-cc-freebsd-amd64
+.PHONY: jerbuild-cross
+jerbuild-cross: chez build chez-cross
+	@test -n "$(CHEZ_TARGET_MACHINE)" \
+		|| { echo "ERROR: set CHEZ_TARGET_MACHINE=<machine-type>" >&2; exit 1; }
+	SCHEME=$(SCHEME) \
+	JERBOA_CHEZ_PREFIX=$(CHEZ_PREFIX) \
+	JERBOA_CROSS_PREFIX=$(CHEZ_CROSS_PREFIX) \
+	JERBOA_XPATCH=$(CHEZ_XPATCH) \
+	TARGET_MACHINE=$(CHEZ_TARGET_MACHINE) \
+	CC="$(CROSS_CC)" \
+	OUTPUT=jerbuild-$(CHEZ_TARGET_MACHINE) \
+	support/build-jerbuild.sh
+
 # ── Cross-compilation ────────────────────────────────────────────────────────
 # Override these to cross-compile:
 #   CHEZ_TARGET_MACHINE — Chez machine type (e.g. ta6osx, ta6le, tarm64le, ta6fb)
diff --git a/support/build-jerbuild.sh b/support/build-jerbuild.sh
index b587ce3..43f332a 100755
--- a/support/build-jerbuild.sh
+++ b/support/build-jerbuild.sh
@@ -7,15 +7,15 @@
 # or a Chez install.
 #
 # Usage:
-#   support/build-jerbuild.sh
-#
-# Output:
-#   ./jerbuild               — the binary
+#   support/build-jerbuild.sh                    # native host build → ./jerbuild
 #
-# Runtime usage of the produced binary:
-#   jerbuild <src-dir> <lib-dir>     — transpile .ss → .sls
-#   jerbuild --jerboa-home           — extract bundled stdlib, print its path
-#   jerbuild --version               — print version + bundled-lib hash
+#   # cross build → ./jerbuild-<target> (driven by Makefile's jerbuild-<os-arch>)
+#   TARGET_MACHINE=ta6le \
+#   JERBOA_CROSS_PREFIX=.chez-cross-ta6le \
+#   JERBOA_XPATCH=build/chez/xc-ta6le/s/xpatch \
+#   CC=x86_64-linux-musl-gcc \
+#   OUTPUT=jerbuild-ta6le \
+#   support/build-jerbuild.sh
 
 set -eu
 
@@ -25,26 +25,81 @@ JERBOA_CHEZ_PREFIX="${JERBOA_CHEZ_PREFIX:-$JERBOA_HOME/.chez}"
 CC="${CC:-cc}"
 OUTPUT="${OUTPUT:-jerbuild}"
 
+# ── Cross-compilation parameters (all optional; unset = native build) ────────
+# TARGET_MACHINE       Chez machine type to emit (e.g. ta6osx, ta6le).
+# JERBOA_CROSS_PREFIX  Install prefix of the cross-built Chez.
+# JERBOA_XPATCH        Path to xc-<target>/s/xpatch (makes host Scheme emit target FASL).
+TARGET_MACHINE="${TARGET_MACHINE:-}"
+JERBOA_CROSS_PREFIX="${JERBOA_CROSS_PREFIX:-}"
+JERBOA_XPATCH="${JERBOA_XPATCH:-}"
+
+if [ -n "$TARGET_MACHINE" ]; then
+    CROSS_BUILD=yes
+    [ -n "$JERBOA_CROSS_PREFIX" ] || { echo "ERROR: TARGET_MACHINE set but JERBOA_CROSS_PREFIX is not" >&2; exit 1; }
+    [ -n "$JERBOA_XPATCH" ]       || { echo "ERROR: TARGET_MACHINE set but JERBOA_XPATCH is not"       >&2; exit 1; }
+    [ -f "$JERBOA_XPATCH" ]       || { echo "ERROR: xpatch not found at $JERBOA_XPATCH"                >&2; exit 1; }
+else
+    CROSS_BUILD=no
+fi
+
 cd "$JERBOA_HOME"
 
 [ -x "$SCHEME" ] || { echo "ERROR: scheme not found at $SCHEME" >&2; exit 1; }
 [ -f "jerbuild.ss" ] || { echo "ERROR: jerbuild.ss not found in $JERBOA_HOME" >&2; exit 1; }
 
+# ── Derive target OS from machine type (le=Linux, osx=macOS, fb=FreeBSD) ─────
+machine_type_os() {
+    case "$1" in
+        *osx)   echo Darwin ;;
+        *nt)    echo Windows ;;
+        *fb)    echo FreeBSD ;;
+        *ob)    echo OpenBSD ;;
+        *nb)    echo NetBSD ;;
+        *s2)    echo Solaris ;;
+        *le|*)  echo Linux ;;
+    esac
+}
+
 HOST_OS=$(uname -s)
-case "$HOST_OS" in
-    Darwin)  OS_LIBS="-lm -lpthread -lncurses -liconv" ;;
-    Linux)   OS_LIBS="-lm -ldl -lpthread -lncurses" ;;
-    FreeBSD) OS_LIBS="-lm -lpthread -lncurses -L/usr/local/lib -liconv" ;;
-    *)       OS_LIBS="-lm -lpthread -lncurses" ;;
-esac
-
-MACHINE_TYPE=$("$SCHEME" -q <<'EOF'
+if [ "$CROSS_BUILD" = yes ]; then
+    TARGET_OS=$(machine_type_os "$TARGET_MACHINE")
+else
+    TARGET_OS="$HOST_OS"
+fi
+
+# Cross builds use `make chez-cross` which configures Chez with --disable-curses
+# --disable-x11 --disable-iconv by default, so libkernel.a won't reference those.
+# Native builds link them because host Chez is built --as-is.
+if [ "$CROSS_BUILD" = yes ]; then
+    case "$TARGET_OS" in
+        FreeBSD) OS_LIBS="-lm -lpthread" ;;
+        Darwin)  OS_LIBS="-lm -lpthread" ;;
+        Linux)   OS_LIBS="-lm -ldl -lpthread -static" ;;
+        *)       OS_LIBS="-lm -lpthread" ;;
+    esac
+else
+    case "$HOST_OS" in
+        Darwin)  OS_LIBS="-lm -lpthread -lncurses -liconv" ;;
+        Linux)   OS_LIBS="-lm -ldl -lpthread -lncurses" ;;
+        FreeBSD) OS_LIBS="-lm -lpthread -lncurses -L/usr/local/lib -liconv" ;;
+        *)       OS_LIBS="-lm -lpthread -lncurses" ;;
+    esac
+fi
+
+# ── Determine machine type + Chez install dir ───────────────────────────────
+if [ "$CROSS_BUILD" = yes ]; then
+    MACHINE_TYPE="$TARGET_MACHINE"
+    CSV_SEARCH_PREFIX="$JERBOA_CROSS_PREFIX/lib"
+else
+    MACHINE_TYPE=$("$SCHEME" -q <<'EOF'
 (display (machine-type)) (exit)
 EOF
 )
+    CSV_SEARCH_PREFIX="$JERBOA_CHEZ_PREFIX/lib"
+fi
 
 CSV_DIR=""
-for prefix in "$JERBOA_CHEZ_PREFIX/lib" /usr/local/lib /usr/lib /usr/lib64 /opt/homebrew/lib /opt/local/lib; do
+for prefix in "$CSV_SEARCH_PREFIX" /usr/local/lib /usr/lib /usr/lib64 /opt/homebrew/lib /opt/local/lib; do
     for d in "$prefix"/csv*/"$MACHINE_TYPE"; do
         if [ -f "$d/libkernel.a" ] && [ -f "$d/scheme.h" ] && [ -f "$d/petite.boot" ]; then
             CSV_DIR="$d"
@@ -52,10 +107,15 @@ for prefix in "$JERBOA_CHEZ_PREFIX/lib" /usr/local/lib /usr/lib /usr/lib64 /opt/
         fi
     done
 done
-[ -n "$CSV_DIR" ] || { echo "ERROR: no Chez install found for $MACHINE_TYPE" >&2; exit 1; }
+[ -n "$CSV_DIR" ] || { echo "ERROR: no Chez install found for $MACHINE_TYPE under $CSV_SEARCH_PREFIX" >&2; exit 1; }
 
 echo "=== jerbuild binary build ==="
-echo "    Host:  $HOST_OS, $MACHINE_TYPE"
+if [ "$CROSS_BUILD" = yes ]; then
+    echo "    Mode:  CROSS ($HOST_OS -> $TARGET_OS, $MACHINE_TYPE)"
+    echo "    xpatch: $JERBOA_XPATCH"
+else
+    echo "    Mode:  native ($HOST_OS, $MACHINE_TYPE)"
+fi
 echo "    CC:    $CC"
 echo "    Chez:  $CSV_DIR"
 echo "    Out:   ./$OUTPUT"
@@ -67,7 +127,7 @@ BUNDLE_TAR=$(mktemp "/tmp/jerbuild-bundle.XXXXXX.tar")
 trap 'rm -rf "$OBJ_DIR" "$WPO_SO" "$BUNDLE_TAR" petite_boot.h scheme_boot.h program_boot.h bundle_tar.h bundle_meta.h "${OUTPUT}-main.c"' EXIT
 
 echo "==> [1/5] WPO compile jerbuild.ss"
-"$SCHEME" --libdirs "$JERBOA_HOME/lib" \
+JERBOA_XPATCH="$JERBOA_XPATCH" "$SCHEME" --libdirs "$JERBOA_HOME/lib" \
     --script "$JERBOA_HOME/support/build-boot.ss" \
     jerbuild.ss "$WPO_SO" "$OBJ_DIR"
 echo ""