Search code examples
clojuregen-class

gen-class not generating a class


I'm having difficulty referencing classes generated via :gen-class.

The smallest example I can show that demonstrates the problem is:

(defproject test-proj
  :dependencies [[org.clojure/clojure "1.8.0"]] 
  :aot [test-proj.test])

(ns test-proj.test
  (:gen-class))

(defn -main []
  (println test_proj.test)) ; Error here

The problem is, this yields a ClassNotFoundException on the marked line.

(I tried all different combinations of - and _ in the above file and the project.clj. I still don't fully understand what requires an underscore and what tolerates a dash. Some things seem to roll with dashes and convert them as needed, whereas I know from messing around that in the -main, I need underscores to reference test_proj.test.)

If I go into the project root file, there's no target folder, so it's not generating the class. If I go into the terminal and run lein compile, it generates the needed classes under target, and the above code runs without error. This is a poor workaround though. What if I modify the file and forget to manually recompile it? It's also a pain to have to manually compile it after any time I do a clean.

As a shot in the dark, I tried using compile right underneath the ns macro:

(compile 'test-proj.test)

If I uses dashes, compile seems to do literally nothing. I may be misinterpreting its use, but it doesn't generate class files under target. If I use underscores, it gives an exception saying that the namespace isn't found.

Is there a way to have the classes generated automatically so I don't need to run lein compile everytime? I thought that that's what the :aot in the project.clj did.


Solution

  • With Leiningen, specify :aot settings. :all is the easiest.

    project.clj

    (defproject test-proj "0.1.0-SNAPSHOT"
      :main test-proj.core
      :aot :all
      :dependencies [[org.clojure/clojure "1.8.0"]])
    

    If you want, you can specify the exact namespaces in an array as seen below:

    project.clj

    (defproject test-proj "0.1.0-SNAPSHOT"
      :main test-proj.core
      :aot [test-proj.core]
      :dependencies [[org.clojure/clojure "1.8.0"]])
    

    Then the following lein command:

    lein compile
    

    Will generate the byte code and .class files as specified in the :aot settings above.

    core.clj

    (ns test-proj.core
        (:gen-class))
    
    (defn -main[]
      (println test_proj.core)
      (println "Hello, World!"))
    

    You want to see something like the below:

    NikoMacBook% lein compile 
    Compiling test-proj.core
    

    Once this is done, check the target folder, contains the proper class file, here test_proj/core.class.

    NikoMacBook% tree target 
    target
    ├── classes
    │   ├── META-INF
    │   │   └── maven
    │   │       └── test-proj
    │   │           └── test-proj
    │   │               └── pom.properties
    │   └── test_proj
    │       ├── core$_main.class
    │       ├── core$fn__38.class
    │       ├── core$loading__5569__auto____36.class
    │       ├── core.class
    │       └── core__init.class
    └── stale
        └── leiningen.core.classpath.extract-native-dependencies
    
    7 directories, 7 files
    

    The following will run the :main namespace, so test-proj.core.

    lein run 
    

    Will output

    NikoMacBook% lein run 
    Compiling test-proj.core
    Compiling test-proj.core
    test_proj.core
    Hello, World!
    

    Note, that the class is calling itself. Note also that if you do not run lein compile beforehand , it will run by itself.