Many Gradle features, like the ability to compile Java code, are added by plugins.

Plugins add new tasks (e.g., JavaCompile), domain objects (e.g., SourceSet), conventions (e.g., Java source is located at src/main/java), and extend core objects or objects from other plugins.

Applying a plugin to a project allows the plugin to extend the project’s and Gradle’s capabilities.

Plugins can:

  • Extend the Gradle model (e.g., add new DSL elements that can be configured).

  • Configure the project according to conventions (e.g., add new tasks or configure sensible defaults).

  • Apply specific configuration (e.g., add organizational repositories or enforce standards).

There are many advantages to applying plugins over adding logic to the project build script:

  • Promotes reuse and reduces the overhead of maintaining similar logic across multiple projects.

  • Allows a higher degree of modularization, enhancing comprehensibility and organization.

  • Encapsulates imperative logic and allows build scripts to be as declarative as possible.

Plugin distribution

Plugins are available in three ways:

  1. Core plugins - Gradle develops and maintains a set of Core Plugins.

  2. Community plugins - Gradle plugins shared in a remote repository such as Maven or the Gradle Plugin Portal.

  3. Local plugins - Gradle enables users to create custom plugins using APIs.

Types of plugins

There are two general types of plugins in Gradle: binary plugins and script plugins.

Binary plugins are written either programmatically by implementing the Plugin interface or through declarations in either Groovy or Kotlin DSL. They can reside within a build script, the project hierarchy, or externally in a plugin jar.

Script plugins are additional build scripts that further configure the build and usually implement a declarative approach to manipulating the build. They are typically used within a build but can be externalized and accessed remotely.

A plugin often starts as a script plugin (because they are easy to write). Then, as the code becomes more valuable, it’s migrated to a binary plugin that can be easily tested and shared between multiple projects or organizations.

Using plugins

To use the build logic encapsulated in a plugin, Gradle needs to perform two steps. First, it needs to resolve the plugin, and then it needs to apply the plugin to the target, usually a Project.

Resolving a plugin means finding the correct version of the jar that contains a given plugin and adding it to the script classpath. Once a plugin is resolved, its API can be used in a build script. Script plugins are self-resolving in that they are resolved from the specific file path or URL provided when applying them. Core binary plugins provided as part of the Gradle distribution are automatically resolved.

Applying a plugin means executing the plugin’s Plugin.apply(T) on the Project you want to enhance with the plugin.

The plugins DSL is recommended to resolve and apply plugins in one step.

Resolving plugins

Gradle provides the core plugins (e.g., JavaPlugin, GroovyPlugin, MavenPublishPlugin, etc.) as part of its distribution, which means they are automatically resolved.

plugins {
  id("java")
}

However, non-core plugins must be resolved before they can be applied. This can be achieved in several ways:

# Application Recommended Usage Where

1

Using the plugins block

Applying community plugins or local plugins in buildSrc to a specific project.

Build script or Settings file

plugins {
  id("org.barfuin.gradle.taskinfo") version "2.1.0"
}

2

Using the buildSrc directory

Applying community or local plugins to multiple subprojects.

Build script

plugins {
    id("org.barfuin.gradle.taskinfo") version "2.1.0"
}
repositories {
    jcenter()
}
dependencies {
    implementation(Libs.Kotlin.coroutines)
}

3

Using the buildscript block

Applying community plugins to be used specifically in the build script or the build logic.

Build script

buildscript {
  repositories {
    maven {
      url = uri("https://plugins.gradle.org/m2/")
    }
  }
  dependencies {
    classpath("org.barfuin.gradle.taskinfo:gradle-taskinfo:2.1.0")
  }
}
plugins {
  id("org.barfuin.gradle.taskinfo") version "2.1.0"
}

4

Using the legacy apply() method

Applying local script plugins.

Build script

apply(plugin = "org.barfuin.gradle.taskinfo")
apply<MyPlugin>()

1. Applying plugins using the plugins{} block

The plugin DSL provides a concise and convenient way to declare plugin dependencies.

The plugins block configures an instance of PluginDependenciesSpec:

plugins {
    application                                     // by name
    java                                            // by name
    id("java")                                      // by id - recommended
    id("org.jetbrains.kotlin.jvm") version "1.9.0"  // by id - recommended
}

Core Gradle plugins are unique in that they provide short names, such as java for the core JavaPlugin.

To apply a core plugin, the short name can be used:

build.gradle.kts
plugins {
    java
}
build.gradle
plugins {
    id 'java'
}

All other binary plugins must use the fully qualified form of the plugin id (e.g., com.github.foo.bar).

To apply a community plugin from Gradle plugin portal, the fully qualified plugin id, a globally unique identifier, must be used:

build.gradle.kts
plugins {
    id("com.jfrog.bintray") version "1.8.5"
}
build.gradle
plugins {
    id 'com.jfrog.bintray' version '1.8.5'
}

See PluginDependenciesSpec for more information on using the Plugin DSL.

Limitations of the plugins DSL

The plugins DSL provides a convenient syntax for users and the ability for Gradle to determine which plugins are used quickly. This allows Gradle to:

  • Optimize the loading and reuse of plugin classes.

  • Provide editors with detailed information about the potential properties and values in the build script.

However, the DSL requires that plugins be defined statically.

There are some key differences between the plugins {} block mechanism and the "traditional" apply() method mechanism. There are also some constraints and possible limitations.

Constrained Syntax

The plugins {} block does not support arbitrary code.

It is constrained to be idempotent (produce the same result every time) and side effect-free (safe for Gradle to execute at any time).

The form is:

build.gradle.kts
plugins {
    id(«plugin id»)                             (1)
    id(«plugin id») version «plugin version»    (2)
}
1 for core Gradle plugins or plugins already available to the build script
2 for binary Gradle plugins that need to be resolved
build.gradle
plugins {
    id «plugin id»                            (1)
    id «plugin id» version «plugin version»   (2)
}
1 for core Gradle plugins or plugins already available to the build script
2 for binary Gradle plugins that need to be resolved

Where «plugin id» and «plugin version» are a string.

Where «plugin id» and «plugin version» must be constant, literal strings.

The plugins{} block must also be a top-level statement in the build script. It cannot be nested inside another construct (e.g., an if-statement or for-loop).

Only in build scripts and settings file

The plugins{} block can only be used in a project’s build script build.gradle(.kts) and the settings.gradle(.kts) file. It must appear before any other block. It cannot be used in script plugins or init scripts.

Applying plugins to all subprojects

Suppose you have a multi-project build, you probably want to apply plugins to some or all of the subprojects in your build but not to the root project.

While the default behavior of the plugins{} block is to immediately resolve and apply the plugins, you can use the apply false syntax to tell Gradle not to apply the plugin to the current project. Then, use the plugins{} block without the version in subprojects' build scripts:

settings.gradle.kts
include("hello-a")
include("hello-b")
include("goodbye-c")
build.gradle.kts
plugins {
    id("com.example.hello") version "1.0.0" apply false
    id("com.example.goodbye") version "1.0.0" apply false
}
hello-a/build.gradle.kts
plugins {
    id("com.example.hello")
}
hello-b/build.gradle.kts
plugins {
    id("com.example.hello")
}
goodbye-c/build.gradle.kts
plugins {
    id("com.example.goodbye")
}
settings.gradle
include 'hello-a'
include 'hello-b'
include 'goodbye-c'
build.gradle
plugins {
    id 'com.example.hello' version '1.0.0' apply false
    id 'com.example.goodbye' version '1.0.0' apply false
}
hello-a/build.gradle
plugins {
    id 'com.example.hello'
}
hello-b/build.gradle
plugins {
    id 'com.example.hello'
}
goodbye-c/build.gradle
plugins {
    id 'com.example.goodbye'
}

You can also encapsulate the versions of external plugins by composing the build logic using your own convention plugins.

2. Applying plugins from the buildSrc directory

buildSrc is an optional directory at the Gradle project root that contains build logic (i.e., plugins) used in building the main project. You can apply plugins that reside in a project’s buildSrc directory as long as they have a defined id.

The following example shows how to tie the plugin implementation class my.MyPlugin, defined in buildSrc, to the id "my-plugin":

buildSrc/build.gradle.kts
plugins {
    `java-gradle-plugin`
}

gradlePlugin {
    plugins {
        create("myPlugins") {
            id = "my-plugin"
            implementationClass = "my.MyPlugin"
        }
    }
}
buildSrc/build.gradle
plugins {
    id 'java-gradle-plugin'
}

gradlePlugin {
    plugins {
        myPlugins {
            id = 'my-plugin'
            implementationClass = 'my.MyPlugin'
        }
    }
}

The plugin can then be applied by id:

build.gradle.kts
plugins {
    id("my-plugin")
}
build.gradle
plugins {
    id 'my-plugin'
}

3. Applying plugins using the buildscript{} block

The buildscript block is used for:

  1. global dependencies and repositories required for building the project (applied in the subprojects).

  2. declaring which plugins are available for use in the build script (in the build.gradle(.kts) file itself).

So when you want to use a library in the build script itself, you must add this library on the script classpath using buildScript:

import org.apache.commons.codec.binary.Base64

buildscript {
    repositories {  // this is where the plugins are located
        mavenCentral()
        google()
    }
    dependencies { // these are the plugins that can be used in subprojects or in the build file itself
        classpath group: 'commons-codec', name: 'commons-codec', version: '1.2' // used in the task below
        classpath 'com.android.tools.build:gradle:4.1.0' // used in subproject
    }
}

