Part 2: Add an Extension
Learn about extensions and how to add them to your plugin to make it configurable.
Step 0: Before you Begin
-
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> {}`.
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'")
}
}
}
}
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:
gradlePlugin {
// Define the plugin
plugins {
create("slack") {
id = "org.example.slack"
implementationClass = "org.example.SlackPlugin"
}
}
}
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:
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)
}
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.
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()}")
}
}
}
}
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 >>