tools-deps

practicalli-johnny 2023-03-16T13:59:47.646279Z

I'd like to thank @alexmiller and other core maintainers who have created a very flexible set of tools (Clojure CLI, tools.deps, tools.build, etc) I appreciate the methodical approach to creating tools and libraries which parallels the approach taken by all Clojure core maintainers and the development of the Clojure language itself. This has greatly helped with the adoption of Clojure and retention of developers and projects using Clojure The communication about up coming features has been very useful in understanding when to adopt certain features and setting expectations. There is a small learning curve for these tools and I really appreciate the support provided on this and many other slack channels by @alexmiller which has been highly valuable to me and hopefully the rest of the Clojure community. Thank you Alex.

👍 1
4
âž• 9
orestis 2023-03-16T16:01:15.797979Z

Uberjar is a jar that contains everything (dependencies and code). Is there a term for two jars where one contains all the dependencies, and another that contains all the code? Think slow moving vs fast moving layers in Docker.

dominicm 2023-03-17T16:38:36.111179Z

@orestis have you looked at pack.alpha?

dominicm 2023-03-17T16:39:30.447779Z

> What I’m after is essentially creating a docker layer with all my (compiled) dependencies, that should, in principle, not changing very often, and another docker layer with all my (compiled) source. Aim is to deploy to production and get fast startup time (hence the compiled requirement). The split of dependencies & application source is exactly what pack takes care of. It doesn’t get involved with compilation though. Splitting the AOT compiled stuff is really difficult as discussed above.

favila 2023-03-16T16:15:03.179849Z

I’m not sure there’s a term of art for that except for (javaee) container-specific deploys (where the container server provides the deps, and WAR files are the application). I think typically when you start separating, you just make a lib directory of dependency jars instead of dependency + application uberjars, because it’s much simpler and safer.

favila 2023-03-16T16:15:29.676729Z

there’s also jar-of-jars, but that’s just another uberjar-like technique to get one self-contained jar

orestis 2023-03-16T16:17:53.122259Z

What I'm after is essentially creating a docker layer with all my (compiled) dependencies, that should, in principle, not changing very often, and another docker layer with all my (compiled) source. Aim is to deploy to production and get fast startup time (hence the compiled requirement).

favila 2023-03-16T16:19:01.797969Z

if these dependencies you want to be slower-moving are clojure-aot compiled, I think there may be some trickiness there

favila 2023-03-16T16:20:33.440569Z

clojure compilation is transitive to all required code, there is no stable clojure ABI, and I’ve heard can be hard to predict what the output class filenames can be

favila 2023-03-16T16:21:48.410319Z

IOW if this is clojure code, I don’t think you can easily and safely separate the “my aot-ed code” from the “this lib’s aot-ed” code the same way you can with java lib class files

favila 2023-03-16T16:23:39.182089Z

but you could separate by clojure source or java classfile depencency jars vs application clojure source jars

favila 2023-03-16T16:24:27.206749Z

but if you clojure-aot, you probably shouldn’t split up the result of the compile into multiple jars or allow the same clojure source to have aot code on the classpath.

orestis 2023-03-16T16:43:37.061209Z

I thought clojure AOT means just generating a .class file per .clj file?

favila 2023-03-16T16:43:58.510959Z

there’s not a straight 1-1 correspondence between class files and clj files

practicalli-johnny 2023-03-16T16:44:17.799429Z

I would push all the built dependencies to a repository, as part of the CI workflow of each project. Then in the project that uses the dependencies the docker image can include those dependencies. The cache would be used unless a newer version of that dependency is pushed to the repository Something like Artefactory or AWS artefact repository

orestis 2023-03-16T16:44:21.108329Z

hm, 1-1 between class file and namespace?

favila 2023-03-16T16:44:26.426739Z

no

favila 2023-03-16T16:44:36.016619Z

try it 🙂

favila 2023-03-16T16:45:15.129009Z

this is why clojure libs don’t distribute jars with class files

favila 2023-03-16T16:45:53.927519Z

https://clojure.org/reference/compilation

orestis 2023-03-16T16:48:08.187809Z

Ah, I was thrown by the usual cannot find foo__init.class for the foo namespace.

orestis 2023-03-16T16:48:45.434019Z

I still think that AOT compiling Clojure dependencies today and using them with AOT compiled Clojure source tomorrow should work, as long as we're talking about the same Clojure/JVM version.

favila 2023-03-16T16:49:59.648579Z

It may work most of the time, but really you’re not meant to combine compilation output from different processes running different classpaths

favila 2023-03-16T16:50:11.529929Z

or, you’re on your own, you may encounter subtle problems

favila 2023-03-16T16:50:28.203279Z

it’s not impossible, but I wouldn’t trust myself to do it right

favila 2023-03-16T16:55:28.680199Z

Here’s a discussion where Colin Fleming (who made Cursive) asks about using bits of aot-compiled stuff together (for incremental compilation in his case.) The thread touches on some of the challenges in more detail. https://clojurians.slack.com/archives/C06E3HYPR/p1651097379296839

favila 2023-03-16T16:55:57.258989Z

This is the key:

favila 2023-03-16T16:56:00.213399Z

> clojure basically behaves like some kind of whole program optimizing compiler, despite not doing any of things those do > [5:19 PM] so like taking apart the output of an optimizing compiler and linking it with something compiled separately is tricky, so is doing that with aot compiled clojure

orestis 2023-03-16T17:28:07.546979Z

Well I can certainly compile everything together and split the output files. If Docker hashes them and the output is the same, problem solved

favila 2023-03-16T17:28:37.884659Z

compilation output is not deterministic

favila 2023-03-16T17:28:51.923649Z

probably never going to hash the same

favila 2023-03-16T17:32:55.071779Z

You can put the contents of .m2 after a clojure -P into your slower-moving layer and probably get most or all of the savings from not having to download dependencies separately, but that doesn’t include the trickier AOT part

favila 2023-03-16T17:33:47.180479Z

or slightly fancier, a tools-deps program that copies all basis jars into a lib directory

favila 2023-03-16T17:34:04.171369Z

then put the lib in a layer

orestis 2023-03-16T17:42:44.952299Z

I’m already doing the Clojure -P and Docker is smart to cache that automatically. Definitely helps to not download the universe. My quest for smaller layers is insignificant compared to that.

orestis 2023-03-18T11:28:11.914149Z

AOT compilation is a basic requirement for us, for faster startup times, so if we can't split AOT-compiled classes that's a no go.