tools-deps

Discuss tools.deps.alpha, tools.build, and the clj/clojure command-line scripts! See also #depstar #clj-new
seancorfield 2020-12-22T00:54:37.001600Z

Ran into an interesting issue while working on depstar 2.0 today: I'd been assuming that you could use :replace-deps to add it as a dependency now that it calculates the project basis from the deps.edn files (and the :aliases you provide it with), and that works great for almost everything... except where it does AOT compilation, which has to be done in the context of the project's code so that the necessary dependencies are available. I looked at what Leiningen does and it seems to set up a specific classloader context for running compilation (based on the project's dependencies etc).

seancorfield 2020-12-22T00:55:40.002700Z

Although calculating the project basis causes all the project's dependencies to become available (locally) for inclusion in the uberjar, they are not on the classpath used to invoke depstar if :replace-deps is used.

seancorfield 2020-12-22T00:59:07.004600Z

Thoughts/suggestions? What I've done for the time being is just documented that if you ask depstar to do AOT-compilation, you'll need to use :extra-deps instead of :replace-deps when specifying it as a dependency. What options do I have if I wanted to run clojure.core/compile in a different classloader context?

seancorfield 2020-12-22T01:02:21.005500Z

(that's not ideal because then you run the risk of depstar's transitive dependencies affecting your project's compiled code in some way)

ghadi 2020-12-22T01:51:19.006600Z

start another JVM just for the compilation from within depstar

ghadi 2020-12-22T01:51:53.007300Z

that’s the approach I’m taking on a similar tool

seancorfield 2020-12-22T02:29:59.008900Z

@ghadi Seems like a bit of a sledgehammer to crack a nut but it is also straightforward I guess. Are you shelling out? If so, how do you decide what command to use? Or is there a JVM-specific spawning process?

seancorfield 2020-12-22T03:46:26.009600Z

Hmm, with ProcessBuildler, that's not bad. And it can generally assume java -cp ... will work I guess?

alexmiller 2020-12-22T04:22:17.010100Z

Separate JVM is what I did in tools build

alexmiller 2020-12-22T04:22:49.010700Z

You can’t run clj if java doesn’t work so ...

seancorfield 2020-12-22T04:23:26.011200Z

And the clojure script sets JAVA_CMD so I can rely on that too.

seancorfield 2020-12-22T04:29:12.013100Z

Here's what I ended up with https://github.com/seancorfield/depstar/blob/develop/src/hf/depstar/uberjar.clj#L541 (it's all due for a big refactor -- most of today has been focused on just getting all the 2.0 functionality working and the next job is creating automated tests and then refactoring)

kenny 2020-12-22T17:57:33.014200Z

I see the output of -Stree has changed with the latest cli version. Are there some docs on what the notation used means?

seancorfield 2020-12-22T18:10:20.015300Z

@kenny X means that version of the dependency is not being used. The keywords at the end of some lines indicate why a particular version was used or not.

kenny 2020-12-22T18:13:12.017900Z

X means it's not being used because it's overridden by something else? It seems that last keyword is only every present on X's.

seancorfield 2020-12-22T18:13:13.018Z

Example from one of our services:

X compojure/compojure 1.6.2 :use-top
    X ring/ring-core 1.6.3 :older-version
      X ring/ring-core 1.6.0 :older-version
      X ring/ring-core 1.6.0 :older-version
    X ring/ring-core 1.7.1 :older-version
  X worldsingles/lowlevel /Developer/workspace/wsmain/clojure/lowlevel :use-top
    . ring/ring-core 1.8.2 :newer-version
        X org.clojure/java.classpath 0.3.0 :older-version
Ring 1.8.2 is the :newer-version that is selected in preference to 1.6.3 which is :older-version, and Compojure 1.6.2 was specified as a top-level dependency (overriding any transient dependency on it).

kenny 2020-12-22T18:14:13.018600Z

Oh I see. What does it mean when a dep doesn't have a keyword?

seancorfield 2020-12-22T18:14:29.019Z

That's just a normal selection of the version.

kenny 2020-12-22T18:15:17.020200Z

As in all deps have the same version of the dep?

seancorfield 2020-12-22T18:16:46.021400Z

The end result is just one set of versions. If there's no conflict of versions, there will be no annotation I believe.

kenny 2020-12-22T18:17:52.022500Z

Right. That's what I'm seeing. Just making sure it was the correct interpretation. This new output is great. How come it isn't documented anywhere? Alpha?

seancorfield 2020-12-22T18:20:35.023600Z

Although:

X org.clojure/java.classpath 0.3.0 :older-version
    . org.clojure/java.classpath 1.0.0
In this case 1.0.0 was selected implicitly (it's only a transitive dep in our code), and the older version was excluded because a newer version had already been found.

seancorfield 2020-12-22T18:21:12.024300Z

I expect @alexmiller can provide more detail when he's around...

alexmiller 2020-12-22T18:27:03.025200Z

It’s a bit more complicated than that, and there are some cases where inclusions list a reason too

alexmiller 2020-12-22T18:27:14.025600Z

Maybe I will doc it today :)

2
alexmiller 2020-12-22T18:27:52.026200Z

I omit it in cases where it’s uninteresting

alexmiller 2020-12-22T18:28:13.026700Z

Like finding a new dep

alexmiller 2020-12-22T18:28:44.027400Z

Or finding same dep version already selected

kenny 2020-12-22T18:33:12.028300Z

In that case, why wouldn't it be annotated with :newer-version like in your first example @seancorfield?

seancorfield 2020-12-22T18:37:59.030300Z

@kenny If it finds version N and later finds version N+1, I would expect it to annotate both, and then if it continues to find version N+1 it will not annotate those (since they are "finding dep version already selected")

seancorfield 2020-12-22T18:39:19.031200Z

If it only finds version N across all deps, it's not going to annotate either.

seancorfield 2020-12-22T18:39:31.031600Z

At least, that's my understanding of what @alexmiller just said.

alexmiller 2020-12-22T19:07:13.032300Z

it doesn't annotate both because it's a single pass breadth-first expansion

alexmiller 2020-12-22T19:07:57.033100Z

(or rather to be clear, all choices have a reason, I'm just suppressing some of them in the printing b/c they're not interesting)

alexmiller 2020-12-22T19:08:58.033800Z

when we first see a lib (version N) during expansion, it will have reason :new-dep

alexmiller 2020-12-22T19:09:32.034300Z

if you later find same lib version N+1, it will have reason :newer-version

alexmiller 2020-12-22T19:11:12.035700Z

but there are a lot of tricky cases

alexmiller 2020-12-22T19:12:08.036200Z

right now the reason codes :new-top-dep, :new-dep, and :same-version are suppressed

borkdude 2020-12-22T19:18:38.037600Z

I'm getting a different resolution for a project that uses tools.reader 1.3.2 and pulls in a mvn lib that depends on tr 1.3.4 (I'm getting tools reader 1.3.4 in this case which I'm happy about), when using that lib via local/root (then it pulls in 1.3.2)

borkdude 2020-12-22T19:18:43.037800Z

Is that expected?

borkdude 2020-12-22T19:18:50.038Z

(I did try -Sforce)

borkdude 2020-12-22T19:20:07.038600Z

I will just remove the tools.reader dep from the project since I always want to use what the local/root project is using anyway

alexmiller 2020-12-22T19:27:40.038800Z

not enough info there for me to tell

alexmiller 2020-12-22T19:28:00.039200Z

local/root dep changes are not automatically picked up, so you do need to -Sforce if those change

alexmiller 2020-12-22T19:28:33.039700Z

if you have an output from -Strace or -Stree, happy to take a look

borkdude 2020-12-22T19:42:31.040400Z

oh yikes, I see what is happening. the mvn lib has AOT-ed class files... which should not happen.

borkdude 2020-12-22T19:55:41.041300Z

Found the culprit: apparently when you have a :main in the top-level of defproject lein will compile on deploy ... which might make sense. I just used that main for testing some stuff. Problem solved.

alexmiller 2020-12-22T20:53:47.042Z

@kenny @seancorfield new page on dep expansion, tracing, tree printing https://clojure.org/reference/dep_expansion

💯 4
alexmiller 2020-12-22T21:05:02.042800Z

undoubtedly more to say, but that is a dump of several things have only been in my head for a long time so felt good to get them out :)

