Extension configuration time vs. Task creation time - parameterizing a custom task's instance with extension properties


(Klaus Baumgartner) #1

Hi,

Writing custom gradle plugins for my company’s project builds I have come across the same issue several times now and never found an answer searching:

I have a plugin that adds stuff to the project: + an extension + an instance of a custom task

The custom task is defined in a dedicated class. It’s built to be reusable. For use in my plugin I want to set some of its properties based on how the extension is configured. Unfortunately the task initialization closure gets executed with applyPlugin(), which is before the extension could be configured by the user.

What is a good (concise, readable) way to parameterize the task with the extension’s properties?

Thanks in advance!


(Klaus Baumgartner) #2

Here’s an example buildscript:

apply plugin: 'tomcat'
  tomcat {
    //...
    startupValidationUrl = "http://localhost:8080/status"
    startupValidationRegex = /(?i).*overall result:.*ok.*/
    startupValidationTimeout = 30
}

(Klaus Baumgartner) #3

Here’s an excerpt from my custom plugin:

void apply(Project project) {
        this.project = project
        project.extensions.create('tomcat', TomcatExtension, project)
        //...
        project.task ([type: HttpStatusTask, dependsOn: 'startTomcat'], 'validateStartup') {
            url = project.extensions.tomcat.startupValidationUrl
            timeout = project.extensions.tomcat.startupValidationTimeout
            validationRegex = project.extensions.tomcat.startupValidationRegex
        }

At the time when the plugin is applied the extension has not yet configured by the buildscript.


(jesper.holmberg) #4

As I understand it, the easiest way to configure this is to use a closure. Change the lines in your plugin to something like this:

url = { project.extensions.tomcat.startupValidationUrl }
 timeout = { project.extensions.tomcat.startupValidationTimeout }
 validationRegex = { project.extensions.tomcat.startupValidationRegex }

Then you rewrite your custom task to receive a closure in it’s setter method and resolve the closure in the execution phase, i.e. the @TaskAction method.


(Peter Niederwieser) #5

It’s an often discussed topic in the forum. For example: http://forums.gradle.org/gradle/topics/custom_task_with_fields_assign_directly_or_via_conventionmapping


(togin) #6

I realize this is an old thread, but do you think you can point to an example of the closure resolution?


(togin) #7

Say I wanted to add a plugin with a custom task that extended Exec:

http://www.gradle.org/docs/current/javadoc/org/gradle/api/tasks/Exec.html

The default @TaskAction for Exec is a private method from what I can tell from the source:

https://github.com/gradle/gradle/blob/master/subprojects/core/src/main/groovy/org/gradle/api/tasks/Exec.java

‘’’ @TaskAction

void exec() {

execResult = execAction.execute();

} ‘’’

Since it is private, creating a new TaskAction and calling it is not possible. The execAction object is private as well, so the method is not able to just be rewritten in the extending class.

I’m not exactly sure how the solution posted above helps me since I can’t put additional code in the TaskAction and still execute the intended code without reimplementing large parts of the Exec class.


(Peter Niederwieser) #8

Most Gradle tasks aren’t designed to be subclassed. Why do you want to subclass ‘Exec’? Do you really want your task to expose all of ‘Exec’'s properties, or do you just want to make your task execute something? In the latter case, you should use ‘Project.exec’.


(togin) #9

I didn’t realize Project.exec existed and that certainly seems like it would be an easier alternative to what I was trying to do. Thanks for the pointer.