Search code examples
clojurecompilationjvmleiningen

Leiningen wont exclude namespaces from uberjar


I have a project where I want certain parts of the code to be able to run on a local environment, and other parts which depend on libraries that only run on a remote environment. E.g.

src/app/core.clj <- can run anywhere
src/app/sandbox/remote_only.clj <- depnds on libs that only function in remote environ

where src/app/core.clj is

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

and src/app/sandbox/remote_only.clj is

(ns app.sandbox.remote-only
  (:require
    [uncomplicate.commons.core :refer [with-release]]
    [uncomplicate.neanderthal
     [native :refer [dv dge]]
     [core :refer [mv mv!]]]))

I want to be able to uberjar and run the code located in core.clj on my local machine without pulling in remote_only.clj which will cause the program to fail locally. According to the docs, Leiningen should be able to accomplish this using profiles and uberjar exclusions e.g.:

(defproject app "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.10.1"]]
  :profiles {:uberjar {:main app.core
                       :init-ns app.core
                       :aot :all}
             :remote {:init-ns app.sandbox.remote_only
                      :dependencies [[uncomplicate/neanderthal "0.43.1"]]}} ; these deps will fail locally
  :uberjar-exclusions [#".*sandbox.*"]
  :repl-options {:init-ns app.core})

compiling the uberjar here will result in:

❯ lein uberjar
Compiling app.core
Compiling app.sandbox.remote-only
Syntax error macroexpanding at (remote_only.clj:1:1).
Execution error (FileNotFoundException) at app.sandbox.remote-only/loading (remote_only.clj:1).
Could not locate uncomplicate/commons/core__init.class, uncomplicate/commons/core.clj or uncomplicate/commons/core.cljc on classpath.

So, explicitly its trying to compile the class that was specifically excluded.

I thought that this kind of problem could be avoided by removing the :all tag from :aot compilation. E.g.:

(defproject app "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.10.1"]]
  :profiles {:uberjar {:main app.core
                       :init-ns app.core
                       :aot [app.core]}
             :remote {:init-ns app.sandbox.remote_only
                      :dependencies [[uncomplicate/neanderthal "0.43.1"]]}} ; these deps will fail locally
  :uberjar-exclusions [#".*sandbox.*"]
  :repl-options {:init-ns app.core})

which causes other problems:

❯ lein uberjar
Compiling app.core
Created /Users/warrenronsiek/Projects/app/target/app-0.1.0-SNAPSHOT.jar
Created /Users/warrenronsiek/Projects/app/target/app-0.1.0-SNAPSHOT-standalone.jar
~/Projects/app                                                                                                                                                                                              5s 15:47:37
❯ java -jar ./target/app-0.1.0-SNAPSHOT.jar 
Exception in thread "main" java.lang.NoClassDefFoundError: clojure/lang/Var

I've tried playing around with all kinds of :exclusions, :jar-exclusions and regexes. Variations of this question have been asked multiple times (1, 2) Nothing works.

How do I get Leiningen to compile uberjars and ignore the specified file(s)?


Solution

  • You are on the right track with changing :aot :all to :aot [app.core] but when you tried to run the JAR, you are running the library version instead of the whole application version:

    java -jar ./target/app-0.1.0-SNAPSHOT.jar 
    

    That doesn't include Clojure or any dependencies. You want:

    java -jar ./target/app-0.1.0-SNAPSHOT-standalone.jar