Gen-Class does not generate a .class file

587 Views Asked by At

I'd like to use Clojure code within Java. The Clojure code itself should implement a Java-interface (TestGenClassInterface).

My project.clj is:

(defproject com.stackoverflow.clojure/tests "0.1.0-SNAPSHOT"
  :description "Tests of Clojure test-framework."
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.6.0"]
                 [instaparse "1.3.4"]]
  :source-paths      ["src/main/clojure"]
  :java-source-paths ["src/main/java"]
  :test-paths        ["src/test/clojure"]
  :java-test-paths   ["src/test/java"]
  ;:aot :all
  )

The Java interface looks like this:

package com.stackoverflow.clojure;

public interface TestGenClassInterface {

    public String addToString(String text, String appendText);

}

The Clojure code is:

(ns com.stackoverflow.clojure.testGenClass
  (:gen-class
     :name com.stackoverflow.clojure.TestGenClass
     :implements com.stackoverflow.clojure.TestGenClassInterface
     :prefix "java-"))

(def ^:private pre "START: ")

(defn java-addToString [this text post]
  (str pre text post))

(java-addToString "TexT" " :END")

I expected, that after running lein compile or "Run as Clojure-Application" in eclipse+CounterClockwise a .class file (named TestGenClass.class) is generated an saved within *compile-path* (here: target/classes/com/stackoverflow/clojure/). Unfortunately it's not.

When adding :aot :all to my project.clj, I get the following stacktrace:

