Leiningen plugin to produce OTF-compiled uberjars. (OTF = on-the-fly)
Note: lein-otf is broken with recent Leiningen, perhaps the 2.3.x–2.4.x series.
I'm not sure why, but I think it has to do with the dependency injection. I'm not particularly invested in fixing this, partly because lein-otf barely even deserves to be a plugin. Patches welcome, though.
Assuming your main ns is foo.bar.routes, create foo.bar.launcher
like so:
(ns foo.bar.launcher
"AOT-prevention dynamic loader stub for `foo.bar.routes`."
(:gen-class))
(defn -main
"Chain to routes.clj"
[& args]
(let [main-ns 'foo.bar.routes]
(require main-ns)
(apply (ns-resolve main-ns '-main) args)))and in your project.clj:
:main foo.bar.launcher
:aot [foo.bar.launcher]Finally, remove :gen-class from foo.bar.routes, you don't need it
anymore.
This is more or less what lein-otf does for you anyway! The launcher namespace (and Clojure) will still be AOT-compiled, but nothing else will be (unless you specify otherwise).
If you want your REPL to start in the real main namespace, add something like this to your project.clj:
:repl-options {:init-ns foo.bar.routes}And that's it!
An uberjar's main class must be compiled ahead of time ("AOT") for the jar to work as an executable. Since AOT compilation is infectious (or "transitive"), most or all of the jar is also AOT'd, along with any linked libraries. This reduces portability.
lein-otf works by injecting a sacrificial Java loader class. This class
inspects the JAR's manifest file for the lein-otf-real-main attribute
(injected when the JAR was created), which bears the name of the
:main namespace or var that was specified in the dependent project.
The loader stub then calls that namespace's -main (or other explicitly
named) function. Because the loader is not statically linked against
the rest of your codebase, AOT compilation is restricted to
namespaces explicitly mentioned in :aot (and anything they link
against, of course.)
Compatible with projects using Clojure 1.3.0 and 1.4.0. Requires Leiningen 2.0 or greater (including preview versions).
- Specify the plugin as a plugin:
:plugins [[org.timmc/lein-otf "2.0.1"]] - Take
:gen-classout of your main namespace. Leave project.clj's:mainpointing to it, but add^:skip-aotmetadata on the namespace symbol. For example::main ^:skip-aot foo.core - Run the
uberjartask, or anything else which invokes it:$ lein uberjar
As with Leiningen itself, lein-otf assumes that the main fn is called -main
unless it is explicitly specified, e.g. foo.core/alt-main.
- Provides
lein uberjar-otfcommand. Works on Clojure v1.3.0.
- Extends support back to projects using Clojure v1.2.0.
- Warns on missing :main
- Likely the last minor release on 1.x branch.
- Major bugfix: Was always rejecting :main in target project.
- Allow :main to have a var name as well, e.g.
foo.core/alt-main. If :main does not contain a slash, var name defaults to-main. This gives parity with Leiningen itself.
- Support and require Leiningen 2.0. Switch to implementation using separated
loader stub dependency and Leiningen 2 automatic hooks and middleware. No
:hooksentry is necessary. Support (deprecated)uberjar-otftask as alias foruberjar.
- No source change. Depend on lein-otf.loader v1.0.1 (patch version bump.)
Copyright (C) 2012–2013 Tim McCormack & Marshall Vandegrift
Distributed under the Eclipse Public License v1.0, the same as Clojure.
License text is provided in ./epl-v1.0.txt.