Open ideas for clj-ant, ordered roughly by leverage-per-effort. Each entry has a status, a one-paragraph sketch, and a rough size.
Statuses:
deftask — Clojure fns as first-class Ant tasks ✅Today there's a wall between Clojure code and the Ant element
tree: you flip out of (a/ant …) to do anything in Clojure, then
flip back. deftask removes that wall by letting any Clojure fn
register itself as an Ant task name:
(a/deftask :slack-notify
(fn [{:keys [channel msg webhook]}]
(slack/post webhook channel msg)))
(a/ant
(t/jar :destfile "app.jar" ...)
(t/scp :file "app.jar" :todir "deploy@host:/srv/")
(a/element :slack-notify :webhook url :channel "#deploys"
:msg "shipped ${version}"))
Inside the tree the new task is indistinguishable from a built-in:
${…} property expansion runs on its attributes, the build logger
emits :task-started / :task-finished, targets containing it can be
invoked with <antcall>, and <macrodef> can wrap it.
Implementation: a single small Java class (cljant.ClojureTask)
that extends org.apache.tools.ant.Task and implements
DynamicAttribute; an IFn registry keyed by task name. About 60
lines of Java + 30 lines of Clojure.
Size: medium. The Java code is contained and unambiguously justified — it's a bridge that can't be done any other way without AOT or a bytecode-gen library.
from-xml — read existing build.xml files into clj-ant data ✅Inverse of execute. Hand it a path or a string, get back a clj-ant
element tree. Massive migration path for shops with thousands of
lines of legacy Ant: walk the tree from Clojure, refactor with
update/assoc/walk, re-emit compact XML with to-xml, or keep
it as data and run with execute!.
Same shape powers a static analyzer over a corpus of build files —
"every <scp> with :trust true", "every <javac> without
:debug".
Size: small. ~80 lines using clojure.xml (built-in, no dep).
Two-way bridge between clj-ant and clojure.tools.build.api.
Direction A: b/javac, b/jar, b/copy-dir etc. wrapped as
clj-ant tasks via deftask so they slot into element trees with
the same shape as everything else. Direction B: clj-ant tasks
called from a tools.build script — a normal fn call. The combined
pitch: tools.build for Clojure-native operations, clj-ant for the
long tail (signing, deployment, archive surgery, SSH, XSLT).
Size: small. ~50 lines wrappers + recipes in
doc/tools-build.md. Mostly documentation.
(elements tree pred) and (transform tree f) ship in core.clj.
Specter / zippers turned out to be unnecessary: tree-seq over
:children already gives a lazy depth-first walk, and the current
transform covers post-order rewrites. See doc/examples.md "Audit
a corpus of build.xml files".
Size: small. Shipped without adding Specter or a zipper layer.
(a/session opts) and (a/with-session [s opts] ...) reuse one Ant
Project across many calls. Properties set in one call are visible in
the next, permanent deftask registrations are re-synced, and per-call
synthetic references/targets are cleaned up in finally. Pass
:project explicitly to opt out for a single call.
BuildException raised by executeTargets is now wrapped into
ex-info carrying :clj-ant/elements, :clj-ant/targets,
:ant/exception-class, and :ant/message (the unwrapped root
message). The original throwable is preserved as :cause for
full stack-trace access. Tooling can pattern-match on failure
mode without parsing strings.
(execute-async! …) returns a Run handle that delegates to a
promise underneath. (cancel! run) interrupts the build
thread; IO tasks abort cleanly, CPU-bound ones may run through.
:cancelled? true lands on the result map regardless to
reflect caller intent.
(watch nodes :paths […] :poll-ms …) re-runs the plan on every
filesystem change under the named paths. Polling-based; pairs
with :session for cheap re-runs.
ICoercible extension protocol ✅as-child is now a protocol dispatch. Out-of-tree types extend
clj-ant.core/ICoercible from their own ns and flow as children
of any task without forking the lib.
defmethod dispatch on tag 💭A multimethod interpret-element that defaults to UnknownElement
but can be specialised per tag. Lets users plug in custom side
effects without deftask's registration step. Powerful but easy to
misuse; deftask covers the legitimate cases.
Size: small.
A graalvm-native-image build of the bb pod for instant startup. Ant
uses a lot of runtime reflection, so this is a substantial
reflect-config.json project for relatively marginal gain over a
warm-JVM pod (which already starts in well under a second).
Size: large. Probably skip unless someone has a hard cold-start requirement.
Same idea as deftask but for types/data structures (your own
ResourceCollection, Selector, Mapper). Useful but rare; most users
will never need it.
Size: medium.
Things deliberately not on the list, with reasons.
lazy-resources. Looks like an escape hatch but
matters for million-file scans where the default count is too
expensive.plan. It's already trivially fast; complexifying
the wire protocol for nothing.Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |