# Writing Plugins

version 8.9

If Gradle or the Gradle community does not offer the specific capabilities your project needs, creating your own plugin could be a solution.

Additionally, if you find yourself duplicating build logic across subprojects and need a better way to organize it, custom plugins can help.

## Custom plugin

A plugin is any class that implements the `Plugin` interface. The example below is the most straightforward plugin, a "hello world" plugin:

build.gradle.kts

    import org.gradle.api.Plugin
    import org.gradle.api.Project

    abstract class SamplePlugin : Plugin<Project> {
        override fun apply(project: Project) {
            project.tasks.create("SampleTask") {
                println("Hello world!")
            }
        }
    }

## Script plugin

Many plugins start as a script plugin coded in a build script. This offers an easy way to rapidly prototype and experiment when building a plugin. Let’s take a look at an example:

build.gradle.kts

    // Define a task
    abstract class CreateFileTask : DefaultTask() {                                     (1)
        @get:Input
        abstract val fileText: Property<String>                                         (2)

        @Input
        val fileName = "myfile.txt"

        @OutputFile
        val myFile: File = File(fileName)

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

    // Define a plugin
    abstract class MyPlugin : Plugin<Project> {                                         (3)
        override fun apply(project: Project) {
            tasks {
                register("createFileTask", CreateFileTask::class) {
                    group = "from my plugin"
                    description = "Create myfile.txt in the current directory"
                    fileText.set("HELLO FROM MY PLUGIN")
                }
            }
        }
    }

    // Apply the local plugin
    apply<MyPlugin>()                                                                   (4)

1 | Subclass `DefaultTask()`.
2 | Use lazy configuration in the task.
3 | Extend the `org.gradle.api.Plugin` interface.
4 | Apply the script plugin.

### 1. Subclass `DefaultTask()`

First, build a task by subclassing `DefaultTask()`.

    abstract class CreateFileTask : DefaultTask() { }

This simple task adds a file to our application’s root directory.

### 2. Use Lazy Configuration

Gradle has a concept called lazy configuration, which allows task inputs and outputs to be referenced before they are actually set. This is done via the `Property` class type.

    abstract val fileText: Property<String>

One advantage of this mechanism is that you can link the output file of one task to the input file of another, all before the filename has even been decided. The `Property` class also knows which task it’s linked to, enabling Gradle to add the required task dependency automatically.

### 3. Extend the `org.gradle.api.Plugin` interface

Next, create a new class that extends the `org.gradle.api.Plugin` interface.

    abstract class MyPlugin : Plugin<Project> {
        override fun apply() {}
    }

You can add tasks and other logic in the `apply()` method.

### 4. Apply the script plugin

Finally, apply the local plugin in the build script.

    apply<MyPlugin>()

When `MyPlugin` is applied in the build script, Gradle calls the `fun apply() {}` method defined in the custom `MyPlugin` class.

This makes the plugin available to the application.

| Script plugins are NOT recommended. Script plugins offer an easy way to rapidly prototype build logic, before migrating it to a more permanent solution such as convention plugins or binary plugins.

## Convention Plugins

Convention plugins are a way to encapsulate and reuse common build logic in Gradle. They allow you to define a set of conventions for a project, and then apply those conventions to other projects or modules.

The example above has been re-written as a convention plugin stored in `buildSrc`:

buildSrc/src/main/kotlin/MyConventionPlugin.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 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 MyConventionPlugin : Plugin<Project> {
        override fun apply(project: Project) {
            project.tasks.register("createFileTask", CreateFileTask::class.java) {
                group = "from my plugin"
                description = "Create myfile.txt in the current directory"
                fileText.set("HELLO FROM MY PLUGIN")
            }
        }
    }

The plugin can be given an `id` using a `gradlePlugin{}` block so that it can be referenced in the root:

buildSrc/build.gradle.kts

    gradlePlugin {
        plugins {
            create("my-convention-plugin") {
                id = "com.gradle.plugin.my-convention-plugin"
                implementationClass = "com.gradle.plugin.MyConventionPlugin"
            }
        }
    }

The `gradlePlugin{}` block defines the plugins being built by the project. With the newly created `id`, the plugin can be applied in other build scripts accordingly:

build.gradle.kts

    plugins {
        application
        id("com.gradle.plugin.my-convention-plugin") // Apply the new plugin
    }

## 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.myconventionplugin") version "1.0.0"
       }

Consult the Developing Plugins chapter to learn more.