A Leiningen Tutorial

A Leiningen Tutorial

Colin Jones

November 26, 2010

Leiningen, a project by Phil Hagelberg , has become one of the most contributed to Clojure projects, and also one of the most used.

UPDATE (2013-12-12): I highly recommend going through the official community-maintained tutorial instead of this blog, which is essentially unmaintained.

The existing alternatives, such as Cake, Polyglot-Maven, and Maven each have advantages of their own but personally, I prefer Leiningen.

This introduction will focus on Leiningen itself, rather than the motivations for using it or about the way it differs from other tools. For some discussion of those differences, see the mailing list discussions on the Clojure project.clj spec, or this StackOverflow question.

For the purposes of this tutorial, just know that Leiningen is, as its tagline states, "A build tool for Clojure designed to not set your hair on fire".

This tutorial is intended for people who have never touched Leiningen—if you’ve used it on a project before, you’re better off jumping straight to the resources I mention at the end of this post, such as the excellent documentation in the project itself.


There are only a few prerequisites for getting going with Leiningen:


Once you’ve downloaded the lein script (or, on Windows, downloaded the zip file and unzipped it), you’ll need to add it to your PATH, and make sure it’s executable (just adding it to the PATH should be enough for Windows):

chmod 755 lein

I keep my lein script in ~/bin, as Phil suggests in the project README. Now, if you just run…

lein self-install

…you’ll see some download activity as Leiningen grabs its latest stable-release jarfile and puts it into the ~/.lein/self-installs directory, where your lein script will be able to find it later, as you get ready to run real tasks.

Trying it out

Let’s start out by creating a new project:

lein new my-sandbox

Now, if you cd my-sandbox, you’ll find that Leiningen has created a directory structure for you, which is typical for Clojure projects:

- project.clj
- src
	- my_sandbox
	- core.clj
- test
	- my_sandbox
	- test
	- core.clj

The only file here that we need to concern ourselves with is the project.clj, which describes the project in several ways:

(defproject my-sandbox "1.0.0-SNAPSHOT"
		:description "FIXME: write"
		:dependencies [[org.clojure/clojure "1.2.0"]
																	[org.clojure/clojure-contrib "1.2.0"]])

The first thing to notice is that this is just Clojure code. No yucky XML to deal with (I know, we shouldn’t dismiss XML out of hand), and things are very clean and minimal. Let’s break it down into parts:

  • defproject is the macro that does the work to collect information about the project so that the Leiningen tasks know what to do.

  • my-sandbox is the name of our project, expressed here as a Clojure symbol.

  • "1.0.0-SNAPSHOT" is the project version, which will become important later.

  • The remainder of the entries are expressed in key-value pairs. It’s essentially a map, even though defproject uses unrolled arguments, so we don’t need the usual curly braces.


Leiningen’s dependency management is really one of the coolest things about it, so let’s take a look at the way you identify dependencies.

[GROUP/]ARTIFACT-ID VERSION is the syntax you use when declaring a dependency. When the GROUP is the same as the ARTIFACT-ID, so the normal syntax would give you something like foobar/foobar "1.0.0", you can safely omit the GROUP and say 5foobar "1.0.0" instead.

The idea is that the GROUP is whatever organization created the code, and the ARTIFACT-ID is the name of the project or artifact itself—pretty intuitive. You’ll notice that our project name and version look suspiciously similar to those of our dependencies, and in fact we can also use the same GROUP/ARTIFACT-ID syntax as the project name to specify that this project belongs in a group different from its name.

Each of the projects in our dependency list will declare its dependencies as well, through the pom.xml file associated with that project. When we actually upload to a Maven repository (such as Clojars), we can use Leiningen to generate that POM file for us, rather than hand-coding it.

Now that we have a little background on how the project.clj is constructed, we can go ahead and pull down our dependencies with the following command (making sure we’re in the my-sandbox directory):

lein deps

And now you have .jar files for your dependencies in a newly created lib directory.

Easy, right? In the event that you do see errors, and in the interest of total disclosure, the dependencies are first downloaded into your ~/.m2/repository directory, then copied from there into your project’s lib directory.

So if you encounter problems, you may want to check to see that you have reasonable permissions and ownership set up for that directory.



lein classpath

…should make it clear that Leiningen sets up your classpath in a reasonable way for your project: the src, test, classes, test-resources, and resources directories are all there, as are the jarfiles for your dependencies.

This is the classpath that most any Leiningen task will use when running from your project directory, and will obviously vary on a per-project basis. The upshot here is that you can fire up a REPL that already has all of your dependencies on the classpath, without resorting to any deprecated add-classpath hackery:

lein repl

Don’t forget that you’ll still need to require or use the namespaces you want to use, as you would with any other Clojure libs besides the ones automatically provided in clojure.core and the other automatically-available namespaces.



lein test

…shows you that the project starts with one failing test, which expects false to be true—not one that I’m particularly keen to make pass, but a good example nonetheless.

It’s pretty trivial to run tests this way, but of course the JVM startup time makes it much more efficient to run tests in a different way:

% lein interactive
lein> test

Leiningen’s interactive mode is a pretty sweet way to avoid taking the JVM startup time hit for every test run, but it’s a bit of a double-edged sword: you might get falsely passing test runs, because of the fact that JVM classes will stick around in memory even after you delete their associated functions.

Just be sure to run tests occasionally outside of interactive mode, and you’ll be sure to catch errors like this quickly.

More Fun!

Leiningen is a mature and interesting enough project that there’s no way to cover it all in a single blog post. I hope that by this point I’ve whet your appetite a bit to learn more about Leiningen (and perhaps Clojure).

By now, you’re definitely ready to move on to the more in-depth README, TUTORIAL, and PLUGINS documents bundled with the Leiningen project on GitHub.

You’ll also get a lot out of joining (and searching) the Leiningen mailing list and asking questions in the #leiningen and/or #clojure IRC channels on FreeNode.

Seriously, if you haven’t visited the IRC channels, do that as soon as possible! Also, running…

lein help

…gives you an idea of all the things that are possible with the base distribution of Leiningen, and a growing plugin ecosystem has been springing up to support various users’ build, distribution, testing, and other needs.

Further, there are many opportunities to jump into the Leiningen code itself and make improvements. Phil frequently tags issues as suitable for newbies to jump in, which is great for people looking to get into doing open-source software in Clojure.