How can I post process .class files?

(Nikita Skvortsov) #1

Given java-based project with mixed languages (Groovy, Java, Kotlin) and multiple *Compile task, how can I post-process generated class files to comply with gradle’s incremental build mechanics?

Call post processing as “doLast” of each “AbstractCompile” is not an option due to potential cross-references between different compilation tasks.

Call post processing as “doLast” of “classes” task effectively invalidates up-to-date checks for compilation (as processing is performed in-place")

What are other options? How do I add a post-processing, so that tests/jar tasks all work as expected?

(Stefan Oehme) #2

This is normally what I’d recommend. I don’t yet understand the problem with cross-references. Since the compile tasks depend on each other sequentially, the references can only go in one direction. Could you elaborate on that?

(Nikita Skvortsov) #3

Given mixed Kotlin-Java project, tasks JavaCompile will only contain java classes in their output (clasess produced by KotlinCompile are on classpath), so if post-processing requires full set of classes (kotlin and java), it can not be launched using JavaCompile's output only.

So far, the best solution out there is to add separate task for post-processing (instrumentation) and one more task, that cahnges compile tasks’ output directories on-the-fly. Here is the code

(Stefan Oehme) #4

The transform should be fine with modifying only the classes of the current task, but being able to link against everything on the classpath, including the Kotlin-compiled classes. I don’t yet understand why the transform would fail.

You definitely should not do that, this will break all kinds of plugins.

(Nikita Skvortsov) #5

I am sorry for misleading you. It is kotlin compilation that is the problem, not java. Kotlin compilation results only contains class files for kotlin sources (java classes are not generated at all, but kotlin code can have references on them).
More details are in this github issue

(Stefan Oehme) #6

Kotlin has to link against something though, I assume you generate stubs? Maybe you could use those for bytecode enhancement as well.

(Nikita Skvortsov) #7

adding related Github issue to discussion:

(Mark P. Hahn) #8

This may be a question for a different thread, but my understanding is that, by convention, the existing tasks are arranged in a directed acyclic graph. This is so that the test task does not run before the assemble task.

Shouldn’t ​your approach be to add a new task and insert it after assemble? Task creation is easy, but I’ve never manipulated the graph, so I’m not sure. Seems to me this would be effective, quick, transparent to plug-ins and simple?

Why am I wrong? -MpH

(Stefan Oehme) #9

The task graph is always a DAG.

If you’ve ever used dependsOn(), you’ve manipulated the graph.

The problem here is that Nikita wants to transform the class files before consumers like the test task use them. There are only two ways to do that. One is to transform them in-place as part of the compile task. The other way (which Nikita will be going for and which Gradle should support in a more first class fashion) is to insert a new step between “compile” and “use the classes”, including a new “unprocessed classes directory”.