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.

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.model.ObjectFactory
import org.gradle.api.provider.Property
import javax.inject.Inject

/**
 * 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!"
 * }
 */
abstract class SlackExtension @Inject constructor(objects: ObjectFactory) {

    // The Slack API token used to authenticate requests
    val token: Property<String> = objects.property(String::class.java)

    // The name or ID of the Slack channel to send the message to
    val channel: Property<String> = objects.property(String::class.java)

    // The message content to send to the channel
    val message: Property<String> = objects.property(String::class.java)
}
plugin/src/main/groovy/org/example/SlackExtension.groovy
package org.example

import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import javax.inject.Inject

/**
 * 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!"
 * }
 */
abstract class SlackExtension {

    // The Slack API token used to authenticate requests
    final Property<String> token

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

    // The message content to send to the channel
    final Property<String> message

    @Inject
    SlackExtension(ObjectFactory objects) {
        this.token = objects.property(String)
        this.channel = objects.property(String)
        this.message = objects.property(String)
    }
}

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 >>