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.
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
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:
leinscript for Unix/Mac OSX , or the zip file for Windows
- A Java runtime environment (at least version 1.5)
- An internet connection, which you’ll need in order to get Leiningen bootstrapped.
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):
1 chmod 755 lein
I keep my lein script in
~/bin, as Phil suggests in the project
README. Now, if you just run…
1 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:
1 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:
1 - project.clj 2 - README 3 - src 4 - my_sandbox 5 - core.clj 6 - test 7 - my_sandbox 8 - test 9 - core.clj
The only file here that we need to concern ourselves with is the project.clj, which describes the project in several ways:
1 (defproject my-sandbox "1.0.0-SNAPSHOT" 2 :description "FIXME: write" 3 :dependencies [[org.clojure/clojure "1.2.0"] 4 [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:
defprojectis the macro that does the work to collect information about the project so that the Leiningen tasks know what to do.
my-sandboxis 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,
defprojectuses 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
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 5
foobar "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
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
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
1 lein deps
And now you have
.jar files for your dependencies in a newly created
Easy, right? In the event that you do see errors, and in the interest of total disclosure,
the dependencies are first downloaded into your
then copied from there into your project’s
So if you encounter problems, you may want to check to see that you have reasonable permissions and ownership set up for that directory.
1 lein classpath
…should make it clear that Leiningen sets up your classpath in a reasonable way for
your project: the
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
1 lein repl
Don’t forget that you’ll still need to
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
1 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:
1 % lein interactive 2 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.
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).
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…
1 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.