build: build gitsafe with installed jerbuild (no ~/mine/jerboa dependency)
ober
eef45f2feda3ae80f57c87107f16035caea3ede7
--- a/.jerbuild +++ b/.jerbuild @@ -1,8 +1,10 @@ -;; jerbuild build config for gitsafe. +;; jerbuild build config for gitsafe: `jerbuild build`. ;; All paths are relative to this file. +;; +;; jerbuild bundles Chez Scheme + the jerboa stdlib, so this needs only +;; jerbuild + a C compiler — no jerboa source checkout. Pure Jerboa: no +;; jerboa-native-rs and no DuckDB. (entry "gitsafe/main-binary.ss") (output "gitsafe-bin") -(requires "cc") -(notes "Pure Jerboa binary build. No jerboa-native-rs archive and no DuckDB are required.") (libdirs ".") deleted file mode 100644 --- a/Dockerfile +++ /dev/null @@ -1,50 +0,0 @@ -# Dockerfile — Build gitsafe-musl using the jerboa21/jerboa base image -# -# Produces a fully static binary with zero runtime dependencies. -# No Chez Scheme or Jerboa installation needed on the target host. -# -# The base image (jerboa21/jerboa) provides stock Chez, musl Chez, -# jerboa libs, and all build dependencies pre-installed. -# -# Usage: -# docker build -t gitsafe-builder . -# docker run --rm gitsafe-builder > gitsafe-musl && chmod +x gitsafe-musl -# -# Or extract via docker cp: -# docker build -t gitsafe-builder . -# id=$(docker create gitsafe-builder) -# docker cp $id:/out/gitsafe-musl ./gitsafe-musl -# docker cp $id:/out/gitsafe-musl.sha256 ./gitsafe-musl.sha256 -# docker rm $id - -FROM jerboa21/jerboa AS builder - -# ── Override stale baked-in Jerboa with host version ──────────────────────── -# The base image ships an older Jerboa; overlay it so the build sees the same -# prelude exports (meta, atom?, etc.) as the local macOS build. -COPY --from=jerboa . /build/mine/jerboa/ - -# ── Copy gitsafe source ───────────────────────────────────────────────────── -COPY . /build/mine/jerboa-gitsafe - -# ── Build gitsafe-musl ─────────────────────────────────────────────────────── -WORKDIR /build/mine/jerboa-gitsafe -RUN make linux-local - -# ── Verify ─────────────────────────────────────────────────────────────────── -RUN ./gitsafe-musl --version -RUN echo "--- Binary info ---" && \ - ls -lh gitsafe-musl && \ - file gitsafe-musl && \ - echo "--- Hardening checks ---" && \ - { file gitsafe-musl | grep -qE 'stripped|no section header' && echo " PASS: stripped" || echo " FAIL: not stripped"; } && \ - { test -f gitsafe-musl.sha256 && echo " PASS: integrity hash present" || echo " FAIL: no hash"; } && \ - echo "--- Path leak check ---" && \ - count=$(strings gitsafe-musl | grep -c '/home/' || true) && \ - { [ "$count" -gt 0 ] && echo " WARNING: home paths found ($count)" || echo " PASS: no home path leaks"; } - -# ── Output ─────────────────────────────────────────────────────────────────── -FROM ubuntu:24.04 -COPY --from=builder /build/mine/jerboa-gitsafe/gitsafe-musl /out/gitsafe-musl -COPY --from=builder /build/mine/jerboa-gitsafe/gitsafe-musl.sha256 /out/gitsafe-musl.sha256 -CMD ["cat", "/out/gitsafe-musl"] --- a/Makefile +++ b/Makefile @@ -1,153 +1,56 @@ -JERBOA_HOME ?= $(realpath $(CURDIR)/../jerboa) -SCHEME ?= $(JERBOA_HOME)/.chez/bin/scheme +# jerbuild bundles Chez Scheme + the jerboa stdlib, so building gitsafe needs +# only `jerbuild` + a C compiler — no jerboa source checkout and no separately +# built Chez. +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 + +LIBDIRS := --libdirs $(CURDIR):$(JH)/lib +JEXEC := $(JERBUILD) exec $(LIBDIRS) +BIN := gitsafe-bin BIN_DIR := $(HOME)/.local/bin TEMPLATE_DIR := $(HOME)/.git-templates HOOK_DIR := $(TEMPLATE_DIR)/hooks -.PHONY: run test binary install clean linux linux-local docker \ - verify-harden help install-native macos gitsafe-macos \ - jerbuild-run jerbuild-test jerbuild-binary - +.PHONY: all build binary run test install clean help .DEFAULT_GOAL := help -run: - JERBOA_HOME=$(JERBOA_HOME) \ - $(SCHEME) -q --libdirs $(CURDIR):$(JERBOA_HOME)/lib --script gitsafe/main.ss -- $(ARGS) +all: binary +# Standalone native binary via .jerbuild (entry gitsafe/main-binary.ss). binary: - JERBOA_HOME=$(JERBOA_HOME) \ - $(SCHEME) -q --libdirs $(CURDIR):$(JERBOA_HOME)/lib --script build-binary.ss - -test: - JERBOA_HOME=$(JERBOA_HOME) \ - $(SCHEME) -q --libdirs $(CURDIR):$(JERBOA_HOME)/lib --script test/test-gitsafe.ss - -# ── jerbuild path: standalone, no jerboa checkout required ─────────────────── -# Uses a single self-contained `jerbuild` binary (Chez + Jerboa stdlib bundled). -# Override JERBUILD=/path/to/jerbuild to point at it. -JERBUILD ?= jerbuild + $(JERBUILD) build -jerbuild-run: - @JH=$$($(JERBUILD) --jerboa-home); \ - env -u JERBOA_HOME $(JERBUILD) exec \ - --libdirs $(CURDIR):$$JH/lib gitsafe/main.ss -- $(ARGS) +build: binary -jerbuild-test: - @JH=$$($(JERBUILD) --jerboa-home); \ - env -u JERBOA_HOME $(JERBUILD) exec \ - --libdirs $(CURDIR):$$JH/lib test/test-gitsafe.ss +run: binary + ./$(BIN) $(ARGS) -# Produce a standalone native gitsafe binary using only the jerbuild bundle -# (no jerboa checkout, no Chez install). The resulting binary is self-contained. -jerbuild-binary: - @JH=$$($(JERBUILD) --jerboa-home); \ - env -u JERBOA_HOME $(JERBUILD) binary \ - --libdirs $(CURDIR):$$JH/lib gitsafe/main.ss gitsafe-jerbuild - @echo "" - @ls -lh gitsafe-jerbuild - @file gitsafe-jerbuild +test: + $(JEXEC) test/test-gitsafe.ss -install: $(if $(filter Darwin,$(shell uname -s)),gitsafe-macos,linux) +install: binary mkdir -p $(BIN_DIR) -ifeq ($(shell uname -s),Darwin) - cp gitsafe-macos $(BIN_DIR)/gitsafe - @echo "Installed gitsafe to $(BIN_DIR)/gitsafe (macOS binary)" -else - cp gitsafe-musl $(BIN_DIR)/gitsafe - @echo "Installed gitsafe to $(BIN_DIR)/gitsafe (static binary)" -endif + install -m 0755 $(BIN) $(BIN_DIR)/gitsafe mkdir -p $(HOOK_DIR) printf '#!/bin/sh\nexec gitsafe pre-commit\n' > $(HOOK_DIR)/pre-commit chmod +x $(HOOK_DIR)/pre-commit printf '#!/bin/sh\nwhile read local_ref local_sha remote_ref remote_sha; do\n gitsafe pre-push --local-ref "$$local_ref" --remote-ref "$$remote_ref" || exit $$?\ndone\n' > $(HOOK_DIR)/pre-push chmod +x $(HOOK_DIR)/pre-push git config --global init.templateDir $(TEMPLATE_DIR) - @echo "" - @echo "Global git hooks installed:" - @echo " $(HOOK_DIR)/pre-commit" - @echo " $(HOOK_DIR)/pre-push" - @echo " git config --global init.templateDir = $(TEMPLATE_DIR)" - @echo "" - @echo "All new repos (git init / git clone) will use gitsafe automatically." - @echo "To add to an existing repo: cd repo && git init" + @echo "Installed gitsafe to $(BIN_DIR)/gitsafe + global git hooks." -install-native: binary - mkdir -p $(BIN_DIR) - cp gitsafe-bin $(BIN_DIR)/gitsafe - @echo "Installed gitsafe-bin to $(BIN_DIR)/gitsafe (native, requires Chez runtime)" - -# ── macOS binary (maximally static) ───────────────────────────────────────── -# Statically links Chez kernel, lz4, zlib, ncurses. -# Only libSystem (always present on macOS) and libiconv are dynamic. -# Use `make macos` or `make gitsafe-macos` to build on macOS. - -macos: gitsafe-macos - -gitsafe-macos: - JERBOA_HOME=$(JERBOA_HOME) \ - ./build-gitsafe-macos.sh - -# ── Static musl binary ────────────────────────────────────────────────────── -# Use `make linux` to build in Docker (canonical, reproducible). -# Use `make linux-local` to build directly on the host (requires -# musl-gcc and a musl-built Chez at ~/chez-musl or JERBOA_MUSL_CHEZ_PREFIX). - -linux: docker - -linux-local: - JERBOA_HOME=$(JERBOA_HOME) \ - ./build-gitsafe-musl.sh - -docker: - @echo "=== Building gitsafe-musl in Docker ===" - docker build --platform linux/amd64 --build-arg CACHE_BUST=$$(date +%s) --build-context jerboa=$(JERBOA_HOME) -t gitsafe-builder . - @id=$$(docker create --platform linux/amd64 gitsafe-builder) && \ - docker cp $$id:/out/gitsafe-musl ./gitsafe-musl && \ - docker cp $$id:/out/gitsafe-musl.sha256 ./gitsafe-musl.sha256 && \ - docker rm $$id >/dev/null && \ - chmod +x gitsafe-musl - @echo "" - @ls -lh gitsafe-musl - @file gitsafe-musl - -verify-harden: linux - @echo "=== Hardening verification ===" - @(file gitsafe-musl | grep -qE 'stripped|no section header') && echo " PASS: binary is stripped" || echo " FAIL: binary not stripped" - @if strings gitsafe-musl | grep -q "$(HOME)"; then \ - echo " WARN: home directory path found in binary"; \ - else \ - echo " PASS: no home directory paths leaked"; \ - fi - @if [ -f gitsafe-musl.sha256 ]; then \ - echo " PASS: gitsafe-musl.sha256 exists ($$(wc -c < gitsafe-musl.sha256) bytes)"; \ - else \ - echo " FAIL: gitsafe-musl.sha256 not found"; \ - fi - @./gitsafe-musl --version >/dev/null 2>&1 && echo " PASS: binary runs" || echo " FAIL: binary doesn't run" - -# ── Cleanup ────────────────────────────────────────────────────────────────── clean: - find . -name '*.so' -delete - find . -name '*.wpo' -delete - rm -f gitsafe-bin gitsafe-musl gitsafe-musl.sha256 gitsafe-macos gitsafe-macos.sha256 gitsafe-jerbuild + find . \( -name '*.so' -o -name '*.wpo' \) -delete 2>/dev/null || true + rm -f gitsafe-bin gitsafe-jerbuild -# ── Help ───────────────────────────────────────────────────────────────────── help: - @echo "gitsafe — secret-scanning git hooks" - @echo "" - @echo "Development:" - @echo " make run ARGS='...' Run gitsafe in interpreter mode" - @echo " make test Run test suite" - @echo "" - @echo "Build & install:" - @echo " make macos macOS build (statically linked, no runtime deps)" - @echo " make linux Linux static build via Docker (canonical)" - @echo " make linux-local Linux static build locally (requires musl-gcc)" - @echo " make install Build + install to ~/.local/bin (auto-detects OS)" - @echo " make verify-harden Verify binary hardening (stripped, no leaks)" - @echo "" - @echo "Native binary (requires local Chez + Jerboa):" - @echo " make binary Build gitsafe-bin (dynamic, native)" - @echo " make install-native Build native + install to ~/.local/bin" + @echo "gitsafe — secret-scanning git hooks (jerbuild + cc only)" @echo "" - @echo " make clean Remove all build artifacts" + @echo " make binary Build the standalone ./gitsafe-bin" + @echo " make run ARGS='...' Build + run ./gitsafe-bin" + @echo " make test Run the test suite" + @echo " make install Build + install to ~/.local/bin + global git hooks" + @echo " make clean Remove build artifacts" deleted file mode 100755 --- a/build-gitsafe-macos.sh +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/bash -# build-gitsafe-macos.sh — Build gitsafe as a maximally-static macOS binary -# -# Statically links: Chez kernel, lz4, zlib, ncurses -# Dynamically links: libSystem (libc/libpthread — always present on macOS), libiconv -# -# Prerequisites: -# - Chez Scheme installed (brew install chezscheme) -# - Jerboa libraries available -# - Homebrew ncurses (brew install ncurses) -# -# Produces: ./gitsafe-macos (single binary, no Chez runtime needed at run time) -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -# Resolve jerboa -if [ -n "${JERBOA_HOME:-}" ]; then - JERBOA_LIB="${JERBOA_HOME}/lib" -elif [ -d "${SCRIPT_DIR}/../jerboa/lib" ]; then - JERBOA_LIB="$(realpath "${SCRIPT_DIR}/../jerboa/lib")" -elif [ -d "${HOME}/mine/jerboa/lib" ]; then - JERBOA_LIB="${HOME}/mine/jerboa/lib" -else - echo "ERROR: Cannot find Jerboa. Set JERBOA_HOME." - exit 1 -fi -export JERBOA_HOME="${JERBOA_HOME:-$(dirname "$JERBOA_LIB")}" -SCHEME="${SCHEME:-${JERBOA_HOME}/.chez/bin/scheme}" - -echo "===================================" -echo "Building gitsafe-macos (static libs)" -echo "===================================" -echo "" -echo "Jerboa: $JERBOA_LIB" - -# Verify macOS -if [ "$(uname -s)" != "Darwin" ]; then - echo "ERROR: This build script is for macOS only." - exit 1 -fi - -# Check Chez -if [ ! -x "$SCHEME" ]; then - echo "ERROR: scheme not found at $SCHEME" - echo "Run 'make chez' in ${JERBOA_HOME} (or set SCHEME=... to override)." - exit 1 -fi - -echo " scheme: $SCHEME ($("$SCHEME" --version 2>&1))" -echo "" - -echo "[1/2] Validating macOS build environment..." - -# Check for ncurses static lib -NCURSES_A="" -for p in /opt/homebrew/opt/ncurses/lib /usr/local/opt/ncurses/lib; do - if [ -f "$p/libncurses.a" ]; then - NCURSES_A="$p/libncurses.a" - break - fi -done -if [ -z "$NCURSES_A" ]; then - echo "WARNING: static libncurses.a not found, will use dynamic -lncurses" - echo " Install: brew install ncurses" -fi -export NCURSES_STATIC_PATH="${NCURSES_A}" -echo " ncurses: ${NCURSES_A:-dynamic}" -echo "" - -echo "[2/2] Running macOS build..." -"$SCHEME" -q --libdirs "${SCRIPT_DIR}:${JERBOA_LIB}" --script build-gitsafe-macos.ss - -# Verify -if [ -f "gitsafe-macos" ]; then - echo "" - echo "===================================" - echo "gitsafe-macos built successfully!" - echo "===================================" - ls -lh gitsafe-macos - echo "" - file gitsafe-macos - echo "" - echo "Dynamic dependencies:" - otool -L gitsafe-macos 2>/dev/null | tail -n +2 || true - echo "" - echo "Test: ./gitsafe-macos --version" - ./gitsafe-macos --version || { echo "ERROR: binary smoke test failed"; exit 1; } -else - echo "ERROR: gitsafe-macos not created" - exit 1 -fi deleted file mode 100644 --- a/build-gitsafe-macos.ss +++ /dev/null @@ -1,159 +0,0 @@ -#!chezscheme -;; Build gitsafe as a maximally-static macOS binary. -;; -;; Usage: make macos -;; (runs via build-gitsafe-macos.sh -> this script) -;; -;; Statically links: Chez kernel, lz4, zlib, ncurses -;; Dynamically links: libSystem (always present), libiconv -;; -;; macOS does not support fully static binaries (Apple linker requires -;; libSystem.B.dylib), but this binary has zero third-party runtime deps. -;; -;; Produces: ./gitsafe-macos (single Mach-O binary, self-contained) - -(import (chezscheme)) - -;; Load shared build logic (defines find-csv-dir and all step functions). -;; jerboa-dir must be defined before any of the shared step functions are CALLED -;; (not before this include — lambdas capture it lazily). -(include "build-common.ss") - -;; --- Locate Chez install directory (macOS) --- -(define chez-dir - (or (getenv "CHEZ_DIR") - (let ([mt (symbol->string (machine-type))] - [home (getenv "HOME")]) - (or (find-csv-dir (format "~a/.local/lib" home) mt) - (find-csv-dir "/opt/homebrew/lib" mt) - (find-csv-dir "/usr/local/lib" mt))))) - -(unless chez-dir - (display "Error: Cannot find Chez install dir. Set CHEZ_DIR.\n") - (exit 1)) - -;; --- Locate Jerboa --- -(define jerboa-dir - (or (getenv "JERBOA_HOME") - (let ([sibling (format "~a/../jerboa" (current-directory))]) - (and (file-exists? sibling) sibling)) - (begin - (display "Error: Cannot find Jerboa. Set JERBOA_HOME.\n") - (exit 1)))) - -(printf "=== gitsafe macOS build ===\n") -(printf "Chez dir: ~a\n" chez-dir) -(printf "Jerboa dir: ~a\n" jerboa-dir) -(printf "Machine type: ~a\n" (machine-type)) - -;; --- Steps 0–3: shared compile + WPO + boot file --- -(setup-library-dirs!) -(do-compile!) -(define wpo-missing (do-wpo!)) -(do-boot! wpo-missing chez-dir) - -;; --- Step 4: Generate C main (macOS — no dlopen stubs needed) --- -(printf "[4/6] Generating C main...\n") - -(call-with-output-file "gitsafe-main-macos.c" - (lambda (out) - (fprintf out "/* Auto-generated — do not edit */\n") - (fprintf out "#include <stdlib.h>\n") - (fprintf out "#include <stdio.h>\n") - (fprintf out "#include <string.h>\n") - (fprintf out "#include <unistd.h>\n") - (fprintf out "#include \"scheme.h\"\n") - (fprintf out "#include \"gitsafe_petite_boot.h\"\n") - (fprintf out "#include \"gitsafe_scheme_boot.h\"\n") - (fprintf out "#include \"gitsafe_boot.h\"\n") - (fprintf out "#include \"gitsafe_program.h\"\n") - (fprintf out "\n") - (fprintf out "int main(int argc, char *argv[]) {\n") - (fprintf out " char prog_path[256];\n") - (fprintf out " const char *tmpdir = getenv(\"TMPDIR\");\n") - (fprintf out " if (!tmpdir) tmpdir = \"/tmp\";\n") - (display " snprintf(prog_path, sizeof(prog_path), \"%s/gitsafe-XXXXXX\", tmpdir);\n" out) - (fprintf out " int fd = mkstemp(prog_path);\n") - (fprintf out " if (fd < 0) { perror(\"mkstemp\"); return 1; }\n") - (fprintf out " if (write(fd, gitsafe_program_data, gitsafe_program_size)\n") - (fprintf out " != (ssize_t)gitsafe_program_size) {\n") - (fprintf out " perror(\"write\"); close(fd); unlink(prog_path); return 1;\n") - (fprintf out " }\n") - (fprintf out " close(fd);\n") - (fprintf out "\n") - (fprintf out " Sscheme_init(NULL);\n") - (fprintf out " Sregister_boot_file_bytes(\"petite\", (void*)petite_boot_data, petite_boot_size);\n") - (fprintf out " Sregister_boot_file_bytes(\"scheme\", (void*)scheme_boot_data, scheme_boot_size);\n") - (fprintf out " Sregister_boot_file_bytes(\"gitsafe\", (void*)gitsafe_boot_data, gitsafe_boot_size);\n") - (fprintf out " Sbuild_heap(NULL, NULL);\n") - (fprintf out " int status = Sscheme_script(prog_path, argc, (const char **)argv);\n") - (fprintf out " unlink(prog_path);\n") - (fprintf out " Sscheme_deinit();\n") - (fprintf out " return status;\n") - (fprintf out "}\n")) - 'replace) - -;; --- Step 5: Compile and link (maximally static, macOS) --- -(printf "[5/6] Compiling and linking (maximally static)...\n") - -(define kernel-a (format "~a/libkernel.a" chez-dir)) -(define chez-lz4-a (format "~a/liblz4.a" chez-dir)) -(define chez-z-a (format "~a/libz.a" chez-dir)) - -(for-each - (lambda (pair) - (unless (file-exists? (cdr pair)) - (printf "Error: ~a not found at ~a\n" (car pair) (cdr pair)) - (exit 1))) - (list (cons "libkernel.a" kernel-a) - (cons "liblz4.a" chez-lz4-a) - (cons "libz.a" chez-z-a))) - -(define ncurses-a - (or (let ([p (getenv "NCURSES_STATIC_PATH")]) - (and p (> (string-length p) 0) (file-exists? p) p)) - (let ([p "/opt/homebrew/opt/ncurses/lib/libncurses.a"]) - (and (file-exists? p) p)) - (let ([p "/usr/local/opt/ncurses/lib/libncurses.a"]) - (and (file-exists? p) p)))) - -(printf " Static libs:\n") -(printf " kernel: ~a\n" kernel-a) -(printf " lz4: ~a\n" chez-lz4-a) -(printf " zlib: ~a\n" chez-z-a) -(printf " ncurses: ~a\n" (or ncurses-a "(dynamic fallback)")) - -(define static-libs - (string-append - kernel-a " " chez-lz4-a " " chez-z-a - (if ncurses-a (string-append " " ncurses-a) ""))) - -(define dynamic-libs - (string-append - (if ncurses-a "" " -lncurses") - " -liconv -lpthread -lm")) - -(let ([cc (or (getenv "CC") "cc")]) - (let ([rc (system (format "~a -c -O2 -I~a -o gitsafe-main-macos.o gitsafe-main-macos.c" - cc chez-dir))]) - (unless (= rc 0) (printf "Error: C compilation failed\n") (exit 1))) - (let* ([cmd (format "~a -o gitsafe-macos gitsafe-main-macos.o ~a~a" - cc static-libs dynamic-libs)]) - (printf " Link: ~a\n" cmd) - (let ([rc (system cmd)]) - (unless (= rc 0) (printf "Error: linking failed\n") (exit 1))))) - -(printf " Stripping binary...\n") -(system "strip -x gitsafe-macos") -(system "shasum -a 256 gitsafe-macos > gitsafe-macos.sha256") - -;; --- Step 6: Cleanup --- -(do-cleanup! "macos") - -(printf "\nDone! Binary: ./gitsafe-macos\n") -(printf " Size: ") -(system "ls -lh gitsafe-macos | awk '{print $5}'") -(printf " SHA256: ") -(system "cat gitsafe-macos.sha256") -(printf "\n Test: ./gitsafe-macos --version\n") -(printf " Deps: otool -L gitsafe-macos\n") deleted file mode 100755 --- a/build-gitsafe-musl.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/bash -# build-gitsafe-musl.sh — Build gitsafe as a fully static binary using musl libc -# -# Prerequisites: -# - musl-gcc installed (apt install musl-tools) -# - Chez Scheme built with: ./configure --threads --static CC=musl-gcc -# and installed to ~/chez-musl (or set JERBOA_MUSL_CHEZ_PREFIX) -# - Jerboa libraries available -# -# The build uses stock scheme (glibc) for the Scheme compilation steps, -# then musl-gcc for the C compilation and linking steps. -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" - -# Resolve jerboa -if [ -n "${JERBOA_HOME:-}" ]; then - JERBOA_LIB="${JERBOA_HOME}/lib" -elif [ -d "${SCRIPT_DIR}/../jerboa/lib" ]; then - JERBOA_LIB="$(realpath "${SCRIPT_DIR}/../jerboa/lib")" -elif [ -d "${HOME}/mine/jerboa/lib" ]; then - JERBOA_LIB="${HOME}/mine/jerboa/lib" -else - echo "ERROR: Cannot find Jerboa. Set JERBOA_HOME." - exit 1 -fi -export JERBOA_HOME="${JERBOA_HOME:-$(dirname "$JERBOA_LIB")}" -SCHEME="${SCHEME:-${JERBOA_HOME}/.chez/bin/scheme}" -if [ ! -x "$SCHEME" ]; then - echo "ERROR: scheme not found at $SCHEME" >&2 - echo "Run 'make chez' in ${JERBOA_HOME} (or set SCHEME=... to override)." >&2 - exit 1 -fi - -echo "===================================" -echo "Building gitsafe-musl (static)" -echo "===================================" -echo "" -echo "Jerboa: $JERBOA_LIB" -echo "" - -# Check musl availability -if ! command -v musl-gcc &>/dev/null; then - echo "ERROR: musl-gcc not found" - echo "Install: sudo apt install musl-tools" - exit 1 -fi - -echo "[1/2] Validating musl toolchain..." -echo " musl-gcc: $(command -v musl-gcc)" -echo "" - -echo "[2/2] Running musl build..." -"$SCHEME" -q --libdirs "${SCRIPT_DIR}:${JERBOA_LIB}" --script build-gitsafe-musl.ss - -# Verify -if [ -f "gitsafe-musl" ]; then - echo "" - echo "===================================" - echo "gitsafe-musl built successfully!" - echo "===================================" - ls -lh gitsafe-musl - echo "" - file gitsafe-musl - echo "" - ldd gitsafe-musl 2>&1 || echo " (Fully static — no dynamic dependencies)" - echo "" - echo "Test: ./gitsafe-musl --version" - ./gitsafe-musl --version || { echo "ERROR: binary smoke test failed"; exit 1; } -else - echo "ERROR: gitsafe-musl not created" - exit 1 -fi deleted file mode 100644 --- a/build-gitsafe-musl.ss +++ /dev/null @@ -1,164 +0,0 @@ -#!chezscheme -;; Build gitsafe as a fully static binary using musl libc. -;; -;; Usage: make linux-local -;; (runs via build-gitsafe-musl.sh → this script) -;; -;; Prerequisites: -;; - musl-gcc installed (apt install musl-tools) -;; - Chez Scheme built with: ./configure --threads --static CC=musl-gcc -;; installed to ~/chez-musl (or set JERBOA_MUSL_CHEZ_PREFIX) -;; - Stock scheme (glibc) for the compilation steps -;; -;; Produces: ./gitsafe-musl (fully static ELF binary, zero runtime dependencies) - -(import (chezscheme)) - -;; Load shared build logic (defines find-csv-dir and all step functions). -;; jerboa-dir must be defined before any of the shared step functions are CALLED -;; (not before this include — lambdas capture it lazily). -(include "build-common.ss") - -;; --- Locate musl-built Chez Scheme --- -(define musl-chez-prefix - (or (getenv "JERBOA_MUSL_CHEZ_PREFIX") - (let* ([home (getenv "HOME")] - [p (format "~a/chez-musl" home)]) - (and (file-exists? p) p)))) - -(unless musl-chez-prefix - (display "Error: Cannot find musl Chez install.\n") - (display " Set JERBOA_MUSL_CHEZ_PREFIX or install to ~/chez-musl\n") - (display " See: https://git.sr.ht/~lisp/jerboa (vendor/ChezScheme — build with --static CC=musl-gcc)\n") - (exit 1)) - -(define musl-chez-dir - (let ([mt (symbol->string (machine-type))]) - (or (find-csv-dir (format "~a/lib" musl-chez-prefix) mt) - (begin - (printf "Error: Cannot find Chez ~a dir under ~a/lib\n" - (machine-type) musl-chez-prefix) - (printf " Expected: ~a/lib/csv<version>/~a/main.o\n" - musl-chez-prefix mt) - (exit 1))))) - -;; --- Locate Jerboa --- -(define jerboa-dir - (or (getenv "JERBOA_HOME") - (let ([sibling (format "~a/../jerboa" (current-directory))]) - (and (file-exists? sibling) sibling)) - (begin - (display "Error: Cannot find Jerboa. Set JERBOA_HOME.\n") - (exit 1)))) - -(printf "=== gitsafe musl static build ===\n") -(printf "Musl Chez dir: ~a\n" musl-chez-dir) -(printf "Jerboa dir: ~a\n" jerboa-dir) -(printf "Machine type: ~a\n" (machine-type)) - -;; --- Steps 0–3: shared compile + WPO + boot file --- -;; Boot files come from the musl Chez (ABI must match the musl kernel). -(setup-library-dirs!) -(do-compile!) -(define wpo-missing (do-wpo!)) -(do-boot! wpo-missing musl-chez-dir) - -;; --- Step 4: Generate C main (musl — with dlopen stubs + Sforeign_symbol) --- -;; -;; dlopen(NULL, ...) returns a fake self-handle so Chez can query its own -;; symbol table. All other dlopen calls return NULL, causing -;; (load-shared-object "libjerboa_native.so") in (std regex) to throw an -;; exception that the guard catches → native-available? = #f → all regex -;; falls back to the pure-Scheme pregexp engine. -;; -;; Sforeign_symbol MUST be called after Sbuild_heap (the foreign entry table -;; is not initialized until then). We register the three Rust regex symbols -;; with a harmless C stub so (std regex)'s (foreign-procedure ...) forms -;; succeed at WPO program init. The stub returns -1 but is never called -;; because native-available? = #f prevents all native regex code paths. -(printf "[4/6] Generating C main...\n") - -(call-with-output-file "gitsafe-main-musl.c" - (lambda (out) - (fprintf out "/* Auto-generated — do not edit */\n") - (fprintf out "#define _GNU_SOURCE\n") - (fprintf out "#include <stdlib.h>\n") - (fprintf out "#include <stdio.h>\n") - (fprintf out "#include <string.h>\n") - (fprintf out "#include <unistd.h>\n") - (fprintf out "#include \"scheme.h\"\n") - (fprintf out "#include \"gitsafe_petite_boot.h\"\n") - (fprintf out "#include \"gitsafe_scheme_boot.h\"\n") - (fprintf out "#include \"gitsafe_boot.h\"\n") - (fprintf out "#include \"gitsafe_program.h\"\n") - (fprintf out "\n") - (fprintf out "/* dlopen/dlsym stubs — fully static musl binary, no dynamic libraries */\n") - (fprintf out "static int _jerboa_native_stub(void) { return -1; }\n") - (fprintf out "void *dlopen(const char *f, int m) { (void)m; return (!f) ? (void*)1 : NULL; }\n") - (fprintf out "void *dlsym(void *h, const char *s) {\n") - (fprintf out " (void)h;\n") - (fprintf out " if (s && (strcmp(s, \"jerboa_regex_compile\") == 0 ||\n") - (fprintf out " strcmp(s, \"jerboa_regex_find\") == 0 ||\n") - (fprintf out " strcmp(s, \"jerboa_regex_free\") == 0))\n") - (fprintf out " return (void *)_jerboa_native_stub;\n") - (fprintf out " return NULL;\n") - (fprintf out "}\n") - (fprintf out "int dlclose(void *h) { (void)h; return 0; }\n") - (fprintf out "char *dlerror(void) { return \"static build\"; }\n") - (fprintf out "\n") - (fprintf out "int main(int argc, char *argv[]) {\n") - (fprintf out " char prog_path[256];\n") - (fprintf out " const char *tmpdir = getenv(\"TMPDIR\");\n") - (fprintf out " if (!tmpdir) tmpdir = \"/tmp\";\n") - (display " snprintf(prog_path, sizeof(prog_path), \"%s/gitsafe-XXXXXX\", tmpdir);\n" out) - (fprintf out " int fd = mkstemp(prog_path);\n") - (fprintf out " if (fd < 0) { perror(\"mkstemp\"); return 1; }\n") - (fprintf out " if (write(fd, gitsafe_program_data, gitsafe_program_size)\n") - (fprintf out " != (ssize_t)gitsafe_program_size) {\n") - (fprintf out " perror(\"write\"); close(fd); unlink(prog_path); return 1;\n") - (fprintf out " }\n") - (fprintf out " close(fd);\n") - (fprintf out "\n") - (fprintf out " Sscheme_init(NULL);\n") - (fprintf out " Sregister_boot_file_bytes(\"petite\", (void*)petite_boot_data, petite_boot_size);\n") - (fprintf out " Sregister_boot_file_bytes(\"scheme\", (void*)scheme_boot_data, scheme_boot_size);\n") - (fprintf out " Sregister_boot_file_bytes(\"gitsafe\", (void*)gitsafe_boot_data, gitsafe_boot_size);\n") - (fprintf out " Sbuild_heap(NULL, NULL);\n") - (fprintf out " /* Register regex native symbols AFTER Sbuild_heap */\n") - (fprintf out " Sforeign_symbol(\"jerboa_regex_compile\", (void *)_jerboa_native_stub);\n") - (fprintf out " Sforeign_symbol(\"jerboa_regex_find\", (void *)_jerboa_native_stub);\n") - (fprintf out " Sforeign_symbol(\"jerboa_regex_free\", (void *)_jerboa_native_stub);\n") - (fprintf out " int status = Sscheme_script(prog_path, argc, (const char **)argv);\n") - (fprintf out " unlink(prog_path);\n") - (fprintf out " Sscheme_deinit();\n") - (fprintf out " return status;\n") - (fprintf out "}\n")) - 'replace) - -;; --- Step 5: Compile and link with musl-gcc (fully static) --- -(printf "[5/6] Compiling and linking with musl-gcc (static)...\n") - -(define link-libs "-lkernel -llz4 -lz -lm -ldl -lpthread") - -(let ([rc (system (format "musl-gcc -c -O2 -I~a -o gitsafe-main-musl.o gitsafe-main-musl.c" - musl-chez-dir))]) - (unless (= rc 0) (printf "Error: C compilation failed\n") (exit 1))) - -(let ([rc (system (format "musl-gcc -o gitsafe-musl gitsafe-main-musl.o -L~a ~a -static -Wl,--allow-multiple-definition" - musl-chez-dir link-libs))]) - (unless (= rc 0) (printf "Error: linking failed\n") (exit 1))) - -(printf " Stripping binary...\n") -(system "strip --strip-all gitsafe-musl") -(system "sha256sum gitsafe-musl > gitsafe-musl.sha256") - -;; --- Step 6: Cleanup --- -(do-cleanup! "musl") - -(printf "\nDone! Binary: ./gitsafe-musl\n") -(printf " Size: ") -(system "ls -lh gitsafe-musl | awk '{print $5}'") -(printf " SHA256: ") -(system "cat gitsafe-musl.sha256") -(printf "\n Test: ./gitsafe-musl --version\n") -(printf " Verify: file gitsafe-musl && ldd gitsafe-musl\n")