Меню:


This article describes the Leiningen tool (version 1.x) for building of projects, written in Clojure.5

What is Leiningen?

Leiningen is a tool for building of code, written in Clojure. Leiningen is much simpler comparing with Maven and allows to define project's configuration using Clojure1. Leiningen uses external tools and libraries to resolve dependencies and build a code, so it's pretty small. This tool is getting more popularity between Clojure developers — it's extensible by using additional modules (plugins), such as plugin for compilation of Java code, and many others.

Out of box Leiningen implements basic tasks — compilation of code, testing, creation of package, installation, etc. Besides this, it also provides basic support for work with Maven, so you can use packages, built by this tool in other projects.

Installation

Leiningen's installation procedures for Unix-like OSes and for MS Windows are slightly different. Installation on Unix is quite easy — you just need to download lein script, make it executable, copy to directory, listed in PATH, and execute lein self-install command. During execution of this command, Leiningen will download and install all packages, that are needed to its work.

To install Leiningen on MS Windows you need to download lein-win32.zip file from project's page. This file contains all necessary programs, so you can unzip it into any directory, add this directory into search path, perform lein self-install command, and start to work with Leiningen.

Structure of Leiningen's project

Leiningen uses fixed structure of project — in the root directory of the project you need to have the project.clj file, that contains project's definition. The only necessary component of the definition is defproject — Clojure's macro, that is expanded into set of build instructions. project.clj can also include other code, written in Clojure, that will executed during build process.

Project's source code should be stored in src directory, tests — in test directory, and additional resources, used by project — in resources directory. The lib directory is used to store libraries, used by project — they are copied there with the lein deps command. List of libraries is calculated using information about dependencies, declared in project. If you want to use library, that isn't stored in one of the Maven's repositories, then you can just copy this library into lib directory, and it will available to your project.

But names of directories aren't hard-coded — you can use defproject's options to change their values:

:source-path
name of directory with project's source code (by default — src);
:compile-path
name of directory for resulting Java classes (by default — classes/);
:resources-path
name of directory with project's resources (by default — resources/);
:test-path
name of directory with test's source code (by default — test/);
:library-path
name of directory, where libraries are stored (by default — lib/).

You can also add additional information to project's definition — description (the :description option) and link to project's home page (the :url option).

The fastest way to create a new project is to use lein new command, that accepts one required argument — name of the project. This command will create a new directory with name of the project, and will create inside it the project.clj file with declaration of the project (including dependencies on Clojure and clojure-contrib), the README file with template of project's description, and two directories — src and test for source code & tests. Now you can start to work with you project.

Project's example

Let look to simple project specified in project.clj with following code (full code of this project you can find at github):

(defproject test-project "1.0-SNAPSHOT"
  :description "A test project."
  :url "http://my-cool-project.com"
  :dependencies [[org.clojure/clojure "1.1.0"]
                 [org.clojure/clojure-contrib "1.1.0"]]
  :dev-dependencies [ [swank-clojure "1.2.0"] ]
)

We define a project test-project with dependencies on Clojure and clojure-contrib libraries, and also have additional dependency on library, that we'll use during development — swank-clojure.

In the src directory there is only one file — simple.clj, that declares namespace simple with following code inside:

(ns simple)

(defn hello
  ([] "Hello world!")
  ([name] (str "Hello " name "!")))

In the test directory we have file simple_test.clj, that contains test for simple. We're using standard library clojure.test to implement test. Test's source code looks following way:

(ns simple-test
  (:use clojure.test)
  (:use simple))

(deftest simple-test
  (is (= (hello) "Hello world!"))
  (is (= (hello "test") "Hello test!")))

This is complete project, and we could execute any Leiningen's command for it.

How to specify dependencies

One of important parts of defproject is declaration of dependencies on external libraries. For code, written in Clojure, main dependency is Clojure itself, as this shown in example above.

There are different types of dependencies and different project options for them:

:dependencies option
standard dependencies — for libraries, that are used in your code;
:dev-dependencies option
dependencies for libraries, that are used during development. For example, dependency on swank-clojure, Leiningen's plugins, etc.

Besides this, exists native-deps plugin, that implements support for dependencies on platform-dependent libraries (native libraries). Here is example of use of this plugin.

Each of these options is a vector, where each element is another vector, holding description of concrete library. This description consists from three elements (you already had seen these descriptions in project's example):

[log4j "1.2.15" :exclusions [javax.mail/mail
                             javax.jms/jms
                             com.sun.jdmk/jmxtools
                             com.sun.jmx/jmxri]]

Repositories

By default Leiningen uses three repository:

Besides this, user can specify additional repositories with :repositories option. This option take one parameter — map with names and URLs of repositories. For example, we can add repository of Apache project with following code:

(defproject test2 "1.0.0-SNAPSHOT"
  :dependencies [....]
  :repositories {"apache-releases" "http://repository.apache.org/content/repositories/releases/"}
  )

Work with Leiningen

Typical workflow when you use Leiningen looks following way:

This process is pretty simple and you repeat it until your code is complete :-)

Basic Leiningen's commands

List of Leiningen's commands isn't fixed — plugins could add new commands to it. In basic configuration, Leiningen implements following commands, that could be run as lein command [options]:

help [command]
shows help on Leiningen's usage. If you specify name of the command after help, then description of command will shown (except repl)
new project_name [options]
creates a new empty project;
deps
download (if necessary) and copies all dependencies into lib/ directory. This command should executed after each change in dependencies!;
compile
performs compilation of code (AOT, ahead-of-time) into Java classes. These classes are stored in classes/ directory. User can control which namespaces should be compiled — you can use :namespaces option to specify a list of namespaces to compile;
test [list_of_namespace]
runs tests from all (or only from specified) namespaces;
jar
creates a package with your code;
uberjar
create independent package with your code and all dependencies included into it (it's much easier to distribute such packages). If you will run this package with java -jar ... then entry point will namespace, specified in :main option, specified defproject;
pom
creates pom.xml file, that contains description of project. This file is needed if you plan to use your package in another project;
install
installs package into Maven's local repository;
clean
deletes all files, created during build (including libraries from lib/ directory);
repl
runs REPL with correctly specified classpath — it includes libraries from lib/ directory, and also directories src/ and classes/. During start lein2 automatically detects presence of jline library, and uses it, so you'll have command history, etc.

Additional commands

Additional commands are implemented by plugins, that are used to extend Leiningen's functionality. There are many popular plugins for Leiningen, and you can find short description of many of them in following blog posting. For example, this are plugins for running of Swank and Nailgun servers, that are implementing interactive work from Emacs or Vim. If you run these servers, then they are using all necessary dependencies, so you will work in the same environment, as your program.

To use Swank server you need to add dependency on swank-clojure3 in :dev-dependencies option4, and after this you could use lein swank command. After execution of this command you'll get Swank server, running on port 4005 and you can connect to it with Emacs' slime-connect command. And if you prefer to use Vim editor, then you need to add dependency on lein-nailgun plugin (more about work with Nailgun you can read on vimclojure's site).

Extending Leiningen's functionality

Leiningen is extensible — if necessary, you can add your own commands. To do this you need to create a project, in which exists leiningen.command_name namespace, containing implementation of your command as a function with name command_name. This function receives project's object as an argument. More detailed information on writing plugins you can find in following blog post. One of example of plugins for Leiningen is the lein-swank plugin, that implements support for Swank server — you can find it in Leiningen's repository with source code.

There are several plugins for Leiningen in Clojars repository. These plugins implement different functionality — automatic uploading of code into Clojars repository, building of Java source code, etc. Names of these plugins usually starting with lein-, that you can use in search. To use these plugins in your project, you need to add them into development dependencies (the :dev-dependencies option).

Conclusion

I hope, that this article helps you in your work with Leiningen. If you have questions you can write me e-mail or leave a comment on site — I'll try to answer on all of them.


1. I should also mention the Polyglot Maven project, that should allow to describe Maven's configurations using different languages — Clojure, Scala, Groovy, etc.

2. In contrast to other commands, repl is implemented directly in lein, not written in Clojure.

3. example of swank-clojure dependency you can see in project's example.

4. and don't forget to run lein deps after adding this dependency!

5. there is also small document in Leiningen's distribution, that describes basic operations.

Last change: 05.03.2013 16:54

blog comments powered by Disqus