Patch: standalone compiler (almost)

13 views
Skip to first unread message

Stuart Sierra

unread,
Nov 16, 2008, 9:47:44 PM11/16/08
to clo...@googlegroups.com
Hi Rich, and all,

I took a stab at writing a static compiler class, i.e. a main() that
just compiles all the .clj files on the command line and saves the
.class files. Patch attached.

It almost works. The only thing that seems to be missing is creating
the class for the namespace itself, like "clojure/core.class". Not
sure what needs to be added to make that work.

-Stuart Sierra

compiler.patch

Rich Hickey

unread,
Nov 16, 2008, 10:34:51 PM11/16/08
to Clojure


On Nov 16, 9:47 pm, "Stuart Sierra" <the.stuart.sie...@gmail.com>
wrote:
Interesting.

I think you've hooked in at the wrong point - better to grab the
clojure.core/compile var and call that. You should also keep the unit
of compilation the lib, not files.

Since it only requires main, might I suggest you write this in Clojure
instead?

Rich

mac

unread,
Nov 17, 2008, 1:43:05 AM11/17/08
to Clojure
This would be a much welcome addition because as someone who is
writing small desktop apps with clojure it would be great if the
startup time could be reduced and I'm guessing it would help a bunch
to not have to wait for all the code to compile from text every time?

/Markus

Stephen C. Gilardi

unread,
Nov 17, 2008, 8:50:05 AM11/17/08
to clo...@googlegroups.com

On Nov 16, 2008, at 10:34 PM, Rich Hickey wrote:

> Since it only requires main, might I suggest you write this in
> Clojure instead?

I gave that a try.

Here's a simple version of a driver for the compiler, stored in src/
clj/clojure/compile.clj:

(ns clojure.compile)

(defn main
"Compiles libs into class files stored at compile-path.
All args are strings"
[compile-path & libs]
(printf "Compiling %d libs to %s\n" (count libs) compile-path)
(flush)
(binding [*compile-path* compile-path]
(doseq [lib libs]
(compile (symbol lib)))))

It works when run from within the Clojure repl:

% ls build/classes/clojure/hello*
ls: build/classes/clojure/*hello*: No such file or directory
% java -cp clojure.jar:src/clj/ clojure.lang.Repl
Clojure
user=> (require 'clojure.compile)
nil
user=> (clojure.compile/main "build/classes" "clojure.hello")
Compiling 1 libs to build/classes
nil
user=>
% ls build/classes/clojure/hello*
build/classes/clojure/hello$main__8.class build/classes/clojure/
hello.class

but when run as a standalone main, it gives an exception that appears
to be related to static initializers:

% java -cp clojure.jar:src/clj/ clojure.compile build/classes
clojure.hello
Compiling 1 libs to build/classes
Exception in thread "main" java.lang.IllegalStateException: Var null/
null is unbound. (hello.clj:3)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:4067)
at clojure.lang.Compiler.analyze(Compiler.java:3896)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:4050)
at clojure.lang.Compiler.analyze(Compiler.java:3896)
at clojure.lang.Compiler.access$100(Compiler.java:38)
at clojure.lang.Compiler$DefExpr$Parser.parse(Compiler.java:365)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:4060)
at clojure.lang.Compiler.analyze(Compiler.java:3896)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:4050)
at clojure.lang.Compiler.analyze(Compiler.java:3896)
at clojure.lang.Compiler.analyze(Compiler.java:3869)
at clojure.lang.Compiler.compile(Compiler.java:4498)
at clojure.lang.RT.compile(RT.java:408)
at clojure.lang.RT.load(RT.java:450)
at clojure.lang.RT.load(RT.java:422)
at clojure.core$load__4423$fn__4425.invoke(core.clj:3343)
at clojure.core$load__4423.doInvoke(core.clj:3342)
at clojure.lang.RestFn.invoke(RestFn.java:413)
at clojure.core$load_one__4386.invoke(core.clj:3189)
at clojure.core$compile__4429.invoke(core.clj:3347)
at clojure.compile$main__5162.doInvoke(compile.clj:23)
at clojure.lang.RestFn.invoke(RestFn.java:428)
at clojure.lang.Var.invoke(Var.java:323)
at clojure.lang.AFn.applyToHelper(AFn.java:195)
at clojure.lang.Var.applyTo(Var.java:436)
at clojure.compile.main(Unknown Source)
Caused by: java.lang.IllegalStateException: Var null/null is unbound.
at clojure.lang.Var.get(Var.java:129)
at clojure.lang.Compiler$FnExpr.parse(Compiler.java:2947)
at clojure.lang.Compiler.analyzeSeq(Compiler.java:4058)
... 25 more
%

For reference, here is the test compilee: src/clj/clojure/hello.clj:

(ns clojure.hello)

(defn main
[greetee]
(printf "Hello, %s wow\n" greetee)
(flush))

I'd appreciate help in getting a standalone invocation of something
like compile.clj above (i.e., using it's own main without Repl or
Script) to work.

In working through this, I also found that a compiler driver written
in Java may be preferable for use via build.xml because of a bootstrap
problem. Until we compile (something like) "compile.clj", we can't
call it as a standalone main. (One could add a step that builds the
compiler driver via a clojure.lang.Script invocation.)

--Steve

Rich Hickey

unread,
Nov 17, 2008, 9:51:58 AM11/17/08
to Clojure
> For reference, here is the test compilee: src/clj/clojure/hello.clj:
>
> (ns clojure.hello)
>
> (defn main
> [greetee]
> (printf "Hello, %s wow\n" greetee)
> (flush))
>
> I'd appreciate help in getting a standalone invocation of something
> like compile.clj above (i.e., using it's own main without Repl or
> Script) to work.
>

I think you've got some of the interim work I've done integrating gen-
class. main now needs to be called -main, as all methods will be
defined with leading -.

> In working through this, I also found that a compiler driver written
> in Java may be preferable for use via build.xml because of a bootstrap
> problem. Until we compile (something like) "compile.clj", we can't
> call it as a standalone main. (One could add a step that builds the
> compiler driver via a clojure.lang.Script invocation.)
>

Good point. This may just be an exercise then.

Rich

Stuart Sierra

unread,
Nov 17, 2008, 10:09:59 AM11/17/08
to Clojure
On Nov 17, 8:50 am, "Stephen C. Gilardi" <squee...@mac.com> wrote:
> In working through this, I also found that a compiler driver written  
> in Java may be preferable for use via build.xml because of a bootstrap  
> problem.

Yes, that's why I wanted to implement it in Java.
-S

Stephen C. Gilardi

unread,
Nov 17, 2008, 10:32:11 AM11/17/08
to clo...@googlegroups.com

> I think you've got some of the interim work I've done integrating gen-
> class. main now needs to be called -main, as all methods will be
> defined with leading -.

I'm at SVN 1106 which doesn't appear to have any changes relative to
1104 in this area. My compiled "hello.clj" works with its function
called simply "main".

--Steve

Stephen C. Gilardi

unread,
Nov 17, 2008, 2:37:29 PM11/17/08
to clo...@googlegroups.com

On Nov 17, 2008, at 9:51 AM, Rich Hickey wrote:
 main now needs to be called -main, as all methods will be defined with leading -.

Seeing that makes me think of Objective-C where instance methods have a leading - and static methods (class methods) having a leading +. I think a convention like that might make for an easier read than associating #^{:static true} with static methods, so I thought I'd mention it.

--Steve

Matt Revelle

unread,
Nov 17, 2008, 3:03:38 PM11/17/08
to clo...@googlegroups.com
I like it and Obj-C inspired syntax on the JVM is cosmic justice.

On Nov 17, 2008, at 2:37 PM, "Stephen C. Gilardi" <sque...@mac.com>
wrote:

Rich Hickey

unread,
Nov 17, 2008, 10:33:12 PM11/17/08
to Clojure


On Nov 17, 8:50 am, "Stephen C. Gilardi" <squee...@mac.com> wrote:
Fixed (SVN 1108) - thanks for the report.

Rich

Stephen C. Gilardi

unread,
Nov 17, 2008, 11:42:00 PM11/17/08
to clo...@googlegroups.com

On Nov 17, 2008, at 10:33 PM, Rich Hickey wrote:

Fixed (SVN 1108) - thanks for the report.

Cool, thanks. I especially like "loadClassForName".

It seems there's something not quite right, though. I did a fresh checkout of 1108 and built with "ant" and ran with "java -jar clojure.jar" and got an exception:

% java -jar clojure.jar
Exception in thread "main" java.lang.IllegalAccessError: tried to access method clojure.lang.Compiler.pushNS()V from class clojure.core
at clojure.core.<clinit>(Unknown Source)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at clojure.lang.RT.loadClassForName(RT.java:1620)
at clojure.lang.RT.load(RT.java:442)
at clojure.lang.RT.load(RT.java:424)
at clojure.lang.RT.doInit(RT.java:461)
at clojure.lang.RT.<clinit>(RT.java:328)
at clojure.lang.Repl.<clinit>(Repl.java:23)
Could not find the main class: clojure.lang.Repl. Program will exit.

--Steve

Stephen C. Gilardi

unread,
Nov 18, 2008, 12:53:28 AM11/18/08
to clo...@googlegroups.com

On Nov 17, 2008, at 11:42 PM, Stephen C. Gilardi wrote:

It seems there's something not quite right, though. I did a fresh checkout of 1108 and built with "ant" and ran with "java -jar clojure.jar" and got an exception:

Making pushNS public on line 4461 of src/jvm/clojure/lang/Compiler.java fixes the exception.

--Steve

Rich Hickey

unread,
Nov 18, 2008, 6:29:14 AM11/18/08
to clo...@googlegroups.com

Fixed - thanks.

Rich


Reply all
Reply to author
Forward
0 new messages