Gradle 4.x and 3.x with the build directory change

So the change to compile different languages to different directories is quite a major breaking change for Grails when compiling Groovy code. For the moment we have asked users to disable that feature whilst we try and move forward with it.

However I am confused as to how we can move forward with supporting this in a way that is compatible with Gradle 3.x. Basically the error suggests we should use setTestClassesDirs(FileCollection) instead, but this method doesn’t exist for Gradle 3.x so doing this would mean a plugin is no longer binary compatible.

In addition there seems to be several breaking problems with regards to source sets. For example in the Grails plugin we create an integrationTest task to run integration tests. This was previously done via:

SourceSet integrationTest = sourceSets.create("integrationTest")
...
File outputDir = integrationTest.output.classesDir
integrationTestTask.setTestClassesDir(outputDir)

The value of outputDir in Gradle 4.x is build/classes/java/main whilst the code gets compiled to build/classes/groovy/integrationTest. This means that with Gradle for these tests no longer run. How do I obtain the correct outputDir value in a portable way that is also compatible with Gradle 3.x?

This seems to work with Gradle 4.x to obtain the right directory:

    private File outputDir(SourceSet integrationTest) {
        return integrationTest['groovy'].outputDir
    }

But fails with No such property: outputDir for class: org.gradle.api.internal.file.DefaultSourceDirectorySet on Gradle 3.5.x

So there seems no way to create a plugin that is compatible with both?

I think the best way to go is to distinguish between gradle versions using gradle.gradleVersion

That would be a fine approach for plugins written in Groovy but I have been moving to Java only plugins per the best practice recommendations given by the Gradle team. It would be impractical to support a code path that is Gradle < 4.x and another that is Gradle >= 4.x in the same Java plugin because we could not compile the code. We would have to resort to reflection to invoke this method on Gradle 4.x or 3.x, to remain binary compatible with both implementations.

Quite the maintenance nightmare. Of course the alternative is to not try supporting more than one version of Gradle at each release :disappointed: