Why does lein uberjar evaluate variables defined with def?

I am trying to understand the behavior of Lieningen when creating uberjar . The following is a minimal example that reproduces the behavior:

 (ns my-stuff.core (:gen-class)) (def some-var (throw (Exception. "boom!"))) (defn -main [& args] (println some-var)) 

When this is done using lein run , it obviously does not work with Exception. However, I do not understand why the execution of lein uberjar also fails using an exception from the variable definition? Why is lein uberjar trying to evaluate the value of a variable? Is this specific to the uberjar task or am I uberjar something more substantial about Clojure or Leiningen?

+6
source share
1 answer

To compile your namespace for uberjar (if you enabled AOT), the clojure compiler must load your namespace. This will always cause all the top level side effects.

The best way to handle this is to never have side effects in the top-level code (whether inside or outside the def form) and have initialization functions to implement any side effects you need to run.

A workaround might be to create a small namespace that uses introspection to load the rest of your code at runtime, but not at compile time — using this function:

 (defn -main [] (require 'my.primary.ns) ((resolve 'my.primary.ns/start))) 

if this namespace is compiled, jvm can find -main and run it, even though your other code does not compile. The require runtime will cause the clojure compiler to load the rest of your code only at runtime, and resolve will require -main to compile cleanly - it returns a reference to var, which then calls your function when called.

+10
source

All Articles