deps phases 3-4: uberscript bundling + conformance harness

Phase 3 — bundling. `jolt uberscript OUT -m NS` requires NS, uses the loader's
recorded load order (a dep finishes loading before its requirer, so it's
topological) to concatenate the used namespaces into one .clj that runs on a
plain jolt with no deps/jpm. `jolt-deps uberscript` resolves first. The loader
records loaded file paths when ctx env has :loaded-files.

Phase 4 — conformance. deps-conformance-test resolves real pure-cljc git libs
and reports load/run status, gated behind JOLT_CONFORMANCE so CI stays offline.
First run: medley works; cuerdas hits a reader gap (filed); stuartsierra/
dependency uses Long/MAX_VALUE (JVM interop, out of scope).

Tests: uberscript-test, deps-conformance-test (skips without the flag).
This commit is contained in:
Yogthos 2026-06-05 23:36:40 -04:00
parent 6a1706df10
commit 30c4bb0dc2
7 changed files with 174 additions and 8 deletions

View file

@ -10,6 +10,8 @@
# jolt-deps run FILE [args] resolve, then run `jolt FILE args` with JOLT_PATH set
# jolt-deps repl resolve, then start a jolt REPL with JOLT_PATH set
# jolt-deps -e EXPR [args] resolve, then `jolt -e EXPR ...` with JOLT_PATH set
# jolt-deps uberscript OUT -m NS
# resolve, then bundle NS + its deps into one .clj
#
# The jolt binary is found via $JOLT_BIN, else `jolt` on PATH.
@ -38,4 +40,5 @@
(= cmd "run") (os/exit (exec-jolt (tuple/slice argv 1)))
(= cmd "repl") (os/exit (exec-jolt []))
(= cmd "-e") (os/exit (exec-jolt argv))
(= cmd "uberscript") (os/exit (exec-jolt argv))
(do (eprint "jolt-deps: unknown command " cmd) (usage) (os/exit 1))))

View file

@ -228,6 +228,9 @@
# (their defs intern there); a library's own `ns` form overrides this.
(ctx-set-current-ns ctx ns-name)
(load-ns-file ctx path)
# Record load order for tooling (uberscript): a dependency finishes
# loading before the file that required it, so this is topological.
(when-let [lf (get (ctx :env) :loaded-files)] (array/push lf path))
(ctx-set-current-ns ctx saved)))))))
(defn- eval-require

View file

@ -324,6 +324,30 @@
(load-string ctx (string "(apply " ns-name "/-main *command-line-args*)")))
([err fib] (report-error err fib) (os/exit 1))))
(defn- run-uberscript [out main-ns]
# Bundle main-ns and everything it requires (from JOLT_PATH roots) into one
# .clj that runs on a plain jolt — no deps, no jpm. We require the entry and
# collect the load order the loader records (deps before dependents).
(when (or (nil? out) (nil? main-ns))
(eprint "Usage: jolt uberscript OUT.clj -m NS") (os/exit 1))
(put (ctx :env) :loaded-files @[])
(try
(load-string ctx (string "(require '[" main-ns "])"))
([err fib] (report-error err fib) (os/exit 1)))
(def seen @{})
(def files @[])
(each f (get (ctx :env) :loaded-files)
(unless (get seen f) (put seen f true) (array/push files f)))
(def buf @"")
(buffer/push-string buf (string ";; Generated by `jolt uberscript` — " (length files) " namespace(s)\n\n"))
(each f files
(buffer/push-string buf (string ";; --- " f " ---\n"))
(buffer/push-string buf (slurp f))
(buffer/push-string buf "\n"))
(buffer/push-string buf (string "\n(apply " main-ns "/-main *command-line-args*)\n"))
(spit out (string buf))
(print "Wrote " out " (" (length files) " namespace(s))"))
(defn- print-help []
(print "Jolt — a Clojure interpreter on Janet\n")
(print "Usage: jolt [opt] [args]\n")
@ -335,6 +359,7 @@
(print " -m, --main NS [args] Require NS and call its -main with the remaining args")
(print " nrepl-server [addr] Start an nREPL server (addr = [host:]port, default 7888)")
(print " (aliases: --nrepl-server, nrepl)")
(print " uberscript OUT -m NS Bundle NS + its required namespaces into one .clj")
(print " --version, version Print the Jolt version")
(print " -h, --help, help Show this help\n")
(print "Dependencies (deps.edn) are handled by the separate jolt-deps tool."))
@ -365,5 +390,10 @@
(eval-flags (argv 0)) (run-eval (get argv 1 "") (array/slice argv 2))
(file-flags (argv 0)) (run-file (get argv 1) (array/slice argv 2))
(main-flags (argv 0)) (run-main (get argv 1) (array/slice argv 2))
(= (argv 0) "uberscript")
(let [out (get argv 1)
rest (array/slice argv 2)
mi (or (index-of "-m" rest) (index-of "--main" rest))]
(run-uberscript out (if mi (get rest (+ mi 1)) nil)))
(= (argv 0) "-") (run-file "/dev/stdin" (array/slice argv 1))
(run-file (argv 0) (array/slice argv 1))))