Add cross-platform build targets
ober
106d38abdb78e71d7583075b96fea3f6bd2b2165
new file mode 100644 --- /dev/null +++ b/.jerbuild.freebsd-amd64 @@ -0,0 +1,46 @@ +;; jerbuild build config for jerboa-shell (jsh) — native FreeBSD amd64. +;; +;; Build on a FreeBSD amd64 host with: +;; jerbuild build --config .jerbuild.freebsd-amd64 +;; +;; This uses only the installed jerbuild bundle, cc, and cargo. It deliberately +;; has no source-checkout, cross-compiler, or absolute runtime-path dependency. + +(entry "jsh-generated.ss") +(output "jsh-freebsd") + +(cc "cc") + +;; FreeBSD libc has dlopen, no separate -ldl. -lutil is for openpty. +;; Chez's expeditor needs termcap, and the packaged Chez kernel references +;; GNU libiconv from /usr/local/lib. +(os-libs "-lm -lpthread -lutil -ltermcap -L/usr/local/lib -liconv") + +(libdirs "_jerbuild-stage" ; stdlib patches — must shadow bundle copies + "src" + "vendor/jerboa-coreutils/lib" + "vendor/jerboa-awk/lib" + "vendor/jerboa-sed/lib" + "vendor/jerboa-aws/lib" + "../jerboa-wormhole" + "vendor/jerboa-yubikey/lib" + "vendor/jerboa-ssh/lib" + "vendor/jerboa-fuse/lib") + +;; FFI shim — same shim used by the macOS + Linux builds. Embed crypto symbols +;; come from the staged jerboa-native-rs archive. +(extra-sources "ffi-shim.c") + +;; Rust archive staged by support/stage-for-jerbuild-freebsd.sh. No coreutils +;; archive is linked on FreeBSD yet; freebsd-main.c provides weak stubs. +(extra-archives + "_jerbuild-stage/libjerboa_native.a" + "_jerbuild-stage/libjerboa_ssh.a" + "_jerbuild-stage/libjerboa_fuse.a") + +(extra-ldflags "-Wl,--export-dynamic") + +;; FreeBSD-specific main.c generated by the pre-build hook. +(main-c "_jerbuild-stage/freebsd-main.c") + +(pre-build "support/stage-for-jerbuild-freebsd.sh") new file mode 100644 --- /dev/null +++ b/.jerbuild.linux-amd64 @@ -0,0 +1,47 @@ +;; jerbuild build config for jerboa-shell (jsh) — native Linux amd64. +;; +;; Build on a Linux amd64 host with: +;; jerbuild build --config .jerbuild.linux-amd64 +;; +;; This intentionally uses only the installed jerbuild bundle, cc, and cargo. +;; There is no Jerboa source checkout, cross compiler, or absolute runtime path. + +(entry "jsh-generated.ss") +(output "jsh-linux-amd64") + +(cc "cc") + +(os-libs "-lm -ldl -lpthread -ltinfo") + +(libdirs "_jerbuild-stage" ; stdlib patches — must shadow bundle copies + "src" + "vendor/jerboa-coreutils/lib" + "vendor/jerboa-awk/lib" + "vendor/jerboa-sed/lib" + "vendor/jerboa-aws/lib" + "../jerboa-wormhole" + "vendor/jerboa-yubikey/lib" + "vendor/jerboa-ssh/lib" + "vendor/jerboa-fuse/lib") + +;; FFI shim — same shim used by the macOS build. Embed crypto symbols come +;; from the staged jerboa-native-rs archive. +(extra-sources "ffi-shim.c") + +;; Rust archives staged by support/stage-for-jerbuild-linux.sh. +(extra-archives + "_jerbuild-stage/libjerboa_native.a" + "_jerbuild-stage/libjsh_coreutils.a" + "_jerbuild-stage/libjerboa_ssh.a" + "_jerbuild-stage/libjerboa_fuse.a") + +;; --export-dynamic + Sforeign_symbol registrations in main.c are how Chez +;; foreign-procedure finds symbols. --allow-multiple-definition tolerates +;; duplicate Rust runtime symbols across the two static archives. +(extra-ldflags "-Wl,--export-dynamic" + "-Wl,--allow-multiple-definition") + +;; Linux-specific main.c is generated by the pre-build hook. +(main-c "_jerbuild-stage/linux-main.c") + +(pre-build "support/stage-for-jerbuild-linux.sh") new file mode 100644 --- /dev/null +++ b/.jerbuild.linux-amd64-native @@ -0,0 +1,35 @@ +;; Compatibility alias for older local commands. The canonical native Linux +;; config is .jerbuild.linux-amd64. + +(entry "jsh-generated.ss") +(output "jsh-linux-amd64") + +(cc "cc") + +(os-libs "-lm -ldl -lpthread -ltinfo") + +(libdirs "_jerbuild-stage" + "src" + "vendor/jerboa-coreutils/lib" + "vendor/jerboa-awk/lib" + "vendor/jerboa-sed/lib" + "vendor/jerboa-aws/lib" + "../jerboa-wormhole" + "vendor/jerboa-yubikey/lib" + "vendor/jerboa-ssh/lib" + "vendor/jerboa-fuse/lib") + +(extra-sources "ffi-shim.c") + +(extra-archives + "_jerbuild-stage/libjerboa_native.a" + "_jerbuild-stage/libjsh_coreutils.a" + "_jerbuild-stage/libjerboa_ssh.a" + "_jerbuild-stage/libjerboa_fuse.a") + +(extra-ldflags "-Wl,--export-dynamic" + "-Wl,--allow-multiple-definition") + +(main-c "_jerbuild-stage/linux-main.c") + +(pre-build "support/stage-for-jerbuild-linux.sh") --- a/Makefile +++ b/Makefile @@ -15,6 +15,35 @@ JERBUILD ?= $(shell if [ -x ./jerbuild ]; then echo ./jerbuild; \ else echo "$(JERBOA_TOOL_DIR)/jerbuild"; fi) JH = $(shell "$(JERBUILD)" --jerboa-home 2>/dev/null) +ifneq ($(JERBOA_FEATURES),) +JSH_FEATURES ?= $(JERBOA_FEATURES) +export JSH_FEATURES +endif +JSH_OUTPUT ?= jsh-macos +VENDOR ?= $(CURDIR)/vendor +COREUTILS ?= $(VENDOR)/jerboa-coreutils/lib +JERBOA_AWK ?= $(VENDOR)/jerboa-awk/lib +JERBOA_SED ?= $(VENDOR)/jerboa-sed/lib +JERBOA_AWS ?= $(VENDOR)/jerboa-aws/lib +JERBOA_WORMHOLE ?= $(VENDOR)/jerboa-wormhole +JERBOA_NATIVE_RS_DIR ?= $(VENDOR)/jerboa-native-rs +JERBOA_NATIVE_HOST_LIB ?= $(JERBOA_NATIVE_RS_DIR)/target/release/libjerboa_native$(SO_EXT) +JSH_NATIVE_NO_SQLITE_FEATURES ?= tls crypto +JERBOA_NATIVE_HOST_FEATURES ?= $(JSH_NATIVE_NO_SQLITE_FEATURES) +JSH_CROSS_FEATURES ?= coreutils +JERBOA_SSH_REPO ?= $(VENDOR)/jerboa-ssh +JSQLITE_REPO ?= $(VENDOR)/jsqlite +JSQLITE_URL ?= https://git.sr.ht/~lisp/jsqlite +JSQLITE_DIR ?= $(JSQLITE_REPO)/src +JSQLITE_STAGE ?= _jerbuild-stage/jsqlite +JERBOA_CRYPTO_REPO ?= $(VENDOR)/jerboa-crypto +JERBOA_FUSE_REPO ?= $(VENDOR)/jerboa-fuse +JERBOA_YUBIKEY_REPO ?= $(VENDOR)/jerboa-yubikey +JERBOA_YUBIKEY_LIB ?= $(JERBOA_YUBIKEY_REPO)/lib +VENDOR_STAGED_LIBS_SCRIPT ?= tools/stage-vendor-libs.sh +VENDOR_OVERRIDES_SCRIPT ?= tools/apply-vendor-overrides.sh +JSH_EMBED ?= $(shell if [ -d $(HOME)/.embed ]; then echo $(HOME)/.embed; else echo embed; fi) + UNAME_S := $(shell uname -s) HOST_ARCH := $(shell uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/') ifeq ($(UNAME_S),Darwin) @@ -31,6 +60,8 @@ endif FFI_LIB = libjsh-ffi$(SO_EXT) LIBDIRS = src:$(JH)/lib +LIBDIRS_JSH = _jerbuild-stage:src:$(COREUTILS):$(JERBOA_AWK):$(JERBOA_SED):$(JERBOA_AWS):$(JERBOA_WORMHOLE):$(JH)/lib +JEXEC = $(JERBUILD) exec --libdirs "$(LIBDIRS_JSH)" DIST_DIR ?= $(CURDIR)/dist RELEASE_EVIDENCE_DIR ?= $(DIST_DIR)/release-evidence TIMING_EVIDENCE_DIR ?= $(DIST_DIR)/timing-evidence @@ -59,14 +90,35 @@ OILS_COMMIT ?= 15de8fd779569e6e3a9f5fcbfc00e7df0ebe0380 OILS_TREE ?= f95f91004abcfdb82dd413e6ef2a7c71591f23d6 SH_SPEC := python3 $(CURDIR)/test/run_spec.py BASH := /bin/bash -JSH ?= $(if $(filter Darwin,$(UNAME_S)),./jsh-macos,./jsh) - -.PHONY: ensure-jerboa-tools ensure-shell-extras extras jerboa binary jsh jsh-macos macos run security test test-binary test-native \ +HOST_OS := $(UNAME_S) +HOST_TERMUX := $(shell if [ "$$(uname -s)" = Linux ] && { [ -n "$$TERMUX_VERSION" ] || [ -d /data/data/com.termux ]; }; then echo 1; fi) +JSH ?= $(if $(HOST_TERMUX),./jsh-android,$(if $(filter Darwin,$(HOST_OS)),./jsh-macos,$(if $(filter FreeBSD,$(HOST_OS)),./jsh-freebsd-$(HOST_ARCH),./jsh-linux-$(HOST_ARCH)))) +PODMAN ?= podman + +.PHONY: all build ensure-jerboa-tools ensure-jerboa-shell ensure-shell-extras ensure-jsqlite stage-jsqlite extras ffi jerboa compile binary jsh jsh-compile jsh-run jsh-binary \ + jsh-macos jsh-macos-base jsh-macos-full macos jsh-jerbuild \ + jsh-macos-minimal configure features features-all features-minimal show-features \ + check-freebsd-amd64-host jsh-freebsd jsh-freebsd-base jsh-freebsd-full freebsd freebsd-amd64 freebsd-arm64 jsh-freebsd-amd64 jsh-freebsd-arm64 \ + jsh-freebsd-minimal \ + linux linux-amd64 linux-arm64 linux-amd64-hardened linux-arm64-hardened \ + jsh-linux-amd64 jsh-linux-arm64 jsh-linux-amd64-hardened jsh-linux-arm64-hardened \ + linux-amd64-refresh-yubikey linux-arm64-refresh-yubikey test-linux test-linux-amd64 jsh-linux-native \ + rust-musl-target-amd64 rust-musl-target-arm64 chez-cross-amd64 chez-cross-arm64 \ + rust-coreutils-linux-amd64 rust-coreutils-linux-arm64 jerboa-native-linux-amd64 jerboa-native-linux-arm64 \ + rust-wireguard-host rust-wireguard-linux-amd64 rust-wireguard-linux-arm64 \ + jerboa-native-host vendor-staged-libs check-vendor-staged-libs-fresh jerboa-yubikey-lib check-yubikey-lib-fresh \ + yubikey-native-linux-amd64 yubikey-native-linux-arm64 vendor-deps vendor-deps-linux vendor-native-rs vendor-native-rs-pruned \ + check-android-host jsh-android jsh-android-native jsh-android-cross jsh-android-minimal jsh-android-full android \ + musl gen-embed embed-crypto gen-certs verify-harden install install-binary install-shims install-hooks learn-codex-shim \ + upload-macos-libs check-platforms run security test test-binary test-native test-mux test-mux-screen test-mux-tcp test-security-policy \ test-native-sanitize test-security-regressions adversarial-corpus audit \ sbom reproducibility-report timing-evidence verify release-evidence \ compat compat-smoke compat-tier0 compat-tier1 compat-tier2 compat-one compat-range compat-debug \ bench clean help +all: binary +build: binary + ensure-jerboa-tools: @set -e; \ if [ -n "$(JERBUILD)" ] && [ -x "$(JERBUILD)" ] && "$(JERBUILD)" --jerboa-home >/dev/null 2>&1; then \ @@ -76,6 +128,9 @@ ensure-jerboa-tools: sh support/ensure-jerboa.sh "$(JERBOA_VERSION)" "$(JERBOA_TOOL_DIR)"; \ fi +ensure-jerboa-shell: + @echo "=== ensure-jerboa-shell compatibility target: sources are already in this repo ===" + ensure-shell-extras: @if [ ! -f "$(JERBOA_SHELL_EXTRAS_DIR)/Makefile" ]; then \ echo "=== Fetching jerboa-shell-extras ==="; \ @@ -93,6 +148,21 @@ extras: ensure-shell-extras JSH_EMBED="$(JERBOA_SHELL_EXTRAS_DIR)/embed" \ EXTRAS_OUTPUT="$(CURDIR)/jsh-extras" +ensure-jsqlite: + @if [ ! -f "$(JSQLITE_DIR)/jsqlite/api.ss" ]; then \ + mkdir -p "$(VENDOR)"; \ + git clone --depth 1 "$(JSQLITE_URL)" "$(JSQLITE_REPO)"; \ + fi + @test -f "$(JSQLITE_DIR)/jsqlite/api.ss" + +stage-jsqlite: ensure-jsqlite + @rm -rf "$(JSQLITE_STAGE)" + @mkdir -p "$(JSQLITE_STAGE)" + @cp -a "$(JSQLITE_DIR)/jsqlite/." "$(JSQLITE_STAGE)/" + @find "$(JSQLITE_STAGE)" -type f \( -name '*.so' -o -name '*.wpo' \ + -o -name '*.ta6le' -o -name '*.ta6fb' \ + -o -name '*.tarm64osx' -o -name '*.tx86_64osx' \) -delete 2>/dev/null || true + ffi-shim-symbols.list: ffi-shim.c tools/extract-ffi-symbols.sh Makefile @echo "=== Regenerating FFI symbol whitelist from ffi-shim.c ===" @tools/extract-ffi-symbols.sh --ffi-shim > $@.tmp && mv $@.tmp $@ @@ -101,6 +171,8 @@ $(FFI_LIB): ffi-shim.c @echo "=== Building interpreted FFI shim: $@ ===" $(CC) $(FFI_CFLAGS) $(SHARED_CFLAGS) -o $@ ffi-shim.c $(HARDEN_LDFLAGS) +ffi: $(FFI_LIB) + jerboa: ensure-jerboa-tools @echo "=== Cleaning stale compiled artifacts ===" @find src -type f \( -name '*.so' -o -name '*.wpo' -o -name '*.tarm64osx' -o -name '*.tx86_64osx' -o -name '*.ta6le' -o -name '*.ta6fb' \) -delete 2>/dev/null || true @@ -117,12 +189,539 @@ jsh-macos: ensure-jerboa-tools ffi-shim-symbols.list jerboa @shasum -a 256 jsh-macos > jsh-macos.sha256 macos: jsh-macos -binary: jsh-macos - -jsh: binary - @cp -f jsh-macos jsh +binary: jsh-binary + +jsh-jerbuild: jsh-macos + +jsh-macos-base: + @$(MAKE) jsh-macos JSH_FEATURES=none + @cp -f jsh-macos jsh-macos-base + @shasum -a 256 jsh-macos-base > jsh-macos-base.sha256 + +jsh-macos-full: + @$(MAKE) jsh-macos JSH_FEATURES=all + @cp -f jsh-macos jsh-macos-full + @shasum -a 256 jsh-macos-full > jsh-macos-full.sha256 + +jsh-macos-minimal: + @echo "=== jsh-macos-minimal is a compatibility alias; building with JSH_FEATURES=none ===" + @$(MAKE) jsh-macos-base + +features-all: + @echo "=== features-all compatibility target: use JSH_FEATURES=all make <target> ===" + +configure features: features-all + +features-minimal: + @echo "=== features-minimal compatibility target: use JSH_FEATURES=none make <target> ===" + +show-features: + @echo "JSH_FEATURES=$${JSH_FEATURES:-$${JERBOA_FEATURES:-all/default by target}}" + +compile: ensure-jerboa-tools stage-jsqlite jerboa gen-embed + @echo "=== Compiling .sls -> .so ===" + $(JERBUILD) exec --libdirs "$(LIBDIRS_JSH)" build-all.ss + +jsh-compile: ensure-jerboa-tools stage-jsqlite ffi vendor-deps jerboa gen-embed + @echo "=== Compiling src/jsh/*.sls -> .so ===" + $(JERBUILD) exec --libdirs "$(LIBDIRS_JSH)" build-jsh.ss + +jsh-binary: + @case "$$(uname -s)" in \ + Darwin) $(MAKE) jsh-macos ;; \ + Linux) if [ -n "$$TERMUX_VERSION" ] || [ -d /data/data/com.termux ]; then \ + echo "ERROR: Android/Termux build target is not present in this repo" >&2; exit 1; \ + elif command -v musl-gcc >/dev/null 2>&1; then \ + echo "=== Building static musl jsh-linux-$(HOST_ARCH) (native) ===" && \ + $(MAKE) ffi-shim-symbols.list ffi jsh-compile && \ + JSH_FEATURES="$(JSH_FEATURES)" ./build-jsh-musl.sh && \ + cp -f jsh-musl jsh-linux-$(HOST_ARCH) && rm -f jsh-musl jsh-musl.sha256 && \ + { sha256sum jsh-linux-$(HOST_ARCH) 2>/dev/null || shasum -a 256 jsh-linux-$(HOST_ARCH); } > jsh-linux-$(HOST_ARCH).sha256 && \ + ls -lh jsh-linux-$(HOST_ARCH); \ + else $(MAKE) jsh-linux-native; fi ;; \ + FreeBSD) $(MAKE) jsh-freebsd-amd64 ;; \ + *) echo "Unsupported platform: $$(uname -s)" >&2; exit 1 ;; \ + esac + +jsh: jsh-binary + @echo "=== jsh binary ready ===" + @case "$$(uname -s)" in \ + Darwin) cp -f jsh-macos jsh ;; \ + Linux) cp -f jsh-linux-$(HOST_ARCH) jsh 2>/dev/null || cp -f jsh-linux-native jsh ;; \ + FreeBSD) cp -f jsh-freebsd-$(HOST_ARCH) jsh 2>/dev/null || cp -f jsh-freebsd jsh ;; \ + esac @ls -lh jsh +jsh-run: stage-jsqlite + $(JSH_FFI_ENV) $(JEXEC) jsh.ss + +musl: ensure-jerboa-tools + @echo "=== Testing musl module ===" + @tmp="$$(mktemp /tmp/jsh-musl-check.XXXXXX.ss)"; \ + printf '%s\n' \ + '#!chezscheme' \ + '(import (jerboa build musl))' \ + '(display "musl-gcc: ")' \ + '(display (if (musl-available?) "available" "not found"))' \ + '(newline)' \ + '(when (musl-available?)' \ + ' (let ([status (validate-musl-setup)])' \ + ' (display "Status: ")' \ + ' (display (car status))' \ + ' (display " - ")' \ + ' (display (cdr status))' \ + ' (newline)))' > "$$tmp"; \ + $(JERBUILD) exec --libdirs "$(JH)/lib" "$$tmp" || true; \ + rm -f "$$tmp" + +gen-embed: ensure-jerboa-tools vendor-native-rs-pruned + @if [ -f src/jsh/embed-data.sls ] && grep -q 'embed-encrypted? #t' src/jsh/embed-data.sls; then \ + echo "=== Skipping gen-embed (encrypted embed-data.sls already exists) ==="; \ + elif [ -t 0 ]; then \ + echo "=== Generating embed-data.sls (from $(JSH_EMBED)/) ==="; \ + $(MAKE) jerboa-native-host; \ + JSH_BUILD_ROOT="$(CURDIR)" JSH_EMBED_DIR="$(abspath $(JSH_EMBED))" \ + JERBOA_NATIVE_LIB="$(abspath $(JERBOA_NATIVE_HOST_LIB))" \ + JSH_DEV_NATIVE=1 $(JEXEC) gen-embed.ss; \ + elif [ ! -f src/jsh/embed-data.sls ]; then \ + echo "=== Generating empty embed-data.sls (batch mode, no TTY) ==="; \ + mkdir -p src/jsh; \ + printf '#!chezscheme\n(library (jsh embed-data)\n (export %%embed-file-table %%embed-encrypted? %%embed-salt %%record-pubkey)\n (import (chezscheme))\n (define %%embed-encrypted? #f)\n (define %%embed-salt #vu8())\n (define %%record-pubkey #f)\n (define %%embed-file-table (make-hashtable string-hash string=?))\n)\n' > src/jsh/embed-data.sls; \ + else \ + echo "=== Skipping gen-embed (batch mode, using existing embed-data.sls) ==="; \ + fi + +jerboa-native-host: vendor-native-rs-pruned + @echo "=== Ensuring host libjerboa_native$(SO_EXT) exists for gen-embed (--features $(JERBOA_NATIVE_HOST_FEATURES)) ===" + cd $(JERBOA_NATIVE_RS_DIR) && \ + RUSTC="$$(rustup which rustc 2>/dev/null || command -v rustc)" \ + "$$(rustup which cargo 2>/dev/null || command -v cargo)" build \ + --release --no-default-features --features "$(JERBOA_NATIVE_HOST_FEATURES)" + @if command -v nm >/dev/null 2>&1 && nm -g "$(JERBOA_NATIVE_HOST_LIB)" 2>/dev/null | grep -Eq 'jerboa_sqlite_|sqlite3_'; then \ + echo "ERROR: native SQLite symbols found in $(JERBOA_NATIVE_HOST_LIB); jsh must use jsqlite" >&2; exit 1; \ + fi + @test -f $(JERBOA_NATIVE_HOST_LIB) + +gen-certs: + @mkdir -p $(HOME)/.embed/tls + @echo "=== Generating self-signed TLS certs ===" + openssl req -x509 -newkey ed25519 -noenc -days 365 \ + -subj '/CN=jsh-mux' -addext 'subjectAltName=IP:0.0.0.0' \ + -keyout $(HOME)/.embed/tls/key.pem -out $(HOME)/.embed/tls/cert.pem 2>/dev/null + @echo " cert: $(HOME)/.embed/tls/cert.pem" + @echo " key: $(HOME)/.embed/tls/key.pem" + @openssl x509 -in $(HOME)/.embed/tls/cert.pem -fingerprint -sha256 -noout 2>/dev/null || true + +embed-crypto: + @echo "=== embed-crypto: now served by libjerboa_native (W-1 / L-1) -- no-op ===" + +check-freebsd-amd64-host: + @case "$$(uname -s)-$$(uname -m)" in \ + FreeBSD-x86_64|FreeBSD-amd64) ;; \ + *) echo "ERROR: .jerbuild.freebsd-amd64 must run on FreeBSD amd64" >&2; exit 1 ;; \ + esac + +jsh-freebsd: check-freebsd-amd64-host ensure-jerboa-tools ensure-jsqlite ffi-shim-symbols.list vendor-deps jerboa vendor-staged-libs gen-embed + @echo "=== Building jsh for FreeBSD with jerbuild ===" + JERBUILD="$(JERBUILD)" JSH_FEATURES="$(JSH_FEATURES)" $(JERBUILD) build --config .jerbuild.freebsd-amd64 + +jsh-freebsd-base: + @$(MAKE) jsh-freebsd JSH_FEATURES=none + +jsh-freebsd-full: + @$(MAKE) jsh-freebsd JSH_FEATURES=all + +jsh-freebsd-minimal: + @echo "=== jsh-freebsd-minimal is a compatibility alias; building with JSH_FEATURES=none ===" + @$(MAKE) jsh-freebsd-base + +freebsd-amd64: + @case "$$(uname -s)-$$(uname -m)" in \ + FreeBSD-x86_64|FreeBSD-amd64) $(MAKE) jsh-freebsd ;; \ + *) echo "ERROR: build freebsd-amd64 natively on a FreeBSD amd64 host (no Podman cross-build)." >&2; exit 1 ;; \ + esac + @cp -f jsh-freebsd jsh-freebsd-amd64 + @ls -lh jsh-freebsd-amd64 + @file jsh-freebsd-amd64 + +freebsd-arm64: + @echo "ERROR: obersh does not provide a FreeBSD arm64 build path yet; only freebsd-amd64 is available." >&2 + @exit 1 + +freebsd: freebsd-amd64 +jsh-freebsd-amd64: freebsd-amd64 +jsh-freebsd-arm64: freebsd-arm64 + +JERBOA_HOME ?= $(VENDOR)/jerboa +SCHEME ?= $(JERBOA_HOME)/.chez/bin/scheme +XC_LIBDIRS = lib:src:_jerbuild-stage:$(VENDOR)/jerboa/lib:$(VENDOR)/jerboa-ssh/lib:$(VENDOR)/jerboa-crypto/src:$(COREUTILS):$(JERBOA_AWK):$(JERBOA_SED):$(JERBOA_AWS):$(JERBOA_WORMHOLE):$(VENDOR)/jerboa-fuse/lib:$(VENDOR)/jerboa-ssl/src:$(VENDOR)/jerboa/src:$(VENDOR)/jerboa-fuse/src:$(JERBOA_YUBIKEY_LIB) +XC_NATIVE_RS_DIR ?= $(if $(wildcard $(VENDOR)/jerboa-native-rs/Cargo.toml),$(VENDOR)/jerboa-native-rs,$(JERBOA_HOME)/jerboa-native-rs) +XC_NATIVE_MUSL_AMD64_LIB = $(XC_NATIVE_RS_DIR)/target/x86_64-unknown-linux-musl/release/libjerboa_native.a +XC_NATIVE_MUSL_ARM64_LIB = $(XC_NATIVE_RS_DIR)/target/aarch64-unknown-linux-musl/release/libjerboa_native.a +JERBOA_NATIVE_FEATURES ?= $(JSH_NATIVE_NO_SQLITE_FEATURES) +XC_YUBIKEY_NATIVE_DIR ?= $(JERBOA_YUBIKEY_REPO)/yubikey-native +XC_YUBIKEY_NATIVE_MUSL_AMD64_LIB = $(XC_YUBIKEY_NATIVE_DIR)/target/x86_64-unknown-linux-musl/release/libyubikey_native.a +XC_YUBIKEY_NATIVE_MUSL_ARM64_LIB = $(XC_YUBIKEY_NATIVE_DIR)/target/aarch64-unknown-linux-musl/release/libyubikey_native.a +XC_WIPE = rm -f jsh-generated.so jsh-generated.wpo jsh-libs.boot jsh-linux-amd64.wp.so jsh-linux-arm64.wp.so 2>/dev/null; for d in $$(printf '%s' "$(XC_LIBDIRS)" | tr ':' ' '); do [ -d "$$d" ] && find "$$d" -type f \( -name '*.so' -o -name '*.wpo' \) -delete 2>/dev/null; done; true + +rust-musl-target-amd64: + @for dir in . "$(XC_YUBIKEY_NATIVE_DIR)"; do \ + [ -d "$$dir" ] || continue; \ + (cd "$$dir" && rustup target list --installed 2>/dev/null | grep -qx x86_64-unknown-linux-musl) || \ + { echo "=== rustup target add x86_64-unknown-linux-musl ($$dir) ==="; \ + (cd "$$dir" && rustup target add x86_64-unknown-linux-musl); }; \ + done + +rust-musl-target-arm64: + @for dir in . "$(XC_YUBIKEY_NATIVE_DIR)"; do \ + [ -d "$$dir" ] || continue; \ + (cd "$$dir" && rustup target list --installed 2>/dev/null | grep -qx aarch64-unknown-linux-musl) || \ + { echo "=== rustup target add aarch64-unknown-linux-musl ($$dir) ==="; \ + (cd "$$dir" && rustup target add aarch64-unknown-linux-musl); }; \ + done + +chez-cross-amd64: vendor-deps-linux + @test -d $(JERBOA_HOME)/.chez-cross-ta6le || { \ + command -v x86_64-linux-musl-gcc >/dev/null 2>&1 || { echo "ERROR: x86_64-linux-musl-gcc not on PATH (brew install FiloSottile/musl-cross/musl-cross --with-x86_64)" >&2; exit 1; }; \ + echo "=== Building ta6le cross Chez in $(JERBOA_HOME) (one-time) ==="; \ + $(MAKE) -C $(JERBOA_HOME) chez-cross CHEZ_TARGET_MACHINE=ta6le CROSS_CC=x86_64-linux-musl-gcc; \ + } + +chez-cross-arm64: vendor-deps-linux + @test -d $(JERBOA_HOME)/.chez-cross-tarm64le || { \ + command -v aarch64-linux-musl-gcc >/dev/null 2>&1 || { echo "ERROR: aarch64-linux-musl-gcc not on PATH (brew install FiloSottile/musl-cross/musl-cross --with-aarch64)" >&2; exit 1; }; \ + echo "=== Building tarm64le cross Chez in $(JERBOA_HOME) (one-time) ==="; \ + $(MAKE) -C $(JERBOA_HOME) chez-cross CHEZ_TARGET_MACHINE=tarm64le CROSS_CC=aarch64-linux-musl-gcc; \ + } + +rust-coreutils-linux-amd64: rust-musl-target-amd64 + @command -v x86_64-linux-musl-gcc >/dev/null 2>&1 || { echo "ERROR: x86_64-linux-musl-gcc not on PATH (brew install FiloSottile/musl-cross/musl-cross --with-x86_64)" >&2; exit 1; } + @if [ ! -f rust-coreutils/target/x86_64-unknown-linux-musl/release/libjsh_coreutils.a ]; then \ + echo "=== Building Rust coreutils for x86_64 Linux musl ==="; \ + RUSTC="$$(rustup which rustc 2>/dev/null || command -v rustc)" \ + CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=x86_64-linux-musl-gcc \ + "$$(rustup which cargo 2>/dev/null || command -v cargo)" build \ + --manifest-path rust-coreutils/Cargo.toml --release --target x86_64-unknown-linux-musl; \ + else echo "=== libjsh_coreutils.a (x86_64-musl) already built ==="; fi + +rust-coreutils-linux-arm64: rust-musl-target-arm64 + @command -v aarch64-linux-musl-gcc >/dev/null 2>&1 || { echo "ERROR: aarch64-linux-musl-gcc not on PATH (brew install FiloSottile/musl-cross/musl-cross --with-aarch64)" >&2; exit 1; } + @if [ ! -f rust-coreutils/target/aarch64-unknown-linux-musl/release/libjsh_coreutils.a ]; then \ + echo "=== Building Rust coreutils for aarch64 Linux musl ==="; \ + RUSTC="$$(rustup which rustc 2>/dev/null || command -v rustc)" \ + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc \ + "$$(rustup which cargo 2>/dev/null || command -v cargo)" build \ + --manifest-path rust-coreutils/Cargo.toml --release --target aarch64-unknown-linux-musl; \ + else echo "=== libjsh_coreutils.a (aarch64-musl) already built ==="; fi + +vendor-staged-libs: vendor-deps + @VENDOR="$(VENDOR)" \ + MAKE="$(MAKE)" \ + JERBUILD="$(JERBUILD)" \ + JERBOA_SSH_REPO="$(JERBOA_SSH_REPO)" \ + JERBOA_FUSE_REPO="$(JERBOA_FUSE_REPO)" \ + JERBOA_YUBIKEY_REPO="$(JERBOA_YUBIKEY_REPO)" \ + $(VENDOR_STAGED_LIBS_SCRIPT) stage + +check-vendor-staged-libs-fresh: vendor-deps + @VENDOR="$(VENDOR)" \ + MAKE="$(MAKE)" \ + JERBUILD="$(JERBUILD)" \ + JERBOA_SSH_REPO="$(JERBOA_SSH_REPO)" \ + JERBOA_FUSE_REPO="$(JERBOA_FUSE_REPO)" \ + JERBOA_YUBIKEY_REPO="$(JERBOA_YUBIKEY_REPO)" \ + $(VENDOR_STAGED_LIBS_SCRIPT) check + +jerboa-yubikey-lib: vendor-staged-libs +check-yubikey-lib-fresh: check-vendor-staged-libs-fresh + +jerboa-native-linux-amd64: rust-musl-target-amd64 vendor-native-rs-pruned + @command -v x86_64-linux-musl-gcc >/dev/null 2>&1 || { echo "ERROR: x86_64-linux-musl-gcc not on PATH (brew install FiloSottile/musl-cross/musl-cross --with-x86_64)" >&2; exit 1; } + @echo "=== Building jerboa-native-rs for x86_64 Linux musl (--features $(JERBOA_NATIVE_FEATURES)) ===" + cd $(XC_NATIVE_RS_DIR) && \ + RUSTC="$$(rustup which rustc 2>/dev/null || command -v rustc)" \ + CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=x86_64-linux-musl-gcc \ + "$$(rustup which cargo 2>/dev/null || command -v cargo)" build \ + --release --target x86_64-unknown-linux-musl --no-default-features --features "$(JERBOA_NATIVE_FEATURES)" + @if command -v nm >/dev/null 2>&1 && nm -g "$(XC_NATIVE_MUSL_AMD64_LIB)" 2>/dev/null | grep -Eq 'jerboa_sqlite_|sqlite3_'; then \ + echo "ERROR: native SQLite symbols found in $(XC_NATIVE_MUSL_AMD64_LIB); jsh must use jsqlite" >&2; exit 1; \ + fi + @test -f $(XC_NATIVE_MUSL_AMD64_LIB) + +jerboa-native-linux-arm64: rust-musl-target-arm64 vendor-native-rs-pruned + @command -v aarch64-linux-musl-gcc >/dev/null 2>&1 || { echo "ERROR: aarch64-linux-musl-gcc not on PATH (brew install FiloSottile/musl-cross/musl-cross --with-aarch64)" >&2; exit 1; } + @echo "=== Building jerboa-native-rs for aarch64 Linux musl (--features $(JERBOA_NATIVE_FEATURES)) ===" + cd $(XC_NATIVE_RS_DIR) && \ + RUSTC="$$(rustup which rustc 2>/dev/null || command -v rustc)" \ + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc \ + "$$(rustup which cargo 2>/dev/null || command -v cargo)" build \ + --release --target aarch64-unknown-linux-musl --no-default-features --features "$(JERBOA_NATIVE_FEATURES)" + @if command -v nm >/dev/null 2>&1 && nm -g "$(XC_NATIVE_MUSL_ARM64_LIB)" 2>/dev/null | grep -Eq 'jerboa_sqlite_|sqlite3_'; then \ + echo "ERROR: native SQLite symbols found in $(XC_NATIVE_MUSL_ARM64_LIB); jsh must use jsqlite" >&2; exit 1; \ + fi + @test -f $(XC_NATIVE_MUSL_ARM64_LIB) + +yubikey-native-linux-amd64: rust-musl-target-amd64 + @command -v x86_64-linux-musl-gcc >/dev/null 2>&1 || { echo "ERROR: x86_64-linux-musl-gcc not on PATH (brew install FiloSottile/musl-cross/musl-cross --with-x86_64)" >&2; exit 1; } + @echo "=== Building yubikey-native (nusb, no FIDO2) for x86_64 Linux musl ===" + cd $(XC_YUBIKEY_NATIVE_DIR) && \ + RUSTC="$$(rustup which rustc 2>/dev/null || command -v rustc)" \ + CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER=x86_64-linux-musl-gcc \ + "$$(rustup which cargo 2>/dev/null || command -v cargo)" build \ + --release --target x86_64-unknown-linux-musl --no-default-features + @test -f $(XC_YUBIKEY_NATIVE_MUSL_AMD64_LIB) + +yubikey-native-linux-arm64: rust-musl-target-arm64 + @command -v aarch64-linux-musl-gcc >/dev/null 2>&1 || { echo "ERROR: aarch64-linux-musl-gcc not on PATH (brew install FiloSottile/musl-cross/musl-cross --with-aarch64)" >&2; exit 1; } + @echo "=== Building yubikey-native (nusb, no FIDO2) for aarch64 Linux musl ===" + cd $(XC_YUBIKEY_NATIVE_DIR) && \ + RUSTC="$$(rustup which rustc 2>/dev/null || command -v rustc)" \ + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-musl-gcc \ + "$$(rustup which cargo 2>/dev/null || command -v cargo)" build \ + --release --target aarch64-unknown-linux-musl --no-default-features + @test -f $(XC_YUBIKEY_NATIVE_MUSL_ARM64_LIB) + +linux-amd64: stage-jsqlite vendor-deps-linux jerboa gen-embed chez-cross-amd64 rust-coreutils-linux-amd64 jerboa-native-linux-amd64 vendor-staged-libs yubikey-native-linux-amd64 + @echo "=== Wiping stale per-arch compiled artifacts (.so/.wpo/boot) ===" + @$(XC_WIPE) + @features="$(JSH_CROSS_FEATURES)"; \ + echo "=== Cross-building jsh-linux-amd64 (x86_64 Linux static, JSH_FEATURES=$$features) ==="; \ + JERBOA_HOME=$(JERBOA_HOME) TARGET_ARCH=amd64 JSH_FEATURES="$$features" JERBOA_NATIVE_FEATURES="$(JERBOA_NATIVE_FEATURES)" JERBOA_NATIVE_A="$(XC_NATIVE_MUSL_AMD64_LIB)" YUBIKEY_NATIVE_A="$(XC_YUBIKEY_NATIVE_MUSL_AMD64_LIB)" $(SCHEME) -q --libdirs "$(XC_LIBDIRS)" --script build-jsh-cross.ss + @echo "=== jsh-linux-amd64 ready ===" + @ls -lh jsh-linux-amd64 + @file jsh-linux-amd64 + +linux-arm64: stage-jsqlite vendor-deps-linux jerboa gen-embed chez-cross-arm64 rust-coreutils-linux-arm64 jerboa-native-linux-arm64 vendor-staged-libs yubikey-native-linux-arm64 + @echo "=== Wiping stale per-arch compiled artifacts (.so/.wpo/boot) ===" + @$(XC_WIPE) + @features="$(JSH_CROSS_FEATURES)"; \ + echo "=== Cross-building jsh-linux-arm64 (aarch64 Linux static, JSH_FEATURES=$$features) ==="; \ + JERBOA_HOME=$(JERBOA_HOME) TARGET_ARCH=arm64 JSH_FEATURES="$$features" JERBOA_NATIVE_FEATURES="$(JERBOA_NATIVE_FEATURES)" JERBOA_NATIVE_A="$(XC_NATIVE_MUSL_ARM64_LIB)" YUBIKEY_NATIVE_A="$(XC_YUBIKEY_NATIVE_MUSL_ARM64_LIB)" $(SCHEME) -q --libdirs "$(XC_LIBDIRS)" --script build-jsh-cross.ss + @echo "=== jsh-linux-arm64 ready ===" + @ls -lh jsh-linux-arm64 + @file jsh-linux-arm64 + +jsh-linux-native: ensure-jerboa-tools ensure-jsqlite ffi-shim-symbols.list vendor-deps jerboa vendor-staged-libs gen-embed + @case "$$(uname -s)-$$(uname -m)" in \ + Linux-x86_64|Linux-amd64) ;; \ + *) echo "ERROR: .jerbuild.linux-amd64 must run on Linux amd64" >&2; exit 1 ;; \ + esac + @echo "=== Building native Linux amd64 jsh with jerbuild ===" + JERBUILD="$(JERBUILD)" JSH_FEATURES="$(JSH_FEATURES)" $(JERBUILD) build --config .jerbuild.linux-amd64 + +linux: linux-amd64 +jsh-linux-amd64: linux-amd64 +jsh-linux-arm64: linux-arm64 + +linux-amd64-hardened: + @echo "ERROR: hardened Linux builds require the newer obersh cross driver with OUTPUT_NAME/JSH_EMBED_OBFUSCATE support." >&2 + @exit 1 + +linux-arm64-hardened: + @echo "ERROR: hardened Linux builds require the newer obersh cross driver with OUTPUT_NAME/JSH_EMBED_OBFUSCATE support." >&2 + @exit 1 + +jsh-linux-amd64-hardened: linux-amd64-hardened +jsh-linux-arm64-hardened: linux-arm64-hardened + +linux-amd64-refresh-yubikey: + @$(MAKE) linux-amd64 VENDOR_REFRESH=jerboa-yubikey + +linux-arm64-refresh-yubikey: + @$(MAKE) linux-arm64 VENDOR_REFRESH=jerboa-yubikey + +test-linux-amd64: + @test -x ./jsh-linux-amd64 || { echo "ERROR: ./jsh-linux-amd64 not built; run 'make linux-amd64' first" >&2; exit 1; } + @$(PODMAN) --version >/dev/null 2>&1 || { echo "ERROR: Podman not available via PODMAN=$(PODMAN)" >&2; exit 1; } + @echo "=== test-linux-amd64: alpine:3 x86_64 ===" + $(PODMAN) run --rm --platform linux/amd64 -v "$$PWD:/work" -w /work docker.io/library/alpine:3 \ + ./jsh-linux-amd64 -c 'echo hello world; echo $$((2 + 3)); for i in 1 2 3; do echo "i=$$i"; done' + +test-linux: test-linux-amd64 + +check-android-host: + @case "$$(uname -s)" in \ + Linux) if [ -n "$$TERMUX_VERSION" ] || [ -d /data/data/com.termux ]; then :; \ + else echo "ERROR: Android targets require Termux/Android support, which is not present in this repo." >&2; exit 1; fi ;; \ + *) echo "ERROR: Android targets require Termux/Android support, which is not present in this repo." >&2; exit 1 ;; \ + esac + +jsh-android jsh-android-native jsh-android-cross jsh-android-minimal jsh-android-full android: check-android-host + @echo "ERROR: Android build scripts are not present in this repo; use obersh for Android targets." >&2 + @exit 1 + +rust-wireguard-host rust-wireguard-linux-amd64 rust-wireguard-linux-arm64: + @echo "ERROR: WireGuard native library sources are not present in this repo; use obersh for WireGuard targets." >&2 + @exit 1 + +VENDOR_REPOS ?= jerboa-coreutils jerboa-awk jerboa-sed jerboa-aws jerboa-yubikey jerboa-ssh jerboa-fuse jerboa-wormhole +VENDOR_REFRESH ?= +LINUX_VENDOR_REPOS = jerboa jerboa-coreutils jerboa-awk jerboa-sed jerboa-aws jerboa-yubikey jerboa-ssh jerboa-fuse jerboa-wormhole + +vendor-deps-linux: VENDOR_REPOS := $(LINUX_VENDOR_REPOS) +vendor-deps-linux: vendor-deps + +vendor-deps: ensure-jsqlite vendor-native-rs-pruned + @echo "=== Vendoring dependencies into vendor/ ===" + @mkdir -p vendor + @for repo in $(VENDOR_REPOS); do \ + needs_clone=0; \ + case " $(VENDOR_REFRESH) " in \ + *" $$repo "*|*" all "*) \ + echo " (refresh) $$repo -- removing existing clone"; \ + rm -rf "vendor/$$repo"; \ + needs_clone=1; \ + ;; \ + esac; \ + if [ ! -d "vendor/$$repo" ]; then \ + needs_clone=1; \ + elif [ ! -f "vendor/$$repo/README.md" ] && [ ! -f "vendor/$$repo/Makefile" ] && [ ! -f "vendor/$$repo/Cargo.toml" ]; then \ + echo " (incomplete) $$repo -- re-cloning"; \ + rm -rf "vendor/$$repo"; \ + needs_clone=1; \ + fi; \ + if [ "$$needs_clone" = "1" ]; then \ + echo " Cloning $$repo..."; \ + git clone --depth 1 "https://git.sr.ht/~lisp/$$repo" "vendor/$$repo" 2>&1 | tail -1; \ + rm -rf "vendor/$$repo/.git"; \ + else \ + echo " (exists) $$repo"; \ + fi; \ + done + @if [ -f "$(JERBOA_AWK)/jerboa-awk/runtime.sls" ]; then \ + perl -0pi -e 's/ \(define \*regex-cache\* \(make-regex-cache\)\)\n\n \(define \(make-regex-cache\)\n \(make-hashtable equal-hash equal\?\)\)/ (define (make-regex-cache)\n (make-hashtable equal-hash equal?))\n\n (define *regex-cache* (make-regex-cache))/m' \ + "$(JERBOA_AWK)/jerboa-awk/runtime.sls"; \ + fi + @VENDOR="$(VENDOR)" JERBOA_SSH_REPO="$(JERBOA_SSH_REPO)" bash "$(VENDOR_OVERRIDES_SCRIPT)" ssh + @echo "=== Vendor complete ===" + +vendor-native-rs: $(JERBOA_NATIVE_RS_DIR) + +vendor-native-rs-pruned: vendor-native-rs + @echo "=== Pruning unused native Rust packages from vendored jerboa-native-rs ===" + @test -f "$(JERBOA_NATIVE_RS_DIR)/Cargo.toml" + @perl -0pi -e 's/^libc = "=0\.2\.[0-9]+"/libc = "0.2"/m; s/^(duckdb|rusqlite|postgres|wasmi|getrandom|rscap|mozjs|mozjs_sys)\s*=\s*\{[^\n]*\}\n//mg; s/^(duckdb_feat|sqlite|postgres_feat|wasm|pcap|spidermonkey)\s*=\s*\[[^\n]*\]\n//mg; s/,\s*"(duckdb_feat|sqlite|postgres_feat|wasm|pcap|spidermonkey)"//g; s/"(duckdb_feat|sqlite|postgres_feat|wasm|pcap|spidermonkey)"\s*,\s*//g; s/"(duckdb_feat|sqlite|postgres_feat|wasm|pcap|spidermonkey)"//g' "$(JERBOA_NATIVE_RS_DIR)/Cargo.toml" + @if [ -f "$(JERBOA_NATIVE_RS_DIR)/src/lib.rs" ]; then \ + perl -0pi -e 's/\n#\[cfg\(feature = "(duckdb_feat|sqlite|postgres_feat|wasm|pcap|spidermonkey)"\)\]\nmod (duckdb_native|sqlite|postgres_native|wasm|pcap_capture|wasm_sm);\n/\n/g' "$(JERBOA_NATIVE_RS_DIR)/src/lib.rs"; \ + perl -0pi -e 's/\n#\[cfg\(feature = "crypto"\)\]\nmod ed25519;\n/\n/g' "$(JERBOA_NATIVE_RS_DIR)/src/lib.rs"; \ + fi + @rm -f "$(JERBOA_NATIVE_RS_DIR)/src/duckdb_native.rs" "$(JERBOA_NATIVE_RS_DIR)/src/sqlite.rs" \ + "$(JERBOA_NATIVE_RS_DIR)/src/postgres_native.rs" "$(JERBOA_NATIVE_RS_DIR)/src/wasm.rs" \ + "$(JERBOA_NATIVE_RS_DIR)/src/pcap_capture.rs" "$(JERBOA_NATIVE_RS_DIR)/src/ed25519.rs" \ + "$(JERBOA_NATIVE_RS_DIR)/src/wasm_sm.rs" + @VENDOR="$(VENDOR)" JERBOA_NATIVE_RS_DIR="$(JERBOA_NATIVE_RS_DIR)" bash "$(VENDOR_OVERRIDES_SCRIPT)" native + @if grep -R -n -E 'duckdb|duckdb_feat|libduckdb|DUCKDB' "$(JERBOA_NATIVE_RS_DIR)/Cargo.toml" "$(JERBOA_NATIVE_RS_DIR)/src" | grep -v -E '^[^:]+:[0-9]+:[[:space:]]*#'; then \ + echo "ERROR: DuckDB references remain in vendored jerboa-native-rs" >&2; exit 1; \ + fi + @if grep -R -n -E 'rusqlite|sqlite3|jerboa_sqlite_|^sqlite\s*=|feature = "sqlite"' "$(JERBOA_NATIVE_RS_DIR)/Cargo.toml" "$(JERBOA_NATIVE_RS_DIR)/src" | grep -v -E '^[^:]+:[0-9]+:[[:space:]]*#'; then \ + echo "ERROR: native SQLite references remain in vendored jerboa-native-rs" >&2; exit 1; \ + fi + +$(JERBOA_NATIVE_RS_DIR): + @mkdir -p vendor + @echo "=== Sparse-cloning jerboa-native-rs from the jerboa monorepo ===" + @tmp="vendor/.jerboa-native-rs-monorepo"; \ + rm -rf "$$tmp"; \ + git clone --depth 1 --filter=blob:none --sparse https://git.sr.ht/~lisp/jerboa "$$tmp"; \ + git -C "$$tmp" sparse-checkout set jerboa-native-rs; \ + mv "$$tmp/jerboa-native-rs" "$(JERBOA_NATIVE_RS_DIR)"; \ + rm -rf "$$tmp" + +verify-harden: binary + @echo "=== Verifying binary hardening ($(JSH)) ===" + @echo "--- Symbol check (should show 'stripped') ---" + @(file $(JSH) | grep -qE 'stripped|no section header') && echo " PASS: binary is stripped" || echo " FAIL: binary not stripped" + @echo "--- Build path leak check ---" + @if strings $(JSH) | grep -q "$(HOME)"; then \ + echo " WARN: home directory path found in binary"; \ + else \ + echo " PASS: no home directory paths leaked"; \ + fi + @echo "--- Integrity hash check ---" + @if [ -f $(JSH).sha256 ]; then \ + echo " PASS: $(JSH).sha256 exists ($$(wc -c < $(JSH).sha256) bytes)"; \ + else \ + echo " FAIL: $(JSH).sha256 not found"; \ + fi + @echo "--- Runtime hardening check (JSH_DEV=1 to skip) ---" + @JSH_DEV=1 $(JSH) -c 'echo ok' >/dev/null 2>&1 && echo " PASS: binary runs in dev mode" || echo " FAIL: binary doesn't run" + @echo "=== Hardening verification complete ===" + +check-platforms: + @tools/check-platforms.sh + +test-security-policy: security + +test-mux: + @echo "ERROR: mux test scripts are not present in this repo; use obersh for mux tests." >&2 + @exit 1 + +test-mux-screen: + @echo "ERROR: mux screen comparison scripts are not present in this repo; use obersh for mux tests." >&2 + @exit 1 + +test-mux-tcp: + @echo "ERROR: mux TCP test scripts are not present in this repo; use obersh for mux tests." >&2 + @exit 1 + +MACOS_LIBS_TAG ?= macos-libs-v1 +MACOS_ARCH := $(shell uname -m | sed 's/arm64/arm64/;s/x86_64/x86_64/') + +upload-macos-libs: + @echo "ERROR: upload-macos-libs requires rust-coreutils/native archive assets that are not present in this repo; use obersh for that release helper." >&2 + @exit 1 + +PREFIX ?= $(HOME)/.local +SHIM_DIR ?= $(HOME)/.jsh/shims + +install: install-binary install-shims + +install-binary: $(if $(JSH_BIN),,jsh) + @set -e; \ + bin="$(JSH_BIN)"; \ + if [ -z "$$bin" ]; then \ + case "$$(uname -s)" in \ + Darwin) bin=./jsh-macos ;; \ + FreeBSD) bin=./jsh-freebsd ;; \ + Linux) case "$$(uname -m)" in \ + x86_64|amd64) bin=./jsh-linux-amd64 ;; \ + aarch64|arm64) bin=./jsh-linux-arm64 ;; \ + *) echo "no native jsh binary for Linux $$(uname -m)" >&2; exit 1 ;; \ + esac ;; \ + *) echo "no native jsh binary for $$(uname -s) -- set JSH_BIN=" >&2; exit 1 ;; \ + esac; \ + fi; \ + if [ ! -x "$$bin" ]; then \ + echo "$$bin not built -- run the matching build target first" >&2; \ + exit 1; \ + fi; \ + mkdir -p "$(PREFIX)/bin"; \ + install -m 755 "$$bin" "$(PREFIX)/bin/jsh"; \ + echo "installed $$bin -> $(PREFIX)/bin/jsh" + +install-shims: + @set -e; \ + mkdir -p "$(SHIM_DIR)"; \ + chmod 700 "$(SHIM_DIR)"; \ + count=0; \ + for f in shims/*; do \ + case "$$f" in *.md|*/README*) continue ;; esac; \ + [ -f "$$f" ] || continue; \ + install -m 755 "$$f" "$(SHIM_DIR)/$$(basename "$$f")"; \ + count=$$((count+1)); \ + done; \ + echo "installed $$count shim(s) -> $(SHIM_DIR)" + +install-hooks: + @if [ ! -d .git ]; then echo "not a git checkout -- cannot install hooks" >&2; exit 1; fi + @mkdir -p .git/hooks + @ln -sf ../../tools/pre-commit .git/hooks/pre-commit + @echo "Installed pre-commit hook -> tools/pre-commit" + +learn-codex-shim: + @echo "ERROR: tools/jsh-learn-policy is not present in this repo; use obersh for learn-codex-shim." >&2 + @exit 1 + run: ensure-jerboa-tools $(FFI_LIB) jerboa $(JSH_FFI_ENV) $(JERBUILD) exec --libdirs "$(LIBDIRS)" jsh.ss @@ -290,16 +889,44 @@ compat-debug: $(OILS_DIR) clean: find src -type f \( -name '*.so' -o -name '*.wpo' -o -name '*.sls' -o -name '*.tarm64osx' -o -name '*.tx86_64osx' -o -name '*.ta6le' -o -name '*.ta6fb' \) -delete 2>/dev/null || true rm -rf jsh-src _jerbuild-stage - rm -f jsh jsh-macos jsh-macos.sha256 ffi-shim-symbols.list libjsh-ffi.dylib libjsh-ffi.so + rm -f jsh jsh-macos jsh-macos.sha256 jsh-macos-base jsh-macos-base.sha256 jsh-macos-full jsh-macos-full.sha256 + rm -f jsh-linux-amd64 jsh-linux-amd64.sha256 jsh-linux-amd64-main.c jsh-linux-amd64.wp.so + rm -f jsh-linux-arm64 jsh-linux-arm64.sha256 jsh-linux-arm64-main.c jsh-linux-arm64.wp.so + rm -f jsh-freebsd jsh-freebsd.sha256 jsh-freebsd-amd64 jsh-freebsd-amd64.sha256 + rm -f jsh-musl jsh-musl.sha256 jsh-generated.ss jsh-generated.so jsh-generated.wpo jsh.boot jsh-libs.boot + rm -f petite_boot.h scheme_boot.h jsh_libs_boot.h jsh_program.h + rm -f ffi-shim-symbols.list libjsh-ffi.dylib libjsh-ffi.so libjsh-ffi-musl.so rm -rf dist help: @echo "jsh — compact Jerboa shell" @echo "" - @echo "Build:" + @echo "Build (native = host os/arch; cross = build another platform from any host):" + @echo " make build Alias for make binary" + @echo " make binary Native build for this host" + @echo " make jsh Native binary for this host, copied to ./jsh" @echo " make jsh-macos Build the macOS binary" - @echo " make jsh Build and copy the native binary to ./jsh" - @echo " make extras Select and build optional feature bundles" + @echo " make jsh-macos-base macOS base build (core shell only)" + @echo " make jsh-macos-full macOS full build (all features)" + @echo " make macos Alias for jsh-macos" + @echo " make linux-amd64 Cross: linux/amd64 static ELF" + @echo " make linux-arm64 Cross: linux/arm64 static ELF" + @echo " make linux-amd64-hardened Present for obersh compatibility; unsupported here" + @echo " make linux-arm64-hardened Present for obersh compatibility; unsupported here" + @echo " make linux Alias for linux-amd64" + @echo " make jsh-linux-amd64 Alias for linux-amd64" + @echo " make jsh-linux-arm64 Alias for linux-arm64" + @echo " make freebsd-amd64 freebsd/amd64 (native on a FreeBSD amd64 host)" + @echo " make freebsd-arm64 Explicit unsupported target; obersh has no arm64 path yet" + @echo " make freebsd Alias for freebsd-amd64" + @echo " make jsh-freebsd FreeBSD binary via jerbuild" + @echo " make jsh-freebsd-base FreeBSD base build (core shell only)" + @echo " make jsh-freebsd-full FreeBSD full build (all features)" + @echo " make android Present for obersh compatibility; unsupported here" + @echo " make extras Select and build optional feature bundles (docs/extras.md)" + @echo " make vendor-deps Clone deps into vendor/ for self-contained builds" + @echo " make gen-embed Regenerate embedded file data" + @echo " make show-features Show current JSH_FEATURES/JERBOA_FEATURES selection" @echo "" @echo "Test:" @echo " make security Release security metadata and secret scan" @@ -313,7 +940,15 @@ help: @echo " make verify Tests, adversarial corpus, audit, timing evidence" @echo " make compat Oils POSIX shell compatibility report" @echo " make compat-one SPEC=name" + @echo " make test-linux-amd64 Optional smoke-run jsh-linux-amd64 under alpine via Podman" @echo " make bench Shell benchmarks" @echo "" + @echo "Install:" + @echo " make install Build/install jsh + shims" + @echo " make install-binary Install just the platform-native jsh binary" + @echo " make install-shims Install shims into ~/.jsh/shims" + @echo "" @echo "Other:" + @echo " make verify-harden Verify binary hardening" + @echo " make gen-certs Generate TLS certs for mux" @echo " make clean Remove generated artifacts" new file mode 100644 --- /dev/null +++ b/build-all.ss @@ -0,0 +1,18 @@ +#!chezscheme +;; Build driver: imports all modules to trigger Chez compilation. +;; Generates .so + .wpo files for the platform-specific binary builds +;; (build-jsh-{macos,musl,freebsd,android}.ss). +(parameterize ([compile-imported-libraries #t] + [generate-wpo-files #t] + [optimize-level 3] + [generate-inspector-information #f]) + (eval '(import + (jsh ast) (jsh registry) (jsh macros) (jsh util) + (jsh environment) (jsh lexer) (jsh arithmetic) (jsh glob) + (jsh fuzzy) (jsh history) (jsh parser) (jsh functions) + (jsh signals) (jsh expander) (jsh redirect) (jsh control) + (jsh jobs) (jsh builtins) (jsh pipeline) (jsh executor) + (jsh completion) (jsh prompt) (jsh lineedit) (jsh fzf) + (jsh script) (jsh startup) (jsh main) (jsh stage) + (jsh worm)) + (interaction-environment))) new file mode 100644 --- /dev/null +++ b/build-jsh-cross.ss @@ -0,0 +1,1255 @@ +#!chezscheme +;;; build-jsh-cross.ss — Cross-compile jsh from macOS to Linux musl +;;; +;;; Usage: +;;; JERBOA_HOME=vendor/jerboa TARGET_ARCH=amd64 scheme \ +;;; --libdirs <libs> --script build-jsh-cross.ss +;;; +;;; TARGET_ARCH selects the cross target: +;;; amd64 (default) — chez ta6le + x86_64-linux-musl-gcc → jsh-linux-amd64 +;;; arm64 — chez tarm64le + aarch64-linux-musl-gcc → jsh-linux-arm64 +;;; +;;; Cross-compile analogue of build-jsh-musl.ss that runs on a non-Linux host. +;;; Uses: +;;; - $JERBOA_HOME/.chez-cross-<machine>/ — cross-built Chez install +;;; - $JERBOA_HOME/build/chez/xc-<machine>/s/xpatch — host compiler emit mode +;;; - <toolchain>-linux-musl-gcc — C compile + final static link +;;; +;;; Produces: jsh-linux-{amd64,arm64} (static Linux ELF, no shared library deps) +;;; +;;; This is the simpler "WPO as program" pattern: jsh.ss + all imports are +;;; bundled into one .so via compile-whole-program, loaded at runtime via +;;; Sscheme_program — no jsh.boot bundling required. + +(import (chezscheme)) + +;; ── Params ────────────────────────────────────────────────────────────────── +(define jerboa-home + (or (getenv "JERBOA_HOME") + (format "~a/vendor/jerboa" (current-directory)))) + +(define target-arch (or (getenv "TARGET_ARCH") "amd64")) + +;; Per-arch dispatch: +;; (arch chez-machine c-toolchain rust-target output-name strip-tool) +(define arch-table + '(("amd64" "ta6le" "x86_64-linux-musl-gcc" "x86_64-unknown-linux-musl" "jsh-linux-amd64" "x86_64-linux-musl-strip") + ("arm64" "tarm64le" "aarch64-linux-musl-gcc" "aarch64-unknown-linux-musl" "jsh-linux-arm64" "aarch64-linux-musl-strip"))) + +(define arch-row + (or (assoc target-arch arch-table) + (error 'build-jsh-cross + (format "unknown TARGET_ARCH (expected amd64|arm64): ~a" target-arch)))) + +(define chez-machine (list-ref arch-row 1)) +(define rust-target (list-ref arch-row 3)) + +(define cross-prefix (format "~a/.chez-cross-~a" jerboa-home chez-machine)) +(define xpatch (format "~a/build/chez/xc-~a/s/xpatch" jerboa-home chez-machine)) +(define cross-cc (or (getenv "CROSS_CC") (list-ref arch-row 2))) +(define cross-strip (list-ref arch-row 5)) + +(define output (list-ref arch-row 4)) +(define source-script "jsh.ss") +(define entry-script "jsh-generated.ss") +(define ffi-shim "ffi-shim.c") + +;; Feature resolution — derive *enabled-features* from JSH_FEATURES env var. +;; JERBOA_FEATURES is accepted as a compatibility alias for CLI ergonomics. +;; ""/"none" → '() (base build) +;; "all" → all known optional features +;; "foo,bar" → '(foo bar) +(define cross-supported-all-features + '(coreutils mux ssh aws worm vault yubikey record sandbox cage rl profiler proxy procwatch embed pass)) + +(define cross-unsupported-features + '()) + +(define (cross-filter-supported-features features) + (let loop ([xs features] [out '()]) + (cond + [(null? xs) (reverse out)] + [(memq (car xs) cross-unsupported-features) + (printf " warning: feature ~a is not supported by build-jsh-cross; omitting from manifest~n" + (car xs)) + (loop (cdr xs) out)] + [else (loop (cdr xs) (cons (car xs) out))]))) + +(define *enabled-features* + (let ([env (or (getenv "JSH_FEATURES") (getenv "JERBOA_FEATURES") "")]) + (cond + [(or (string=? env "") (string=? env "none")) '()] + [(string=? env "all") + cross-supported-all-features] + [else + (cross-filter-supported-features + (let split ([i 0] [start 0] [acc '()]) + (cond + [(= i (string-length env)) + (let ([s (substring env start i)]) + (if (string=? s "") (reverse acc) + (reverse (cons (string->symbol s) acc))))] + [(char=? (string-ref env i) #\,) + (let ([s (substring env start i)]) + (split (+ i 1) (+ i 1) + (if (string=? s "") acc (cons (string->symbol s) acc))))] + [else (split (+ i 1) start acc)])))]))) + +(define (feature-enabled? feature) + (memq feature *enabled-features*)) + +(define (sh-quote s) + (let ([out (open-output-string)]) + (display "'" out) + (let loop ([i 0]) + (when (< i (string-length s)) + (let ([ch (string-ref s i)]) + (if (char=? ch #\') + (display "'\\''" out)