Building Clojure with Maven 3
Disclaimers and Expectations
There are several ways to build Clojure.
Some of those include, Leiningen, Ant, and Maven.
I've written other posts on building Clojure using Ant, and there's probably a post in me about Leiningen.
Maven just happens to be a strong contender in the build tools arena, since it's one of the main options where I work now.
There also happen to be several ways of getting your Clojure code into a Maven project... I'm AOT'ing my code (which will be put in a JAR file
that is put in a WAR file that is deployed to JBoss...).
I use Leiningen when I'm working on a personal project that I plan on sharing with others (Amotoen), and I have used Ant in the past. If I were to choose a build tool for a new project I'd base my decision off of a few key points:
- Is this project meant to fit in with some other selection of projects that are already using something? If so, try to stick with the standard
- Will this project be shared with the Clojure community? If so, use Leiningen---it's probably the Clojure community standard
- Do I have a personal preference? You personally might want to lean on your experience (Ant, Make, whatever), or you might want to expand (Leiningen, Maven 3)
Aside from freeing this discussion from needing to evangelize some selling point of some particular build tool, I'm also not going to spend time teaching Maven. I'm expecting that you'll be able to pick up Maven from the free book online by the maintainers. I'm also expecting that you'll have some Maven guru who can help answer questions, since they tend to be the best route to picking up Maven. With that out of the way, let's get started!
Setup
This will be based off a multi-module build, so I'm assuming that one of the modules you have in your project produces a JAR file.
I happen to call that folder 'JAR'.
There's nothing pertinent about any of the other folders or pom filesjust set them up as you normally would, just make sure you've
included the JAR folder in the parent's modules element.
For the pom file in the JAR folder, you'll want to add a few things. First, the plugin element that is added to the plugins element inside either your build element or a relevant profile element (make sure the values match your setup):
<plugin> <groupId>com.theoryinpractise</groupId><artifactId>clojure-maven-plugin</artifactId><version>1.3.5</version> <executions> <execution><phase>compile</phase><goals><goal>compile</goal></goals> <configuration> <sourceDirectories><sourceDirectory>src/main/clojure</sourceDirectory></sourceDirectories> <copiedNamespaces><namespace>!tld.company.project.*</namespace></copiedNamespaces> <copyDeclaredNamespaceOnly>true</copyDeclaredNamespaceOnly> <mainClass>tld.company.product.SomeMainClass</mainClass> </configuration> </execution> </executions> </plugin>
The next bit that you need to add brings in some Clojure repositories (place these two in the repositories element):
<repository> <id>clojure-releases</id> <url>http://build.clojure.org/releases</url> </repository> <repository> <id>clojars</id> <url>http://clojars.org/repo/</url> </repository>
Finally, you'll want to add a couple of dependencies to the Clojure and Contrib JARs (in the dependencies element):
<dependency> <groupId>org.clojure</groupId> <artifactId>clojure</artifactId> <version>1.2.0</version> </dependency> <dependency> <groupId>org.clojure</groupId> <artifactId>clojure-contrib</artifactId> <version>1.2.0</version> </dependency>
Don't forget, as I've explained above, that the JAR pom, where you're adding all of this, has a packaging type of JAR (<packaging>jar</packaging>).
Now, just create a folder structure as you defined in the above sourceDirectory element:
project-root | |-JAR | |-src | |-main | |-clojure | |-tld | |-company | |-project | |-SomeMainClass.clj
Finally, make sure that your SomeMainClass.clj Clojure file structure follows:
(ns tld.company.project.SomeMainClass (:gen-class :main false :init init :state state :extends tld.company.SomeSuperClass :exposes-methods { methodABCInSuper the_symbol_I_want_to_use_to_invoke_MethodABCInSuper methodXYZInSuper superMethodXYZInSuper} :methods [ [somethingElsePubliclyAccessible [] String] ;[somethingPubliclyAccessible [ParamType1 ParamType2] ReturnType] ;[anotherThingPubliclyAccessible [] "[Ltld.company.SomeClass_returned_as_an_element_in_an_Array;"]] ]) (:use (tld.company SomeClojureClassOnThePath)) (:import [java.sql Timestamp] ; Or whatever imports you need [javax.management ObjectName] ; None of these are 'required' [javax.security.jacc PolicyContext] [org.slf4j LoggerFactory Logger])) (def #^{:private true} log (LoggerFactory/getLogger "SomeMainClass")) (defn -init [] [[] (ref {})]) (defn- some-private-clojure-method [] true) ; Notice the dash ('-') before the method name... ; You need to take 'this' as the first param - others simply go after (defn -somethingElsePubliclyAccessible [this] "This should work...")
With that, you should be good to go.