tasks.register('encode') {
    doLast {
        def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
        println new String(encodedString)
    }
}

And you can apply the globally declared dependencies in the subproject that needs it:

plugins {
    id 'com.android.application'
}

Binary plugins published as external jar files can be added to a project by adding the plugin to the build script classpath and then applying the plugin.

External jars can be added to the build script classpath using the buildscript{} block as described in External dependencies for the build script:

build.gradle.kts
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5")
    }
}

apply(plugin = "com.jfrog.bintray")
build.gradle
buildscript {
    repositories {
        gradlePluginPortal()
    }
    dependencies {
        classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.5'
    }
}

apply plugin: 'com.jfrog.bintray'

4. Applying script plugins using the legacy apply() method

A script plugin is an ad-hoc plugin, typically written and applied in the same build script. It is applied using the legacy application method:

class MyPlugin : Plugin<Project> {
    override fun apply(project: Project) {
        println("Plugin ${this.javaClass.simpleName} applied on ${project.name}")
    }
}

apply<MyPlugin>()

Let’s take a rudimentary example of a plugin written in a file called other.gradle located in the same directory as the build.gradle file:

public class Other implements Plugin<Project> {
    @Override
    void apply(Project project) {
        // Does something
    }
}

First, import the external file using:

apply from: 'other.gradle'

Then you can apply it:

apply plugin: Other

Script plugins are automatically resolved and can be applied from a script on the local filesystem or remotely:

build.gradle.kts
apply(from = "other.gradle.kts")
build.gradle
apply from: 'other.gradle'

Filesystem locations are relative to the project directory, while remote script locations are specified with an HTTP URL. Multiple script plugins (of either form) can be applied to a given target.

Plugin Management

The pluginManagement{} block may only appear in the settings.gradle(.kts) file, where it must be the first block in the file or in an Initialization Script:

settings.gradle.kts
pluginManagement {
    plugins {
    }
    resolutionStrategy {
    }
    repositories {
    }
}
rootProject.name = "plugin-management"
init.gradle.kts
settingsEvaluated {
    pluginManagement {
        plugins {
        }
        resolutionStrategy {
        }
        repositories {
        }
    }
}
settings.gradle
pluginManagement {
    plugins {
    }
    resolutionStrategy {
    }
    repositories {
    }
}
rootProject.name = 'plugin-management'
init.gradle
settingsEvaluated { settings ->
    settings.pluginManagement {
        plugins {
        }
        resolutionStrategy {
        }
        repositories {
        }
    }
}

Custom Plugin Repositories

By default, the plugins{} DSL resolves plugins from the public Gradle Plugin Portal.

Many build authors would also like to resolve plugins from private Maven or Ivy repositories because they contain proprietary implementation details or to have more control over what plugins are available to their builds.

To specify custom plugin repositories, use the repositories{} block inside pluginManagement{}:

settings.gradle.kts
pluginManagement {
    repositories {
        maven(url = "./maven-repo")
        gradlePluginPortal()
        ivy(url = "./ivy-repo")
    }
}
settings.gradle
pluginManagement {
    repositories {
        maven {
            url './maven-repo'
        }
        gradlePluginPortal()
        ivy {
            url './ivy-repo'
        }
    }
}

This tells Gradle to first look in the Maven repository at ../maven-repo when resolving plugins and then to check the Gradle Plugin Portal if the plugins are not found in the Maven repository. If you don’t want the Gradle Plugin Portal to be searched, omit the gradlePluginPortal() line. Finally, the Ivy repository at ../ivy-repo will be checked.

Plugin Version Management

A plugins{} block inside pluginManagement{} allows all plugin versions for the build to be defined in a single location. Plugins can then be applied by id to any build script via the plugins{} block.

One benefit of setting plugin versions this way is that the pluginManagement.plugins{} does not have the same constrained syntax as the build script plugins{} block. This allows plugin versions to be taken from gradle.properties, or loaded via another mechanism.

Managing plugin versions via pluginManagement:

settings.gradle.kts
pluginManagement {
  val helloPluginVersion: String by settings
  plugins {
    id("com.example.hello") version "${helloPluginVersion}"
  }
}
build.gradle.kts
plugins {
    id("com.example.hello")
}
gradle.properties
helloPluginVersion=1.0.0
settings.gradle
pluginManagement {
  plugins {
        id 'com.example.hello' version "${helloPluginVersion}"
    }
}
build.gradle
plugins {
    id 'com.example.hello'
}
gradle.properties
helloPluginVersion=1.0.0

The plugin version is loaded from gradle.properties and configured in the settings script, allowing the plugin to be added to any project without specifying the version.

Plugin Resolution Rules

Plugin resolution rules allow you to modify plugin requests made in plugins{} blocks, e.g., changing the requested version or explicitly specifying the implementation artifact coordinates.

