Fork me on GitHub
#tools-build
<
2022-07-14
>
allandaviesza19:07:38

Hello, I've noticed an issue when using the compile-clj function and I'm wondering if it's already known? Namespaces in user.clj (if you mistakenly have it in your classpath) get loaded before compiling starts, seems they get cached in *loaded-libs*, then when compiling the main namespace, if those same namespaces are required, they never get loaded and no .class file is created for them.

allandaviesza19:07:18

I can fix this issue by either remove user.clj from the classpath

;; or by adding this binding to the compile script in write-compile-script!
#'clojure.core/*loaded-libs* `(ref (sorted-set))

dpsutton19:07:13

there’s a great thread touching on this in #clojure i think

allandaviesza19:07:46

ye looks like a similar issue, here clojure.main seems to cause the user ns to load.

hiredman19:07:45

it is anything

hiredman20:07:09

if you load the clojure runtime and a http://user.clj is on the classpath it will be loaded

allandaviesza20:07:25

why not clear the loaded-libs cache in the compile script?

hiredman20:07:47

loaded-libs isn't everything

hiredman20:07:07

clearing loaded-libs will allow require to load things again, effectively forcing a reload-all, but that doesn't clear out the existing state of those namespaces before hand

hiredman20:07:50

all kinds of stuff can bleed over and cause further issues

hiredman20:07:15

the most reliable thing is a nice clean state, fresh clojure process with nothing loaded

allandaviesza20:07:17

thanks for the info, I'll be sure to keep user.clj out of my classpath when compiling 😬

allandaviesza20:07:58

yea, seems the compile-clj is trying it's best to do that

dpsutton21:07:05

I’m trying to understand :parents and :dependents in a basis map. My ultimate goal is to get the transitive closure of jars required by a jar: in this case 'com.google.cloud.sql/postgres-socket-factory.

dpsutton21:07:03

I’m a bit surprised that the lib itself doesn’t point to things it depends on :

classpath=> (get-in basis [:libs 'com.google.cloud.sql/postgres-socket-factory])
{:mvn/version "1.6.0",
 :deps/manifest :mvn,
 :parents #{[]},
 :paths
 ["/Users/dan/.m2/repository/com/google/cloud/sql/postgres-socket-factory/1.6.0/postgres-socket-factory-1.6.0.jar"]}
But several other libs point back at it, even though there seem to be parent and descendants keys which makes me think the graph should have edges on both sides

dpsutton21:07:50

for example

com.google.oauth-client/google-oauth-client
 {:mvn/version "1.33.2",
  :deps/manifest :mvn,
  :dependents [com.google.api-client/google-api-client],
  :parents
  #{[com.google.cloud.sql/postgres-socket-factory
     com.google.cloud.sql/jdbc-socket-factory-core
     com.google.api-client/google-api-client]},
  :paths
  ["/Users/dan/.m2/repository/com/google/oauth-client/google-oauth-client/1.33.2/google-oauth-client-1.33.2.jar"]}
Here google-oauth-client has a parent relationship to the postgres-socket-factory but the inverse of that doesn’t seem to be present

dpsutton21:07:41

and the docstring of create-basis states that > :libs - lib map, per resolve-deps but resolve-deps doesn’t explain this relationship so I’m a bit lost

Alex Miller (Clojure team)21:07:41

:dependents are things that depend on this lib

Alex Miller (Clojure team)21:07:16

:parents should be considered implementation details right now

dpsutton21:07:36

ah ok. So i can traverse bottom up but not top down (at least for the moment)

Alex Miller (Clojure team)21:07:33

and it's somewhat tricky in that what is reflected in the final lib set may not correspond to what was in the original dep expansion

Alex Miller (Clojure team)21:07:41

due to version selection, exclusions, etc

dpsutton21:07:19

sure. i meant traverse by linear searching for dependents rather than direct access by parent (there’s no index for the direction i’m starting from)

Alex Miller (Clojure team)21:07:20

you're trying to answer a question whose answer is not necessarily in the data you are looking at

dpsutton21:07:30

oh that’s surprising to me. I guess this information is not available anywhere then? I can resort to getting (System/getProperty "java.class.path") from a repl started with clj -Sdeps {the-lib}. Is there no other way?

Alex Miller (Clojure team)21:07:28

you can probably extract something close to what you by doing what you're doing (and that's what old versions of -Stree did - that code is still in tools.deps.alpha). The code in tools.deps.alpha.tree is probably better but really requires taking a trace of the dep expansion so duplicates the work done to calculate the basis

dpsutton21:07:55

thanks. i’ll look at that. many thanks for answering questions

Alex Miller (Clojure team)21:07:12

depending on whether you care, you could just call calc-trace then trace->tree to get an actual tree (specifically, the tree you see now with -Stree or -X:deps tree) with a lot of data (probably way more data than you want)

dpsutton21:07:02

i’m ok with data. Ideally just want jars because i need to find out if i have duplicate classfiles on the classpath. So I just need an authoritative way to enumerate jars, then enumerate their files, and then intersect those

Alex Miller (Clojure team)21:07:25

what you just said doesn't seem to require what you're asking for?

Alex Miller (Clojure team)21:07:49

why not just do that directly off the classpath?

Alex Miller (Clojure team)21:07:27

is that because you don't have the classpath, you're doing it from deps?

dpsutton21:07:36

because we load other jars at runtime for extension. and adding the postgres-socket-factory to the main app breaks lazily loading the bigquery driver.

Alex Miller (Clojure team)21:07:07

well rather than using the lib map, you could grab the classpath from the basis

Alex Miller (Clojure team)21:07:54

specifically :classpath-roots from the basis

dpsutton21:07:56

good point. It would be a little overinclusive (many more jars than what is brought in by the socket factory) but won’t be underinclusive. Thanks

Alex Miller (Clojure team)21:07:38

I think if you find the conflict, should be easy to understand why there is one

dpsutton21:07:10

agree. Thank you very much

Alex Miller (Clojure team)21:07:32

and btw, I'd love to have this packaged as a tool I could run somehow :) people need this all the time

dpsutton21:07:00

I just randomly looked for duplicated files and was surprised to see we had so many. Somehow have two copies of batik stuff for images

Alex Miller (Clojure team)21:07:11

that batik stuff is a mess

Alex Miller (Clojure team)21:07:21

they have some jars with circular dependencies

dpsutton21:07:46

i’m assuming some very long days at work

Alex Miller (Clojure team)21:07:56

broke tools.deps a long while back

dpsutton21:07:33

i can’t imagine the edge cases it takes to get nice new tooling to play with every dark corner of the java ecosystem

dpsutton22:07:11

and still headscratching. The jars are completely disjoint. We were pretty sure we had two different copies of com.google.cloud.ServiceOption on the classpath under different classloaders. But doesn’t seem to be the case

Alex Miller (Clojure team)22:07:07

the same class loaded in different classloaders are two different classes

Alex Miller (Clojure team)22:07:03

that is, class identity is based on classloader+name

Alex Miller (Clojure team)22:07:29

I'm not sure I said that is the most understandable way so let me know if that doesn't make sense

dpsutton22:07:41

I’m very handwavy right now. We seem to have found the offending class but somehow the longer I spend on this the less I feel i know where solid ground is 🙂

Alex Miller (Clojure team)22:07:17

if classloader A loads class C, and classloader B loads class C, then instances of B are not instances of A

Alex Miller (Clojure team)22:07:39

(C is the identical class on disk)

Alex Miller (Clojure team)22:07:19

in general, the way to avoid this is to always have A or B in a parent-child relationship where the parent loads C