duct icon indicating copy to clipboard operation
duct copied to clipboard

Document how to use duct with tools.deps

Open RickMoynihan opened this issue 5 years ago • 6 comments

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 the lein-duct plugin to ensure that duct_hierarchy.edn files from dependencies are merged together into a single duct_hierarchy.edn at the top of the root of the classpath. The main reason this is necessary is because the lein 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's getResources 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 your uberjar 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 of duct_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.

RickMoynihan avatar May 28 '19 13:05 RickMoynihan

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.

weavejester avatar May 28 '19 18:05 weavejester

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.

RickMoynihan avatar May 29 '19 08:05 RickMoynihan

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.

https://github.com/EwenG/badigeon.git

zerg000000 avatar May 29 '19 09:05 zerg000000

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}})

ianbishop avatar Nov 01 '19 15:11 ianbishop

Pack + Capsule has worked beautifully for me out of the box. +1.

WhittlesJr avatar Aug 13 '20 16:08 WhittlesJr

HERE is an example of deps.tools project with duct and integration of both solutions for creating uberjars:

  • pack + capsule,
  • lein uberjar + lein-tools-deps.

rynkowsg avatar Jun 25 '21 22:06 rynkowsg