Compiling com.stackoverflow.clojure.testGenClass
Exception in thread "main" java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol, compiling:(com/stackoverflow/clojure/testGenClass.clj:1:1)
    at clojure.lang.Compiler.analyzeSeq(Compiler.java:6651)
    at clojure.lang.Compiler.analyze(Compiler.java:6445)
    at clojure.lang.Compiler.analyze(Compiler.java:6406)
    at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5782)
    at clojure.lang.Compiler$TryExpr$Parser.parse(Compiler.java:2191)
    at clojure.lang.Compiler.analyzeSeq(Compiler.java:6644)
    at clojure.lang.Compiler.analyze(Compiler.java:6445)
    at clojure.lang.Compiler.analyze(Compiler.java:6406)
    at clojure.lang.Compiler$BodyExpr$Parser.parse(Compiler.java:5782)
    at clojure.lang.Compiler$FnMethod.parse(Compiler.java:5217)
    at clojure.lang.Compiler$FnExpr.parse(Compiler.java:3846)
    at clojure.lang.Compiler.analyzeSeq(Compiler.java:6642)
    at clojure.lang.Compiler.analyze(Compiler.java:6445)
    at clojure.lang.Compiler.analyzeSeq(Compiler.java:6632)
    at clojure.lang.Compiler.analyze(Compiler.java:6445)
    at clojure.lang.Compiler.analyze(Compiler.java:6406)
    at clojure.lang.Compiler$InvokeExpr.parse(Compiler.java:3665)
    at clojure.lang.Compiler.analyzeSeq(Compiler.java:6646)
    at clojure.lang.Compiler.analyze(Compiler.java:6445)
    at clojure.lang.Compiler.analyze(Compiler.java:6406)
    at clojure.lang.Compiler.compile1(Compiler.java:7221)
    at clojure.lang.Compiler.compile1(Compiler.java:7216)
    at clojure.lang.Compiler.compile(Compiler.java:7292)
    at clojure.lang.RT.compile(RT.java:398)
    at clojure.lang.RT.load(RT.java:438)
    at clojure.lang.RT.load(RT.java:411)
    at clojure.core$load$fn__5066.invoke(core.clj:5641)
    at clojure.core$load.doInvoke(core.clj:5640)
    at clojure.lang.RestFn.invoke(RestFn.java:408)
    at clojure.core$load_one.invoke(core.clj:5446)
    at clojure.core$compile$fn__5071.invoke(core.clj:5652)
    at clojure.core$compile.invoke(core.clj:5651)
    at user$eval9.invoke(form-init4595004281107083893.clj:1)
    at clojure.lang.Compiler.eval(Compiler.java:6703)
    at clojure.lang.Compiler.eval(Compiler.java:6693)
    at clojure.lang.Compiler.load(Compiler.java:7130)
    at clojure.lang.Compiler.loadFile(Compiler.java:7086)
    at clojure.main$load_script.invoke(main.clj:274)
    at clojure.main$init_opt.invoke(main.clj:279)
    at clojure.main$initialize.invoke(main.clj:307)
    at clojure.main$null_opt.invoke(main.clj:342)
    at clojure.main$main.doInvoke(main.clj:420)
    at clojure.lang.RestFn.invoke(RestFn.java:421)
    at clojure.lang.Var.invoke(Var.java:383)
    at clojure.lang.AFn.applyToHelper(AFn.java:156)
    at clojure.lang.Var.applyTo(Var.java:700)
    at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol
    at clojure.lang.RT.seqFrom(RT.java:505)
    at clojure.lang.RT.seq(RT.java:486)
    at clojure.core$seq.invoke(core.clj:133)
    at clojure.core$map$fn__4245.invoke(core.clj:2551)
    at clojure.lang.LazySeq.sval(LazySeq.java:40)
    at clojure.lang.LazySeq.seq(LazySeq.java:49)
    at clojure.lang.RT.seq(RT.java:484)
    at clojure.core$seq.invoke(core.clj:133)
    at clojure.core$map$fn__4245.invoke(core.clj:2551)
    at clojure.lang.LazySeq.sval(LazySeq.java:40)
    at clojure.lang.LazySeq.seq(LazySeq.java:49)
    at clojure.lang.Cons.next(Cons.java:39)
    at clojure.lang.RT.boundedLength(RT.java:1654)
    at clojure.lang.RestFn.applyTo(RestFn.java:130)
    at clojure.core$apply.invoke(core.clj:624)
    at clojure.core$mapcat.doInvoke(core.clj:2586)
    at clojure.lang.RestFn.invoke(RestFn.java:423)
    at clojure.core$generate_class.invoke(genclass.clj:164)
    at clojure.core$gen_class.doInvoke(genclass.clj:638)
    at clojure.lang.RestFn.invoke(RestFn.java:1557)
    at clojure.lang.Var.invoke(Var.java:519)
    at clojure.lang.AFn.applyToHelper(AFn.java:270)
    at clojure.lang.Var.applyTo(Var.java:700)
    at clojure.lang.Compiler.macroexpand1(Compiler.java:6552)
    at clojure.lang.Compiler.analyzeSeq(Compiler.java:6630)
    ... 46 more
Compilation failed: Subprocess failed
3

There are 3 best solutions below

0
Marko Topolnik On BEST ANSWER

The proximate issue you have is a missing :aot :all in your project.clj file. Without that the ahead-of-time compilation will not be attempted.

Once you put that in, you'll have the following further issues:

  • you must prefix the names of the functions designated to become Java methods (default prefix is -);

  • you must include this as an explicit first argument of each such method (this is the recommended convention, but any name is acceptable);

  • you must correct your :implements clause: it takes a vector as the value, even if it has a single member.

1
GregA100k On

According to http://clojure.org/compilation

... the implementation functions for instance methods will always take an additional first arg corresponding to the object the method is called upon, called by convention 'this' here.

Try adding 'this' to each function definition.

(defn java-addToString [this text post]
  ...
0
GregA100k On

http://clojure.org/compilation mentions the compilation process makes use of

... *compile-path*, which must be in the classpath

The default location is a classes folder.

The second answer to Stack Overflow question Compiling Clojure? mentions that this path is relative to the jvm startup folder. If you did lein repl from the main project folder (the folder where project.clj lives) then creating a classes folder there should make it work.