Extension initialization vs task configuration

Hi,

I’m working on a plugin with its own DSL extension. When the plugin is applied to the project (Plugin.apply method), the extension is created, and some tasks are registered. Each task is registered with the project.register(<taskName>, <taskClass>, <configurationAction>) method. Each <configurationAction> configures the task based on the extension. Since configuration applies after evaluation, I was expecting the extension to be initialized at this moment.

The problem: depending on the content in the build.gradle file, each <configurationAction> may receive an instance of the extension that is not yet initialized. The value of each property providers in the extension may not reflect the value of the property configured in the build.gradle file. Sometimes, the extension providers are initialized, sometimes they are not.

Q1 - When exactly is an extension initialized based on the content of the build.gradle file (referring to evaluation/configuration/execution phases and project/plugin/tasks lifecycle) ?
Q2 - What can I do to perform additional configuration actions on tasks with an initialized extension, in a reliable way?

Thanks in advance for your help!
BR,
Vincent

This should not matter in a properly designed implementation. The configurationAction that runs should be wiring things up, but it should not be looking for the exact values to be known yet. For example, take the following plugin code:

class MyExtension {
    Property<String> someProperty
}

class MyTask extends DefaultTask {
    Property<String> someProperty

    @TaskAction
    void printProperty() {
        println someProperty.get()
    }
}

class MyPlugin implements Plugin<Project> {
    void apply(Project project) {
        MyExtension extension = project.extensions.create("myExtension", MyExtension)

        project.register("myTask", MyTask) { task ->
            task.someProperty.set(extension.someProperty)
        }
    }
}

The configuration action wires up the extension property to the task property, but it does not cause the value to be evaluated yet. Now let’s use the plugin:

apply plugin: MyPlugin

myTask.get().group = "eager" // configuration action will run here due to myTask.get()

myExtension {
    someProperty = "Hello world!"
}

This will still work (prints Hello world! when running gradle myTask) because even though the configurationAction runs on the second line of code before the extension’s someProperty has been set, the calculation of the value does not occur until execution time in the TaskAction.

What you don’t want to do is force the calculation of the value in the configurationAction. i.e. if the task property was declared a String and not a Property<String>:

project.register("myTask", MyTask) { task ->
    task.someProperty = extension.someProperty.get()
}

This would throw an IllegalStateExeception with the build shown.

1 Like

Hi James,

The configurationAction that runs should be wiring things up, but it should not be looking for the exact values to be known yet.

This is exactly the detail I was missing, and I could not find it neither in the documentation on lazy configuration, nor in the API. I would suggest at least the API mentions what can/shall be done in the configuration action of the TaskContainer.register method.

Thanks a lot for your help!
BR,
Vincent