skip to content

Script Plugins (apply from)

Standalone scripts applied with apply from:, and their limits — no type-safe accessors, no real compilation, poor caching. Interviewers ask so you can explain why precompiled convention plugins replaced them.

on this pageshow

questions

5

What is a script plugin in Gradle, and how do you apply one?

level: juniorimportance: must knowfreq 55%

answer

  1. plain .gradle.kts file by path/URL
  2. apply(from = ...) / apply from:
  3. no plugin ID, no jar
  4. executes against Project target
  5. no type-safe accessors

basics

~10 s

A script plugin is a plain Gradle script (e.g. other.gradle.kts) holding shared build logic. You apply it with apply(from = "other.gradle.kts") in Kotlin DSL or apply from: 'other.gradle' in Groovy.

solid answer

~40 s

A **script plugin** is just an ordinary Gradle build script file containing reusable configuration. Unlike binary plugins (identified by an ID and packaged as a jar), a script plugin is referenced by its file path or URL. You apply it with `apply(from = "versions.gradle.kts")` (Kotlin DSL) or `apply from: 'versions.gradle'` (Groovy DSL). When applied, its statements execute against the applying object — usually the `Project`, so `tasks`, `dependencies`, etc. are in scope. It is the simplest way to share small bits of logic (version constants, a shared task) across modules without publishing a plugin. The trade-off: no `plugins {}` block usage, no type-safe accessors, and it can't itself live in `buildSrc` as a precompiled plugin.

code

kotlin · 8 lines
kotlin
// gradle/shared-config.gradle.kts
repositories { mavenCentral() }
tasks.register("hello") {
    doLast { println("shared task from script plugin") }
}

// build.gradle.kts
apply(from = "gradle/shared-config.gradle.kts")

go deeper

for a junior

Know it's a shared .gradle(.kts) file applied with apply(from = ...) and that it runs like a normal build script.

for a middle

Explain the apply target, the to argument, and the difference from ID-based binary plugins.

for a senior

Discuss why script plugins lose type-safe accessors and when to graduate to a precompiled/binary plugin.

for a principal

Frame script plugins as a tactical convenience and set org guidance to prefer convention/precompiled plugins for cross-repo standardization and supply-chain safety.

## What a script plugin is Gradle has two broad plugin flavors: - **Binary (or "precompiled") plugins** — compiled code implementing `Plugin<T>`, identified by a plugin **ID**, applied through the `plugins {}` block. - **Script plugins** — a plain `.gradle` / `.gradle.kts` file you point at by **path or URL**. There is no ID, no jar, no metadata. Applying a script plugin essentially *inlines and executes* that script's body against a target object (the **`apply` target**). By default the target is the `Project` the apply call runs in, so inside the script you can write `dependencies { }`, `tasks.register(...)`, `repositories { }` — exactly as in a normal build file. ## How to apply ```kotlin // build.gradle.kts (Kotlin DSL) apply(from = "gradle/shared-config.gradle.kts") ``` ```groovy // build.gradle (Groovy DSL) apply from: 'gradle/shared-config.gradle' ``` The path is resolved relative to the applying script's project directory. You can also apply from a remote URL: `apply(from = "https://example.com/shared.gradle.kts")` — handy but a supply-chain risk. ## Apply target You can change what the script configures via the `to` argument: ```kotlin subprojects { apply(from = rootProject.file("common.gradle.kts"), to = this) } ``` Here each subproject becomes the target, so the script's `dependencies {}` configures each subproject. ## When to use Script plugins shine for tiny, build-local sharing: a `versions.gradle.kts` holding version constants, or applying the same handful of tasks to several modules. For anything reusable across **repositories** you should publish a binary plugin instead. ## Key limitation preview Because the script is interpreted as a generic build script and not compiled against a known plugin classpath, **type-safe accessors** generated by the `plugins {}` block (e.g. the `application {}` extension accessor) are NOT available inside a script plugin. You fall back to `configure<...>()` / `the<...>()` or string-based lookups.

  • Can you apply a script plugin from a remote URL, and why might you avoid it?
    Yes — apply(from = "https://.../x.gradle.kts"). It's discouraged because it's a supply-chain risk (remote code executed at configuration time), it breaks offline/reproducible builds, and adds network flakiness.
  • What object does the script body configure by default?
    The Project in which apply() is called. You can redirect it with the `to` argument (e.g. apply(from = ..., to = subproject)).

saying these in an interview costs you the question

  • Claiming script plugins are applied via the plugins {} block (they are not — that's for ID-based plugins).
  • Saying script plugins give type-safe accessors (they don't).

context

open as a page

How do script plugins differ from precompiled script plugins, and why would you migrate from one to the other?

level: middleimportance: must knowfreq 50%

basics

~20 s

A script plugin is a loose .gradle.kts applied via apply(from = ...) with no compilation, no type-safe accessors, and weak reuse. A precompiled script plugin lives in buildSrc/build-logic, is compiled, gets an ID, applies via plugins {}, and keeps type-safe accessors.

open as a page

A teammate copies application { mainClass.set(...) } into a script plugin and it fails to compile. Why, and what are the workarounds?

level: middleimportance: should knowfreq 38%

basics

~10 s

Inside an apply(from=) script plugin there are no type-safe accessors, so the application {} accessor doesn't exist. Use configure<JavaApplication> { ... } or the<JavaApplication>() instead, or move the logic into a precompiled plugin.

open as a page

When does apply(from = ...) execute relative to the rest of the build script, and what subtle issues does that cause?

level: seniorimportance: should knowfreq 30%

basics

~20 s

apply(from = ...) runs synchronously at configuration time, at the exact point it appears in the script. Statements after it see its effects; statements before it don't. Ordering bugs arise when a script plugin depends on, or is depended on by, later lines.

open as a page

When are script plugins still an appropriate choice, and when should you reach for buildSrc/build-logic or a published binary plugin instead?

level: seniorimportance: should knowfreq 28%

basics

~10 s

Use script plugins for tiny, build-local, low-ceremony sharing (version constants, one shared task). Move to buildSrc/build-logic precompiled plugins for compiled, testable, type-safe convention logic, and to a published binary plugin when sharing across repositories.

open as a page