If Gradle or the Gradle community does not offer the specific capabilities your project needs, creating your own custom plugin could be a solution.
Additionally, if you find yourself duplicating build logic across subprojects and need a better way to organize it, convention plugins can help.
Script plugin
A plugin is any class that implements the Plugin
interface.
For example, this is a "hello world" plugin:
abstract class SamplePlugin : Plugin<Project> { (1)
override fun apply(project: Project) { (2)
project.tasks.register("ScriptPlugin") {
doLast {
println("Hello world from the build file!")
}
}
}
}
apply<SamplePlugin>() (3)
class SamplePlugin implements Plugin<Project> { (1)
void apply(Project project) { (2)
project.tasks.register("ScriptPlugin") {
doLast {
println("Hello world from the build file!")
}
}
}
}
apply plugin: SamplePlugin (3)
1 | Extend the org.gradle.api.Plugin interface. |
2 | Override the apply method. |
3 | apply the plugin to the project. |
1. Extend the org.gradle.api.Plugin
interface
Create a class that extends the Plugin
interface:
abstract class SamplePlugin : Plugin<Project> {
}
class SamplePlugin implements Plugin<Project> {
}
2. Override the apply
method
Add tasks and other logic in the apply()
method:
override fun apply() {
}
void apply(Project project) {
}
3. apply
the plugin to your project
When SamplePlugin
is applied in your project, Gradle calls the fun apply() {}
method defined.
This adds the ScriptPlugin
task to your project:
apply<SamplePlugin>()
apply plugin: SamplePlugin
Note that this is a simple hello-world
example and does not reflect best practices.
Script plugins are not recommended. |
The best practice for developing plugins is to create convention plugins or binary plugins.
Pre-compiled script plugin
Pre-compiled script plugins offer an easy way to rapidly prototype and experiment.
They let you package build logic as *.gradle(.kts)
script files using the Groovy or Kotlin DSL.
These scripts reside in specific directories, such as src/main/groovy
or src/main/kotlin
.
To apply one, simply use its ID
derived from the script filename (without .gradle
).
You can think of the file itself as the plugin, so you do not need to subclass the Plugin
interface in a precompiled script.
Let’s take a look at an example with the following structure:
.
└── buildSrc
├── build.gradle.kts
└── src
└── main
└── kotlin
└── my-create-file-plugin.gradle.kts
Our my-create-file-plugin.gradle.kts
file contains the following code:
abstract class CreateFileTask : DefaultTask() {
@get:Input
abstract val fileText: Property<String>
@Input
val fileName = "myfile.txt"
@OutputFile
val myFile: File = File(fileName)
@TaskAction
fun action() {
myFile.createNewFile()
myFile.writeText(fileText.get())
}
}
tasks.register<CreateFileTask>("createMyFileTaskInConventionPlugin") {
group = "from my convention plugin"
description = "Create myfile.txt in the current directory"
fileText.set("HELLO FROM MY CONVENTION PLUGIN")
}
abstract class CreateFileTask extends DefaultTask {
@Input
abstract Property<String> getFileText()
@Input
String fileName = "myfile.txt"
@OutputFile
File getMyFile() {
return new File(fileName)
}
@TaskAction
void action() {
myFile.createNewFile()
myFile.writeText(fileText.get())
}
}
tasks.register("createMyFileTaskInConventionPlugin", CreateFileTask) {
group = "from my convention plugin"
description = "Create myfile.txt in the current directory"
fileText.set("HELLO FROM MY CONVENTION PLUGIN")
}
The pre-compiled script can now be applied in the build.gradle(.kts
) file of any subproject:
plugins {
id("my-create-file-plugin") // Apply the pre-compiled convention plugin
`kotlin-dsl`
}
plugins {
id 'my-create-file-plugin' // Apply the pre-compiled convention plugin
id 'groovy' // Apply the Groovy DSL plugin
}
The createFileTask
task from the plugin is now available in your subproject.
Binary Plugins
A binary plugin is a plugin that is implemented in a compiled language and is packaged as a JAR file. It is resolved as a dependency rather than compiled from source.
For most use cases, convention plugins must be updated infrequently. Having each developer execute the plugin build as part of their development process is wasteful, and we can instead distribute them as binary dependencies.
There are two ways to update the convention plugin in the example above into a binary plugin.
-
Use composite builds:
settings.gradle.ktsincludeBuild("my-plugin")
-
Publish the plugin to a repository:
build.gradle.ktsplugins { id("com.gradle.plugin.my-plugin") version "1.0.0" }
Let’s go with the second solution.
This plugin has been re-written in Kotlin and is called MyCreateFileBinaryPlugin.kt
.
It is still stored in buildSrc
:
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import java.io.File
abstract class CreateFileTask : DefaultTask() {
@get:Input
abstract val fileText: Property<String>
@Input
val fileName = project.rootDir.toString() + "/myfile.txt"
@OutputFile
val myFile: File = File(fileName)
@TaskAction
fun action() {
myFile.createNewFile()
myFile.writeText(fileText.get())
}
}
class MyCreateFileBinaryPlugin : Plugin<Project> {
override fun apply(project: Project) {
project.tasks.register("createFileTaskFromBinaryPlugin", CreateFileTask::class.java) {
group = "from my binary plugin"
description = "Create myfile.txt in the current directory"
fileText.set("HELLO FROM MY BINARY PLUGIN")
}
}
}
The plugin can be published and given an id
using a gradlePlugin{}
block so that it can be referenced in the root:
group = "com.example"
version = "1.0.0"
gradlePlugin {
plugins {
create("my-binary-plugin") {
id = "com.example.my-binary-plugin"
implementationClass = "MyCreateFileBinaryPlugin"
}
}
}
publishing {
repositories {
mavenLocal()
}
}
group = 'com.example'
version = '1.0.0'
gradlePlugin {
plugins {
create("my-binary-plugin") {
id = "com.example.my-binary-plugin"
implementationClass = "MyCreateFileBinaryPlugin"
}
}
}
publishing {
repositories {
mavenLocal()
}
}
Then, the plugin can be applied in the build file:
plugins {
id("my-create-file-plugin") // Apply the pre-compiled convention plugin
id("com.example.my-binary-plugin") // Apply the binary plugin
`kotlin-dsl`
}
plugins {
id 'my-create-file-plugin' // Apply the pre-compiled convention plugin
id 'com.example.my-binary-plugin' // Apply the binary plugin
id 'groovy' // Apply the Groovy DSL plugin
}
Consult the Developing Plugins chapter to learn more.