This guide walks you through the process of creating reusable build logic as a Gradle plugin — located within a project’s buildSrc directory — which can then be extracted later into its own project, published, and applied to other Gradle builds.

Plugins provide the conventions, task types and other build logic that allow build script authors to focus on the elements that are unique to their build. For example, the Java Plugin provides a standard directory layout and tasks for performing such work as compiling source, generating Javadocs and running unit tests.

This guide introduces you to a very simple plugin, but it does not represent the limits of what you can do with a plugin. Anything you can do in a Gradle build you can do in a plugin.

What you’ll build

You’ll write a simple plugin that adds a new task type and creates a task of that type in whatever project it’s applied to. You will also prove that the plugin works and see its effect by applying the plugin to its host build.

What you’ll need

Create a project

You’ll need to create a directory for the project and then switch to it:

$ mkdir greeting-plugin
$ cd greeting-plugin

Next, create the following directory structure for the plugin code:

$ mkdir -p buildSrc/src/main/java/org/example/greeting

The buildSrc directory is one way to encapsulate build logic that you want to keep out of the project’s build scripts. It’s useful for custom task types as well as plugins. You can read about more about it in the user manual.

If you want to use a plugin across multiple builds, you will need to publish it. See Next steps for links to information on how to do that. In essence, you promote the plugin into its own project, i.e. not inside another project’s buildSrc directory, and configure its build to upload the packaged plugin to the Gradle Plugin Portal or some other repository.

Create the plugin

Create the class GreetingPlugin in the directory you just created — buildSrc/src/main/java/org/example/greeting — and set its contents to the following:

buildSrc/src/main/java/org/example/greeting/GreetingPlugin.java
package org.example.greeting;

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

public class GreetingPlugin implements Plugin<Project> {
    public void apply(Project project) {
        project.getTasks().create("hello", Greeting.class, (task) -> { (1)
            task.setMessage("Hello");
            task.setRecipient("World");                                (2)
        });
    }
}
1 Creates a new task named hello of type Greeting (which you will define shortly)
2 Sets default values for the new task

This is the entry point for the plugin and the Gradle Project object provides access to the entire Gradle API, which allows you to do the same things as you can do in a build script. In this case, you are creating a simple task called hello in the target project.

Use the Gradle DSL Reference and Javadocs to learn what you can do with the Gradle API. Start with the entry for Project. You can find out more about what you can achieve by also following the links in Next steps.

Next, you’ll create the class for the task type that the plugin is using. Add a new Greeting class in the same package as the plugin:

buildSrc/src/main/java/org/example/greeting/Greeting.java
package org.example.greeting;

import org.gradle.api.DefaultTask;
import org.gradle.api.tasks.TaskAction;

public class Greeting extends DefaultTask {
    private String message;
    private String recipient;

    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }

    public String getRecipient() { return recipient; }
    public void setRecipient(String recipient) { this.recipient = recipient; }

    @TaskAction
    void sayGreeting() {
        System.out.printf("%s, %s!\n", getMessage(), getRecipient()); (1)
    }
}
1 Prints out the configured greeting when the task runs
You can learn more about creating your own task types in the user manual.

You now have a plugin, but a plugin alone doesn’t do anything. You need to apply it to a project for it to be useful, which is what you’ll do next.

Apply the plugin to the host project

Create a build script file in the root of the host project — the greeting-plugin directory — with the following contents:

build.gradle
apply plugin: org.example.greeting.GreetingPlugin (1)
build.gradle.kts
apply<org.example.greeting.GreetingPlugin>() (1)
1 This applies your plugin to the current Project instance, adding the hello task to the build.
The syntax is different from the plugins {} block you may be used to. That is because the plugin source resides in the buildSrc directory and has no identifier (you’ll add one shortly). See the user manual for more information on the different ways to apply a plugin.

You can now verify that your plugin is working by running its hello task in the main build:

$ gradle hello
:buildSrc:compileJava
:buildSrc:compileGroovy UP-TO-DATE
:buildSrc:processResources UP-TO-DATE
:buildSrc:classes
:buildSrc:jar
:buildSrc:assemble
:buildSrc:compileTestJava UP-TO-DATE
:buildSrc:compileTestGroovy UP-TO-DATE
:buildSrc:processTestResources UP-TO-DATE
:buildSrc:testClasses UP-TO-DATE
:buildSrc:test UP-TO-DATE
:buildSrc:check UP-TO-DATE
:buildSrc:build
:hello
Hello, World!

The bulk of the output reflects that the files in buildSrc are treated as a Java project, which needs to be built first. Once that happens, the classes inside that project become available in your main build and the main build can execute the task or tasks that you specified.

Your build is currently using the default property values for the greeting, hence why it prints out "Hello, World!". This doesn’t have to be the case as you can configure the task directly in the build script:

build.gradle
hello { (1)
    message = "Hi"
    recipient = "Gradle"
}
build.gradle.kts
import org.example.greeting.Greeting

tasks.getByName<Greeting>("hello") { (1)
    message = "Hi"
    recipient = "Gradle"
}
1 Configures multiple properties of the task named hello
You can learn more about the syntax for configuring tasks in the user manual.

Now when you run the hello task — using -q to hide the buildSrc output this time — you’ll see the following:

$ gradle -q hello
Hi, Gradle!

Your plugin is now functionally complete and you’ve seen it in action in the above build. There is just one more thing we want to show you, which helps make the build script a bit tidier and also helps when it comes to publishing your plugin: adding a plugin identifier.

Declare a plugin identifier

In most cases, you apply plugins using an ID because they are easier to remember than fully-qualified class names. They also result in tidier build files. So it makes sense to ensure that your own plugin can also be applied in the same way, which is why you will now declare an identifier for the plugin.

Create the following properties file:

buildSrc/src/main/resources/META-INF/gradle-plugins/org.example.greeting.properties
implementation-class=org.example.greeting.GreetingPlugin

Gradle uses this file to determine which class implements the Plugin interface. The name of this properties file excluding the .properties extension becomes the identifier of the plugin.

You must put the properties file in the directory META-INF/gradle-plugins as Gradle will try to resolve the file from that specific location in the plugin JAR.

That’s all you need to do in your plugin, so now you can replace the following line of the build script:

build.gradle
apply plugin: org.example.greeting.GreetingPlugin
build.gradle.kts
apply<org.example.greeting.GreetingPlugin>()

with one that uses the plugin ID:

build.gradle
plugins {
  id 'org.example.greeting'
}
build.gradle.kts
plugins {
  id("org.example.greeting")
}

Note how the name of the properties file — org.example.greeting.properties — matches the plugin ID. That is required.

Always qualify the plugin name with a namespace that is unique to you instead of the "org.example" used in this guide. Doing so helps avoid name clashes between plugins. You can find more details about plugin IDs in the user manual.

Summary

You’re now done! You have successfully created a plugin and used it within a build. Along the way, you’ve learned how to:

  • Put build logic into a plugin

  • Use the buildSrc directory for a plugin’s classes

  • Give the plugin an ID and apply it in a build script

This guide focuses on the essence of what a plugin is, but most plugins are far more substantial in the features that they provide. The next section will guide you towards learning more about what plugins can do and how you should implement them.

Help improve this guide

Have feedback or a question? Found a typo? Like all Gradle guides, help is just a GitHub issue away. Please add an issue or pull request to gradle-guides/writing-gradle-plugins and we’ll get back to you.