kenny 2020-12-22T21:48:45.043Z

How do you get to this page without the link?

seancorfield 2020-12-22T21:56:58.043200Z

It linked in two places on the deps/CLI reference page.

kenny 2020-12-22T21:57:23.043400Z

Ah. Isn't it odd how it's not in the left nav though?

seancorfield 2020-12-22T21:58:47.043600Z

It's not a reference in and of itself: it's just extra material for an existing reference. Not every page is directly linked in the left nav.

seancorfield 2020-12-22T22:00:04.043800Z

The deps/CLI reference page is the entry point. The new page is linked from dep expansion under the Resolve dependencies section of Operation and also from tree printing under Other Programs (for -X:deps tree).

kenny 2020-12-22T22:00:19.044Z

Oh, okay. I could potentially see that being confusing for someone who is directly sent this link. That person has no way to "back out" to contextualize 🙂

Eugen 2020-12-22T22:55:57.045200Z

hi, what is the equivalent in deps.edn of :java-source-paths ["src/java/"] from leiningen ? I would like to add deps.end for clj-antlr so I can use it from git. The java directory contains java sources: https://github.com/aphyr/clj-antlr

dominicm 2020-12-22T23:04:57.045600Z

@eugen.stan no equivalent. You'll have to use javac yourself first.

Eugen 2020-12-22T23:06:23.046300Z

thanks, I keep finding these edge cases 🙂 . Do you have any examples?

seancorfield 2020-12-22T23:46:47.047900Z

@eugen.stan He means you literally run the javac command yourself to compile the .java files to .class files.

seancorfield 2020-12-22T23:48:36.049400Z

This isn't an "edge case" -- this is just something that the Clojure CLI was not intended to do, by design. Leiningen is sort of a "kitchen sink": it has a whole bunch of tools included that have nothing to do with just "running Clojure code" (which is what the Clojure CLI is designed for).

seancorfield 2020-12-22T23:49:30.050500Z

People are building "tools" that mimic some of the stuff that Leiningen does (or that is provided by one of the many Leiningen plugins) and those "tools" are just small Clojure programs that you run via the Clojure CLI.

seancorfield 2020-12-22T23:50:57.051600Z

When you look at the general design principles behind Clojure -- and a lot of the things that Cognitect folks have produced -- there's a deliberate choice to keep things simple: each tool should do one job.

seancorfield 2020-12-22T23:52:45.053600Z

Someone could build a tool to compile Java source code -- a tool you could run from the Clojure CLI -- but you'd still need to run that tool and then run other tools and/or the CLI itself. There's still no "task pipeline" for the Clojure CLI. Maybe tools.build will provide that "task pipeline"? We'll have to wait and see what the Cognitect folks produce.