Monday, March 14, 2011

Clojure & Leiningen in a Java Project

These days I've been busy with other stuff to do (including but not limited to some logistics and fixing a couple of papers); however, these morning, I picked up my good old mixed java/clojure project to port it with leiningen.I'm pretty happy with IntelliJ build system, but I needed some more flexibility.

There are some interesting issues with the project:

  1. it has both Java and Clojure files
  2. it has both aot and regular Clojure files
  3. it depends from lots of different Jars
  4. it depends from a "private library" which has not been (and cannot be) placed under publicly available repositories right now

The idea is that: Java code does not explicitly depend on clojure anymore. It is safe to compile Java before clojure; this is a major upgrade from (this and this and made things easier). On the other hand I have some "utility" clojure files which have not to explicitly aot compiled, but are required by the aot compiled sources. And I have some wrapper clojure scripts which depend on everything and is also compiled but it does not give any trouble, apparently.

To give an idea, this is the project:

I also installed lein-javac, even though I'm not sure it is needed. However, I can compile the Java code and that is fine. That was the easy part. And using "official" jar files was also easy Moreover, since interactions from aot compiled and regular clojure code did not give me any issues that is the state:

  1. it has both Java and Clojure files
  2. it has both aot and regular Clojure files
  3. it depends from lots of different Jars
  4. it depends from a "private library" which has not been (and cannot be) placed under publicly available repositories right now

Unfortunately, it seems to me that lein does not detect dependencies among aot compiled sources. That is to say, I have to manually order the files in the correct order. I hope that I just missed some lein feature, because this is just crazy. My first idea was that I could compile all the namespaces that contained all aot compiled code (I structured the project because of that). Apparently, it works; however if the compilation does not take place in the correct order, it does not work. Please, someone tell me I'm wrong. Someone told me and today with a simpler regular expression everything worked. Basically I'm not excluding files which are not required to be aot compiled from aot compilation. I just compile everything and that's it.

The only "problem" was dependency from a proprietary jar file we are developing. I found out that simply placing it in the deps was not enough as the directory is often removed and recreated. I put up a kludge:

Now everything kind of works. I'm not sure it's an improvement over the IntelliJ variant. I can use Emacs+swank (which is good), but I have a tricky "manual" step if I want to add aot compiled files. Besides, since there is a lot of setup to run the project, I'm not even sure that swank is a huge advantage, as I could only use it to run snippets "outside" the core.

So essentially it boils down to whether I prefer Emacs or IntelliJ for this project. Being heavily framework based, exploratory programming is rather hard to do, which removes some appeal from slime, as LaClojure REPL is adequate. Moreover, I prefer IntelliJ on pure Java (and I have some) and it seems to better autocomplete Java stuff (e.g., in the :import sections). So perhaps the trick is IntelliJ+LaClojure+Leiningen.

Technorati Tags:,, ,

2 comments:

Phil said...

> it seems to me that lein does not detect dependencies among aot compiled sources. That is to say, I have to manually order the files in the correct order.

This sounds very strange. If you can reproduce it, could you open an issue? I've never heard of anyone having this problem; in fact most people have the opposite problem of having dependency namespaces be compiled even when they don't want them to be.

> Apparently, it works; however if the compilation does not take place in the correct order, it does not work.

It could be that there's a circular dependency; there's a bug in Clojure 1.2 where the error message you get when you try to compile a circular dependency is very misleading. If you can show a case where running clojure.core/compile from the repl works but lein compile doesn't, I'd be very interested in seeing that.

> it depends from a "private library" which has not been (and cannot be) placed under publicly available repositories right now

You should take a look at Archiva or Nexus for this; they are very easy to set up and make sharing private dependencies among a team much, much easier.

Unknown said...

> This sounds very strange. If you can reproduce it, could you open an issue?

If I could, I would. Basically I have all the clojure files in a clojure sub-directory. Then the ones which are not to be aot are in a *.clojure directory.

I blamed it on leiningen not dealing with deps, but further experiments (and your suggestions, and the code) point out that it does indeed support dependencies.

Consequently I inverted my reasoning: the *.clojure/*.clj files do not /need/ to be compiled; if the aot toys with them, there is nothing bad with it. So I just put a big regex which catches them all.

Now everything works.

> It could be that there's a circular dependency

No, I'm sure this is not the case. I avoid circular dependencies whenever is possible (which means almost always, as I tend break them as soon as they occur).

I have some implicit circular dependencies, but they go through runtime gimmicks and are not dependencies at compile time (that is to say, the framework I use dynamically loads and instantiates stuff given some parameters I feed in the engine, but that happens at runtime).

> You should take a look at Archiva or Nexus for this

I know. However, we are doing very exploratory stuff. We also work with students and it appears that maven or even git are beyond their grasp.