Writing a Gradle Plugin / 2

In the previous part of this series we have started writing our own gradle plugin. Let’s go on. As last time, the code can be seen at https://github.com/andreabergia/sample-gradle-plugin/tree/b7aeccc31e6285d2fbe4dab8024d1d1cbbddf698.

Creating a Task class Link to heading

A rather common thing to do when writing a Gradle plugin is to create a class that implements the Task interface. In turn, this allows users of your plugin to create tasks of your class, which will execute a Groovy (or Java) method when the task is executed. An example:

class SayHiTask extends DefaultTask {
    @TaskAction
    def sayHi() {
        println "Hello from our task"
    }
}

class SamplePlugin implements Plugin<Project> {
    void apply(Project project) {
        project.task('sayHi', type: SayHiTask)
    }
}

Now our plugin is instantiating automatically a task, named sayHi, of our class. The method that will get invoked when the task is executed is the annotated with @TaskAction.

Configuring the task Link to heading

Now, let’s say we want to configure our task; for example we want the message that is printed to be configurable. We can simply expose a property in our task, and configure it in the project that uses it:

class SayHiTask extends DefaultTask {
    String message = "Hello from our task"

    @TaskAction
    def sayHi() {
        println message
    }
}

// In our build.gradle file, when using the plugin
sayHi.message = "Ho ho ho"

Extension objects Link to heading

Quite a few common Gradle plugins don’t expose a configuration for the tasks, but rather one for the plugin. For instance, let’s suppose we want to be able to configure our plugin in this rather gradle-idiomatic way:

// In our build.gradle file, when using the plugin
samplePlugin {
    message "Hello"
}

To do this, we need to create what Gradle calls an “extension object” for our plugin, named samplePlugin. The type of this object can be any class; in our case it will be a simple class that will have an instance variable for the String to print and a message method, which can be invoked with a String argument. Let’s see the code:

class SamplePlugin implements Plugin<Project> {
    void apply(Project project) {
        project.extensions.create('samplePlugin', SamplePluginConfig)
        project.task('sayHi', type: SayHiTask)
    }
}

class SamplePluginConfig {
    String message = "Hello from our plugin"

    def message(s) {
        message = s as String
    }
}

class SayHiTask extends DefaultTask {
    @TaskAction
    def sayHi() {
        println project.samplePlugin.message
    }
}

Notice the method SamplePluginConfig.message. Without it, in our build.gradle file, when using the plugin, we would have to write:

samplePlugin {
    message = "Hello"
}

With it, we can write

samplePlugin {
    message "Hello"
}

There isn’t really much of a difference; however, in most gradle plugins I’ve seen around, the second form is far more common, so I’ve shown you how to allow it in our plugin.

Conclusions Link to heading

In the next part, we will discuss some more useful tricks, such as instantiating many copies of a task according to the configuration given in the extension object.