Explore how to effectively manage dependencies in Clojure projects using Leiningen, including project.clj configuration, adding dependencies, resolving conflicts, and utilizing profiles.
Leiningen is the de facto build automation tool for Clojure, providing a robust framework for managing dependencies, building projects, and automating tasks. In this section, we will delve into the intricacies of managing dependencies using Leiningen, focusing on the project.clj
file, adding and resolving dependencies, configuring repositories, and utilizing profiles for environment-specific configurations.
project.clj
File§The project.clj
file is the heart of a Leiningen project, containing metadata about the project, its dependencies, and various configurations. Let’s break down the key components of this file:
(defproject my-clojure-app "0.1.0-SNAPSHOT"
:description "A sample Clojure application"
:url "http://example.com/my-clojure-app"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.10.3"]
[ring/ring-core "1.9.0"]]
:main ^:skip-aot my-clojure-app.core
:target-path "target/%s"
:profiles {:uberjar {:aot :all}})
(defproject my-clojure-app "0.1.0-SNAPSHOT")
, where my-clojure-app
is the project name and 0.1.0-SNAPSHOT
is the version.[group/artifact "version"]
.:main
.Dependencies in Clojure are managed through Maven-style coordinates, consisting of a group ID, artifact ID, and version. To add a dependency, simply include it in the :dependencies
vector in project.clj
.
:dependencies [[org.clojure/clojure "1.10.3"]
[compojure "1.6.2"]
[cheshire "5.10.0"]]
Leiningen supports several version constraints:
"1.6.2"
specifies an exact version."[1.6,1.7)"
specifies a range, including 1.6
but excluding 1.7
."[1.6,)"
specifies any version greater than or equal to 1.6
.By default, Leiningen uses Maven Central and Clojars for resolving dependencies. However, you can configure additional repositories, including private ones.
:repositories [["central" {:url "https://repo1.maven.org/maven2/"}]
["clojars" {:url "https://repo.clojars.org/"}]
["my-private-repo" {:url "https://my.private.repo/repo"
:username :env/my_repo_user
:password :env/my_repo_pass}]]
Dependency conflicts occur when different libraries require different versions of the same dependency. Leiningen provides tools to resolve these conflicts.
Use :exclusions
to prevent specific transitive dependencies from being included.
:dependencies [[some-library "1.0.0" :exclusions [org.clojure/clojure]]]
Use :managed-dependencies
to enforce specific versions across the project.
:managed-dependencies [[org.clojure/clojure "1.10.3"]]
Profiles in Leiningen allow you to define environment-specific settings, such as different dependencies or JVM options for development, testing, and production.
:profiles {:dev {:dependencies [[ring/ring-mock "0.4.0"]]
:jvm-opts ["-Dconf=dev-config.edn"]}
:test {:dependencies [[midje "1.9.10"]]}
:prod {:jvm-opts ["-server" "-Xmx2g"]}}
Suppose you want to add the clj-http
library for HTTP client capabilities. Update your project.clj
:
:dependencies [[org.clojure/clojure "1.10.3"]
[clj-http "3.12.3"]]
If library-a
requires commons-io
version 2.6
and library-b
requires version 2.4
, you can resolve this by excluding commons-io
from one of the libraries and specifying the desired version in :dependencies
.
:dependencies [[library-a "1.0.0" :exclusions [commons-io]]
[library-b "2.0.0"]
[commons-io "2.6"]]
project.clj
.project.clj
; use environment variables instead.Managing dependencies with Leiningen is a critical skill for Clojure developers, especially in enterprise environments where projects can have complex dependency graphs. By understanding project.clj
, effectively adding and resolving dependencies, configuring repositories, and utilizing profiles, you can ensure your projects are well-organized and maintainable.