Open RickMoynihan opened 5 years ago
The uberjar issue with duct_hierarchy.edn
was what stopped me from porting Duct over to clj-new. It's good to hear that capsule uberjars are a potential solution.
Well, I'm glad to be helpful.
It's early days but they seem to work quite well.
Though saying this capsule's currently seem to create reflective access warnings on JDK9+, though there are open issues and it looks like people are trying to fix it.
Other things to note about capsules are that they do the double JVM kickstart dance a little like leiningen. Like leiningen you can also alegedly trampoline them. This extra complexity might be a reason some people want to avoid them; though so far they seem to work quite well.
I've not yet looked into pack.alpha's other uberjarring options, such as skinny jars or OneJar, but these might also be good candidates.
This library provides all the small building blocks for customising the whole package process. It might be a good candidate, if no other pre-build option could fully satisfy the needs.
You can also use lein-tools-deps
and lein uberjar
to build your jar. We wanted to produce an aot'd jar which was not possible with Capsule. This allows you to keep using your existing deps.edn
normally.
Here's a sample project.clj
:
(defproject project "0.1.0-SNAPSHOT"
:main ^:skip-aot project.main
:plugins [[duct/lein-duct "0.12.1"]
[lein-tools-deps "0.4.5"]]
:middleware [lein-duct.plugin/middleware
lein-tools-deps.plugin/resolve-dependencies-with-deps-edn]
:lein-tools-deps/config {:config-files [:install :user :project]}
:prep-tasks ["javac" "compile" ["run" ":duct/compiler"]]
:resource-paths ["resources"]
:profiles {:uberjar {:aot :all}})
Pack + Capsule has worked beautifully for me out of the box. +1.
I've just ported a duct project to work with tools.deps, and I've worked around some of the issues that come up.
Below are some notes on the issues and those which I have found solutions for. At some point they may be a useful basis for forming some documentation on the subject, or perhaps changing the duct project templates themselves.
The main issue I encountered is how to generate duct uberjars. With
lein
duct requires the addition of thelein-duct
plugin to ensure thatduct_hierarchy.edn
files from dependencies are merged together into a singleduct_hierarchy.edn
at the top of the root of the classpath. The main reason this is necessary is because thelein uberjar
process flattens all jars into one classpath root. It's worth noting that this is not what typically happens at a REPL. At a REPL the classpath is typically composed of your dependency jars, so the files are indepedently addressable via the classloader'sgetResources
which returns all matches for a given file on the classpath appropriately disambiguated by their jar/classloader.duct.core
correctly iterates over this which means providing you don't destroy this context in youruberjar
step, you shouldn't need a plugin to handle merge conflicts (lein uses:uberjar-merge-with
).So how can you generate uberjars without destroying that context, and do so from
tools.deps
? The answer is pack.alpha and specifically their support for creating capsule uberjars. Essentially Capsule provides us a way to package an uberjar of jars instead of a munged uberjar of classes; capsule then transparently manages the extraction process and the rebuilding of the classpath. The upshot is that your uberjar is then a much more similar environment to your repl, and duct will naturally handle the resolution ofduct_hierarchy.edn
itself.The only other thing I know duct does that I have not yet looked at, is
:aot
compiling the dependencies specified in your duct system by calling:duct/compile
. I expect for those that require this, it should be relatively easy to add.