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
|
|
@ -11,16 +11,22 @@ git submodule update --init # vendor/sci (used by the SCI bootstrap tests)
|
|||
jpm build
|
||||
```
|
||||
|
||||
This produces two executables under `build/`:
|
||||
This produces `build/jolt` — one binary that is both the runtime (REPL,
|
||||
file/expr runner, nREPL server) and the dependency front-end (`deps.edn`
|
||||
resolution, see below). The whole `.clj` standard library
|
||||
(`clojure.string`/`set`/`walk`/`edn`/`zip`, `jolt.http`/`interop`/`shell`/
|
||||
`nrepl`) is baked in at build time, so it loads from any directory — the artifact
|
||||
is self-contained. (`clojure.core` is built into the runtime in Janet and
|
||||
auto-referred, so it's always available.)
|
||||
|
||||
- **`jolt`** — the runtime: REPL, file/expr runner, nREPL server. The whole `.clj`
|
||||
standard library (`clojure.string`/`set`/`walk`/`edn`/`zip`, `jolt.http`/
|
||||
`interop`/`shell`/`nrepl`) is baked into this binary at build time, so it loads
|
||||
from any directory — the build artifact is self-contained. (`clojure.core` is
|
||||
built into the runtime in Janet and auto-referred, so it's always available.)
|
||||
- **`jolt-deps`** — a separate tool that resolves a `deps.edn` (see below). It
|
||||
sits beside the runtime the way `jpm` sits beside `janet`; the runtime itself
|
||||
knows nothing about deps.edn.
|
||||
The runtime **core** stays deps-agnostic: it only reads source roots from
|
||||
`JOLT_PATH`. Dependency resolution lives in a separate CLI front-end module
|
||||
(`src/jolt/deps.janet`) that the `jolt` entry point calls *before* running your
|
||||
code, and that lazily loads `jpm` (for git fetch + cache) only when it actually
|
||||
resolves. So a run with no `deps.edn` never touches the resolver, and an app
|
||||
baked from its own entry — which imports `jolt/api`, not the CLI — never links
|
||||
it at all. (`build/` also contains a `jolt-deps` shim that just forwards to
|
||||
`jolt` so old scripts keep working; prefer calling `jolt` directly.)
|
||||
|
||||
Needs `jpm` and a recent Janet — developed and CI-tested against **1.41**. The
|
||||
futures and core.async layers use Janet's threaded `ev/` channels (`ev/thread`,
|
||||
|
|
@ -54,23 +60,24 @@ JOLT_PATH=/path/to/lib/src build/jolt myfile.clj
|
|||
|
||||
## Dependencies via deps.edn
|
||||
|
||||
`jolt-deps` reads a `deps.edn` in the current directory, fetches its
|
||||
dependencies, and runs `jolt` with the resolved source directories on
|
||||
`JOLT_PATH`.
|
||||
`jolt` reads a `deps.edn` in the current directory, fetches its dependencies,
|
||||
and puts the resolved source directories on `JOLT_PATH` for the run. A `deps.edn`
|
||||
in the working dir is **auto-resolved** for the runnable commands (`repl`, `-m`,
|
||||
`-e`, `nrepl-server`, a `FILE`); the explicit subcommands below also work
|
||||
anywhere:
|
||||
|
||||
```bash
|
||||
jolt-deps path # print the resolved roots (':'-joined)
|
||||
jolt-deps run FILE [args] # resolve, then run `jolt FILE …`
|
||||
jolt-deps repl # resolve, then start a REPL
|
||||
jolt-deps -e EXPR [args] # resolve, then evaluate EXPR
|
||||
jolt-deps -A:dev path # include the :dev alias's extra paths/deps
|
||||
jolt-deps -M:test [args] # run the :test alias's :main-opts through jolt
|
||||
jolt-deps tasks # list :tasks from deps.edn
|
||||
jolt-deps task NAME [args] # run a task
|
||||
jolt -M:test [args] # run the :test alias's :main-opts (the usual entry)
|
||||
jolt -A:dev repl # run a command with the :dev alias's extra paths/deps
|
||||
jolt run FILE [args] # resolve, then run FILE
|
||||
jolt path # print the resolved roots (':'-joined)
|
||||
jolt tasks # list :tasks from deps.edn
|
||||
jolt task NAME [args] # run a task
|
||||
```
|
||||
|
||||
`jolt-deps` launches the `jolt` binary it finds on `PATH` (override with
|
||||
`$JOLT_BIN`).
|
||||
So, for example, to start an nREPL server that loads a project and its deps,
|
||||
add `:aliases {:nrepl {:main-opts ["nrepl-server"]}}` to `deps.edn` and run
|
||||
`jolt -M:nrepl` (or just `jolt nrepl-server`, which auto-resolves the `deps.edn`).
|
||||
|
||||
Example `deps.edn`:
|
||||
|
||||
|
|
@ -82,7 +89,7 @@ Example `deps.edn`:
|
|||
```
|
||||
|
||||
```bash
|
||||
jolt-deps run -m myapp.main
|
||||
jolt run -m myapp.main
|
||||
```
|
||||
|
||||
### What's supported
|
||||
|
|
@ -102,8 +109,8 @@ jolt-deps run -m myapp.main
|
|||
with the project winning.
|
||||
- **tasks** — `:tasks {clean "rm -rf target" test {:doc "run the suite"
|
||||
:main-opts ["-e" "(run-tests)"]}}`. A string task is a shell command; a map
|
||||
task runs jolt with its `:main-opts`. `jolt-deps tasks` lists, `jolt-deps
|
||||
task NAME` runs.
|
||||
task runs jolt with its `:main-opts`. `jolt tasks` lists, `jolt task NAME`
|
||||
runs.
|
||||
|
||||
Conflicts resolve the tools.deps way: resolution is breadth-first, so a
|
||||
top-level coordinate always beats a transitive one for the same lib, and
|
||||
|
|
@ -124,14 +131,14 @@ hash of the project `deps.edn` + the user `deps.edn` + the selected aliases.
|
|||
|
||||
### Bundling into one file
|
||||
|
||||
`jolt uberscript OUT.clj -m NS` (or `jolt-deps uberscript …`, which resolves deps
|
||||
first) bundles `NS` and every namespace it requires — your code plus its
|
||||
dependencies — into a single `.clj` in dependency order, ending with a call to
|
||||
`NS/-main`. The result runs on a plain `jolt` with no `JOLT_PATH`, no deps
|
||||
fetched, and no jpm:
|
||||
`jolt uberscript OUT.clj -m NS` bundles `NS` and every namespace it requires —
|
||||
your code plus its dependencies — into a single `.clj` in dependency order,
|
||||
ending with a call to `NS/-main`. Run it from a project dir and the `deps.edn`
|
||||
is resolved first, so dependency namespaces are on the path to bundle. The
|
||||
result runs on a plain `jolt` with no `JOLT_PATH`, no deps fetched, and no jpm:
|
||||
|
||||
```bash
|
||||
jolt-deps uberscript app.clj -m myapp.main
|
||||
jolt uberscript app.clj -m myapp.main
|
||||
jolt app.clj arg1 arg2
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,13 @@ Scope, decided up front:
|
|||
"the classpath" is an ordered list of source directories.
|
||||
- **piggyback on jpm** — reuse jpm's git fetch + cache; don't write a package
|
||||
manager.
|
||||
- **separate tool** — resolution lives in `jolt-deps`, beside the runtime, the
|
||||
way `jpm` sits beside `janet`. The `jolt` runtime knows nothing about deps.edn.
|
||||
- **deps-agnostic runtime core** — resolution is a CLI front-end concern, not a
|
||||
runtime one. The `jolt` *runtime* knows nothing about deps.edn; it only reads
|
||||
source roots from `JOLT_PATH`. The `jolt` *CLI* resolves a deps.edn into that
|
||||
env var before running, in a module (`deps.janet`) that loads `jpm` lazily.
|
||||
(This was a separate `jolt-deps` binary originally; it was folded into `jolt`
|
||||
for a single-binary UX — the code boundary stayed, only the executable merged.
|
||||
A back-compat `jolt-deps` shim still ships and forwards to `jolt`.)
|
||||
|
||||
## How jpm handles dependencies
|
||||
|
||||
|
|
@ -70,9 +75,15 @@ The loader (`evaluator.janet/find-ns-file`) resolves a namespace by searching th
|
|||
context's `:source-paths` in order (the stdlib `src/jolt` first), trying `<ns>.clj`
|
||||
then `<ns>.cljc`. Extra roots come from `JOLT_PATH` or `init`'s `:paths` option.
|
||||
|
||||
`jolt-deps` (`src/jolt/deps_cli.janet`, its own `declare-executable`) ties it
|
||||
together: it resolves the roots and runs the `jolt` binary with them on
|
||||
`JOLT_PATH`. The runtime's only dependency interface is that env var.
|
||||
The `jolt` CLI (`src/jolt/main.janet`, `resolve-deps-argv`) ties it together: on
|
||||
a deps subcommand — or any runnable command in a directory that has a `deps.edn`
|
||||
— it resolves the roots, sets `JOLT_PATH`/`JOLT_APP_PATHS`, and de-sugars the
|
||||
argv into a plain runtime command (`-M:alias` → the alias `:main-opts`, `run
|
||||
FILE` → `FILE`, …) that the normal dispatch then runs. `main.janet` imports
|
||||
`deps.janet`, so the resolver ships in the `jolt` binary; but `deps.janet` loads
|
||||
`jpm` lazily, and the runtime modules (`api`/`backend`/RT) never import it, so an
|
||||
app baked from its own `jolt/api` entry doesn't link it. The runtime's only
|
||||
dependency interface remains that one env var.
|
||||
|
||||
`jolt uberscript` bundles a namespace and everything it requires into one
|
||||
standalone `.clj`. It requires the entry namespace and uses the order in which
|
||||
|
|
@ -111,8 +122,7 @@ a regex feature like Unicode property classes (`\p{…}`).
|
|||
## Janet dependencies: `:jpm/module`
|
||||
|
||||
A jolt project can depend on janet libraries. jpm owns their installation;
|
||||
`deps.edn` declares the requirement and `jolt-deps` verifies it at resolve
|
||||
time:
|
||||
`deps.edn` declares the requirement and `jolt` verifies it at resolve time:
|
||||
|
||||
```clojure
|
||||
:deps {janet/spork-http {:jpm/module "spork/http"
|
||||
|
|
@ -121,8 +131,8 @@ time:
|
|||
|
||||
- `:jpm/module` — the janet module path that must be importable.
|
||||
- `:jpm/install` (optional) — the jpm package to install when it isn't;
|
||||
`jolt-deps` runs `jpm install <name>` once, then re-checks. Without it the
|
||||
resolve fails with the install hint.
|
||||
`jolt` runs `jpm install <name>` once, then re-checks. Without it the resolve
|
||||
fails with the install hint.
|
||||
|
||||
A `:jpm/module` dep contributes no source roots. At runtime the `janet.*`
|
||||
interop bridge autoloads the module on first reference
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue