So you have your cool project, which you now want to move to the
clojuresque plugin for Gradle. And suddenly your build is broken,
because it cannot find a required class, you generate with gen-class
.
Building with ant works. So what is going on?
You have some project with namespace a
. It uses some generated class B
.
; In B.clj...
(ns B
(:gen-class))
; In a.clj...
(ns a
(:import B))
Everything is stitched together with an ant task like this:
<target name="aot">
<java classname="clojure.lang.Compile" failonerror="true">
<classpath>
<path location="${classes.dir}"/>
<path location="${src.dir}"/>
<fileset dir="${lib.dir}" includes="**/*.jar"/>
</classpath>
<sysproperty key="clojure.compile.path" value="${classes.dir}"/>
<arg value="B"/>
<arg value="a"/>
</java>
</target>
And all was well.
Then you decided for various other reasons to switch to clojuresque. And the build suddenly blows up. „Huh? It can't find class B anymore?“ Yes. And the reason is the incomplete dependency tree.
I'm not sure what happened in the last 10 years in the Java community, but they
obviously didn't improve over make
. Leting the compiler figure out the
dependencies is obviously broken, because it knows only its own language. As
soon as you mix languages you are in trouble or you need some super-compiler
knowing (the backends for) different languages. The above example is a clear
failure even considering only one language: the compiler does not recognise
the :import
as a dependency[1].
There are tools – like Ant and Gradle – which all claim to be an
improvement over make
and they all fail miserably. The only build tool
superior to make
I saw so far is Cook. It is the only one, which goes out
of its way to get the dependencies right. It allows easy hooking into the build
logic to extract and specify dependencies. The gap are the languages, which do
not allow easy extraction of dependencies (as well as the mapping of
dependencies to compiler input).
To „fix“ our code, we have to complete the dependency graph and tell the
compiler, that we actually need B
for a
. The easiest way to do this is to
add B
also as a :require
d lib.
(ns a
(:require B)
(:import B))
Well, but maybe I'm just a dinosaur still living in the C world.
Published by Meikel Brandmeyer on .
I'm a long-time Clojure user and the developer of several open source projects mostly involving Clojure. I try to actively contribute to the Clojure community.
My most active projects are at the moment VimClojure, Clojuresque and ClojureCheck.
Copyright © 2009-2014 All Right Reserved. Meikel Brandmeyer