Learn about extensions and how to add them to your plugin to make it configurable.

In this section, you’ll:

  • Understand what plugin extensions are and why they are useful.

  • Add an extension class to your plugin with configurable properties.

  • Register the extension to make it available to users in their build scripts.

Step 0: Before you Begin

  1. You initialized your plugin in part 1.

Step 1: Understanding Plugin Extensions

A plugin extension is a powerful feature that allows users to configure your plugin using a declarative DSL block in their build.gradle(.kts) file. This keeps plugin configuration clean and easy to read.

For example, a user could configure your plugin like this:

slack {
    token = "..."
    channel = "#builds"
    message = "Build completed!"
}

Behind the scenes, this slack {} block maps directly to a Kotlin or Groovy class with properties. Gradle handles the magic of injecting the values into your plugin’s logic.

Step 2: Rename the Plugin Class

First, let’s rename our plugin class from the generic "Hello World" example.

Rename the file plugin/src/main/kotlin/org/example/PluginTutorialPlugin.kt to plugin/src/main/kotlin/org/example/SlackPlugin.kt.

Rename the file plugin/src/main/groovy/org/example/PluginTutorialPlugin.groovy to plugin/src/main/groovy/org/example/SlackPlugin.groovy.

Most modern IDEs, like IntelliJ IDEA, can automatically handle both the file and class name updates for you.

Next, we’ll update the class name. The abstract keyword is important here because it allows Gradle to use its dependency injection framework to provide the plugin with services it needs, like the ObjectFactory.

Change class PluginTutorialPlugin: Plugin<Project> {} to abstract class SlackPlugin: Plugin<Project> {}.

Change class PluginTutorialPlugin implements Plugin<Project> {} to abstract class SlackPlugin implements Plugin<Project> {}.

plugin/src/main/kotlin/org/example/SlackPlugin.kt
abstract class SlackPlugin: Plugin<Project> {
    override fun apply(project: Project) {
        // Register a task
        project.tasks.register("greeting") { task ->
            task.doLast {
                println("Hello from plugin 'org.example.greeting'")
            }
        }
    }
}
plugin/src/main/groovy/org/example/SlackPlugin.groovy
abstract class SlackPlugin implements Plugin<Project> {
    void apply(Project project) {
        // Register a task
        project.tasks.register("greeting") {
            doLast {
                println("Hello from plugin 'org.example.greeting'")
            }
        }
    }
}

You’ll also need to update the build.gradle(.kts) file to point to the new plugin class name:

plugin/build.gradle.kts
gradlePlugin {
    // Define the plugin
    plugins {
        create("slack") {
            id = "org.example.slack"
            implementationClass = "org.example.SlackPlugin"
        }
    }
}
plugin/build.gradle
gradlePlugin {
    // Define the plugin
    plugins {
        slack {
            id = 'org.example.slack'
            implementationClass = 'org.example.SlackPlugin'
        }
    }
}

Step 3: Define the Extension Class

Now, let’s define the class that holds the configurable properties for our plugin. We’ll use Gradle’s Property<T> type for these properties, as this makes them configurable lazily and fully compatible with the Configuration Cache.

Declare the extension as an abstract class with abstract property getters. These are managed properties: Gradle generates the implementation at runtime through class decoration and creates the Property<T> instances for you, so there is no need to write a constructor or to inject an ObjectFactory manually.

An extension that carries no custom logic can also be declared as an interface with abstract getters. Gradle will fully instantiate it for you in the same way.

Create a new file called plugin/src/main/kotlin/org/example/SlackExtension.kt and add the following code:

plugin/src/main/kotlin/org/example/SlackExtension.kt
package org.example

import org.gradle.api.provider.Property

/**
 * The SlackExtension class defines a custom extension for the plugin.
 * This allows users to configure the plugin in their build script via a DSL block, e.g.:
 *
 * slack {
 *     token = "..."
 *     channel = "#general"
 *     message = "Hello from Gradle!"
 * }
 *
 * The `abstract val` declarations are Gradle managed properties: Gradle
 * generates the implementation at runtime through class decoration and
 * creates the `Property<T>` instances automatically. There is no need to
 * declare a constructor or to inject an `ObjectFactory` manually.
 */
abstract class SlackExtension {

    // The Slack API token used to authenticate requests
    abstract val token: Property<String>

    // The name or ID of the Slack channel to send the message to
    abstract val channel: Property<String>

    // The message content to send to the channel
    abstract val message: Property<String>
}
plugin/src/main/groovy/org/example/SlackExtension.groovy
package org.example

import org.gradle.api.provider.Property

/**
 * The SlackExtension class defines a custom extension for the plugin.
 * This allows users to configure the plugin in their build script via a DSL block, e.g.:
 *
 * slack {
 *     token = "..."
 *     channel = "#general"
 *     message = "Hello from Gradle!"
 * }
 *
 * The abstract getters are Gradle managed properties: Gradle generates the
 * implementation at runtime through class decoration and creates the
 * `Property<T>` instances automatically. There is no need to declare a
 * constructor or to inject an `ObjectFactory` manually.
 */
abstract class SlackExtension {

    // The Slack API token used to authenticate requests
    abstract Property<String> getToken()

    // The name or ID of the Slack channel to send the message to
    abstract Property<String> getChannel()

    // The message content to send to the channel
    abstract Property<String> getMessage()
}

This class will provide the configuration for the slack DSL block we saw earlier.

Step 4: Register the Extension in the Plugin

The final step is to register the SlackExtension in our SlackPlugin class. This tells Gradle to create the extension and make it available in the build script.

Let’s update the apply method to register our new extension and modify the dummy task to use its properties.

plugin/src/main/kotlin/org/example/SlackPlugin.kt
abstract class SlackPlugin: Plugin<Project> {
    override fun apply(project: Project) {
        // Create the 'slack' extension so users can configure token, channel, and message
        val extension = project.extensions.create("slack", SlackExtension::class.java)

        // Register a task that uses the values from the extension
        project.tasks.register("sendTestSlackMessage") {
            // Use `doLast` to define the action that runs when the task is executed.
            it.doLast {
                println("${extension.message.get()} to ${extension.channel.get()}")
            }
        }
    }
}
plugin/src/main/groovy/org/example/SlackPlugin.groovy
abstract class SlackPlugin implements Plugin<Project> {
    void apply(Project project) {
        // Create the 'slack' extension so users can configure the token, channel, and message.
        def extension = project.extensions.create('slack', SlackExtension)

        // Register a task named 'sendTestSlackMessage'.
        project.tasks.register('sendTestSlackMessage') { task ->
            // Use `doLast` to define the action that runs when the task is executed.
            task.doLast {
                println "${extension.message.get()} to ${extension.channel.get()}"
            }
        }
    }
}

After this step, a user can configure your plugin, and the sendTestSlackMessage task will use those values when it runs.

Next Step: Create a Custom Task >>