build: build jdns with installed jerbuild (no ~/mine/jerboa dependency)

ober

8d9bf774364e2560c6fa1f14259b8a723a021373

diff --git a/.gitignore b/.gitignore
index f82bf31..ce155e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,3 +37,7 @@ bench/results/**/*.pid
 # Rust wasm build artifacts (the lib/jerboa-dns/sandbox/*.wasm outputs
 # are committed; the intermediate target/ dir is not)
 wasm/target/
+
+# jerbuild build artifacts
+/jdns
+support/ffi-symbols.gen
diff --git a/.jerbuild b/.jerbuild
index 8f224c7..650c5b9 100644
--- a/.jerbuild
+++ b/.jerbuild
@@ -1,10 +1,19 @@
-;; Build jdns with a standalone jerbuild binary.
+;; Build jdns with a standalone jerbuild binary: `jerbuild build`.
+;;
+;; jerbuild bundles Chez Scheme + the jerboa stdlib + the jerboa-native Rust
+;; crate, so this needs only jerbuild + a C compiler + cargo (with the
+;; wasm32-unknown-unknown target for the sandbox payloads). No jerboa source
+;; checkout. jerboa-native is built with just the wasm sandbox feature.
 
 (entry "bin/jdns.ss")
 (output "jdns")
-(requires "cc" "cargo" "bundled jerboa-native-rs")
-(notes "No DuckDB. The wasm/ workspace builds DNS/CDB wasm payloads; jerboa-native-rs only needs the wasm sandbox feature.")
 (libdirs "lib")
