Incremental-friendly aspectj bytecode weaving

I’ve found this aspectj plugin not to work very well and have been looking to replace it.

The idea I’ve been pursuing is to stick a weave task between the compile and classes tasks of the java plugin. The weave task invokes ajc on the .class files produced by the compile task to perform Bytecode weaving.

If we do this in-place it alters the class files produced by the normal java task. Which makes compileJava be not up-to-date. Which makes weaveAspect not up-to-date. Which makes everyone unhappy that we’ve hindered the incremental build.

To avoid stomping on the output of the normal compile task, we can have our aspectj task put its outputs elsewhere. But we haven’t figured out how to make subsequent tasks like testCompile or jar look at the woven class files rather than those produced by the regular java compile task.

Does anyone have any advice on this subject? Is there a non-hacky way to do this pursuant to Gradle idiom?

Hey Sam,

I’d do weaving in a doLast() action on the JavaCompile task instead of in a separate task. Then the snapshotting of the compile output will see the enhanced classes instead of the original ones, making incremental compile work. The only downside is that the weaving itself will not be incremental. But AspectJ probably doesn’t support incremental weaving anyway, so I don’t think that’ll be an issue.

Cheers,
Stefan

Thanks Stefan, that helped a lot!

It seems this isn’t true any more. In my doLast on javaCompile, I’m seeing javaCompile not work incrementally - the up-to-date check works correctly but if a compilation is required the whole source directory gets recompiled not only the changed files.

Can you elaborate on what you do exactly?
It works fine here with the small test task

abstract class Inc : DefaultTask() {
    @get:Incremental
    @get:InputDirectory
    abstract val input: DirectoryProperty

    @get:OutputDirectory
    abstract val output: DirectoryProperty

    @TaskAction
    fun doIt(inputChanges: InputChanges) {
        inputChanges.getFileChanges(input).forEach {
            if (it.fileType != FILE) {
                return
            }

            println(it)
            when (it.changeType) {
                ADDED -> it.file.copyTo(output.get().asFile.resolve(it.file.name), overwrite = true)
                MODIFIED -> it.file.copyTo(output.get().asFile.resolve(it.file.name), overwrite = true)
                REMOVED -> output.get().asFile.resolve(it.file.name).delete()
            }
        }
    }
}

And a custom action

val inc by tasks.registering(Inc::class) {
    input.set(layout.buildDirectory.dir("in"))
    output.set(layout.buildDirectory.dir("out"))
    doLast {
        output.get().asFile.resolve("one").writeText("FOO")
    }
}