# Writing Plugins

version 9.2.0-20250828011837+0000

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:

build.gradle.kts

    abstract class SamplePlugin : Plugin<Project> { (1)
        override fun apply(project: Project) {  (2)
            project.tasks.register("ScriptPlugin") {
                doLast {
                    println("Hello world from the build file!")
                }
            }
        }
    }

build.gradle

    class SamplePlugin implements Plugin<Project> { (1)
        void apply(Project project) {   (2)
            project.tasks.register("ScriptPlugin") {
                doLast {
                    println("Hello world from the build file!")
                }
            }
        }
    }

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:

build.gradle.kts

    abstract class SamplePlugin : Plugin<Project> {
    }

build.gradle

    class SamplePlugin implements Plugin<Project> {
    }

### 2. Override the `apply` method

Add tasks and other logic in the `apply()` method:

build.gradle.kts

    override fun apply() {

    }

build.gradle

    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:

build.gradle.kts

    apply<SamplePlugin>()

build.gradle

    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:

buildSrc/src/main/kotlin/my-create-file-plugin.gradle.kts

    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")
    }

buildSrc/src/main/groovy/my-create-file-plugin.gradle

    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:

build.gradle.kts

    plugins {
        id("my-create-file-plugin")  // Apply the pre-compiled convention plugin
        `kotlin-dsl`
    }

build.gradle

    plugins {
        id 'my-create-file-plugin' // Apply the pre-compiled convention plugin
        id 'groovy' // Apply the Groovy DSL plugin
    }

The `createMyFileTaskInConventionPlugin` 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.

1. Use composite builds:

   settings.gradle.kts

       includeBuild("my-plugin")

2. Publish the plugin to a repository:

   build.gradle.kts

       plugins {
           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`:

buildSrc/src/main/kotlin/MyCreateFileBinaryPlugin.kt

    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 MyCreateFileTask : DefaultTask() {
        @get:Input
        abstract val fileText: Property<String>

        @Input
        val filePath = project.layout.settingsDirectory.file("myfile.txt").asFile.path

        @OutputFile
        val myFile: File = File(filePath)

        @TaskAction
        fun action() {
            myFile.createNewFile()
            myFile.writeText(fileText.get())
        }
    }

    class MyCreateFileBinaryPlugin : Plugin<Project> {
        override fun apply(project: Project) {
            project.tasks.register("createFileTaskFromBinaryPlugin", MyCreateFileTask::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:

buildSrc/build.gradle.kts

    group = "com.example"
    version = "1.0.0"

    gradlePlugin {
        plugins {
            create("my-binary-plugin") {
                id = "com.example.my-binary-plugin"
                implementationClass = "MyCreateFileBinaryPlugin"
            }
        }
    }

    publishing {
        repositories {
            mavenLocal()
        }
    }

buildSrc/build.gradle

    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:

build.gradle.kts

    plugins {
        id("my-create-file-plugin")  // Apply the pre-compiled convention plugin
        id("com.example.my-binary-plugin") // Apply the binary plugin
        `kotlin-dsl`
    }

build.gradle

    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.