Embed runtime source so a self-contained joltc can build apps
`joltc build` inlines the runtime (host/chez/rt.ss and everything it loads, the seed, compile-eval, loader, ffi, the vendored irregex) into each app binary by reading those files off disk. That works from a jolt checkout but not from the installed self-contained binary, which has no source tree: joltc build -m app.core => Exception in call-with-input-file: failed for host/chez/rt.ss: no such file build-joltc now bakes the exact transitive closure of files the build inlines into the binary as embedded resources (keyed by the path the `(load "…")` forms use), and build.ss/dce.ss read runtime source through bld-source-string, which takes the embedded copy when present and falls back to disk otherwise. So the same joltc builds apps both from a checkout and standalone. The release workflow now smoke-tests a self-contained build (compile a tiny app from an isolated dir, run it) — this is exactly what shipped broken, so it now gates the release. buildsmoke/shakesmoke/staticnativesmoke unchanged and green. Build tooling only — no re-mint, no runtime change.
This commit is contained in:
parent
f6bd2c6de5
commit
db08ecc1bc
4 changed files with 65 additions and 7 deletions
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
|
|
@ -93,6 +93,22 @@ jobs:
|
||||||
out="$(./target/release/joltc -e '(reduce + (range 10))')"
|
out="$(./target/release/joltc -e '(reduce + (range 10))')"
|
||||||
test "$out" = "45" || { echo "joltc -e gave '$out', want 45"; exit 1; }
|
test "$out" = "45" || { echo "joltc -e gave '$out', want 45"; exit 1; }
|
||||||
|
|
||||||
|
# The binary is a self-contained COMPILER: it must `build` an app with no
|
||||||
|
# jolt source on disk. Run from an isolated dir (nothing but the tiny app)
|
||||||
|
# so a build that reaches for host/chez/*.ss on the filesystem fails here,
|
||||||
|
# not on a user's machine.
|
||||||
|
- name: Smoke a self-contained build
|
||||||
|
run: |
|
||||||
|
joltc="$(pwd)/target/release/joltc"
|
||||||
|
work="$(mktemp -d)"
|
||||||
|
mkdir -p "$work/app/src/app"
|
||||||
|
printf '{:paths ["src"]}\n' > "$work/app/deps.edn"
|
||||||
|
printf '(ns app.core)\n(defn -main [& _] (println "built:" (reduce + (range 10))))\n' \
|
||||||
|
> "$work/app/src/app/core.clj"
|
||||||
|
( cd "$work/app" && "$joltc" build -m app.core -o app )
|
||||||
|
out="$("$work/app/app")"
|
||||||
|
test "$out" = "built: 45" || { echo "self-contained build ran '$out', want 'built: 45'"; exit 1; }
|
||||||
|
|
||||||
- name: Package
|
- name: Package
|
||||||
run: |
|
run: |
|
||||||
ver="${GITHUB_REF_NAME}"
|
ver="${GITHUB_REF_NAME}"
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,32 @@
|
||||||
(bld-walk-files root "" '())))
|
(bld-walk-files root "" '())))
|
||||||
(list "jolt-core" "stdlib")))
|
(list "jolt-core" "stdlib")))
|
||||||
|
|
||||||
|
;; Embed every runtime .ss the build inlines into an app (the transitive closure of
|
||||||
|
;; the manifest's loads: rt.ss + all it loads, the seed, compile-eval, loader, ffi,
|
||||||
|
;; png, vendored irregex). Keyed by the exact path the (load "…") forms use, so
|
||||||
|
;; build.ss's bld-source-string reads them from the binary with no jolt source on
|
||||||
|
;; disk. Traversal mirrors bld-emit-runtime/bld-inline-line via the same
|
||||||
|
;; bld-file-lines + bld-load-path, so the embedded set is exactly what build reads.
|
||||||
|
(define (jb-collect-load-paths)
|
||||||
|
(let ((seen (make-hashtable string-hash string=?)) (order '()))
|
||||||
|
(define (walk path)
|
||||||
|
(when (and path (not (hashtable-ref seen path #f)))
|
||||||
|
(hashtable-set! seen path #t)
|
||||||
|
(set! order (cons path order))
|
||||||
|
(for-each (lambda (l) (walk (bld-load-path l))) (bld-file-lines path))))
|
||||||
|
(for-each (lambda (entry) (when (string? entry) (walk (bld-load-path entry))))
|
||||||
|
bld-runtime-manifest)
|
||||||
|
(for-each (lambda (kv) (walk (bld-load-path (cdr kv)))) bld-tagged-loads)
|
||||||
|
(reverse order)))
|
||||||
|
|
||||||
|
(define (jb-emit-runtime-embeds out)
|
||||||
|
(for-each
|
||||||
|
(lambda (path)
|
||||||
|
(put-string out (string-append
|
||||||
|
"(register-embedded-resource! " (ei-str-lit path) " "
|
||||||
|
(ei-str-lit (read-file-string path)) ")\n")))
|
||||||
|
(jb-collect-load-paths)))
|
||||||
|
|
||||||
;; The launcher (Chez scheme-start): replicates host/chez/cli.ss but reads argv
|
;; The launcher (Chez scheme-start): replicates host/chez/cli.ss but reads argv
|
||||||
;; from the scheme-start lambda and has no repo root to cd into (all source is
|
;; from the scheme-start lambda and has no repo root to cd into (all source is
|
||||||
;; embedded; JOLT_PWD defaults to cwd via io/jolt.main). build.ss is already
|
;; embedded; JOLT_PWD defaults to cwd via io/jolt.main). build.ss is already
|
||||||
|
|
@ -134,6 +160,8 @@
|
||||||
(bld-emit-runtime out #f #f)
|
(bld-emit-runtime out #f #f)
|
||||||
(put-string out "\n;; === build driver (inlined for self-contained `jolt build`) ===\n")
|
(put-string out "\n;; === build driver (inlined for self-contained `jolt build`) ===\n")
|
||||||
(bld-inline-line "(load \"host/chez/build.ss\")" out 0)
|
(bld-inline-line "(load \"host/chez/build.ss\")" out 0)
|
||||||
|
(put-string out "\n;; === embedded runtime source (self-contained `build` reads these) ===\n")
|
||||||
|
(jb-emit-runtime-embeds out)
|
||||||
(put-string out "\n;; === embedded jolt-core + stdlib source ===\n")
|
(put-string out "\n;; === embedded jolt-core + stdlib source ===\n")
|
||||||
(jb-emit-source-embeds out)
|
(jb-emit-source-embeds out)
|
||||||
(put-string out "\n;; === joltc launcher ===\n")
|
(put-string out "\n;; === joltc launcher ===\n")
|
||||||
|
|
|
||||||
|
|
@ -141,12 +141,23 @@
|
||||||
(q2 (let scan ((i (+ q1 1))) (if (char=? (string-ref s i) #\") i (scan (+ i 1))))))
|
(q2 (let scan ((i (+ q1 1))) (if (char=? (string-ref s i) #\") i (scan (+ i 1))))))
|
||||||
(substring s (+ q1 1) q2)))))
|
(substring s (+ q1 1) q2)))))
|
||||||
|
|
||||||
(define (bld-file-lines path)
|
;; runtime source for PATH: from the binary's embedded store if present (a
|
||||||
(call-with-input-file path
|
;; self-contained joltc building an app, with no jolt checkout on disk), else read
|
||||||
(lambda (p)
|
;; from disk (running from a source checkout). build-joltc embeds every runtime
|
||||||
(let loop ((acc '()))
|
;; .ss the manifest inlines, so `build` never touches the filesystem for them.
|
||||||
(let ((l (get-line p)))
|
(define (bld-source-string path)
|
||||||
(if (eof-object? l) (reverse acc) (loop (cons l acc))))))))
|
(let ((emb (hashtable-ref embedded-resources path #f)))
|
||||||
|
(if (string? emb) emb (read-file-string path))))
|
||||||
|
|
||||||
|
(define (bld-string-lines s)
|
||||||
|
(let ((n (string-length s)))
|
||||||
|
(let loop ((i 0) (start 0) (acc '()))
|
||||||
|
(cond ((>= i n) (reverse (if (> i start) (cons (substring s start i) acc) acc)))
|
||||||
|
((char=? (string-ref s i) #\newline)
|
||||||
|
(loop (+ i 1) (+ i 1) (cons (substring s start i) acc)))
|
||||||
|
(else (loop (+ i 1) start acc))))))
|
||||||
|
|
||||||
|
(define (bld-file-lines path) (bld-string-lines (bld-source-string path)))
|
||||||
|
|
||||||
;; Emit one line to OUT, recursively inlining a `(load ...)` of a repo file.
|
;; Emit one line to OUT, recursively inlining a `(load ...)` of a repo file.
|
||||||
(define (bld-inline-line line out depth)
|
(define (bld-inline-line line out depth)
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,10 @@
|
||||||
;; str re-serializes the read form (compiled identically; comments/whitespace are
|
;; str re-serializes the read form (compiled identically; comments/whitespace are
|
||||||
;; irrelevant).
|
;; irrelevant).
|
||||||
(define (dce-blob-records path)
|
(define (dce-blob-records path)
|
||||||
(call-with-input-file path
|
;; bld-source-string (build.ss) reads the embedded copy when running from a
|
||||||
|
;; self-contained joltc, else the file on disk — so tree-shake works with no
|
||||||
|
;; jolt checkout present. Forward ref: build.ss loads after this file.
|
||||||
|
(call-with-port (open-input-string (bld-source-string path))
|
||||||
(lambda (p)
|
(lambda (p)
|
||||||
(let loop ((acc '()))
|
(let loop ((acc '()))
|
||||||
(let ((form (read p)))
|
(let ((form (read p)))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue