dh-clojure-lein(7)
π dh-clojure-lein(7) π
okay bestie, sit down, grab a boba π§, because today's main character is the dh-clojure-lein manual page β that's Debhelper Clojure tools, section 7 β and she is serving. π
Let's get into it, no cap.
β¨ NAME β¨
dh-clojure-lein β Debhelper support for Leiningen projects π«Ά
πΈ DESCRIPTION πΈ
So the whole Leiningen-support situation is two things, bestie. One: a Debhelper build system that you can reach through the dh --buildsystem argument. Two: support for both automatic and customizable adjustments to your projects. It's giving "we handle the boring bits, you vibe". π
β οΈ Heads up fr fr: this is currently under development, and you should not consider it stable until (at least) thedh-clojuremajor version is no longer0and this notice changes. So likeβ¦ don't build your whole empire on it yet. π
π USAGE π
π οΈ Buildsystem
The build system exists to make packaging Leiningen-based Clojure projects for Debian β¨easyβ¨, and you activate it by handing --buildsystem=leiningen to dh, like so:
%:
dh $@ --buildsystem=leiningen
Once she's on, she shows up and does her thing during these debhelper stages β each one is a whole moment:
- configure β Sets up a
debian/dh-clojure/tmp/maven-reposymlink pointing at/usr/share/maven-repo, soleincan use it as a:local-repo. πͺ - build β Creates a
debian/dh-clojure/tmp/pom.xmlfor use in thedebian/PACKAGENAME.pomsfile (which she also creates, multitasking queen π), and she creates an unversioned symlink intarget/for the jar. - install β Calls helpers from the
maven-repo-helperpackage to install the built jars into/usr/share/java, plus poms and jar symlinks into/usr/share/maven-repo, and to generate the${maven:Depends}substitution variable. - test β Runs
lein test. π§ͺ - clean β Runs
lein cleanand cleans up after all the previous steps. tidying up like the responsible icon she is π§Ή
π§ Project Adjustment (middleware)
Your project.clj is gonna get automatically adjusted by the buildsystem (see above, bestie, we just talked about it π). By default, she sets all of the dependency versions in the project to "debian" β with the exception of "org.clojure/clojure", which gets set to "1.x" instead. special little guy, gets special treatment. β¨
Now here's the fun part: you can customize those project adjustments with code in an optional debian/dh-clojure-lein.clj file. π That file is allowed to define a dh-clj-adjust-project function, and she gets invoked like this:
(dh-clj-adjust-project get-project when ...)
Let's break that down because it's lowkey important:
- The first argument is a function that returns the Leiningen project map.
- The second argument tells you the current point in the project build sequence. Right now it'll be
:before-pluginsor:after-middlewareβ but to leave room for future changes, unexpected values should be ignored, and so should additional arguments. (be flexible, bestie π¦)
dh-clj-adjust-project must return the (get-project) map after whatever adjustments you made. Check the EXAMPLES below for the tea. β
An :after-middleware call can assume the project map has already been adjusted by a preceding :before-plugins call. And a call for a given keyword may happen more than once, so any actions you take must be repeatable. π
When you're making dependency adjustments: any changes to ":plugins" should be done during :before-plugins invocations, and your other dependency changes should normally happen during :after-middleware invocations. π«‘
π‘ Also note: all definition names starting with dh-clj are reserved by dh-clojure-lein. Don't touch, they're taken, periodt.
Now, in the project map that gets passed to dh-clj-adjust-project, the values of the dependency-related entries ":dependencies", ":managed-dependencies", and ":plugins" are not dependency vectors (like in project.clj) β they're in leiningen's dependency map format, which is way more suitable for manipulation. For example, this:
[[org.clojure/clojure "42.x"]]
is represented as:
{{:group-id "org.clojure", :artifact-id "clojure"}
{:artifact-id "clojure", :group-id "org.clojure", :version "42.x"}}
Though in most cases this won't even matter, because the following client functions (defined in "debian.dh-clojure-lein.client") handle the common project manipulations for you (peep the EXAMPLES section below π). Note that only the public functions in "client" are part of the stable API. Other functions in dh-clojure-lein should not be invoked via "dh-clojure-lein.clj".
Here's the client function lineup, besties:
(add-dep DEP-MAP DEP-SPEC)β Adds the dependency specified by DEP-SPEC (the dependency must not be present in the original project).(del-dep DEP-MAP DEP-KEY)β Removes the dependency specified by DEP-KEY (the dependency must be present in the original project).(set-dep DEP-MAP DEP-KEY FIELD VALUE)β Sets the specified FIELD (":version"or":exclusions") of the dependency specified by DEP-VEC to the given value (the dependency must be present in the original project). Setting":exclusions"to"nil"will remove all exclusions from the dependency.(get-dep DEP-MAP DEP-SPEC [default])β Behaves exactly like"clojure.core/get", returning the"{:group-id ...}"map in DEP-MAP, if any. π―(active-profiles PROJECT)β Returns a sequence of the active profiles like"(:base :system :user :provided :dev)". So to check whether the":test"profile is active:"(some #{:test} (active-profiles PROJECT))".
And a DEP-KEY? It's the subset of a lein project dependency vector that uniquely identifies a dependency β either a symbol like "org.clojure/tools.cli", or a a lein project dependency vector starting with that symbol, optionally followed by a ":classifier", and/or ":extension", for example "[org.clojure/tools.cli :classifier "test"]".
A DEP-SECs is a lein project dependency vector, except it may omit the version, for example "[org.clojure/tools.cli "1.x"]" or "[org.clojure/tools.cli :classifier "test"]".
π Note that unless otherwise specified, the default value for all dependencies will be set to either"debian", or β for"org.clojure/clojure"β to"1.x".
Some adjustments should only apply when specific profiles are active β for example, removing a dependency in a test profile. You can investigate which profile(s) are active via (active-profiles PROJECT) (listed above, bestie, scroll up π
).
And a lil tip from me to you: Emacs clojure-mode and paredit (available via elpa-clojure-mode and elpa-paredit in Debian) can be quite helpful if you're editing a lot of these files. They give you automatic indentation, highlighting, structural editing, etc. Obsessed. π
π₯ EXAMPLES π₯
π Project adjustments
Go check the Clojure API <https://clojure.github.io/clojure/clojure.core-api.html#clojure.core> for docs on specific calls like "assoc", "update", and "->".
Remove a plugin:
(require '[debian.dh-clojure-lein.client :as deb])
(defn dh-clj-adjust-project [get-project when & _]
(if-not (= when :before-plugins)
(get-project)
(update (get-project) :plugins deb/del-dep 'lein-pprint)))
Adjust dependency:
(require '[debian.dh-clojure-lein.client :as deb])
(defn dh-clj-adjust-project [get-project when & _]
(if (= when :after-middleware)
(update (get-project) :dependencies deb/set-dep 'y :version "5.x"))
(get-project))
Adjust more dependencies:
(require '[debian.dh-clojure-lein.client :as deb])
(defn adjust-deps [deps]
(-> deps
(deb/add-dep '[w "1.x"])
(deb/add-dep 'org.clojure/core.match)
(deb/del-dep 'murphy)
(deb/set-dep 'y :version "5.x")
(deb/set-dep 'y :exclusions nil) ;; delete exclusions
(deb/set-dep '[x/y :classifier "test"] :version "5.x")))
(defn dh-clj-adjust-project [get-project when & _]
(if (= when :after-middleware)
(update (get-project) :dependencies adjust-deps)
(get-project)))
Equivalently:
(require '[debian.dh-clojure-lein.client :as deb])
(defn dh-clj-adjust-project [get-project when & _]
(if-not (= when :after-middleware)
(get-project)
(-> (get-project)
(assoc :version "11.x")
(update :dependencies deb/add-dep '[w "1.x"])
(update :dependencies deb/add-dep 'org.clojure/core.match)
(update :dependencies deb/del-dep 'murphy)
(update :dependencies deb/set-dep 'y :version "5.x")
(update :dependencies deb/set-dep '[x/y :classifier "test"] :version "5.x"))))
Make profile-specific adjustments:
(require '[debian.dh-clojure-lein.client :as deb])
(defn adjust-deps [deps proj]
(let [test? (some #{:test} (deb/active-profiles proj))
dev? (some #{:dev} (deb/active-profiles proj))]
(cond-> deps
true (deb/add-dep '[w "1.x"])
dev? (deb/add-dep 'a/b)
test? (deb/del-dep 'murphy)
true (deb/set-dep 'x :version "5.x")
true (deb/set-dep '[x/y :classifier "test"] :version "5.x"))))
(defn dh-clj-adjust-project [get-project when & _]
(let [proj (get-project)]
(if (= when :after-middleware)
(update proj :dependencies adjust-deps proj)
proj)))
Equivalently:
(require '[debian.dh-clojure-lein.client
:refer [active-profiles add-dep set-dep]])
(defn fix-shared [deps]
(-> %
(add-dep '[w "1.x"])
(set-dep 'x :version "5.x")
(set-dep '[x/y :classifier "test"] :version "5.x")))
(defn adjust-deps [deps proj]
(let [test? (some #{:test} (active-profiles proj))
dev? (some #{:dev} (active-profiles proj)])
(cond
test? (-> deps fix-shared (add-dep 'a/b))
dev? (-> deps fix-shared (del-dep 'murphy))
:else (fix-shared deps))))
(defn dh-clj-adjust-project [get-project when & _]
(let [proj (get-project)]
(if (= when :after-middleware)
(update proj :dependencies adjust-deps proj)
proj)))
Remove a dependency that might not exist:
(cond-> deps
...
(deb/get-dep deps 'x/y) (deb/del-dep 'x/y)
...)
Debugging time π β you can use Clojure's existing print statements, for example:
(prn :active (active-profiles))
And for more readable output that can be inserted directly into the threading forms (e.g. "->" and "-><gt"), you can do something like this:
(require 'clojure.pprint)
(defn spy> [x tag]
(binding [*out* *err*] (clojure.pprint/pprint [tag x]))
x)
(defn spy>> [tag x]
(binding [*out* *err*] (clojure.pprint/pprint [tag x]))
x)
...
(defn fix-shared [deps]
(-> %
(add-dep '[w "1.x"])
(spy> :after-w)
(set-dep 'x :version "5.x")
(spy> :after-version)
(set-dep '[x/y :classifier "test"] :version "5.x")))
πΈ ENVIRONMENT πΈ
okay bestie, here's where you learn how to whisper sweet nothings to the buildsystem through the environment π these variables are how you tell dh-clojure-lein what you want without touching a single line of config. some of them are internal β that means the buildsystem sets them for itself and you should probably not go poking at them unless you know exactly what you're doing (iykyk π).
DH_CLOJURE_INSTALL_TEST_JARβ so if your project gets built in a way that spits out a "test" jar artifact, set this bad boy to"true"anddh-clojurewill include that test jar in the package. otherwise it justβ¦ doesn't β¨DH_CLOJURE_LEIN_CREATE_MAVEN_REPO(internal) β during testing this one gets set in the environment, and when it's set, it tells the buildsystem to use its value as the command that createsdebian/dh-clojure/tmp/maven-repo. what's the point? it gives you a writablemaven-repoinstead of the normal symlink over to/usr/share/.... no cap, that's the whole vibe πDH_CLOJURE_LEIN_BUILD(internal) β during the build this is set to"true"inlein's environment. and when that's the case anddh-clojureis installed, Debian'sleinwill invokedebian.dh-clojure-lein/adjust-projectat various points during the build β which is what lets all thosedh-clj-adjust-projectcalls we talked about earlier actually happen π«Ά
the reason it has to work like this? Debian might not package some of the plugins, or might package versions that differ from the ones specified in project.clj, so the adjustments must be made before Leiningen goes looking for them. which means they genuinely cannot be handled by a dh-clojure-lein middleware (like, say, a plugin). it's giving "this is the only way" and honestly, it is π
DH_CLOJURE_USJ_NAMEβ by default the buildsystem uses the project name as theusj-namewhen it writes out the package.pomsfile. and what isusj-name, you ask? it's the filename for the jar that gets installed under/usr/share/javaπ
here's the tea: sometimes two Java/Clojure projects share the same name, which can lead to package conflicts π to work around that, you can set the DH_CLOJURE_USJ_NAME environment variable to override the usj-name. problem solved, slay π
LEIN_HOME(internal) β during the build,LEIN_HOMEis set to$(pwd)/debian/dh-clojure/tmp/lein-homein the environment. that's it, that's the whole thing β¨LEIN_OFFLINE(internal) β during the build,LEIN_OFFLINEis set to"true"in the environment, and that's to make sureleindoesn't try to grab any dependencies off the network π no sneaky internet access during a build, periodt.
π FILES π
debian/dh-clojure-lein.clj
π SEE ALSO π
and that's the whole page bestie, you ate that πβ¨ go forth and package some Clojure, the Debian way π
this was version 0.4.3, dated 2026-08-07, from dh-clojure-lein(7) π
π¦ where this page comes from
- ubuntu stonking: dh-clojure 0.4.3 Β· LGPL-2.1-or-later OR EPL-1.0+
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms