gradlebuild.gradlegradle-plugingradle-kotlin-dsl

How can I dynamically change a Gradle plugin's version depending on certain criteria?


We have a multi platform project, however each platform has its own platform-dependent implementation of some classes. In our case, 2 of these target platforms require the same plugin, however they require different versions of that plugin.

So far, I have tried to do this by setting pluginVersion via either -PbuildingForB or gradle.taskGraph.whenReady, then setting a classpath in the dependencies block of the buildscript block, and finally applying the plugin after the plugins block. I have also tried to use extra instead of var/val. Unfortunately, this errors with Unresolved reference: pluginVersion and Cannot get property 'pluginVersion' on extra properties extension as it does not exist, respectfully.

val versionA = "1.0"
val versionB = "2.0"
var pluginVersion = versionA

pluginVersion = project.findProperty("buildingForB")?.let {
  if (it == "true") versionB else versionA
} ?: versionA

gradle.taskGraph.whenReady {
  if (tasks.contains(task("buildPlatformB")))
    pluginVersion = versionB
}

buildscript {
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath("com.example:example-plugin:${pluginVersion]}")
  }
}

plugins {
  ...
}

apply(plugin = "com.example")

errors with Unresolved reference: pluginVersion


Solution

  • You can choose the version entirely within the buildscript block, eg:

    buildscript {
        val pluginVersion = findProperty("buildingForB")?.let { "2.0" } ?: "1.0"
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath("com.example:example-plugin:$pluginVersion")
        }
    }
    
    // Using the *id* of the plugin, which is different to the group and artifact name above
    apply(plugin = "com.example")
    

    This must be done because the buildscript block is compiled separately before the rest of the script.

    Discussion

    As discussed in the comments, this approach will not give the elegant Kotlin accessors associated with the plugin, as these are only generated when plugins are added in the plugins block1; and the plugins block can only use hard-coded versions2.

    Full functionality is however still available without the accessors but not with full type safety. This is the same approach that a plugin author would have to take when writing a binary plugin.

    Also, taking a step back, this approach is not following Gradle best practice. The recommended approach would be to move to use a multi-project build, where each module used a different versions of the plugin and built a single target.


    1https://docs.gradle.org/current/userguide/kotlin_dsl.html#type-safe-accessors 2https://docs.gradle.org/current/userguide/plugins.html#plugins_dsl_limitations