William Carter
William Carter

Reputation: 1305

How to use gradle plugin configuration in the plugin apply?

I'm writing a custom Gradle plugin which requires a certain dependency to be added to the subproject. This dependency requires version information from the user so I decided to create an Extension. However, since my properties are referenced in my plugin's apply method, I can't actually mutate them until after they're referenced. My plugin looks some thing like this:

class MyPluginExtension {
  abstract String scalaLibraryVersion
  abstract String macroParadiseVersion
}

class MyPlugin implements Plugin<Project> {
  void apply(Project project) {
    project.extensions.create("myConfig", PluginExtension)

    project.configure(project) {
      apply plugin: 'scala'
    }

    String pluginConfiguration = 'myPluginConfiguration'
    project.configurations.create(pluginConfiguration)

    String scalaLib = project.myConfig.scalaLibraryVersion
    String paradise = project.myConfig.macroParadiseVersion
    String paradiseDependency = "org.scalamacros:paradise_${scalaLib}:${paradise}"

    project.dependencies.add(pluginConfiguration, paradiseDependency)

    project.tasks.withType(ScalaCompile) {
      scalaCompileOptions.additionalParameters += [
          "-Xplugin:" + project.configurations.getByName(pluginConfiguration).asPath,
          "-Xplugin-require:macroparadise"
      ]
    }
  }
}

And it will be applied like this:

project("incorrect-subproject-1") {
  apply plugin: "carter.my-plugin"
  myConfig.scalaLibraryVersion = "2.11.12" // This is too late. The library version is already set to null
}

project("incorrect-subproject-2") {
  myConfig.scalaLibraryVersion = "2.11.12" // This is too early. myConfig references nothing
  apply plugin: "carter.my-plugin"
}

Is there a better way to do this? This plugin is to be used by other developers so I'm really keen to keep the API as nice as possible. Ideally I'd want something like:

project("incorrect-module-1") {
  apply plugin: "carter.my-plugin" {
    scalaLibraryVersion = "2.11.12"
    macroParadiseVersion = "2.1.1"
  }

  // rest of this subproject's configuration
}


I have tried to add the dependency during one of the lifecycle callbacks as described in this Gradle support thread. However, I have found that the beforeResolve callback happens too early and my extension config isn't used and the afterEvaluate callback happens too late and I get:

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring project ':incorrect-subproject-1'.
> Cannot change dependencies of configuration ':incorrect-subproject-1:myPluginConfiguration' after it has been resolved.

Surely if beforeResolve isn't late enough then this kind of thing is impossible? Is there a way to delay dependency resolution until after the project is "evaluated"?

By the way I am using Gradle version 5.1

Upvotes: 1

Views: 435

Answers (1)

Chris Boucher
Chris Boucher

Reputation: 36

You should be able to achieve this by referencing these settings in project.beforeEvaluate. To give you an example:

void apply(Project project) {
    super.apply(project)

    project.extensions.create("myConfig", Configuration)

    project.beforeEvaluate {
        String scalaLib = project.myConfig.scalaLibraryVersion
        String paradise = project.myConfig.macroParadiseVersion
        String paradiseDependency = "org.scalamacros:paradise_${scalaLib}:${paradise}"

        project.dependencies.add(pluginConfiguration, paradiseDependency)

        project.tasks.withType(ScalaCompile) {
          scalaCompileOptions.additionalParameters += [
            "-Xplugin:" + project.configurations.getByName(pluginConfiguration).asPath,
            "-Xplugin-require:macroparadise"
          ]
        }

    }
}

Upvotes: 2

Related Questions