Build tools like lein and maven make it very easy to use libraries in
clojure. Just go to clojars or
central and find the one you want. However,
there are several situations where dependency resolution does not go
as planned. A lot of these are a result of using version ranges for a dependency.
I’m going to pick on Christophe Grand and
use his regex library for some
examples of what can happen. It contains a version range for
clojure of [1.2.0,). This means it wants version 1.2.0 or later.
Version ranges check a pom for every version
Given this project.clj:
Running lein deps produces:
This produces a large number of requests for poms. Unfortunately
Aether, the library underneath lein2/maven3, cannot merge the two
dependencies on clojure without downloading everything. This is a
rather slight annoyance, but it makes clean builds, such as on
travis-ci, very noisy.
Version ranges cause surprises
lein deps :tree shows:
Here is a project with a direct dependency on clojure 1.1.0. This is
outside of the range desired by net.cgrand/regex. Aether lets version
ranges take priority over normal “soft” dependencies, and the project
uses 1.5.0-alpha2 instead. Normally a direct dependency will take
priority over any transitive dependencies. This situation causes a
surprise for the user, who would not be expecting a transitive version
range. The user has to go explicitly add an :exclusion to get the
desired version of clojure.
Version ranges are hard to track down
lein deps :tree shows:
Here the project has another dependency – serializable-fn. It wants
clojure 1.3.0. The dependency tree shows that clojure 1.3.0 got
chosen because of serializable-fn. However, this only happens because
net.cgrand/regex has a version range. This association of why a
dependency was downloaded does not help the user determine where an
:exclusion should go. Using the data they would place an :exclusion
on serializable-fn, but that would just lead to getting 1.5.0-alpha2
as above.
Version ranges cause unrepeatable builds
Open ended version ranges also create unrepeatable builds. Since
net.cgrand/regex uses the latest above 1.2.0, building a version now,
versus building a version a week from now may result in different
clojure versions running the test suite.
Do not use version ranges
Do not use version ranges. There have been several instances of people
asking for help on #clojure or #leiningen and a version range being
the underlying issue. They cause problems for other developers
and users of your libraries.