A precompiled script plugin is a .gradle.kts (Kotlin DSL) or .gradle (Groovy DSL) script stored in a plugin source set (e.g. buildSrc or an included build like build-logic). Gradle automatically compiles it into a proper plugin class, packages it like a normal plugin, and assigns the plugin ID based on the file name.

For example, you place a script in build-logic/src/main/kotlin/java-library-conventions.gradle.kts:

some-project/
├─ settings.gradle.kts
├─ build-logic/
│  ├─ build.gradle.kts
│  └─ src/
│     └─ main/
│        └─ kotlin/
│           └─ java-library-conventions.gradle.kts  (1)
└─ app/
   ├─ build.gradle.kts                              (2)
   └─ src/
1 Pre-compiled script plugin
2 Build file applies the plugin

Gradle automatically compiles that script into a plugin. The plugin ID is derived from the file name: java-library-conventions.gradle.kts.

You can now apply it in any build like a regular plugin. In the build file of the app project:

app/build.gradle.kts
plugins {
    // Apply by ID derived from the file name:
    id("java-library-conventions")
}

Setting the plugin ID

The plugin ID for a precompiled script is derived from its file name and optional package declaration.

For example, a script named code-quality.gradle(.kts) located in src/main/groovy (or src/main/kotlin) without a package declaration would be exposed as the code-quality plugin:

buildSrc/build.gradle.kts
plugins {
    `kotlin-dsl`
}
app/build.gradle.kts
plugins {
    id("code-quality")
}
buildSrc/build.gradle
plugins {
    id 'groovy-gradle-plugin'
}
app/build.gradle
plugins {
    id 'code-quality'
}

On the other hand, a script named code-quality.gradle.kts located in src/main/kotlin/my with the package declaration my would be exposed as the my.code-quality plugin:

buildSrc/build.gradle.kts
plugins {
    `kotlin-dsl`
}
app/build.gradle.kts
plugins {
    id("my.code-quality")
}

When creating precompiled script plugins intended for Settings or Init scripts, the filename suffix determines where Gradle applies them:

  • .settings.gradle or .settings.gradle.kts → interpreted as Plugin<Settings>

  • .init.gradle or .init.gradle.kts → interpreted as Plugin<Init>

  • .gradle or .gradle.kts → interpreted as the default Plugin<Project>

Groovy pre-compiled script plugins cannot have packages.

Making a plugin configurable using extensions

Extension objects are commonly used in plugins to expose configuration options and additional functionality to build scripts.

When you apply a plugin that defines an extension, you can access the extension object and configure its properties or call its methods to customize the behavior of the plugin or tasks provided by the plugin.

A Project has an associated ExtensionContainer object that contains all the settings and properties for the plugins that have been applied to the project. You can provide configuration for your plugin by adding an extension object to this container.

Let’s update our greetings example:

buildSrc/src/main/kotlin/greetings.gradle.kts
// Create extension object
interface GreetingPluginExtension {
    val message: Property<String>
}

// Add the 'greeting' extension object to project
val extension = project.extensions.create<GreetingPluginExtension>("greeting")
buildSrc/src/main/groovy/greetings.gradle
// Create extension object
interface GreetingPluginExtension {
    Property<String> getMessage()
}

// Add the 'greeting' extension object to project
def extension = project.extensions.create("greeting", GreetingPluginExtension)

You can set the value of the message property directly with extension.message.set("Hi from Gradle,").

However, the GreetingPluginExtension object becomes available as a project property with the same name as the extension object. You can now access message like so:

buildSrc/src/main/kotlin/greetings.gradle.kts
// Where the<GreetingPluginExtension>() is equivalent to project.extensions.getByType(GreetingPluginExtension::class.java)
the<GreetingPluginExtension>().message.set("Hi from Gradle")
buildSrc/src/main/groovy/greetings.gradle
extensions.findByType(GreetingPluginExtension).message.set("Hi from Gradle")

If you apply the greetings plugin, you can set the convention in your build script:

app/build.gradle.kts
plugins {
    application
    id("greetings")
}

greeting {
    message = "Hello from Gradle"
}
app/build.gradle
plugins {
    id 'application'
    id('greetings')
}

configure(greeting) {
    message = "Hello from Gradle"
}

Adding default configuration as conventions

In plugins, you can define default values, also known as conventions, using the project object.

Convention properties are properties that are initialized with default values but can be overridden:

buildSrc/src/main/kotlin/greetings.gradle.kts
// Create extension object
interface GreetingPluginExtension {
    val message: Property<String>
}

// Add the 'greeting' extension object to project
val extension = project.extensions.create<GreetingPluginExtension>("greeting")

// Set a default value for 'message'
extension.message.convention("Hello from Gradle")
buildSrc/src/main/groovy/greetings.gradle
// Create extension object
interface GreetingPluginExtension {
    Property<String> getMessage()
}

// Add the 'greeting' extension object to project
def extension = project.extensions.create("greeting", GreetingPluginExtension)