To add resolution rules, use the resolutionStrategy{} inside the pluginManagement{} block:

settings.gradle.kts
pluginManagement {
    resolutionStrategy {
        eachPlugin {
            if (requested.id.namespace == "com.example") {
                useModule("com.example:sample-plugins:1.0.0")
            }
        }
    }
    repositories {
        maven {
            url = uri("./maven-repo")
        }
        gradlePluginPortal()
        ivy {
            url = uri("./ivy-repo")
        }
    }
}
settings.gradle
pluginManagement {
    resolutionStrategy {
        eachPlugin {
            if (requested.id.namespace == 'com.example') {
                useModule('com.example:sample-plugins:1.0.0')
            }
        }
    }
    repositories {
        maven {
            url './maven-repo'
        }
        gradlePluginPortal()
        ivy {
            url './ivy-repo'
        }
    }
}

This tells Gradle to use the specified plugin implementation artifact instead of its built-in default mapping from plugin ID to Maven/Ivy coordinates.

Custom Maven and Ivy plugin repositories must contain plugin marker artifacts and the artifacts that implement the plugin. For more information on publishing plugins to custom repositories, read Gradle Plugin Development Plugin.

See PluginManagementSpec for complete documentation for using the pluginManagement{} block.

Plugin Marker Artifacts

Since the plugins{} DSL block only allows for declaring plugins by their globally unique plugin id and version properties, Gradle needs a way to look up the coordinates of the plugin implementation artifact.

To do so, Gradle will look for a Plugin Marker Artifact with the coordinates plugin.id:plugin.id.gradle.plugin:plugin.version. This marker needs to have a dependency on the actual plugin implementation. Publishing these markers is automated by the java-gradle-plugin.

For example, the following complete sample from the sample-plugins project shows how to publish a com.example.hello plugin and a com.example.goodbye plugin to both an Ivy and Maven repository using the combination of the java-gradle-plugin, the maven-publish plugin, and the ivy-publish plugin.

build.gradle.kts
plugins {
    `java-gradle-plugin`
    `maven-publish`
    `ivy-publish`
}

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

gradlePlugin {
    plugins {
        create("hello") {
            id = "com.example.hello"
            implementationClass = "com.example.hello.HelloPlugin"
        }
        create("goodbye") {
            id = "com.example.goodbye"
            implementationClass = "com.example.goodbye.GoodbyePlugin"
        }
    }
}

publishing {
    repositories {
        maven {
            url = uri(layout.buildDirectory.dir("maven-repo"))
        }
        ivy {
            url = uri(layout.buildDirectory.dir("ivy-repo"))
        }
    }
}
build.gradle
plugins {
    id 'java-gradle-plugin'
    id 'maven-publish'
    id 'ivy-publish'
}

group 'com.example'
version '1.0.0'

gradlePlugin {
    plugins {
        hello {
            id = 'com.example.hello'
            implementationClass = 'com.example.hello.HelloPlugin'
        }
        goodbye {
            id = 'com.example.goodbye'
            implementationClass = 'com.example.goodbye.GoodbyePlugin'
        }
    }
}

publishing {
    repositories {
        maven {
            url layout.buildDirectory.dir("maven-repo")
        }
        ivy {
            url layout.buildDirectory.dir("ivy-repo")
        }
    }
}

Running gradle publish in the sample directory creates the following Maven repository layout (the Ivy layout is similar):

plugin markers

Legacy Plugin Application

With the introduction of the plugins DSL, users should have little reason to use the legacy method of applying plugins. It is documented here in case a build author cannot use the plugin DSL due to restrictions in how it currently works.

build.gradle.kts
apply(plugin = "java")
build.gradle
apply plugin: 'java'

Plugins can be applied using a plugin id. In the above case, we are using the short name "java" to apply the JavaPlugin.

Rather than using a plugin id, plugins can also be applied by simply specifying the class of the plugin:

build.gradle.kts
apply<JavaPlugin>()
build.gradle
apply plugin: JavaPlugin

The JavaPlugin symbol in the above sample refers to the JavaPlugin. This class does not strictly need to be imported as the org.gradle.api.plugins package is automatically imported in all build scripts (see Default imports).

Furthermore, one needs to append the ::class suffix to identify a class literal in Kotlin instead of .class in Java.

Furthermore, it is unnecessary to append .class to identify a class literal in Groovy as it is in Java.

Using a Version Catalog

When a project uses a version catalog, plugins can be referenced via aliases when applied.

Let’s take a look at a simple Version Catalog:

gradle/libs.versions.toml
[versions]
intellij-plugin = "1.6"

[plugins]
jetbrains-intellij = { id = "org.jetbrains.intellij", version.ref = "intellij-plugin" }

Then a plugin can be applied to any build script using the alias method:

build.gradle.kts
plugins {
    alias(libs.plugins.jetbrains.intellij)
}