Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Directly calling `clojure.lang.Compiler.compile` to AOT compile Clojure from Java

Tags:

clojure

Is it legitimate to compile Clojure from Java using clojure.lang.Compiler.compile?

Our Java application used to create instances of Clojure objects by using clojure.lang.Compiler.load on a .clj source which had a proxy macro. Now we want to AOT compile these objects using gen-class rather than proxy. This is so the .class files can be moved around and we instantiate the objects later on.

The best I’ve managed so far is to convince clojure.lang.Compiler.compile to produce me the loader classfile (“my/domain/lib__init.class”). But I didn’t seem to get the accompanying .class files, one per function in the namespace (“my/domain/lib$fnname__1234.class”) and a stub class file for each gen-class. This is in a sandbox using the very simple first example at http://clojure.org/compilation. I thought I’d correctly set compile-path, and set it to something in the classpath, but maybe there’s still an issue there.

In any case, our actual application is RCP (Eclipse) based, which probably means they'll be more to figure out in terms of OSGI classloader classpath. In the meantime I’m just wondering if using clojure.lang.Compiler.compile directly is even a valid approach?

Edit: So I've now got it producing all the .class files, but it needed various bits of undocumented initializing which suggests to me that methods on clojure.lang.Compiler are meant to be internal. I had to set *compile-path* appropriately and *compile-files* to true, e.g.

RT.var("clojure.core", "*compile-files*").bindRoot(true);
like image 540
Jon Stafford Avatar asked Oct 22 '22 08:10

Jon Stafford


2 Answers

You should try to use clojure.core/compile function. Also you can check out how lein handles the :aot param to do aot compilation.

like image 131
Ankur Avatar answered Oct 30 '22 19:10

Ankur


Here is an example of driving the Clojure compiler directly from Java.

        String clojureScript = "(ns user) (def hello (fn [] \"Hello world\"))";
        String notionalScriptFileName="hello.clj";
        String outputDirectory="/my/output/dir";

        Var compilePath = RT.var("clojure.core", "*compile-path*");            
        Var compileFiles = RT.var("clojure.core", "*compile-files*");

        Var.pushThreadBindings(RT.map(compilePath, outputDirectory, compileFiles, Boolean.TRUE));                        
        Compiler.compile(new StringReader(clojureScript), notionalScriptFileName, notionalScriptFileName);
        Var.popThreadBindings();

I've tested this with Clojure 1.4.0... your mileage may vary with other versions.

As the API stands you need to provide a "notional" file name. This only seems to be used to generate the .class file names. The class files it generates are written to the outputDirectory.

like image 20
Jon Iles Avatar answered Oct 30 '22 18:10

Jon Iles