// Set a default value for 'message'
extension.message.convention("Hello from Gradle")

extension.message.convention(…​) sets a convention for the message property of the extension. This convention specifies that the value of message should default to "Hello from Gradle".

If the message property is not explicitly set, its value will be automatically set to "Hello from Gradle".

Mapping extension properties to task properties

Using an extension and mapping it to a custom task’s input/output properties is common in plugins.

In this example, the message property of the GreetingPluginExtension is mapped to the message property of the GreetingTask as an input:

buildSrc/src/main/kotlin/greetings.gradle.kts
// Create extension object
interface GreetingPluginExtension {
    val message: Property<String>
}

// Add the 'greeting' extension object to project
val extension = project.extensions.create<GreetingPluginExtension>("greeting")

// Set a default value for 'message'
extension.message.convention("Hello from Gradle")

// Create a greeting task
abstract class GreetingTask : DefaultTask() {
    @Input
    val message = project.objects.property<String>()

    @TaskAction
    fun greet() {
        println("Message: ${message.get()}")
    }
}

// Register the task and set the convention
tasks.register<GreetingTask>("hello") {
    message.convention(extension.message)
}
buildSrc/src/main/groovy/greetings.gradle
// Create extension object
interface GreetingPluginExtension {
    Property<String> getMessage()
}

// Add the 'greeting' extension object to project
def extension = project.extensions.create("greeting", GreetingPluginExtension)

// Set a default value for 'message'
extension.message.convention("Hello from Gradle")

// Create a greeting task
abstract class GreetingTask extends DefaultTask {
    @Input
    abstract Property<String> getMessage()

    @TaskAction
    void greet() {
        println("Message: ${message.get()}")
    }
}

// Register the task and set the convention
tasks.register("hello", GreetingTask) {
    message.convention(extension.message)
}
$ gradle -q hello
Message: Hello from Gradle

This means that changes to the extension’s message property will trigger the task to be considered out-of-date, ensuring that the task is re-executed with the new message.

You can find out more about types that you can use in task implementations and extensions in Lazy Configuration.

Applying external plugins

In order to apply an external plugin in a precompiled script plugin, it has to be added to the plugin project’s implementation classpath in the plugin’s build file:

buildSrc/build.gradle.kts
plugins {
    `kotlin-dsl`
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("com.bmuschko:gradle-docker-plugin:6.4.0")
}
buildSrc/build.gradle
plugins {
    id 'groovy-gradle-plugin'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.bmuschko:gradle-docker-plugin:6.4.0'
}

It can then be applied in the precompiled script plugin:

buildSrc/src/main/kotlin/my-plugin.gradle.kts
plugins {
    id("com.bmuschko.docker-remote-api")
}
buildSrc/src/main/groovy/my-plugin.gradle
plugins {
    id 'com.bmuschko.docker-remote-api'
}

The plugin version in this case is defined in the dependency declaration.

Example of a precompiled script plugin in the Settings scope

Precompiled script plugins can also target the Settings scope. The following example defines a simple plugin in a separate build called settings-plugin.

Configure plugin management for the plugin build:

settings-plugin/settings.gradle.kts
pluginManagement {
    repositories {
        gradlePluginPortal()
        mavenCentral()
    }
}
settings-plugin/settings.gradle
pluginManagement {
    repositories {
        gradlePluginPortal()
        mavenCentral()
    }
}

Set up the build to support precompiled script plugins:

settings-plugin/build.gradle.kts
plugins {
    `kotlin-dsl`
}

repositories {
    mavenCentral()
    gradlePluginPortal()
}

dependencies {
    implementation("com.gradle:develocity-gradle-plugin:4.1")
}
settings-plugin/build.gradle
plugins {
    id('groovy-gradle-plugin')
}

repositories {
    mavenCentral()
    gradlePluginPortal()
}

dependencies {
    implementation('com.gradle:develocity-gradle-plugin:4.1')
}
Notice that we used the practice described in Applying external plugins.

Write the precompiled script plugin itself:

settings-plugin/src/main/kotlin/my-plugin.settings.gradle.kts
plugins {
    id("com.gradle.develocity")
}
settings-plugin/src/main/groovy/my-plugin.settings.gradle
plugins {
    id('com.gradle.develocity')
}

Because the file name ends with .settings.gradle.kts, Gradle compiles it into a plugin with a Settings scope. The plugin ID is my-plugin. You can use it as follows in a consumer project (root settings file of the consuming project):

settings.gradle.kts
pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
    includeBuild("./settings-plugin")
}

plugins {
    id("my-plugin")
}
settings.gradle
pluginManagement {
    repositories {
        mavenCentral()
        gradlePluginPortal()
    }
    includeBuild('./settings-plugin')
}

plugins {
    id('my-plugin')
}
Because the plugin was precompiled as a Settings plugin (the .settings.gradle(.kts) suffix), you apply it in the settings file’s plugins {} block, not in a project build.gradle(.kts) file.