At what point in the build lifecycle are plugin extensions processed?

Hi all,

I am writing a plugin whereby I want to use the plugin’s extension object to drive the creation of certain tasks. However I’m finding that the extension object doesn’t seem to have been populated from my build file when I try to access it outside a task’s action block.

This doesn’t seem to be covered in the gradle docs writing custom plugins section that I’m following and I’m therefore wondering if I’m approaching this in a non-gradle-idiomatic way? Is there an approach that I can use to make this possible?

Here’s an example which you can copy/paste to a build.gradle file to easily demonstate what I’m seeing:

import groovy.transform.ToString
   apply plugin: ResourcesPlugin
  class ResourcesPlugin implements Plugin<Project> {
  void apply(Project project) {
          // Add the 'resourcesPlugin' extension object to the project
    project.extensions.create("resourcesPlugin", ResourcesPluginExtension)
          // Add a task that uses the configuration
    project.task("hello") {
      description = "Container task which processes all defined resource definitions"
      group = "resourcesPlugin"
              doFirst {
        //Show that we can access the Plugin's extension here:
        project.resourcesPlugin.resourceDefinitions.each {
          //This seems to work as we get the ResourceDefinition printed to the console.
          println it
        }
      }
    }
          // However this same access doesn't seem to work as, at this point, project.resourcesPlugin.resourceDefinitions seems to be null
    // If the Plugin extension only available after the task configuration phase?
    // I want to use it to create tasks dynamically, is there an approach I can use to do this?
    if(project.resourcesPlugin.resourceDefinitions == null) {
      println "project.resourcesPlugin.resourceDefinitions is null :-("
      // Create tasks dynamically based on what the user has specified in the extension's closure
    }
  }
}
  @ToString
class ResourcesPluginExtension {
  def String message = 'Hello from GreetingPlugin'
  ResourceDefinition[] resourceDefinitions
}
  @ToString
class ResourceDefinition {
  String pathRegex
  String action
  boolean filter
}
  resourcesPlugin {
    resourceDefinitions = [[pathRegex: "i1", action: "a1", filter: true],
                                  [pathRegex: "i2", action: "a2", filter: false]]
}

If you run the example above using the command:

gradle hello

You should see the following output:

project.resourcesPlugin.resourceDefinitions is null :-(
:hello
ResourceDefinition(i1, a1, true)
ResourceDefinition(i2, a2, false)

Cheers,

Edd

Hey,

Creating tasks dynamically, based on user’s input from the extension object is not as great as it will be in future :slight_smile: Here’re some options.

  1. For some scenarios, task rules might be better than dynamically created tasks. Essentially, the task rules are one way to have tasks created dynamically. 2. If you want to dynamically create a task based on the details from the extension objects, afterEvaluate {} block is an option. It’s not great but it’ll get you where you want to be. In future we plan to address it much better. 3. Another option is creating all tasks but only some of them will actually be executed, depending on the users’ input. For this pattern, onlyIf {} property on the task is handy.

Hope that helps!

1 Like

Hey Szczepan,

Thanks for getting back to me. I think I may have found a solution which allows me to get the work I need done without spawning tasks after all. I will see how this progresses but will keep your tips in mind should it not work out.

Cheers,

Edd

It would really be nice to get a better mechanism. It’s so hard to understand what happens when new to gradle.