build: build jdns with installed jerbuild (no ~/mine/jerboa dependency)
ober
8d9bf774364e2560c6fa1f14259b8a723a021373
--- 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 --- 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" --- 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 new file mode 100755 --- /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" new file mode 100644 --- /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; +}