Fold jolt-deps into the jolt binary (#133)
Dependency resolution now lives in the `jolt` CLI itself instead of a separate jolt-deps executable. `jolt` resolves a deps.edn into JOLT_PATH/JOLT_APP_PATHS in-process and dispatches the deps subcommands: jolt -M:alias [args] run the alias :main-opts jolt -A:alias CMD run CMD with the alias paths jolt run FILE resolve, then run FILE jolt path | tasks | task NAME A deps.edn in the working dir is auto-resolved for the runnable commands (repl/-m/-e/nrepl-server/FILE), so e.g. `jolt -M:nrepl` (or plain `jolt nrepl-server`) starts an nREPL with the project and its deps loaded. The runtime core stays deps-agnostic — it only reads JOLT_PATH. The resolver (deps.janet) is reached only from the CLI entry and loads jpm lazily, so a run with no deps.edn never touches it and an app baked from its own jolt/api entry never links it. resolve-deps-argv only resolves on an explicit deps command or when a deps.edn is present; help/version never do. jolt-deps stays as a thin deprecation shim that forwards to `jolt`, so existing scripts keep working. Docs (README, CLAUDE.md, building-and-deps, tools-deps) and the help text updated. Co-authored-by: Yogthos <yogthos@gmail.com>
This commit is contained in:
parent
048de0200d
commit
30a12f39ff
8 changed files with 274 additions and 139 deletions
|
|
@ -1,45 +1,16 @@
|
|||
# jolt-deps — a separate tool that resolves a deps.edn into Jolt source roots.
|
||||
# jolt-deps — deprecated shim. Dependency resolution is now built into `jolt`
|
||||
# itself (the runtime stays deps-agnostic; the CLI front-end resolves deps.edn
|
||||
# into JOLT_PATH in-process). This binary forwards everything to `jolt` so
|
||||
# existing scripts keep working — prefer calling `jolt` directly:
|
||||
#
|
||||
# Mirrors how jpm is a tool beside the janet runtime: the jolt runtime knows
|
||||
# nothing about deps.edn — it just searches the roots in JOLT_PATH (see
|
||||
# api/init). This tool does the resolution (git + :local deps, via jpm's fetch
|
||||
# cache) and either prints the roots or launches jolt with JOLT_PATH set.
|
||||
# jolt-deps -M:nrepl -> jolt -M:nrepl
|
||||
# jolt-deps path -> jolt path
|
||||
# jolt-deps run FILE -> jolt run FILE
|
||||
#
|
||||
# jolt-deps [-A:a:b] path print the resolved roots (':'-joined), e.g.
|
||||
# JOLT_PATH=$(jolt-deps path) jolt file.clj
|
||||
# jolt-deps [-A:a:b] run FILE [args]
|
||||
# resolve, then `jolt FILE args` with JOLT_PATH set
|
||||
# jolt-deps [-A:a:b] repl resolve, then a jolt REPL with JOLT_PATH set
|
||||
# jolt-deps [-A:a:b] -e EXPR resolve, then `jolt -e EXPR ...`
|
||||
# jolt-deps -M:a[:b] [args] resolve with the aliases, then run jolt with
|
||||
# the last alias's :main-opts ++ args
|
||||
# jolt-deps uberscript OUT -m NS resolve, then bundle NS + deps into one .clj
|
||||
# jolt-deps tasks list :tasks (merged user+project deps.edn)
|
||||
# jolt-deps task NAME [args] run a task: a string task is a shell command
|
||||
# (args appended); a {:main-opts [...]} task
|
||||
# runs jolt with those args ++ extra args
|
||||
#
|
||||
# -A:dev:test selects aliases (tools.deps style): their :extra-paths and
|
||||
# :extra-deps join the resolution. A user-level deps.edn ($JOLT_CONFIG, else
|
||||
# $XDG_CONFIG_HOME/jolt, else ~/.jolt) merges under the project's.
|
||||
# The jolt binary is found via $JOLT_BIN, else `jolt` on PATH.
|
||||
# The jolt binary is found via $JOLT_BIN, else the `jolt` sitting next to this
|
||||
# shim (built as a pair), else `jolt` on PATH.
|
||||
|
||||
(import ./deps :as deps)
|
||||
|
||||
(defn- parse-alias-flag
|
||||
"\"-A:dev:test\" -> [:dev :test] (also accepts -M:...)."
|
||||
[arg]
|
||||
(map keyword (filter |(not= "" $) (string/split ":" (string/slice arg 2)))))
|
||||
|
||||
(defn- roots [aliases]
|
||||
(if (os/stat "deps.edn") (deps/resolve-deps-cached "deps.edn" nil aliases) @[]))
|
||||
|
||||
(defn- jolt-bin
|
||||
"The jolt executable: $JOLT_BIN, else the `jolt` sitting NEXT TO this
|
||||
jolt-deps binary (the pair is built together — running a checkout's
|
||||
build/jolt-deps by path must not pick up some other jolt, or fail when
|
||||
none is on PATH), else `jolt` from PATH."
|
||||
[]
|
||||
(defn- jolt-bin []
|
||||
(or (os/getenv "JOLT_BIN")
|
||||
(let [self (or (first (dyn :args)) (dyn :executable))
|
||||
slashes (when self (string/find-all "/" self))
|
||||
|
|
@ -49,56 +20,8 @@
|
|||
(when (and sibling (os/stat sibling)) sibling))
|
||||
"jolt"))
|
||||
|
||||
(defn- exec-jolt [aliases extra-args]
|
||||
# Set JOLT_PATH in our own env and let the child inherit it (os/execute's env
|
||||
# arg isn't honored here; inheriting is reliable).
|
||||
(def rs (string/join (roots aliases) ":"))
|
||||
(def existing (os/getenv "JOLT_PATH"))
|
||||
(os/setenv "JOLT_PATH" (if (and existing (> (length existing) 0)) (string rs ":" existing) rs))
|
||||
# The project's OWN source roots (jolt-87e): the runtime scopes whole-program
|
||||
# inference to namespaces under these, inferring deps per-ns at load instead of
|
||||
# re-inferring every transitive dep in one fixpoint. Absent => the runtime
|
||||
# treats all namespaces as app (whole-program over everything, as before).
|
||||
(when (os/stat "deps.edn")
|
||||
(os/setenv "JOLT_APP_PATHS" (string/join (deps/project-source-roots "deps.edn" aliases) ":")))
|
||||
(os/execute [(jolt-bin) ;extra-args] :p))
|
||||
|
||||
(defn- usage []
|
||||
(print "usage: jolt-deps [-A:alias[:alias]] [path | run FILE [args] | repl | -e EXPR [args]]")
|
||||
(print " jolt-deps -M:alias[:alias] [args] (runs the alias :main-opts)")
|
||||
(print " jolt-deps uberscript OUT -m NS"))
|
||||
|
||||
(defn main [&]
|
||||
(var argv (tuple/slice (or (dyn :args) @[]) 1))
|
||||
(var aliases nil)
|
||||
# leading -A:... selects aliases for whatever command follows
|
||||
(while (string/has-prefix? "-A" (or (get argv 0) ""))
|
||||
(set aliases (array/concat (or aliases @[]) (parse-alias-flag (get argv 0))))
|
||||
(set argv (tuple/slice argv 1)))
|
||||
(def cmd (get argv 0))
|
||||
(cond
|
||||
(or (nil? cmd) (= cmd "help") (= cmd "-h") (= cmd "--help")) (usage)
|
||||
(string/has-prefix? "-M" cmd)
|
||||
(let [als (array/concat (or aliases @[]) (parse-alias-flag cmd))
|
||||
mo (or (deps/alias-main-opts "deps.edn" als)
|
||||
(do (eprint "jolt-deps: no :main-opts in aliases " (string/format "%j" (map string als)))
|
||||
(os/exit 1)))]
|
||||
(os/exit (exec-jolt als [;mo ;(tuple/slice argv 1)])))
|
||||
(= cmd "path") (print (string/join (roots aliases) ":"))
|
||||
(= cmd "tasks")
|
||||
(each row (deps/tasks "deps.edn")
|
||||
(print (row 0) (if (row 1) (string "\t" (row 1)) "")))
|
||||
(= cmd "task")
|
||||
(let [name (get argv 1)
|
||||
spec (when name (deps/task-spec "deps.edn" name))]
|
||||
(cond
|
||||
(nil? name) (do (eprint "jolt-deps: task needs a name") (os/exit 1))
|
||||
(nil? spec) (do (eprint "jolt-deps: no such task: " name) (os/exit 1))
|
||||
(= :shell (spec :type))
|
||||
(os/exit (os/execute ["sh" "-c" (string/join [(spec :cmd) ;(tuple/slice argv 2)] " ")] :p))
|
||||
(os/exit (exec-jolt aliases [;(spec :argv) ;(tuple/slice argv 2)]))))
|
||||
(= cmd "run") (os/exit (exec-jolt aliases (tuple/slice argv 1)))
|
||||
(= cmd "repl") (os/exit (exec-jolt aliases []))
|
||||
(= cmd "-e") (os/exit (exec-jolt aliases argv))
|
||||
(= cmd "uberscript") (os/exit (exec-jolt aliases argv))
|
||||
(do (eprint "jolt-deps: unknown command " cmd) (usage) (os/exit 1))))
|
||||
(def argv (tuple/slice (or (dyn :args) @[]) 1))
|
||||
(eprint "jolt-deps is deprecated — dependency resolution is built into `jolt` now "
|
||||
"(e.g. `jolt -M:nrepl`, `jolt path`, `jolt run FILE`).")
|
||||
(os/exit (os/execute [(jolt-bin) ;argv] :p)))
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
(use ./config)
|
||||
(use ./reader)
|
||||
(import ./core :as jcore)
|
||||
(import ./deps :as deps)
|
||||
|
||||
(def jolt-version "0.1.0")
|
||||
|
||||
|
|
@ -622,6 +623,14 @@
|
|||
(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, git + :local deps — resolved into JOLT_PATH):")
|
||||
(print " -M:a[:b] [args] Run the alias(es) :main-opts ++ args")
|
||||
(print " -A:a[:b] CMD [args] Run CMD (repl/-m/nrepl-server/…) with the alias paths")
|
||||
(print " run FILE [args] Run FILE with deps.edn resolved")
|
||||
(print " path Print the resolved source roots (':'-joined)")
|
||||
(print " tasks List :tasks from deps.edn")
|
||||
(print " task NAME [args] Run a :tasks entry (shell string or :main-opts)")
|
||||
(print " A deps.edn in the working dir is auto-resolved for repl/-m/-e/nrepl-server/FILE.\n")
|
||||
(print "Running a program (a file, -m/-M) direct-links by default; type inference")
|
||||
(print "and specialization are opt-in via JOLT_OPTIMIZE (the cost is paid once, then")
|
||||
(print "cached). The repl, -e, and nrepl-server stay open so you can redefine vars.")
|
||||
|
|
@ -632,7 +641,7 @@
|
|||
(print " JOLT_DIRECT_LINK=1 force direct-linking + optimization on (e.g. for -e)")
|
||||
(print " JOLT_WHOLE_PROGRAM=1 force the whole-program pass on")
|
||||
(print " JOLT_INTERPRET=1 run the tree-walking interpreter\n")
|
||||
(print "Dependencies (deps.edn) are handled by the separate jolt-deps tool."))
|
||||
(print " JOLT_PATH=dir1:dir2 extra source roots (set automatically by deps resolution)"))
|
||||
|
||||
(def- help-flags {"-h" true "--help" true "help" true "-?" true})
|
||||
(def- version-flags {"--version" true "version" true})
|
||||
|
|
@ -641,13 +650,94 @@
|
|||
(def- file-flags {"-f" true "--file" true})
|
||||
(def- main-flags {"-m" true "--main" true})
|
||||
|
||||
# --- deps.edn integration (the old jolt-deps tool, folded in) ----------------
|
||||
# The runtime stays deps-agnostic: it reads source roots from JOLT_PATH (applied
|
||||
# in `main`, below). This layer resolves a deps.edn into those roots IN-PROCESS,
|
||||
# so a single `jolt` binary both resolves dependencies and runs code. ./deps
|
||||
# loads jpm (git fetch + cache) lazily — only at an actual resolve — so a run
|
||||
# with no deps.edn never pulls it in, and an app baked from its own entry (which
|
||||
# imports jolt/api, not this CLI) never links the resolver at all.
|
||||
|
||||
(defn- parse-alias-flag
|
||||
``"-A:dev:test" / "-M:dev" -> [:dev :test].``
|
||||
[arg]
|
||||
(map keyword (filter |(not= "" $) (string/split ":" (string/slice arg 2)))))
|
||||
|
||||
(defn- deps-roots [aliases]
|
||||
(if (os/stat "deps.edn") (deps/resolve-deps-cached "deps.edn" nil aliases) @[]))
|
||||
|
||||
(defn- set-deps-env! [aliases]
|
||||
# Prepend the resolved roots to any existing JOLT_PATH; `main` (below) applies
|
||||
# them to the ctx source-paths. JOLT_APP_PATHS scopes whole-program inference
|
||||
# to the project's own namespaces — deps load-infer per-ns instead (jolt-87e).
|
||||
(def rs (string/join (deps-roots aliases) ":"))
|
||||
(def existing (os/getenv "JOLT_PATH"))
|
||||
(os/setenv "JOLT_PATH" (if (and existing (> (length existing) 0)) (string rs ":" existing) rs))
|
||||
(when (os/stat "deps.edn")
|
||||
(os/setenv "JOLT_APP_PATHS" (string/join (deps/project-source-roots "deps.edn" aliases) ":"))))
|
||||
|
||||
(defn- resolve-deps-argv
|
||||
``Resolve deps.edn (when relevant) and de-sugar the deps subcommands into a
|
||||
plain runtime argv that `main` then dispatches on. The pure deps queries
|
||||
(path/tasks/task) print and exit. A runnable command resolves a deps.edn in
|
||||
cwd when one is present — so `jolt repl` / `-m` / `nrepl-server` pick up the
|
||||
project and its dependencies — and is a no-op (resolver untouched) with none.``
|
||||
[argv]
|
||||
# leading -A:alias flags apply to whatever command follows
|
||||
(var aliases nil)
|
||||
(var rest argv)
|
||||
(while (string/has-prefix? "-A" (or (get rest 0) ""))
|
||||
(set aliases (array/concat (or aliases @[]) (parse-alias-flag (get rest 0))))
|
||||
(set rest (array/slice rest 1)))
|
||||
(def cmd (get rest 0))
|
||||
(cond
|
||||
# pure deps queries — produce output and exit, never start the runtime
|
||||
(= cmd "path")
|
||||
(do (print (string/join (deps-roots aliases) ":")) (os/exit 0))
|
||||
(= cmd "tasks")
|
||||
(do (each row (deps/tasks "deps.edn")
|
||||
(print (row 0) (if (row 1) (string "\t" (row 1)) "")))
|
||||
(os/exit 0))
|
||||
(= cmd "task")
|
||||
(let [name (get rest 1)
|
||||
spec (when name (deps/task-spec "deps.edn" name))]
|
||||
(cond
|
||||
(nil? name) (do (eprint "jolt: task needs a name") (os/exit 1))
|
||||
(nil? spec) (do (eprint "jolt: no such task: " name) (os/exit 1))
|
||||
(= :shell (spec :type))
|
||||
(os/exit (os/execute ["sh" "-c" (string/join [(spec :cmd) ;(array/slice rest 2)] " ")] :p))
|
||||
(do (set-deps-env! aliases)
|
||||
(array/concat @[] (spec :argv) (array/slice rest 2)))))
|
||||
# -M:aliases — resolve and run the alias's :main-opts ++ extra args
|
||||
(and cmd (string/has-prefix? "-M" cmd))
|
||||
(let [als (array/concat (or aliases @[]) (parse-alias-flag cmd))
|
||||
mo (deps/alias-main-opts "deps.edn" als)]
|
||||
(if mo
|
||||
(do (set-deps-env! als) (array/concat @[] mo (array/slice rest 1)))
|
||||
(do (eprint "jolt: no :main-opts in alias(es) " (string/format "%j" (map string als)))
|
||||
(os/exit 1))))
|
||||
# explicit `run FILE` — resolve, then run the file
|
||||
(= cmd "run")
|
||||
(do (set-deps-env! aliases) (array/slice rest 1))
|
||||
# any runnable command: resolve a deps.edn if present (or if -A forced it);
|
||||
# help/version never resolve, and with no deps.edn + no -A the resolver and
|
||||
# jpm are never touched.
|
||||
(and (not (help-flags cmd)) (not (version-flags cmd))
|
||||
(or aliases (os/stat "deps.edn")))
|
||||
(do (set-deps-env! aliases) rest)
|
||||
# nothing deps-related — argv unchanged
|
||||
true rest))
|
||||
|
||||
(defn main [&]
|
||||
(def args (or (dyn :args) @[])) # @["jolt" arg1 arg2 ...]
|
||||
(def argv (if (> (length args) 1) (array/slice args 1) @[]))
|
||||
# Resolve deps.edn + de-sugar deps subcommands (jolt-deps folded in): sets
|
||||
# JOLT_PATH/JOLT_APP_PATHS in our env (read just below) and rewrites argv to a
|
||||
# plain runtime command. A no-deps invocation passes through untouched.
|
||||
(def argv (resolve-deps-argv (if (> (length args) 1) (array/slice args 1) @[])))
|
||||
(ctx-set-current-ns ctx "user")
|
||||
# JOLT_PATH must be applied at runtime: this `ctx` is built into the image at
|
||||
# build time, so its source-paths can't capture the runtime environment.
|
||||
# `jolt-deps` sets JOLT_PATH to the resolved deps.edn source roots.
|
||||
# resolve-deps-argv (above) sets it from the resolved deps.edn source roots.
|
||||
(when-let [jp (os/getenv "JOLT_PATH")]
|
||||
(each p (string/split ":" jp)
|
||||
(when (> (length p) 0) (array/push (get (ctx :env) :source-paths) p))))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue