The simplest plugin you can develop is called a precompiled script plugins.

These are Kotlin (.kts) or Groovy (.gradle) scripts. They behave like normal Gradle plugins, but you write them using the same syntax as regular build scripts.

Some benefits of precompiled script plugins:

  • Encapsulation: Keep your build.gradle(.kts) files clean by moving reusable logic into named plugins.

  • Tooling support: Full IDE support, like autocomplete and navigation.

  • Avoid boilerplate: No need to write full plugin classes or register plugins manually.

  • Faster adoption: Use familiar script syntax while reaping the benefits of structured plugin development.

Convention Plugins

Most precompiled script plugins are used as convention plugins. A Gradle term for reusable build logic that applies standard plugin configurations, defaults, and behaviors across many projects.

Convention plugins are especially useful in large or multi-project builds where consistency is important.

They typically look like this:

buildSrc/src/main/kotlin/java-library-convention.gradle.kts
plugins {
    `java-library`
    checkstyle
}

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

checkstyle {
    maxWarnings = 0
    // ...
}

tasks.withType<JavaCompile> {
    options.isWarnings = true
    // ...
}

dependencies {
    testImplementation("junit:junit:4.13")
    // ...
}
buildSrc/src/main/groovy/java-library-convention.gradle
plugins {
    id 'java-library'
    id 'checkstyle'
}

java {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}

checkstyle {
    maxWarnings = 0
    // ...
}

tasks.withType(JavaCompile) {
    options.warnings = true
    // ...
}

dependencies {
    testImplementation("junit:junit:4.13")
    // ...
}

And are applied in other projects like this:

build.gradle.kts
plugins {
    `java-library-convention`
}
build.gradle
plugins {
    id 'java-library-convention'
}

Structuring Precompiled Script Plugins

Precompiled script plugins are typically placed in either:

  1. A dedicated buildSrc directory in your build, or

  2. A separate included build (often named build-logic).

1. Using buildSrc

Precompiled script plugins or convention plugins will often be found in the special buildSrc directory:

.
└── buildSrc
    ├── build.gradle.kts
    └── src
       └── main
          └── kotlin
             └── myproject.java-conventions.gradle.kts
.
└── buildSrc
    ├── build.gradle
    └── src
       └── main
          └── groovy
             └── myproject.java-conventions.gradle

2. Using a Composite Build

Precompiled script plugins or convention plugins will often be found in a Composite Build with a name similar to build-logic:

build-logic
├── settings.gradle.kts
├── build.gradle.kts
└── src
   └── main
      └── kotlin
         └── myproject.java-conventions.gradle.kts
build-logic
├── settings.gradle
├── build.gradle
└── src
   └── main
      └── groovy
         └── myproject.java-conventions.gradle

Applying a Precompiled Script Plugin

You apply a precompiled script plugin using its ID, derived from the script filename (excluding the .gradle.kts or .gradle extension).

plugins {
    id("myproject.java-conventions")
}
plugins {
    id("myproject.java-conventions")
}

The script itself acts as the plugin. There is no need to explicitly implement the Plugin interface; a requirement for binary plugins.

Publishing a Precompiled Script Plugin

You can publish a precompiled script plugin to a repository, either a public one such as the Gradle Plugin Portal, or a private artifact repository like Maven, Artifactory, or Nexus.

However, publishing precompiled script plugins is not typical or recommended (they are meant for internal use only). To publish them, you should first convert the precompiled script plugin into a binary plugin.

Example of a Precompiled Script Plugin

Let’s walk through creating a simple convention plugin using a precompiled script plugin in buildSrc.

1. Create a buildSrc directory

In the root of your project, add a buildSrc directory:

.   // root project
├── ...     // other project dirs and files
└── buildSrc
    ├── build.gradle.kts
    └── src
       └── main
          └── kotlin
.   // root project
├── ...     // other project dirs and files
└── buildSrc
    ├── build.gradle
    └── src
       └── main
          └── groovy

2. Add a build file

Create buildSrc/build.gradle.kts with the supporting build logic your convention plugin will need to compile:

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

repositories {
    gradlePluginPortal()
}
buildSrc/build.gradle
plugins {
    id 'groovy-gradle-plugin'
}

repositories {
    gradlePluginPortal()
}

3. Create a plugin script

Inside buildSrc/src/main/kotlin or buildSrc/src/main/groovy, write your convention plugin as a .gradle(.kts) file:

.
└── buildSrc
    ├── build.gradle.kts
    └── src
       └── main
          └── kotlin
             └── myproject.java-conventions.gradle.kts

In this example, the myproject.java-conventions.gradle(.kts) file contains the following code:

buildSrc/src/main/kotlin/myproject.java-conventions.gradle.kts
plugins {
    id("java")
}

// Disable the test report for the individual test task
tasks.named<Test>("test") {
    reports.html.required = false
}

// Share the test report data to be aggregated for the whole project
configurations.create("binaryTestResultsElements") {
    isCanBeResolved = false
    isCanBeConsumed = true
    attributes {
        attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.DOCUMENTATION))
        attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named("test-report-data"))
    }
    outgoing.artifact(tasks.test.map { task -> task.getBinaryResultsDirectory().get() })
}

repositories {
    mavenCentral()
}
buildSrc/src/main/groovy/myproject.java-conventions.gradle
plugins {
    id 'java'
}

// Disable the test report for the individual test task
test {
    reports.html.required = false
}

// Share the test report data to be aggregated for the whole project
configurations {
    binaryTestResultsElements {
        canBeResolved = false
        canBeConsumed = true
        attributes {
            attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.DOCUMENTATION))
            attribute(DocsType.DOCS_TYPE_ATTRIBUTE, objects.named(DocsType, 'test-report-data'))
        }
        outgoing.artifact(test.binaryResultsDirectory)
    }
}

repositories {
    mavenCentral()
}

The name of the script (myproject.java-conventions.gradle(.kts)) becomes the plugin ID: myproject.java-conventions.

4. Apply the plugin in your project

In the build.gradle(.kts) of a consuming project, apply the plugin:

core/build.gradle.kts
plugins {
    id("myproject.java-conventions")
}

dependencies {
    testImplementation("junit:junit:4.13")
}
core/build.gradle
plugins {
    id 'myproject.java-conventions'
}

dependencies {
    testImplementation 'junit:junit:4.13'
}