This article describes how to use Maven to build projects written in Clojure (or in Clojure, and other languages, such as Java).
Maven is a software project life cycle management tool. It implements dependencies resolution (with automatic download of missing dependencies from repositories), building & testing of code, deployment of software, etc. Maven's functionality is extensible with plugins, so it's possible to use it not only for Java code (primary goal of this tool), but also for code, written in other languages. You can read more about Maven in following book (freely available).
Maven differs from other tools, such as Ant — it describes what we want to do, in contrast with Ant, that describes how to do it. Maven uses declarative style to describe tasks that we want to execute, and all described tasks are performed by the corresponding plugins.
Description of software lifecycle and information about project is stored in
that should exist in root directory of the project (and in root directories of
sub-projects, if your project is separated into several modules). Project's information
includes name, identifier and version of the project, and often includes more information:
URL of project's site, information about source code repository (so you can use
scm:update goal to update code, for example), etc.
Project Object Model (POM) defines set of stages for project's lifecycle — they are
called "lifecycle phases". Each phase can include several tasks (goals), that define what
will be performed on given stage. There are several common stages: compilation (
test), creation of package (
package), and installation (
install). Each of these
phases has dependencies on other phases, that should be executed before its invocation
(compilation should be executed before testing, testing before packaging, etc.).
Usually developer uses phase's name to start a process. For example,
mvn package, or
install, etc. But developer can also execute concrete Maven's goal. To do this, he
should specify name of plugin, that implements concrete goal, and task name in given
plugin. For example,
mvn clojure:run will start Clojure and execute script, specified in
configuration. We need to mention, that list of goals, that are executed for concrete
lifecycle phase isn't constant — you can change this list by modifying plugin's
Clojure's support in Maven is provided by clojure-maven-plugin, that is available in
Maven's central repository, so it always available1. As a base for your projects you
pom.xml file from clojure-maven-example project.
If you already have
pom.xml in your project, then to enable this plugin, you will need to
add following code into
<plugins> section of
<plugin> <groupId>com.theoryinpractise</groupId> <artifactId>clojure-maven-plugin</artifactId> <version>1.3.10</version> </plugin>
Attention: version number could be changed as development continues. To find latest plugin's version number you can use sites mvnrepository or Jarvana, that contains information about packages, registered in Maven's repositories. Besides this, you can omit plugin version — in this case, Maven will automatically use latest available version (although this isn't always good idea).
Declaration of this plugin will give you all implemented functionality — compilation,
testing & running of code, written in Clojure, etc. Although, out of box you'll need to
use complete goals names, such as
But you can make your life easier if you'll add these goals into list of goals for
concrete lifecycle phases (
test). To do this you need to add section
<executions> into plugin's description, as in following example:
<plugin> <groupId>com.theoryinpractise</groupId> <artifactId>clojure-maven-plugin</artifactId> <version>1.3.10</version> <executions> <execution> <id>compile</id> <phase>compile</phase> <goals> <goal>compile</goal> </goals> </execution> <execution> <id>test</id> <phase>test</phase> <goals> <goal>test</goal> </goals> </execution> </executions> </plugin>
In this case, source code, written in Clojure will be compiled — this useful if you
gen-class, that will be used from Java, or if you don't want to provide source
code for your application. But sometimes it's much better just to pack source code into
jar, and it will compiled during loading of package (this is default behaviour when you're
clojure packaging type) — this allows to avoid binary incompatibility between
different versions of Clojure. To put source code into jar, you need to add following
resources section (or change packaging type to
<resource> <directory>src/main/clojure</directory> </resource>
By default, Clojure's source code is placed in the
src/main/clojure directory of the
project's tree, while source code for tests is placed in the
These default values could be changed in plugin's configuration.
clojure-maven-plugin implements several commands (goals) that could be divided into
scriptsconfiguration directives. This goals is often used to run project with correct dependencies;
replScript— for example, you can put some initialization code into it. If the JLine library was specified in dependencies, then it will be loaded automatically, making your work in REPL more comfortable;
There are several Clojure-related repositories. All Clojure versions (stable & development) are published at Sonatype repository that is periodically synchronized with Maven Central. Clojars is repository that is used by Clojure community to publish their projects.
To use repository you need to add following code into
repositories section in
<repository> <id>clojars</id> <url>http://clojars.org/repo/</url> </repository>
Maven automatically downloads the all necessary dependencies from default repository, and
repositories, specified by user (as shown above). Downloaded packages are stored in
user's home directory and could be used by other projects without additional downloading.
Each package is uniquely identified by combination of three parameters — group's name
groupId tag), artifact's name (the
artifactId tag), and version (the
To use Clojure in your project you need at least specify dependency on language itself.
Right now, the stable version of Clojure is 1.4.0. To declare this dependency, add
following code into
dependencies section of
<dependency> <groupId>org.clojure</groupId> <artifactId>clojure</artifactId> <version>1.4.0</version> </dependency>
If you want to use the latest version of the language, then you need to add corresponding
repository (snapshots) and use version number like
1.5.0-master-SNAPSHOTS instead of version
To perform some tasks, implemented by
clojure-maven-plugin, you need to specify additional
clojure:swankgoal, then you need to specify dependency on
<dependency> <groupId>swank-clojure</groupId> <artifactId>swank-clojure</artifactId> <version>1.4.2</version> </dependency>
clojure:nailguntask, then you need to download distribution from vimclojure's site, build it, as described in documentation, and install into local Maven repository. And after this, you need to add following dependency on
vimclojurewith following code:
<dependency> <groupId>de.kotka</groupId> <artifactId>vimclojure</artifactId> <version>X.Y.Z</version> </dependency>
mvn clojure:replgoal is executed. You can add dependency for this library with following code:
<dependency> <groupId>jline</groupId> <artifactId>jline</artifactId> <version>0.9.94</version> </dependency>
Developer can change plugin's configuration options, such as location of source code,
scripts names, etc. To change some parameter, you need to add its new value into
configuration section of the plugin's description. For example, you can specify name of
the script, that will be executed during testing, using following code:
<plugin> <groupId>com.theoryinpractise</groupId> <artifactId>clojure-maven-plugin</artifactId> <version>1.3.10</version> <configuration> <testScript>src/test/clojure/test.clj</testScript> </configuration> ..... </plugin>
Following options are used to specify options related to source code & compilation:
sourceDirectorytag) that contains source code written in Clojure, and that will be packed into resulting jar (and compiled, if corresponding option is specified);
testSourceDirectorytag) with tests, written in Clojure;
true) or disables (
false) warnings about reflection during compilation of source code.
Besides this, you can control which namespaces will be compiled and/or for which
namespaces testing of source code will be performed. To do this, you need to add
namespaces tag into configuration and list corresponding namespaces inside it (each of
item should be wrapped into
namespace tag). You can use regular expressions to specify
all necessary namespaces, and you can also use
! to exclude namespaces from this list. In
addition to this option, you can use other two:
testDeclaredNamespaceOnly (with values
false) — they control, will be these
namespace limitations applied during compilation and/or testing.
There are also several options that are used to specify parameters for execution of your code and/or tests:
scripttag) or several (
scriptstag with nested
scripttags) names of scripts with code, that will executed when you'll execute the
clojure:testtask. If there was no value specified in plugin's configuration, then plugin will automatically generate run script for all tests, that was found in project;
clojure:repltask (it's also used by
clojure:nailguntasks). This code will executed before entering into REPL, so you can use it to specify initialization code for your working environment;
true) or disable (
false) executions of tests if you run REPL or your code via Maven. You can also change this value by using Maven's command-line option. For example, using following command
javaprocess on every invocation.
I think, that this article provides enough information for you to start use Maven together with Clojure. If you have Clojure-only project, and you don't plan to use all power of Maven, then may be you can look to the Leiningen — this tool was created to build projects, written mostly in Clojure. Another interesting project is Polyglot Maven, the main goal of it is creation of special DSL (Domain Specificl Language) using different languages (Clojure, Scala, Groovy) for description of Maven's configurations (for Clojure this language is almost the same as language implemented in Leiningen).
Other examples of using Maven with Clojure you can find in different projects: Incanter (as example of project, consisting from several modules), labrepl and clojure-maven-example. More information on Clojure and Maven you can also find in following blog posts:
clojure-maven-plugin, there is also Zi plugin, that was developed as part of
Pallet project. In contrast to
clojure-maven-plugin it's written in Clojure, and more
tightly integrated with Clojure-specific subsystems, such Marginalia, Ritz, etc..
Last change: 05.03.2013 16:54