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 pom.xml
file,
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 mvn
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 (compile
),
testing (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 mvn
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
configuration.
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
can use 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 pom.xml
:
<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 clojure:compile
, clojure:test
& clojure:run
.
But you can make your life easier if you'll add these goals into list of goals for
concrete lifecycle phases (compile
and 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
implement 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
declaring 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
code into resources
section (or change packaging type to clojure
):
<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 src/test/clojure
directory.
These default values could be changed in plugin's configuration.
clojure-maven-plugin
implements several commands (goals) that could be divided into
following groups:
clojure:compile
clojure:test
clojure:test-with-junit
clojure:add-source
...-sources.jar
;clojure:add-testsource
...-testsources.jar
.clojure:run
script
and/or scripts
configuration directives. This goals is often used to run project with correct
dependencies;clojure:repl
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;clojure:swank
clojure.swank.port
);clojure:nailgun
clojure.nailgun.port
).clojure:marginalia
clojure:autodoc
clojure:gendoc
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 pom.xml
:
<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
(the groupId
tag), artifact's name (the artifactId
tag), and version (the version
tag).
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 pom.xml
file:
<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
1.4.0
.
To perform some tasks, implemented by clojure-maven-plugin
, you need to specify additional
dependencies:
clojure:swank
goal, then you need to specify dependency on swank-clojure
package:<dependency> <groupId>swank-clojure</groupId> <artifactId>swank-clojure</artifactId> <version>1.4.2</version> </dependency>
clojure:nailgun
task, 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
vimclojure
with following code:<dependency> <groupId>de.kotka</groupId> <artifactId>vimclojure</artifactId> <version>X.Y.Z</version> </dependency>
mvn clojure:repl
goal 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:
sourceDirectories
sourceDirectory
tag) that contains source code written in Clojure, and that
will be packed into resulting jar (and compiled, if corresponding option is specified);
testSourceDirectories
testSourceDirectory
tag) with tests, written in Clojure;warnOnReflection
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: compileDeclaredNamespaceOnly
and
testDeclaredNamespaceOnly
(with values true
or 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:
script
and scripts
script
tag) or several (scripts
tag with nested script
tags) names of scripts with code, that will executed when you'll execute the
clojure:run
task;testScript
clojure:test
task. 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;replScript
clojure:repl
task
(it's also used by clojure:swank
and clojure:nailgun
tasks). This code will executed
before entering into REPL, so you can use it to specify initialization code for your
working environment;runWithTests
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 mvn clojure:repl
clojureOptions
java
process 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
);1. Besides 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