+;; Static binary: static main.c sets JERBOA_STATIC=1 so the std modules use the
+;; linked-in jerboa-native symbols (registered from the generated list) rather
+;; than dlopen. The list is regenerated per-platform by the Makefile.
+(main-c "support/static-main.c")
+(ffi-symbols "support/ffi-symbols.gen")
+;; Build the sandboxed DNS/CDB wasm payloads before embedding.
 (pre-build "sh wasm/build.sh")
 (rust-crates
   ("@bundle/jerboa-native-rs/Cargo.toml"
diff --git a/Makefile b/Makefile
index de0a198..ce26847 100644
--- a/Makefile
+++ b/Makefile
@@ -1,91 +1,56 @@
-JERBOA_HOME ?= $(HOME)/mine/jerboa
-SCHEME ?= $(JERBOA_HOME)/.chez/bin/scheme
-JERBOA ?= $(JERBOA_HOME)/lib
-LIBDIRS = lib:$(JERBOA)
-
-WASM_DIR = wasm
-WASM_DNS_OUT = lib/jerboa-dns/sandbox/dns_parser.wasm
-WASM_CDB_OUT = lib/jerboa-dns/sandbox/cdb_parser.wasm
-WASM_OUTPUTS = $(WASM_DNS_OUT) $(WASM_CDB_OUT)
-WASM_SOURCES = $(WASM_DIR)/Cargo.toml \
-               $(WASM_DIR)/dns/Cargo.toml $(WASM_DIR)/dns/src/lib.rs \
-               $(WASM_DIR)/cdb/Cargo.toml $(WASM_DIR)/cdb/src/lib.rs
-
-.PHONY: all build test clean jdns jdns-data wasm wasm-clean \
-        static static-macos static-linux static-linux-arm64 static-freebsd \
-        static-clean
-
-all: build
-
-build:
-	@echo "Compiling jerboa-dns libraries..."
-	@$(SCHEME) --libdirs "$(LIBDIRS)" --compile-imported-libraries --script build.ss
-
-# Build the sandboxed DNS parser and CDB lookup engine as WebAssembly
-# modules. Requires rustup with the wasm32-unknown-unknown target installed.
-wasm: $(WASM_OUTPUTS)
-
-$(WASM_OUTPUTS): $(WASM_SOURCES)
+# jerbuild bundles Chez Scheme + the jerboa stdlib + the jerboa-native Rust
+# crate, so building jdns needs only jerbuild + a C compiler + cargo (with the
+# wasm32-unknown-unknown target for the sandbox payloads). No jerboa checkout.
+JERBUILD ?= jerbuild
+JH := $(shell $(JERBUILD) --jerboa-home 2>/dev/null)
+ifeq ($(JH),)
+$(error jerbuild not found on PATH (or '$(JERBUILD) --jerboa-home' failed). Install jerbuild, or set JERBUILD=/path/to/jerbuild)
+endif
+
+NATIVE_DIR := $(JH)/jerboa-native-rs/target/release
+NATIVE_A   := $(NATIVE_DIR)/libjerboa_native.a
+LIBDIRS    := --libdirs lib:$(JH)/lib
+JEXEC      := $(JERBUILD) exec $(LIBDIRS)
+BIN        := jdns
+
+WASM_DIR     = wasm
+WASM_OUTPUTS = lib/jerboa-dns/sandbox/dns_parser.wasm lib/jerboa-dns/sandbox/cdb_parser.wasm
+
+.PHONY: all build binary wasm wasm-clean test clean
+
+all: binary
+
+# Standalone native binary via .jerbuild (entry bin/jdns.ss -> jdns).
+# Two passes: pass 1 builds the wasm payloads (pre-build) + cargo-builds
+# jerboa-native (wasm feature) into jerbuild's cache; we then regenerate the
+# FFI symbol list from that archive and relink. A static main.c sets
+# JERBOA_STATIC=1 so the std modules use the registered symbols, not dlopen.
+binary:
+	@touch support/ffi-symbols.gen
+	$(JERBUILD) build
+	sh support/gen-ffi-symbols.sh
+	$(JERBUILD) build
+
+build: binary
+
+# Build just the sandboxed DNS/CDB WebAssembly modules.
+wasm:
 	@$(WASM_DIR)/build.sh
 
 wasm-clean:
 	rm -rf $(WASM_DIR)/target $(WASM_OUTPUTS)
 
-jdns: build
-	$(SCHEME) --libdirs "$(LIBDIRS)" --script bin/jdns.ss
-
-jdns-data: build
-	$(SCHEME) --libdirs "$(LIBDIRS)" --script bin/jdns-data.ss $(ARGS)
-
-test: build
-	@echo "Running tests..."
-	@# macOS strips DYLD_LIBRARY_PATH through sh; DYLD_FALLBACK_LIBRARY_PATH
-	@# is preserved and works for finding libjerboa_native.dylib so the
-	@# wasm sandbox tests can exercise the sandbox path. On Linux/BSD,
-	@# LD_LIBRARY_PATH is honored normally.
+# Run the test suite in interpreter mode. The std wasm/sqlite modules dlopen
+# libjerboa_native from jerbuild's cache, so point the loader there.
+test: binary
 	@for f in tests/*-test.ss; do \
 		echo "  $$f"; \
-		DYLD_FALLBACK_LIBRARY_PATH="$(JERBOA)" \
-		LD_LIBRARY_PATH="$(JERBOA):$$LD_LIBRARY_PATH" \
-		$(SCHEME) --libdirs "$(LIBDIRS)" --script "$$f" || exit 1; \
+		DYLD_FALLBACK_LIBRARY_PATH="$(NATIVE_DIR)" \
+		LD_LIBRARY_PATH="$(NATIVE_DIR):$$LD_LIBRARY_PATH" \
+		$(JEXEC) "$$f" || exit 1; \
 	done
 	@echo "All tests passed."
 
-# Cross-builds. JERBOA_HOME must contain the cross-built Chez prefixes
-# (.chez-cross-ta6le for linux-amd64, .chez-cross-ta6fb for freebsd-amd64,
-# .chez-cross-tarm64le for linux-arm64) plus the matching xpatch under
-# build/chez/xc-*/s/xpatch. See ~/mine/jerboa for how those are built.
-#
-# Each target produces both jdns-<plat> and jdns-data-<plat> binaries.
-static: static-macos
-
-static-macos:
-	JERBOA_HOME=$(JERBOA_HOME) $(SCHEME) --libdirs "$(LIBDIRS)" \
-	    --script static/build-jdns-macos.ss
-
-static-linux:
-	JERBOA_HOME=$(JERBOA_HOME) $(SCHEME) --libdirs "$(LIBDIRS)" \
-	    --script static/build-jdns-linux.ss
-
-static-linux-arm64:
-	JERBOA_HOME=$(JERBOA_HOME) $(SCHEME) --libdirs "$(LIBDIRS)" \
-	    --script static/build-jdns-linux-arm64.ss
-
-static-freebsd:
-	JERBOA_HOME=$(JERBOA_HOME) $(SCHEME) --libdirs "$(LIBDIRS)" \
-	    --script static/build-jdns-freebsd.ss
-
-static-clean:
-	rm -f jdns-macos-arm64    jdns-data-macos-arm64
-	rm -f jdns-linux-amd64    jdns-data-linux-amd64
-	rm -f jdns-linux-arm64    jdns-data-linux-arm64
-	rm -f jdns-freebsd-amd64  jdns-data-freebsd-amd64
-	rm -f jdns*.wp.so jdns*-main.c
-	rm -f jdns*-libs.boot
-	rm -f petite_boot.h scheme_boot.h app_boot.h program_boot.h
-
 clean:
-	find lib -name '*.so' -delete 2>/dev/null || true
-	find lib -name '*.wpo' -delete 2>/dev/null || true
-	find lib \( -name '*.tarm64osx' -o -name '*.ta6le' \
-	         -o -name '*.ta6fb'     -o -name '*.tarm64le' \) -delete 2>/dev/null || true
+	rm -f $(BIN)
+	find lib \( -name '*.so' -o -name '*.wpo' \) -delete 2>/dev/null || true
diff --git a/support/gen-ffi-symbols.sh b/support/gen-ffi-symbols.sh
new file mode 100755
index 0000000..0ad50bf
--- /dev/null
+++ b/support/gen-ffi-symbols.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+# Regenerate support/ffi-symbols.gen from the jerboa-native archive that
+# jerbuild cargo-builds into its bundle cache. Run between the two `jerbuild
+# build` passes so the static binary registers exactly the jerboa_* symbols
+# present for THIS platform + feature set (e.g. epoll on Linux, kqueue on
+# macOS). The output is .gitignored — it is a build artifact, not source.
+set -e
+JH="$(jerbuild --jerboa-home 2>/dev/null)"
+A="$JH/jerboa-native-rs/target/release/libjerboa_native.a"
+OUT="$(dirname "$0")/ffi-symbols.gen"
+if [ ! -f "$A" ]; then
+  echo "gen-ffi-symbols: $A not found (run 'jerbuild build' once first)" >&2
+  : > "$OUT"
+  exit 0
+fi
+{
+  echo "# AUTOGENERATED by support/gen-ffi-symbols.sh from libjerboa_native.a."
+  echo "# jerboa-native FFI symbols registered via Sforeign_symbol() (see .jerbuild)."
+  echo ""
+  nm -gjU "$A" 2>/dev/null | sed 's/^_//' | grep -E '^jerboa_[A-Za-z0-9_]+$' | sort -u
+} > "$OUT"
+echo "gen-ffi-symbols: wrote $(grep -c '^jerboa_' "$OUT") symbols to $OUT"
diff --git a/support/static-main.c b/support/static-main.c
new file mode 100644
index 0000000..4fca78c
--- /dev/null
+++ b/support/static-main.c
@@ -0,0 +1,55 @@
+/* Custom main.c for a standalone static jerbuild binary (referenced by
+ * .jerbuild as main-c). Identical to jerbuild's stock template with ONE
+ * addition: it sets JERBOA_STATIC=1 before building the heap.
+ *
+ * The bundled std modules — (std db sqlite-native), (std crypto native-rust),
+ * etc. — read JERBOA_STATIC at library-load time and, when set, trust the
+ * jerboa-native symbols that are linked into this binary (and registered via
+ * register_ffi_symbols() below from the .jerbuild ffi-symbols list) instead of
+ * trying to dlopen a libjerboa_native.{so,dylib} that does not exist in a
+ * static build.
+ */
+#include "scheme.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include "petite_boot.h"
+#include "scheme_boot.h"
+#include "program_boot.h"
+#include "ffi_symbols.h"
+
+static const char *write_program_tmpfile(void) {
+    static char path[] = "/tmp/jerboa-prog-XXXXXX";
+    int fd = mkstemp(path);
+    if (fd < 0) { perror("mkstemp"); exit(1); }
+    ssize_t n = write(fd, program_boot_data, program_boot_size);
+    if (n != (ssize_t)program_boot_size) {
+        perror("write"); close(fd); unlink(path); exit(1);
+    }
+    close(fd);
+    return path;
+}
+
+int main(int argc, const char *argv[]) {
+    /* Trust the linked-in jerboa-native symbols (see file header). */
+    setenv("JERBOA_STATIC", "1", 1);
+
+    Sscheme_init(NULL);
+    Sregister_boot_file_bytes("petite",
+        (void *)petite_boot_data, petite_boot_size);
+    Sregister_boot_file_bytes("scheme",
+        (void *)scheme_boot_data, scheme_boot_size);
+    Sbuild_heap(NULL, NULL);
+    register_ffi_symbols();
+
+    const char *prog_path = write_program_tmpfile();
+    int status = Sscheme_program(prog_path, argc, argv);
+    unlink(prog_path);
+
+    Sscheme_deinit();
+    return status;
+}