4

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.

Carcigenicate
  • 43,494
  • 9
  • 68
  • 117
  • Have you tried `:aot :all` in the project file? Does it give a different result? – clartaq Sep 27 '17 at 13:41
  • @clartaq No, I did not. I'll try that when I get off of work. Thanks. – Carcigenicate Sep 27 '17 at 15:13
  • @clartaq Nope :(. After changing it to `:aot :all`, I still get the same error. Still isn't creating a `target` folder. – Carcigenicate Sep 27 '17 at 15:40
  • According to the "Clojust Cookbook": "Once your project is configured for AOT compilation, you can compile it by invoking `lein compile` at the command line.". Seems as though specifying AOT doesn't actually cause the compilation; it only marks what should be compiled later. – Carcigenicate Sep 27 '17 at 15:45
  • It works if you run `lein run`, but not if you invoke the `-main` directly. `lein run` isn't ideal here though, since I'm testing a side part of my project. I suppose I could temporarily re-route the main for the sake of my sanity. – Carcigenicate Sep 27 '17 at 15:50
  • Now that I've typed everything out, I'm not sure how I expect it to know when to re/compile. I guess I thought the IDE/Cursive could listen for changes? Or something? Am I expecting too much? – Carcigenicate Sep 27 '17 at 16:09
  • The `compile` function will compile the classes into the directory specified by the `*compile-path*`. On my system, that is usually something like `project-name\target\classes` when I run the REPL as a Run/Debug configuration under Cursive. Might be different for you. – clartaq Sep 27 '17 at 17:34
  • @clartaq I couldn't get it to work, but it also wasn't what I was focusing on. Maybe I'll playable round with it more. Thanks. – Carcigenicate Sep 27 '17 at 18:14
  • I'm probably missing something about how you want to run your program. I can get your example to work a couple of ways. I can add a `:main test-proj.test` line to the `project.clj` file then run `lein run` and it works as expected. – clartaq Sep 27 '17 at 18:18
  • In a REPL, I can start with `(require '[test-proj.test :as tst])` and then run `(tst/-main)` to get the same results. You don't really need to `(compile...)` in the REPL. It doesn't generate any class files this way though. – clartaq Sep 27 '17 at 18:21
  • @clartaq For your first comment, it works with `lein run`, but I can't really use it for my current project. I'm working on a side bit that's far away from the main. I'll need to `run` the full program at some point, but also need to be able to just run the side part as well. Without constantly modifying the `:main` attribute in `project.clj`, I don't know how to run specific parts of a project. And for your second comment, I need the class files. I'm making a JavaFX interface, and it requires having a named class. – Carcigenicate Sep 27 '17 at 21:23
  • It turns out JavaFX doesn't need the class for much. I can `lein compile`, make changes, and the changes will be reflected, even without a recompilation. I can see this potentially introducing odd behavior if I'm not careful, but I think I'm going to just periodically `lein compile`. Didn't want to, but it seems to be the more reliable way. – Carcigenicate Sep 27 '17 at 23:15

1 Answers1

1

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.

Nicolas Modrzyk
  • 13,961
  • 2
  • 36
  • 40
  • I didn't want to have to manually call `lein compile` constantly, but after playing around, it seems to be the only way. Thanks. – Carcigenicate Sep 28 '17 at 10:53