Add standalone jerbuild binary
ober
63f70de4d041a69b09434477ef76c09d12549bac
--- a/Makefile +++ b/Makefile @@ -147,6 +147,38 @@ BINARY_OUTPUT ?= jerboa-bin binary: chez build SCHEME=$(SCHEME) JERBOA_CHEZ_PREFIX=$(CHEZ_PREFIX) support/build-binary.sh $(BINARY_ENTRY) $(BINARY_OUTPUT) +# ── jerbuild standalone binary ─────────────────────────────────────────────── +# Builds a self-contained ./jerbuild that bundles Chez + the Jerboa stdlib +# (lib/jerboa + lib/std). On any box with cargo/rustc/cc, this single binary +# is enough to transpile any Jerboa project's .ss sources to .sls. +# +# ./jerbuild --jerboa-home # extract bundled lib/, print path +# ./jerbuild <src> <lib> # transpile +.PHONY: jerbuild jerbuild-smoke +jerbuild: chez build + SCHEME=$(SCHEME) JERBOA_CHEZ_PREFIX=$(CHEZ_PREFIX) support/build-jerbuild.sh + +# End-to-end smoke test: with no JERBOA_HOME and only ./jerbuild, transpile a +# small (export greet) module, then exec a script that imports + calls it. +# Catches regressions in transpile + bundled stdlib + exec subcommand. +jerbuild-smoke: jerbuild + @SMOKE=$$(mktemp -d /tmp/jerbuild-smoke.XXXX); \ + mkdir -p $$SMOKE/in/demo; \ + printf '(export greet)\n(def (greet n) (format "hello, ~a!" n))\n' \ + > $$SMOKE/in/demo/hello.ss; \ + printf '(import (demo hello)) (import (chezscheme))\n(display (greet "jerbuild")) (newline)\n' \ + > $$SMOKE/main.ss; \ + cd $$SMOKE && env -u JERBOA_HOME $(CURDIR)/jerbuild in out >/dev/null; \ + JH=$$(env -u JERBOA_HOME $(CURDIR)/jerbuild --jerboa-home); \ + out=$$(cd $$SMOKE && env -u JERBOA_HOME $(CURDIR)/jerbuild exec --libdirs out:$$JH/lib main.ss 2>&1); \ + rm -rf $$SMOKE; \ + if [ "$$out" = "hello, jerbuild!" ]; then \ + echo "jerbuild-smoke: PASS — $$out"; \ + else \ + echo "jerbuild-smoke: FAIL — expected 'hello, jerbuild!', got: $$out" >&2; \ + exit 1; \ + fi + # ── Cross-compilation ──────────────────────────────────────────────────────── # Override these to cross-compile: # CHEZ_TARGET_MACHINE — Chez machine type (e.g. ta6osx, ta6le, tarm64le, ta6fb) @@ -323,6 +355,27 @@ typed-wrappers: typed-build: typed-rust typed-wrappers @cargo build --manifest-path $(TYPED_RUST_DIR)/Cargo.toml +# Project-attach: emit library-form wrappers (one .sls per typed module, +# importable as `(library-name ...)`) plus the Rust crate. Used by sibling +# projects (e.g. jerboa-code) to compile typed modules into their own libdir +# and link the resulting archive into their own binary. +# +# Required: +# TYPED_PROJECT_SOURCES — typed .ss source files +# TYPED_PROJECT_WRAPPER_DIR — libdir root where .sls wrappers are written +# (e.g. .../src for (jcode typed pricing) → +# .../src/jcode/typed/pricing.sls) +# TYPED_PROJECT_RUST_DIR — Rust crate output directory +typed-project-build: + @test -n "$(TYPED_PROJECT_SOURCES)" || { echo "ERROR: set TYPED_PROJECT_SOURCES" >&2; exit 1; } + @test -n "$(TYPED_PROJECT_WRAPPER_DIR)" || { echo "ERROR: set TYPED_PROJECT_WRAPPER_DIR" >&2; exit 1; } + @test -n "$(TYPED_PROJECT_RUST_DIR)" || { echo "ERROR: set TYPED_PROJECT_RUST_DIR" >&2; exit 1; } + @$(SCHEME) --libdirs $(LIBDIRS) --script support/typed-rust.ss \ + $(TYPED_PROJECT_RUST_DIR) $(TYPED_PROJECT_SOURCES) + @$(SCHEME) --libdirs $(LIBDIRS) --script support/typed-wrappers.ss \ + --library $(TYPED_PROJECT_WRAPPER_DIR) $(TYPED_PROJECT_SOURCES) + @cargo build --release --manifest-path $(TYPED_PROJECT_RUST_DIR)/Cargo.toml + typed-wrapper-smoke: @$(MAKE) --no-print-directory typed-build TYPED_RUST_SOURCES=tests/fixtures/typed/rust-basic.ss TYPED_WRAPPER_DIR=build/typed/jerboa-smoke @lib="$$(find $(TYPED_RUST_DIR)/target/debug -maxdepth 1 \( -name 'libjerboa_typed_generated.dylib' -o -name 'libjerboa_typed_generated.so' \) -print | head -n 1)"; \ --- a/jerbuild.ss +++ b/jerbuild.ss @@ -176,6 +176,30 @@ ;;;; Reading and classifying source forms ;;;; ============================================================ +(define gerbil-char-name-table + ;; Gerbil/Jerboa character names → Chez character names. + ;; Chez recognises #\escape via R6RS extension; older Chez and the + ;; sequence #\xNN are universal. The most common Gerbil-only name in + ;; practice is #\escape; we also normalise a few aliases. + '(("escape" . "esc"))) + +(define (gerbil-char-name->chez name) + (let ([hit (assoc name gerbil-char-name-table)]) + (if hit (cdr hit) name))) + +(define (read-char-name str start len) + ;; Collect the alphabetic character-name starting at str[start]. + ;; Returns (values name end-index) where str[end-index] is the first + ;; char that is NOT part of the name (whitespace or a delimiter). + (let loop ([j start] [acc '()]) + (if (>= j len) + (values (list->string (reverse acc)) j) + (let ([c (string-ref str j)]) + (if (or (char-whitespace? c) + (memv c '(#\( #\) #\[ #\] #\; #\"))) + (values (list->string (reverse acc)) j) + (loop (+ j 1) (cons c acc))))))) + (define (preprocess-brackets str) ;; Convert Jerboa/Chez bracket syntax [x y z] → (x y z) ;; in source text, while correctly skipping strings, comments, @@ -209,33 +233,26 @@ (loop (+ i 1) #f #f)] [else (loop (+ i 1) in-string #f)])] - ;; Character literal: #\x — copy # \ and the character name verbatim - ;; Must not convert #\[ or #\] — those are char literals, not list brackets + ;; Character literal: #\x — copy # \ and the character name verbatim. + ;; Must not convert #\[ or #\] — those are char literals, not brackets. + ;; Alphabetic names get translated through gerbil-char-name->chez so + ;; Gerbil-isms like #\escape become Chez-readable #\esc. [(and (char=? ch #\#) (< (+ i 1) len) (char=? (string-ref str (+ i 1)) #\\)) (write-char ch out) (write-char (string-ref str (+ i 1)) out) - (if (>= (+ i 2) len) - (loop (+ i 2) #f #f) - (let ([nc (string-ref str (+ i 2))]) - ;; If single non-alphabetic char (like #\[ #\] #\( #\space etc.): - ;; write it verbatim and continue WITHOUT going through the bracket handler - (if (not (char-alphabetic? nc)) - (begin - (write-char nc out) - (loop (+ i 3) #f #f)) - ;; Alphabetic: read the whole name (e.g. newline, space, nul) - (let char-loop ([j (+ i 2)]) - (if (>= j len) - (loop j #f #f) - (let ([ac (string-ref str j)]) - (if (or (char-whitespace? ac) - (memv ac '(#\( #\) #\[ #\] #\; #\"))) - (loop j #f #f) - (begin - (write-char ac out) - (char-loop (+ j 1))))))))))] + (cond + [(>= (+ i 2) len) + (loop (+ i 2) #f #f)] + [(not (char-alphabetic? (string-ref str (+ i 2)))) + (write-char (string-ref str (+ i 2)) out) + (loop (+ i 3) #f #f)] + [else + (let-values ([(name end) + (read-char-name str (+ i 2) len)]) + (display (gerbil-char-name->chez name) out) + (loop end #f #f))])] ;; Block comment #| ... |# [(and (char=? ch #\#) (< (+ i 1) len) @@ -284,6 +301,40 @@ [(char=? ch #\]) (write-char #\) out) (loop (+ i 1) #f #f)] + ;; Hash-bang datum: #!void → (void), #!eof → (eof-object). + ;; (Skips #!chezscheme / #!r6rs / #!fold-case / #!no-fold-case, + ;; which Chez handles natively, and #!optional / #!key / #!rest / + ;; #!default, which Chez also reads natively.) + [(and (char=? ch #\#) + (< (+ i 1) len) + (char=? (string-ref str (+ i 1)) #\!)) + ;; Collect the identifier after #! + (let bang-loop ([j (+ i 2)]) + (if (and (< j len) + (let ([c (string-ref str j)]) + (or (char-alphabetic? c) + (char-numeric? c) + (memv c '(#\- #\_))))) + (bang-loop (+ j 1)) + (let ([name (substring str (+ i 2) j)]) + (cond + [(string=? name "void") + (display "(void)" out) + (loop j #f #f)] + [(string=? name "eof") + (display "(eof-object)" out) + (loop j #f #f)] + [(string=? name "unbound") + ;; Treat like void at read time — call sites that want a + ;; real unbound check should use cond-expand etc. + (display "(void)" out) + (loop j #f #f)] + [else + ;; Pass through: #!chezscheme, #!r6rs, #!fold-case, + ;; #!no-fold-case, #!optional, #!key, #!rest, #!default + (write-char ch out) + (write-char (string-ref str (+ i 1)) out) + (loop (+ i 2) #f #f)]))))] ;; Everything else — copy verbatim [else (write-char ch out) @@ -291,8 +342,8 @@ (define (read-source-file path) ;; Read all top-level S-expressions from a .ss file. - ;; Preprocesses Jerboa/Chez bracket syntax [x y z] → (x y z). - ;; Returns a list of forms. + ;; Preprocesses Jerboa/Chez bracket syntax [x y z] → (x y z) + ;; and hash-bang datums (#!void, #!eof). (let* ([raw (call-with-input-file path (lambda (port) (let ([p (open-output-string)]) @@ -1288,8 +1339,79 @@ (exit 1)))) ;;;; ============================================================ +;;;; Subcommands: exec / compile / transpile (default) +;;;; ============================================================ + +(define (split-libdirs str) + ;; Split a libdir list on : or ; into individual paths. + (let ([sep (if (string=? (or (getenv "OS") "") "Windows_NT") #\; #\:)]) + (string-split-char str sep))) + +(define (parse-libdirs-flag args) + ;; Scan for --libdirs <path> (or --libdirs=<path>) anywhere in args. + ;; Returns (values libdir-list remaining-args). + (let loop ([args args] [libs '()] [acc '()]) + (cond + [(null? args) + (values libs (reverse acc))] + [(string=? (car args) "--libdirs") + (when (null? (cdr args)) + (error 'jerbuild "--libdirs requires a value")) + (loop (cddr args) (append libs (split-libdirs (cadr args))) acc)] + [(string-starts-with? (car args) "--libdirs=") + (let ([val (substring (car args) 10 (string-length (car args)))]) + (loop (cdr args) (append libs (split-libdirs val)) acc))] + [else + (loop (cdr args) libs (cons (car args) acc))]))) + +(define (apply-libdirs! libs) + (unless (null? libs) + (library-directories libs))) + +(define (run-exec args) + ;; jerbuild exec [--libdirs <p>] <script.ss> [script-args...] + (let-values ([(libs rest) (parse-libdirs-flag args)]) + (when (null? rest) + (error 'jerbuild "exec: missing script path")) + (apply-libdirs! libs) + (command-line-arguments (cdr rest)) + (load (car rest)))) + +(define (run-compile args) + ;; jerbuild compile [--libdirs <p>] [--wpo] <file.ss>... + (let-values ([(libs rest) (parse-libdirs-flag args)]) + (apply-libdirs! libs) + (compile-imported-libraries #t) + (let loop ([rest rest] [wpo? #f] [files '()]) + (cond + [(null? rest) + (when wpo? (generate-wpo-files #t)) + (when (null? files) + (error 'jerbuild "compile: no input files")) + (for-each + (lambda (f) + (printf "compiling ~a\n" f) + (load f)) + (reverse files))] + [(string=? (car rest) "--wpo") + (loop (cdr rest) #t files)] + [else + (loop (cdr rest) wpo? (cons (car rest) files))])))) + +;;;; ============================================================ ;;;; Entry point ;;;; ============================================================ -(let-values ([(src-dir lib-dir) (parse-args (command-line-arguments))]) - (jerbuild src-dir lib-dir)) +(let ([args (command-line-arguments)]) + (cond + [(and (pair? args) (string=? (car args) "exec")) + (run-exec (cdr args))] + [(and (pair? args) (string=? (car args) "compile")) + (run-compile (cdr args))] + [(and (pair? args) (string=? (car args) "transpile")) + (let-values ([(src-dir lib-dir) (parse-args (cdr args))]) + (jerbuild src-dir lib-dir))] + [else + ;; Default: transpile (backward compat with existing Makefiles). + (let-values ([(src-dir lib-dir) (parse-args args)]) + (jerbuild src-dir lib-dir))])) new file mode 100755 --- /dev/null +++ b/support/build-jerbuild.sh @@ -0,0 +1,302 @@ +#!/bin/sh +# build-jerbuild.sh — Build the standalone `jerbuild` binary. +# +# jerbuild is a self-contained .ss → .sls transpiler. It embeds the Jerboa +# stdlib (lib/jerboa + lib/std) as a tarball, so it can be used to build any +# Jerboa project on a box with cargo/rustc/cc — without a Jerboa checkout +# or a Chez install. +# +# Usage: +# support/build-jerbuild.sh +# +# Output: +# ./jerbuild — the binary +# +# Runtime usage of the produced binary: +# jerbuild <src-dir> <lib-dir> — transpile .ss → .sls +# jerbuild --jerboa-home — extract bundled stdlib, print its path +# jerbuild --version — print version + bundled-lib hash + +set -eu + +JERBOA_HOME="${JERBOA_HOME:-$(cd "$(dirname "$0")/.." && pwd)}" +SCHEME="${SCHEME:-$JERBOA_HOME/.chez/bin/scheme}" +JERBOA_CHEZ_PREFIX="${JERBOA_CHEZ_PREFIX:-$JERBOA_HOME/.chez}" +CC="${CC:-cc}" +OUTPUT="${OUTPUT:-jerbuild}" + +cd "$JERBOA_HOME" + +[ -x "$SCHEME" ] || { echo "ERROR: scheme not found at $SCHEME" >&2; exit 1; } +[ -f "jerbuild.ss" ] || { echo "ERROR: jerbuild.ss not found in $JERBOA_HOME" >&2; exit 1; } + +HOST_OS=$(uname -s) +case "$HOST_OS" in + Darwin) OS_LIBS="-lm -lpthread -lncurses -liconv" ;; + Linux) OS_LIBS="-lm -ldl -lpthread -lncurses" ;; + FreeBSD) OS_LIBS="-lm -lpthread -lncurses -L/usr/local/lib -liconv" ;; + *) OS_LIBS="-lm -lpthread -lncurses" ;; +esac + +MACHINE_TYPE=$("$SCHEME" -q <<'EOF' +(display (machine-type)) (exit) +EOF +) + +CSV_DIR="" +for prefix in "$JERBOA_CHEZ_PREFIX/lib" /usr/local/lib /usr/lib /usr/lib64 /opt/homebrew/lib /opt/local/lib; do + for d in "$prefix"/csv*/"$MACHINE_TYPE"; do + if [ -f "$d/libkernel.a" ] && [ -f "$d/scheme.h" ] && [ -f "$d/petite.boot" ]; then + CSV_DIR="$d" + break 2 + fi + done +done +[ -n "$CSV_DIR" ] || { echo "ERROR: no Chez install found for $MACHINE_TYPE" >&2; exit 1; } + +echo "=== jerbuild binary build ===" +echo " Host: $HOST_OS, $MACHINE_TYPE" +echo " CC: $CC" +echo " Chez: $CSV_DIR" +echo " Out: ./$OUTPUT" +echo "" + +WPO_SO="${OUTPUT}.wp.so" +OBJ_DIR=$(mktemp -d "/tmp/jerbuild-obj.XXXXXX") +BUNDLE_TAR=$(mktemp "/tmp/jerbuild-bundle.XXXXXX.tar") +trap 'rm -rf "$OBJ_DIR" "$WPO_SO" "$BUNDLE_TAR" petite_boot.h scheme_boot.h program_boot.h bundle_tar.h bundle_meta.h "${OUTPUT}-main.c"' EXIT + +echo "==> [1/5] WPO compile jerbuild.ss" +"$SCHEME" --libdirs "$JERBOA_HOME/lib" \ + --script "$JERBOA_HOME/support/build-boot.ss" \ + jerbuild.ss "$WPO_SO" "$OBJ_DIR" +echo "" + +echo "==> [2/5] Tarball lib/jerboa + lib/std (source files only)" +# Only .ss and .sls — strip cached .so/.wpo build artifacts. +# Use find + cpio piping so we don't depend on GNU tar features. +(cd "$JERBOA_HOME" && find lib/jerboa lib/std \ + \( -name '*.ss' -o -name '*.sls' \) -type f \ + | sort \ + | tar -cf "$BUNDLE_TAR" -T -) +BUNDLE_BYTES=$(wc -c < "$BUNDLE_TAR" | tr -d ' ') +BUNDLE_SHA=$(shasum -a 256 "$BUNDLE_TAR" 2>/dev/null | awk '{print $1}' \ + || sha256sum "$BUNDLE_TAR" | awk '{print $1}') +echo " bundle: $BUNDLE_BYTES bytes, sha256=${BUNDLE_SHA%????????????????????????????????????????????????}…" +echo "" + +echo "==> [3/5] Embed boot files + program + bundle as C arrays" +embed() { + in="$1"; stem="$2" + var="${stem}_data"; sz="${stem}_size"; out="$stem.h" + printf 'static const unsigned char %s[] = {\n' "$var" > "$out" + od -An -tx1 -v "$in" \ + | sed -e '/^[[:space:]]*$/d' \ + -e 's/^ *//;s/ *$//;s/ */ /g;s/ /,0x/g;s/^/0x/;s/$/,/' >> "$out" + printf '};\nstatic const unsigned int %s = sizeof(%s);\n' "$sz" "$var" >> "$out" +} +embed "$CSV_DIR/petite.boot" petite_boot +embed "$CSV_DIR/scheme.boot" scheme_boot +embed "$WPO_SO" program_boot +embed "$BUNDLE_TAR" bundle_tar + +cat > bundle_meta.h <<EOF +static const char bundle_sha256[] = "$BUNDLE_SHA"; +EOF +echo "" + +echo "==> [4/5] Generate ${OUTPUT}-main.c" +cat > "${OUTPUT}-main.c" <<'CMAIN' +/* jerbuild — standalone Jerboa .ss → .sls transpiler. + * Generated by support/build-jerbuild.sh. */ +#include "scheme.h" +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#include "petite_boot.h" +#include "scheme_boot.h" +#include "program_boot.h" +#include "bundle_tar.h" +#include "bundle_meta.h" + +/* Cache layout: + * $XDG_CACHE_HOME/jerbuild/<sha256>/ (or ~/.cache/jerbuild/<sha256>/) + * bundle.tar — bytes baked into binary, written on first run + * .complete — marker file (touch after successful extract) + * lib/jerboa/... — extracted Jerboa stdlib + * lib/std/... — extracted std library + * + * The sha256 in the path means a new binary version transparently lives in + * its own directory; old extractions stay valid for older binaries. + */ + +static int mkdir_p(const char *path) { + char tmp[1024]; + size_t n = strlen(path); + if (n + 1 > sizeof(tmp)) { errno = ENAMETOOLONG; return -1; } + memcpy(tmp, path, n + 1); + for (size_t i = 1; i < n; i++) { + if (tmp[i] == '/') { + tmp[i] = '\0'; + if (mkdir(tmp, 0755) != 0 && errno != EEXIST) return -1; + tmp[i] = '/'; + } + } + if (mkdir(tmp, 0755) != 0 && errno != EEXIST) return -1; + return 0; +} + +static const char *cache_root(char *buf, size_t n) { + const char *xdg = getenv("XDG_CACHE_HOME"); + if (xdg && *xdg) { + snprintf(buf, n, "%s/jerbuild", xdg); + return buf; + } + const char *home = getenv("HOME"); + if (!home || !*home) { + struct passwd *pw = getpwuid(getuid()); + home = pw ? pw->pw_dir : "/tmp"; + } + snprintf(buf, n, "%s/.cache/jerbuild", home); + return buf; +} + +static const char *ensure_extracted(void) { + static char root[1024]; + static char target[1024]; + static char marker[1100]; + static char bundle[1100]; + static int ready = 0; + if (ready) return target; + + cache_root(root, sizeof(root)); + snprintf(target, sizeof(target), "%s/%s", root, bundle_sha256); + snprintf(marker, sizeof(marker), "%s/.complete", target); + + struct stat st; + if (stat(marker, &st) == 0) { ready = 1; return target; } + + if (mkdir_p(target) != 0) { + fprintf(stderr, "jerbuild: mkdir -p %s: %s\n", target, strerror(errno)); + exit(1); + } + + snprintf(bundle, sizeof(bundle), "%s/bundle.tar", target); + int fd = open(bundle, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + fprintf(stderr, "jerbuild: open %s: %s\n", bundle, strerror(errno)); + exit(1); + } + ssize_t w = write(fd, bundle_tar_data, bundle_tar_size); + close(fd); + if (w != (ssize_t)bundle_tar_size) { + fprintf(stderr, "jerbuild: short write to %s\n", bundle); + exit(1); + } + + /* Extract via system tar. cd-into-target before extract so paths are + * relative to $target/, not $CWD. */ + char cmd[2400]; + snprintf(cmd, sizeof(cmd), "cd '%s' && tar -xf '%s'", target, bundle); + int rc = system(cmd); + if (rc != 0) { + fprintf(stderr, "jerbuild: tar -xf %s failed (rc=%d)\n", bundle, rc); + exit(1); + } + unlink(bundle); + + fd = open(marker, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + fprintf(stderr, "jerbuild: cannot touch %s: %s\n", marker, strerror(errno)); + exit(1); + } + close(fd); + + ready = 1; + return target; +} + +static const char *write_program_tmpfile(void) { + static char path[] = "/tmp/jerbuild-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[]) { + /* Fast paths that avoid booting Chez. */ + if (argc == 2 && strcmp(argv[1], "--jerboa-home") == 0) { + printf("%s\n", ensure_extracted()); + return 0; + } + if (argc == 2 && strcmp(argv[1], "--bundle-sha256") == 0) { + printf("%s\n", bundle_sha256); + return 0; + } + if (argc == 2 && strcmp(argv[1], "--version") == 0) { + printf("jerbuild (bundled-lib sha256=%s)\n", bundle_sha256); + return 0; + } + if (argc == 2 && (strcmp(argv[1], "-h") == 0 || + strcmp(argv[1], "--help") == 0)) { + fputs( + "Usage:\n" + " jerbuild <src> <lib> # transpile .ss -> .sls\n" + " jerbuild transpile <src> <lib> [--force]\n" + " jerbuild compile --libdirs <p> [--wpo] FILE...\n" + " # compile-imported-libs\n" + " jerbuild exec --libdirs <p> FILE [args...] # load+run a Scheme script\n" + " jerbuild --jerboa-home # extract+print stdlib path\n" + " jerbuild --version\n", + stdout); + return 0; + } + + 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); + + const char *prog_path = write_program_tmpfile(); + int status = Sscheme_program(prog_path, argc, argv); + unlink(prog_path); + + Sscheme_deinit(); + return status; +} +CMAIN +echo "" + +echo "==> [5/5] Compile + link -> $OUTPUT" +EXTRA_ARCHIVES="" +for a in liblz4.a libz.a; do + [ -f "$CSV_DIR/$a" ] && EXTRA_ARCHIVES="$EXTRA_ARCHIVES $CSV_DIR/$a" +done + +# shellcheck disable=SC2086 +$CC -I. -I"$CSV_DIR" -O2 \ + -o "$OUTPUT" \ + "${OUTPUT}-main.c" \ + "$CSV_DIR/libkernel.a" \ + $EXTRA_ARCHIVES \ + $OS_LIBS + +echo "" +echo "=== Build complete ===" +ls -lh "$OUTPUT" +file "$OUTPUT" 2>/dev/null || true