clojure-dev

cfleming 2022-04-27T22:09:39.296839Z

Is there any way of obtaining all the class files produced by the AOT compilation of a namespace? My reading of the code says no, but I’d love to be proved wrong.

➕ 1
borkdude 2022-04-27T22:13:37.999409Z

That would be useful for graalvm purposes as well

2022-04-27T22:15:09.640529Z

sounds like you want to separate the classes produced by aot namespace A from the class files produced by namespace B, which A depends on

cfleming 2022-04-27T22:16:41.657009Z

For my use case (ensuring that incremental compilation in IntelliJ works) I’d want both, but I can imagine that separating them would also be useful (e.g. I only want to AOT my classes, and I’m happy to have the rest run-time compiled).

2022-04-27T22:19:05.063619Z

clojure basically behaves like some kind of whole program optimizing compiler, despite not doing any of things those do

2022-04-27T22:19:53.124689Z

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

cfleming 2022-04-27T22:20:03.302659Z

Right, I’m assuming that if you compile several namespaces, that the dependent classes are re-written each time. I haven’t checked that, though.

2022-04-27T22:20:32.870499Z

the name of classes produced by the compiler are not deterministic

Alex Miller (Clojure team) 2022-04-27T22:20:33.575209Z

no, they're not (answering cfleming's question)

Alex Miller (Clojure team) 2022-04-27T22:21:09.786329Z

classes that exist are not recompiled because they are not loaded because they've already been loaded

cfleming 2022-04-27T22:22:19.605429Z

Ah, that makes sense.

Alex Miller (Clojure team) 2022-04-27T22:23:08.150769Z

going back to original question, do you mean "including transitively compiled classes"? or just from that namespace

cfleming 2022-04-27T22:23:28.452129Z

I really wish the names were deterministic, it would help debugging as well.

cfleming 2022-04-27T22:25:27.954439Z

In my case, including transitive classes. When Clojure is AOT compiled in Cursive, I compile a namespace at a time (due to how the IntelliJ compiler framework works). In order for incremental compilation not to get messed up (not just for Clojure but for other compilers that might be working in the same project) I should be able to produce a mapping from source files to output files.

cfleming 2022-04-27T22:26:23.233629Z

Thinking about the transitive case, that’s tricky because which class files are produced depends on which namespaces have been previously compiled (the not loaded thing).

2022-04-27T22:27:21.042619Z

it also depends on loaded

cfleming 2022-04-27T22:27:22.887299Z

Currently I just map the source file to the ns initialisation class, which works for detecting which namespaces need to be compiled, but probably leaves lots of crud around in the output directories.

2022-04-27T22:27:46.719869Z

because if a transitive namespace is already loaded, it won't be loaded again when compiling, so no classfiles will be written to disk

Alex Miller (Clojure team) 2022-04-27T22:27:58.832479Z

why is this different than the Java model it's presumably written for?

Alex Miller (Clojure team) 2022-04-27T22:28:41.295249Z

Java source files produce N class files and depend on M other classes (which may overlap)

cfleming 2022-04-27T22:28:59.053449Z

Because when you compile a Java file, you can deterministically figure out which class files will be produced.

cfleming 2022-04-27T22:29:24.538159Z

Right, but compiling one Java file doesn’t produce class files from other classes.

Alex Miller (Clojure team) 2022-04-27T22:29:29.673849Z

so you need this a priori?

cfleming 2022-04-27T22:30:41.943279Z

Well, Cursive has always done this and no-one has complained, so it’s probably not an urgent problem. I suspect very few people use the AOT compilation feature anyway. But as borkdude mentioned, it would be a useful feature for other cases too, like graalvm.

cfleming 2022-04-27T22:31:31.717449Z

I understand that it’s a hard problem due to how the Clojure compiler has always worked, though.

Alex Miller (Clojure team) 2022-04-27T22:31:55.521319Z

like there are a set of name patterns you could look for (foo, foo__init, foo$... etc) but you're right that there is no way to know the actual set of classes until you compile.

borkdude 2022-04-27T22:32:06.375609Z

For a graalvm plugin I wrote I needed to know every class produced by clojure. I worked around this by collecting all the namespace names and using that as package wildcards, but it's better if you could reliably get the collection of class names.

borkdude 2022-04-27T22:32:18.436779Z

I also ran into the non-deterministic names

cfleming 2022-04-27T22:32:37.113779Z

And doing that, you wouldn’t get the transitive ones either, right?

cfleming 2022-04-27T22:33:36.914169Z

The thing is, for a particular namespace compilation, it’s not even deterministic which namespace’s files will be created, since the dependency namespaces might have been previously loaded.

cfleming 2022-04-27T22:34:57.301499Z

But for my AOT case, I probably want to figure out the transitive closure of dependent namespaces for each namespace I compile, and then return all those namespaces’ classes as potentially resulting from the compilation of the original source file.

cfleming 2022-04-27T22:36:00.380299Z

Since if that namespace is compiled cold with an empty output directory, all those files will be produced.

Alex Miller (Clojure team) 2022-04-27T22:36:10.293309Z

well, the dependency is really the reverse direction and ideally you'd compile those in the other direction

Alex Miller (Clojure team) 2022-04-27T22:36:55.141549Z

that's the effect you're getting but via side effects

cfleming 2022-04-27T22:37:07.558469Z

Right.

cfleming 2022-04-27T22:37:48.630619Z

Ok, I’ll take a look and see if all that is worth it. I’m not keen on scanning the output directory, but it looks like that’s really the only option.

cfleming 2022-04-27T22:39:05.361429Z

In my case, the set of files to compile is passed to me by the IntelliJ framework, and doesn’t include the dependencies from libraries. So I’ll always be compiling top-down, I can’t compile bottom up.

cfleming 2022-04-27T22:39:30.045189Z

At least, if that is possible I haven’t figured out how to do it.

borkdude 2022-04-27T22:39:52.361129Z

unless you analyze those files yourself maybe and then figure out the bottom dependencies?

cfleming 2022-04-27T22:40:36.874589Z

Yes, I could do that, and in order to correctly create the output set I’ll have to do that (i.e. pass the tree of namespace dependencies to the compiler).

2022-04-27T22:47:53.025999Z

the tools build compile clj task does something like that

2022-04-27T22:49:45.284949Z

the bug that resulted in that being added might be interesting reading as well

2022-04-27T22:50:45.984619Z

oh, guess the discussion is all in slack and not in jira https://clojure.atlassian.net/browse/TBUILD-7

2022-04-27T22:52:26.126919Z

basically compiling each namespace in some random order would break things, but in dependency order wouldn't

cfleming 2022-04-27T22:56:52.004799Z

Interesting, thanks, I’ll see if I can dig out the slack conversation.

cfleming 2022-04-27T23:00:45.969769Z

Yeah, there’s lots of interesting things in #tools-build if you search for “topo”

2022-04-27T23:02:47.921189Z

I think in #tools-deps too