Leiningen checkout dependencies are a useful
feature. Checkout dependencies allow you to work on a library and consuming
project at the same time. By setting up checkout dependencies you can
lein install in the library project; it appears on the
classpath of the consuming project. An example of what this looks like
can be found in the
or in a
By default, Leiningen adds the
:compile-path directories of the checkout
projects to your consuming project’s classpath. It also recurses and
adds the checkouts of your checkouts (and keeps recursing).
You can override what gets added to your classpath by
:checkout-deps-shares to your project.clj. This key’s value should
be a vector of functions that when applied to your checkouts' project
map return the paths that should be included on the classpath. The
default values can be found
and an example of overriding the default behavior can be found in the
I ran into a situation this week where having my checkouts'
:test-paths on the classpath caused issues my consuming project. My
first pass at fixing this problem was to add
[:source-paths :resource-paths :compile-path] to my project.clj. This
didn’t work. My project.clj looked like below.
1 2 3 4
Why didn’t it work? It didn’t work because of how Leiningen merges
duplicate keys in the project map. When Leiningen merges the various
configuration maps (from merging profiles, merging defaults, etc) and
it encounters values that are collections it combines them (more
details found in
lein pprint :checkout-deps-shares shows what we end up with.
We’ve ended up with the default values and the values we specified in
the project.clj. This isn’t hard to fix. To tell Leiningen to replace
the value instead of merging you add the
^:replace metadata to the
value. Below is the same project.clj but with
1 2 3 4
This solves the problem of
:test-paths showing up on the classpath
but it introduces another problem. Checkouts' checkout dependencies no
longer show up on the classpath. This is because
leiningen.core.classpath/checkout-deps-paths is no longer applied to
leiningen.core.classpath/checkout-deps-paths Leiningen stops
recursing and, as a result, no longer picks up checkouts' checkout
dependencies. My first attempt at fixing this was to modify my
project.clj so the
:checkout-deps-shares section looked like below.
The above fails. It runs but doesn’t actually add the correct directories to the classpath. The next attempt is below.
This attempt failed quicker. Now an exception is thrown when trying to run Leiningen tasks.
The next one works. It takes advantage of dynamic eval through read-eval syntax. With the below snippet the checkouts' checkouts are added to the classpath.
Hopefully this is useful to someone else. It took a bit of digging to figure it out and many incorrect attempts to get correct. The full example project.clj is below.
1 2 3 4 5