Gradle 1.x and 2.x compatible plugins


(Thibault Kruse) #1

Hi,my gradle-groovysh-plugin does not work with Gradle 2.0. I am trying to fix that, but I am stuck. My goal would be to create a jar that works with both gradle 1.x and gradle 2.x, becuase else it would be twice the hassle to release and provide support.

What seems to be causing the main problem is that I subclass org.gradle.api.tasks.compile.JavaCompile, which lead to this

Caused by: java.lang.VerifyError: (class: com/tkruse/gradle/groovysh/PatchedMainCompileTask, method: super$7$setJavaCompiler signature: (Lorg/gradle/api/internal/tasks/compile/Compiler;)V) Illegal use of nonvirtual function call
        at com.tkruse.gradle.groovysh.ShellTask.class$(ShellTask.groovy)
        at com.tkruse.gradle.groovysh.ShellTask.$get$$class$com$tkruse$gradle$groovysh$PatchedMainCompileTask(ShellTask.groovy)
        at com.tkruse.gradle.groovysh.ShellTask.<init>(ShellTask.groovy:13)
        at com.tkruse.gradle.groovysh.BuildShellTask.<init>(BuildShellTask.groovy:24)
        at com.tkruse.gradle.groovysh.BuildShellTask_Decorated.<init>(Unknown Source)
        at org.gradle.api.internal.DependencyInjectingInstantiator.newInstance(DependencyInjectingInstantiator.java:48)
        at org.gradle.api.internal.ClassGeneratorBackedInstantiator.newInstance(ClassGeneratorBackedInstantiator.java:36)
        at org.gradle.api.internal.project.taskfactory.TaskFactory$1.call(TaskFactory.java:124)

I can rebuild a jar using gradle2, but then that jar will not run with gradle 1.x:

Caused by: java.lang.NoClassDefFoundError: org/codehaus/groovy/runtime/typehandling/ShortTypeHandling
        at com.tkruse.gradle.groovysh.PatchedMainCompileTask.<clinit>(PatchedMainCompileTask.groovy:12)

I subclassed JavaCompile because i needed the plugin to generate a java class and compile it, like this.

class PatchedMainCompileTask extends JavaCompile {
      static final String NAME = 'compileGroovyshPatchedMain'
    static final String PATCH_CLASS_NAME = 'PatchedMain'
    static final String PATCH_CLASS_CAN_NAME = 'org.codehaus.groovy.tools.shell.' + PATCH_CLASS_NAME
    static final String CONFIGURATION_NAME = 'appShellCompileMainConf'
      PatchedMainCompileTask() {
        this.group = 'help'
        this.outputs.upToDateWhen { false }
    }
      @Override
    void executeWithoutThrowingTaskFailure() {
        project.configurations.create(CONFIGURATION_NAME)
        TaskHelper.addGroovyDependencies(project, CONFIGURATION_NAME, project.groovysh.groovyVersion)
        File genFile = TaskHelper.generatePatchedMain(project, PATCH_CLASS_NAME)
        this.destinationDir = new File(project.buildDir, 'groovyshClasses')
        this.source = genFile
        this.classpath = (project.configurations.getByName(CONFIGURATION_NAME).asFileTree
                + project.files(genFile.getParent()))
        super.executeWithoutThrowingTaskFailure()
    }
  }

So, is it possible at all to create custom plugins that are compatible with gradle1.x and gradle 2.x? Is there any easy way for me to get that? Else is there any complex way? Or what is the recommendation for releasing the same plugin in separate versions for gradle 1 and gradle 2?


(Peter Niederwieser) #2

Plugins implemented in Groovy are unlikely to work both with Gradle 1.x and 2.x, as Groovy 2.3 isn’t binary backwards compatible with Groovy 1.8. Instead, you’ll have to publish two plugin versions compiled against the different Groovy/Gradle versions. Plugins implemented in Java don’t have this restriction.


(Thibault Kruse) #3

Hm, okay. I tried just writing that one file in Java already, wbut ran into similar problems, will try that agai though.

Another question; when releasing two versions, is thre already some idea of where to best put the version name? I mean, if the plugin were referred to (in gradle portal) like:

id "com.jfrog.bintray" version "0.4.1"

is it then better to have plugins with two different IDs for each gradle version, but possible same version, or would you rather recommend using the same id but different version numbers? In my opinion it would be good to settle for some recommended naming scheme early, like having an “.gradle1” suffix in the plugin id or something.


(Thibault Kruse) #4

So what put me off the first time was this cryptic message: ‘Class com.tkruse.gradle.groovysh.PatchedMainCompileTask_Decorated has no constructor that accepts parameters [] or that is annotated with @Inject.’ Which should have told me just to make my class PatchedMainCompileTask and it’s Constructor public already (I forgot when those when migrating the class to Java).

Then, by just changing the subclass into a Java file, I can build a jar using gradle1.12(groovy 1.8) which works fine with gradle2 and gradle1, it seems. However, building a jar with gradle2 (groovy 2.3.3), the jar fails to run under gradle 1.12.

Caused by: java.lang.ClassNotFoundException: org.codehaus.groovy.runtime.typehandling.ShortTypeHandling

So I might try for now to just release a single jar that was build using gradle1.12 (groovy 1.8).

Just sumnmarizing my findings here. I was also surprised that other gradle projects I with 3rd party plugins have still ran at all with Groovy 2.0, but since the jars there were probably also compiled with gradle 1.x, they might still run for the same reason mine still runs. I will ask the Groovy guys on groovy-dev for opinions.


(Thibault Kruse) #5

I think it may be wise to recommend to custom Gradle plugin developers to use gradle 1.12 for building the jar (and gradle1 & 2 to test the jar), for the time being, to increase the chances of being both gradle1 and gradle2 compatible. Of course this is no guarantee, but apparently groovy1.8 jars are more forward compatible than groovy2.3 jars are backwards-compatible.


(Peter Niederwieser) #6

If you need to publish new plugin versions that support both Gradle 1.x and 2.x, I’d recommend to either publish separate artifacts or port the plugin to Java.


(Thibault Kruse) #7

This news might be relevant: http://glaforge.appspot.com/article/groovy-2-3-5-out-with-upward-compatibility