dh-clojure-lein(7)

πŸ“¦
version 0.4.3 Β· 3 versions
, in ubuntu stonking

πŸ’– 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) the dh-clojure major version is no longer 0 and 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-repo symlink pointing at /usr/share/maven-repo, so lein can use it as a :local-repo. πŸͺ„
  • build β€” Creates a debian/dh-clojure/tmp/pom.xml for use in the debian/PACKAGENAME.poms file (which she also creates, multitasking queen πŸ‘‘), and she creates an unversioned symlink in target/ for the jar.
  • install β€” Calls helpers from the maven-repo-helper package 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 clean and 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-plugins or :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" and dh-clojure will 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 creates debian/dh-clojure/tmp/maven-repo. what's the point? it gives you a writable maven-repo instead 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" in lein's environment. and when that's the case and dh-clojure is installed, Debian's lein will invoke debian.dh-clojure-lein/adjust-project at various points during the build β€” which is what lets all those dh-clj-adjust-project calls 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 the usj-name when it writes out the package .poms file. and what is usj-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_HOME is set to $(pwd)/debian/dh-clojure/tmp/lein-home in the environment. that's it, that's the whole thing ✨
  • LEIN_OFFLINE (internal) β€” during the build, LEIN_OFFLINE is set to "true" in the environment, and that's to make sure lein doesn'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