Gradle Core Concepts

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against Gradle 9.8.1 (the current release on 2026-10-16; 8.14.6 is the last 8.x release). Every example was run through the Gradle wrapper on JDK 26, on macOS, in a scratch copy of the tidepool build, with the Kotlin DSL and again with the Groovy DSL. The daemon and the code both used the same JDK 26; Java toolchains are the subject of a later page. Gradle 8.14 was not available here, so what is said about it comes from the official documentation and release notes and is labelled "not run here". The build output was trimmed only where a line says so, the scratch directory was replaced by <work>, and the decimal comma in the program output comes from the locale of the machine.

Gradle is a build tool whose model is a programmable task graph: a build is a set of projects, each project is a set of tasks, and Gradle decides which tasks to run, and in which order, from the dependencies that the build scripts and the plugins declare between them. This page names every concept once, so that the rest of the Gradle pages can use the words without explaining them again. The same concepts in the other two tools are in Ant Build File Structure and Maven POM Fundamentals; a side-by-side comparison of the three models is on Comparing Ant, Maven & Gradle, and the ideas that all three tools share are on Build Automation Fundamentals.

The concepts at a glance

The Gradle user manual introduces six terms on its first page. They are the vocabulary of everything that follows.

Concept What it means Where it lives

Build

The process and the environment that produce outputs: one invocation of Gradle over one or more projects and their build scripts.

A directory tree with a settings file at its root (and the gradlew wrapper scripts beside it).

Project

A piece of software that can be built, such as an application or a library. A build has one root project and zero or more subprojects.

One directory per project, named in the settings file.

Task

A basic unit of work, such as compiling code, running tests or creating an archive. Tasks are declared in build scripts or added by plugins.

Registered in a project, addressed by a path such as :tidepool-cli:run.

Build script

A script (build.gradle.kts in Kotlin or build.gradle in Groovy) that configures a project: its plugins, its dependencies, its tasks.

The directory of the project it configures.

Plugin

A reusable piece of build logic that extends Gradle, usually by adding tasks, configurations and conventions to a project (the Java plugin, for example).

Applied by a plugins {} block in a build script.

Dependency

Another module or project that a project needs, resolved by Gradle during the build.

Declared in the dependencies {} block of a build script.

A seventh item, the settings file, is not in that table but is the one file that makes a directory a build: settings.gradle.kts (or settings.gradle) names the root project and lists the subprojects. In a single-project build the settings file is optional; in a multi-project build it is mandatory.

The running example

The examples use the three-module tidepool build that the whole section shares: tidepool-model (a library of records), tidepool-engine (a library that depends on the model and reads a CSV file with Apache Commons CSV) and tidepool-cli (an application that depends on the engine and uses picocli). Because Gradle builds are scripts, the quickest way to see each concept is to read the few files that define this build.

The project structure

A Gradle project is recognised by the gradlew and gradlew.bat scripts in its root directory. For tidepool the files that Gradle itself reads are these (the source files of the three modules are omitted):

tidepool
├── gradle
│   ├── libs.versions.toml
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── gradle.properties
├── settings.gradle.kts
├── build.gradle.kts
├── data
│   └── stations.csv
├── tidepool-model
│   ├── build.gradle.kts
│   └── src/main/java/...
├── tidepool-engine
│   ├── build.gradle.kts
│   └── src/main/java/...
└── tidepool-cli
    ├── build.gradle.kts
    └── src/main/java/...

(The tree was written from find output; the ... lines stand for the Java packages.) Running a build also creates two directories next to the settings file that must not be committed: .gradle/ (project-specific caches, including the configuration cache) and build/ (the outputs of the root project; every subproject has its own build/ too). What each file is for:

File Purpose

gradlew, gradlew.bat, gradle/wrapper/*

The Gradle Wrapper: two scripts, a small JAR and a properties file that name the Gradle version, so that the build downloads and runs the version it was written for. Committed to version control. See Gradle Wrapper; Maven’s counterpart is Maven Wrapper.

settings.gradle.kts

The settings file: the name of the build and the list of subprojects. It is evaluated before any build script.

build.gradle.kts (root)

The build script of the root project: configuration shared by all projects, and tasks that belong to the whole build.

tidepool-*/build.gradle.kts

One build script per subproject: its plugins and dependencies.

gradle/libs.versions.toml

The version catalog: the versions and coordinates of the libraries in one place. Gradle generates type-safe accessors such as libs.commons.csv from it.

gradle.properties

Build settings and project properties, for example org.gradle.configuration-cache=true.

The settings file

The settings file is a script, the first one Gradle evaluates. It gives the root project a name, declares where dependencies come from for the whole build and includes the subprojects.

Kotlin DSL: settings.gradle.kts
rootProject.name = "tidepool"

dependencyResolutionManagement {
    repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
    repositories {
        mavenCentral()
    }
}

include("tidepool-model", "tidepool-engine", "tidepool-cli")
Groovy DSL: settings.gradle
rootProject.name = 'tidepool'

dependencyResolutionManagement {
    repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
    repositories {
        mavenCentral()
    }
}

include 'tidepool-model', 'tidepool-engine', 'tidepool-cli'

rootProject.name overrides the default, which would be the name of the directory. include creates one project per name, and the name is also the directory that holds the project. With FAIL_ON_PROJECT_REPOS, a build script that declares its own repositories makes the build fail, which keeps all repository declarations in this one place. In Maven the same job is split between the parent POM’s <modules> and <repositories>; the Gradle equivalent of a <module> entry is include. The details of settings files are on the later page Settings & Build Scripts.

projects shows what Gradle made of this file. With Gradle 9.8.1 and the Kotlin DSL (the Groovy DSL printed the same text):

./gradlew projects
Calculating task graph as no cached configuration is available for tasks: projects

> Configure project :
Configuring :

> Task :projects

Projects:

------------------------------------------------------------
Root project 'tidepool'
------------------------------------------------------------

Location: <work>/tidepool

Project hierarchy:

Root project 'tidepool'
+--- Project ':tidepool-cli'
+--- Project ':tidepool-engine'
\--- Project ':tidepool-model'

Project locations:

project ':tidepool-cli' - /tidepool-cli
project ':tidepool-engine' - /tidepool-engine
project ':tidepool-model' - /tidepool-model

To see a list of the tasks of a project, run gradlew <project-path>:tasks
For example, try running gradlew :tidepool-cli:tasks

BUILD SUCCESSFUL in 5s
1 actionable task: 1 executed
Configuration cache entry stored.

Projects are identified by a path: : is the root project, and :tidepool-cli is the subproject of that name. The first line of the output and the line Configuring : are explained in the section on the build phases below.

The build scripts

Each subproject has a build script that applies plugins and declares dependencies. The scripts use the version catalog, so no version number appears in them.

The version catalog: gradle/libs.versions.toml
[versions]
commons-csv = "1.14.1"
picocli = "4.7.7"
junit = "6.1.3"

[libraries]
commons-csv = { module = "org.apache.commons:commons-csv", version.ref = "commons-csv" }
picocli = { module = "info.picocli:picocli", version.ref = "picocli" }
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" }

The catalog is a TOML file and is the same for both DSLs; the build scripts of the two other subprojects follow.

Kotlin DSL: tidepool-engine/build.gradle.kts
plugins {
    `java-library`
}

dependencies {
    api(project(":tidepool-model"))
    implementation(libs.commons.csv)
}
Groovy DSL: tidepool-engine/build.gradle
plugins {
    id 'java-library'
}

dependencies {
    api project(':tidepool-model')
    implementation libs.commons.csv
}
Kotlin DSL: tidepool-cli/build.gradle.kts
plugins {
    application
}

dependencies {
    implementation(project(":tidepool-engine"))
    implementation(libs.picocli)
}

application {
    mainClass = "io.tidepool.cli.Main"
}
Groovy DSL: tidepool-cli/build.gradle
plugins {
    id 'application'
}

dependencies {
    implementation project(':tidepool-engine')
    implementation libs.picocli
}

application {
    mainClass = 'io.tidepool.cli.Main'
}

The plugins {} block applies the plugins. java-library adds the compile, test and jar tasks and the api and implementation configurations; application adds run, startScripts and the distribution tasks (distZip, distTar) on top of the Java plugin. A dependency written as api(project(":tidepool-model")) is a project dependency: it makes Gradle build tidepool-model first and put its JAR on the compile classpath of tidepool-engine and of everything that depends on tidepool-engine. implementation keeps a library off the compile classpath of consumers (so tidepool-cli sees Commons CSV only at run time). Dependencies are declared in more detail on Declaring Dependencies & Configurations and the catalog on Version Catalogs; Maven’s counterparts are described on Dependencies, Scopes & Mediation.

The root script has a different job: it holds what is common to the whole build and the tasks that belong to the build itself. It is the next section’s subject.

Properties

gradle.properties supplies build settings. The one in the example turns on the configuration cache, which is why the outputs on this page say Configuration cache entry stored. and Reusing configuration cache.:

org.gradle.configuration-cache=true

Every org.gradle.* property is covered on Build Environment & Properties. The configuration cache is off by default in Gradle 9.8.1 (making it the default is announced for Gradle 10, not released); Gradle 9.0 made it the preferred mode, and Gradle 8.1 made it stable. The examples on this page are compatible with it, except for the one that is shown failing on purpose.

Tasks and the task graph

A task is the unit of work. Plugins add most tasks, but any build script can register its own. The root script of tidepool registers two tasks and applies the base plugin, which adds the lifecycle tasks assemble, check, build and clean to a project that has no language plugin.

Kotlin DSL: build.gradle.kts (root)
plugins {
    base
}

allprojects {
    group = "io.tidepool"
    version = "1.0.0"
}

println("Configuring ${project.path}")

val checkStations = tasks.register("checkStations") {
    group = "tidepool"
    description = "Counts the stations in data/stations.csv."
    val stations = layout.projectDirectory.file("data/stations.csv")
    doLast {
        val count = stations.asFile.readLines().size - 1
        println("data/stations.csv lists $count stations")
    }
}

tasks.register("release") {
    group = "tidepool"
    description = "Checks the data and builds the distribution."
    dependsOn(checkStations, ":tidepool-cli:distZip")
}
Groovy DSL: build.gradle (root)
plugins {
    id 'base'
}

allprojects {
    group = 'io.tidepool'
    version = '1.0.0'
}

println "Configuring ${project.path}"

def checkStations = tasks.register('checkStations') {
    group = 'tidepool'
    description = 'Counts the stations in data/stations.csv.'
    def stations = layout.projectDirectory.file('data/stations.csv')
    doLast {
        def count = stations.asFile.readLines().size() - 1
        println "data/stations.csv lists ${count} stations"
    }
}

tasks.register('release') {
    group = 'tidepool'
    description = 'Checks the data and builds the distribution.'
    dependsOn checkStations, ':tidepool-cli:distZip'
}

Three ideas are in this script:

  • tasks.register registers a task lazily: the block that configures it runs only if the task is part of the requested build (the "task configuration avoidance" API that appeared in Gradle 4.9). The older tasks.create and task name { } forms configure the task immediately, even if nobody asked for it.

  • doLast { } adds an action, the code that runs in the execution phase. Everything outside it runs while the build is configured.

  • dependsOn adds an edge to the graph. The path :tidepool-cli:distZip points at a task in another project, so the root release task makes Gradle build the whole distribution of the CLI, and, through the project dependencies, the two libraries before it.

The listing of tasks that a project offers is a task, too:

./gradlew tasks
...
Tidepool tasks
--------------
checkStations - Counts the stations in data/stations.csv.
release - Checks the data and builds the distribution.
...

(Lines marked ... were left out; the full report also lists the build, distribution, help and verification tasks that the base and application plugins added, and it says that "some of the displayed tasks may belong to subprojects". The group of a task is the heading under which tasks lists it, and help --task release prints the path, the type, the options and the description of one task.) A task path is built like a project path: :checkStations is a task of the root project, and :tidepool-cli:run is the task run of the tidepool-cli subproject. Running gradlew run without a path would run every task called run in every project of the build, which here is the one in tidepool-cli.

The task graph

Gradle does not execute tasks in the order in which they are written. It collects the requested tasks and everything they depend on into a directed acyclic graph and runs each task after its dependencies. --dry-run prints that order without executing any action:

./gradlew release --dry-run
Calculating task graph as no cached configuration is available for tasks: release

> Configure project :
Configuring :
:checkStations SKIPPED
:tidepool-model:compileJava SKIPPED
:tidepool-engine:compileJava SKIPPED
:tidepool-cli:compileJava SKIPPED
:tidepool-cli:processResources SKIPPED
:tidepool-cli:classes SKIPPED
:tidepool-cli:jar SKIPPED
:tidepool-engine:processResources SKIPPED
:tidepool-engine:classes SKIPPED
:tidepool-engine:jar SKIPPED
:tidepool-model:processResources SKIPPED
:tidepool-model:classes SKIPPED
:tidepool-model:jar SKIPPED
:tidepool-cli:startScripts SKIPPED
:tidepool-cli:distZip SKIPPED
:release SKIPPED

BUILD SUCCESSFUL in 1s
Configuration cache entry stored.

Reading it from the bottom: release needs distZip, which needs startScripts and the jar of the CLI, which needs the compiled classes, which need the jars of tidepool-engine and tidepool-model, because the CLI depends on the engine and the engine’s api dependency on the model puts the model on the CLI’s compile classpath too. Nothing connects checkStations to the compilation, so Gradle is free to run them in any order (and with --parallel, at the same time); the order of independent tasks can differ between runs and between the two DSL runs.

Running the task, with the entry that --dry-run stored in the configuration cache:

./gradlew release
Reusing configuration cache.

> Task :checkStations
data/stations.csv lists 3 stations

> Task :tidepool-engine:processResources NO-SOURCE
> Task :tidepool-cli:processResources NO-SOURCE
> Task :tidepool-model:processResources NO-SOURCE
> Task :tidepool-model:compileJava
> Task :tidepool-model:classes
> Task :tidepool-model:jar
> Task :tidepool-engine:compileJava
> Task :tidepool-engine:classes
> Task :tidepool-engine:jar
> Task :tidepool-cli:compileJava
> Task :tidepool-cli:classes
> Task :tidepool-cli:jar
> Task :tidepool-cli:startScripts
> Task :tidepool-cli:distZip
> Task :release

BUILD SUCCESSFUL in 479ms
9 actionable tasks: 9 executed
Configuration cache entry reused.

The suffix after a task name says what Gradle did with it: no suffix means the task ran, NO-SOURCE means its inputs were empty and it was skipped (there is no src/main/resources), and the next run shows UP-TO-DATE for tasks whose inputs and outputs did not change:

Reusing configuration cache.
> Task :tidepool-cli:processResources NO-SOURCE
> Task :tidepool-model:processResources NO-SOURCE
> Task :tidepool-engine:processResources NO-SOURCE

> Task :checkStations
data/stations.csv lists 3 stations

> Task :tidepool-model:compileJava UP-TO-DATE
> Task :tidepool-model:classes UP-TO-DATE
> Task :tidepool-model:jar UP-TO-DATE
> Task :tidepool-engine:compileJava UP-TO-DATE
> Task :tidepool-engine:classes UP-TO-DATE
> Task :tidepool-engine:jar UP-TO-DATE
> Task :tidepool-cli:compileJava UP-TO-DATE
> Task :tidepool-cli:classes UP-TO-DATE
> Task :tidepool-cli:jar UP-TO-DATE
> Task :tidepool-cli:startScripts UP-TO-DATE
> Task :tidepool-cli:distZip UP-TO-DATE
> Task :release

BUILD SUCCESSFUL in 561ms
9 actionable tasks: 1 executed, 8 up-to-date
Configuration cache entry reused.

checkStations has no declared inputs or outputs, so Gradle cannot tell that it need not run and always runs it; it is the one executed task of 9 actionable tasks: 1 executed, 8 up-to-date. release and the classes tasks are lifecycle tasks with no actions of their own: they are not counted among the actionable tasks, and release shows no outcome label at all. Declaring inputs and outputs is the subject of Incremental Build, the tasks themselves of Tasks and Writing Custom Tasks. The Maven counterpart of a task is a plugin goal, which Maven binds to a fixed lifecycle phase (Maven Build Lifecycle); the Ant counterpart is a target with a depends list (Ant Targets & Extension Points). The difference is that Gradle’s graph is built from the dependencies it discovers, including those between the projects, not from a lifecycle that was fixed in advance.

Running the application is also a task. The application plugin’s run task takes its arguments from --args, and the working directory is the project directory of tidepool-cli, hence the ..:

./gradlew :tidepool-cli:run --args="../data/stations.csv SF" -q
Configuring :
San Francisco: -0,869 m

(-q suppresses the task lines, but not the println of the root script.)

How the concepts fit together

The diagram shows what Gradle does with the files above, from the settings file to the executed tasks.

flowchart TD invoke["./gradlew build (the wrapper picks the Gradle version)"] --> settings["settings.gradle.kts: names the build, includes the subprojects"] settings --> root["Root project"] settings --> sub["Subprojects"] root --> rscript["build.gradle.kts of the root"] sub --> sscript["build.gradle.kts of each subproject"] rscript --> plugins["Plugins applied by plugins {}"] sscript --> plugins plugins --> tasks["Tasks registered in each project"] rscript --> tasks sscript --> deps["Dependencies: libraries and project dependencies"] deps --> taskgraph["Task graph (edges: dependsOn, inputs and outputs, project dependencies)"] tasks --> taskgraph taskgraph --> run["Selected tasks run in dependency order"]

Gradle names three phases for this flow. In the initialization phase it evaluates the settings file and creates a Project object for every included project. In the configuration phase it evaluates every build script, which registers tasks and builds the task graph. In the execution phase it runs the tasks that were requested and what they depend on. A build script’s statements run in the configuration phase unless they are inside an action such as doLast { }, which is why the println("Configuring ...") of the root script produced output before any task. The phases have their own page, Build Lifecycle.

The output above shows a consequence. When the configuration cache has no entry for the requested tasks, Gradle prints Calculating task graph as no cached configuration is available and runs the configuration phase (hence Configuring :). When it has one, it prints Reusing configuration cache., skips the initialization and configuration work and goes straight to execution, so the root script’s println is not run again. That is the reason why an example of the cache is a good test of the distinction between configuration code and execution code.

A configuration-cache problem

Because the cache stores the task graph and the state of the tasks, the objects a task action refers to must be serialisable. A script-level variable is not. This version of the root script, written the obvious way, keeps the file reference in a variable of the script and uses it in the action:

val stationsFile = layout.projectDirectory.file("data/stations.csv")

val checkStations = tasks.register("checkStations") {
    group = "tidepool"
    description = "Counts the stations in data/stations.csv."
    doLast {
        val count = stationsFile.asFile.readLines().size - 1
        println("data/stations.csv lists $count stations")
    }
}

With org.gradle.configuration-cache=true in gradle.properties, Gradle 9.8.1 runs the tasks and then fails the build when it tries to store the entry (the same shape in the Groovy DSL, with a def variable, was run too and succeeded, because a def in a Groovy script is a local variable of the script’s run method and is captured on its own; the problem is specific to the Kotlin script’s property):

FAILURE: Build failed with an exception.

* What went wrong:
Configuration cache problems found in this build.

1 problem was found storing the configuration cache.
- Task `:checkStations` of type `org.gradle.api.DefaultTask`: cannot serialize Gradle script object references as these are not supported with the configuration cache.
  See https://docs.gradle.org/9.8.1/userguide/configuration_cache_requirements.html#config_cache:requirements:disallowed_types
...
BUILD FAILED in 584ms
9 actionable tasks: 9 executed
Configuration cache entry discarded with 1 problem.

(The lines for the report location and the generic hints were left out.) The fix is the form used in the example above: read the value into a local val inside the registration block, so that the action captures only the file reference, not the script. The configuration cache is the subject of Configuration Cache & Isolated Projects.

Invoking Gradle

There are three ways to start a build, and a real project uses the first and the third:

  • In the IDE: IntelliJ IDEA, Android Studio, Visual Studio Code, Eclipse and NetBeans import the build through Gradle’s Tooling API and invoke Gradle when you build, clean or run the application. See CI & IDE Integration.

  • On the command line, with an installed Gradle: gradle build, gradle test, gradle clean build. See Installation.

  • With the wrapper, the recommended way: ./gradlew build (gradlew.bat build on Windows). The wrapper reads gradle/wrapper/gradle-wrapper.properties, downloads that Gradle version on first use and runs it.

The wrapper of the example project, as it should be committed (the official download URL, the checksum of the -bin distribution and the defaults of the wrapper task of Gradle 9.8.1; the build was actually run from a local copy of the same archive):

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=dce76f55f8e251a3a1f130eb120f30b3d271de2b76c9b0729d316b5a1b6dc01f
distributionUrl=https\://services.gradle.org/distributions/gradle-9.8.1-bin.zip
networkTimeout=10000
retries=0
retryBackOffMs=500
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

The full command line is described on Command-Line Interface, and the daemon that the client starts on Gradle Daemon. The single most useful habit is to ask Gradle what it knows, not to read the scripts: projects, tasks, help --task <task> and dependencies print the structure, the tasks and the dependency tree of a build.

The three tutorial tracks

The official user manual teaches these concepts in three tracks. Each starts with gradle init; the beginner and intermediate tracks generate a Java application (gradle init --type java-application --dsl kotlin, or --dsl groovy), and the advanced one generates a plugin project:

  • Beginner: six parts, from Initializing the Project and Running Tasks through Understanding Dependencies and Applying Plugins to Exploring Incremental Builds and Enabling the Build Cache. It starts at Part 1: Initializing the Project.

  • Intermediate: seven parts, which cover the same ground as this page and the next ones: Understanding the Build Lifecycle, Multi-Project Builds, Writing the Settings File, Writing a Build Script, Writing Tasks and Writing Plugins. It starts at Part 1: Initializing the Project.

  • Advanced: eight parts that build a plugin step by step: an extension, a custom task, a unit test, a DataFlow action, a functional test, a consumer project and local publishing. It starts at Part 1: Initializing the Project.

gradle init itself is described on Installation. A reasonable reading order for this section is: this page, the wrapper, the command line, the build lifecycle, the settings file and build scripts, tasks, plugins, then dependencies.

How the concepts differ from Ant and Maven

Idea Ant Maven Gradle

Unit of work

A task inside a target

A plugin goal, run in a lifecycle phase

A task, in a graph

Project descriptor

build.xml

pom.xml

build.gradle.kts or build.gradle, plus settings.gradle.kts

Multi-project

<subant>, <import>

<modules> in an aggregator POM

include(...) in the settings file

Extension point

Custom tasks, macros, antlibs

Plugins

Plugins, and tasks written in the build script

Build logic is

Imperative XML

Declarative XML, with the lifecycle fixed

A script (a program) in Kotlin or Groovy

Wrapper

None

Maven Wrapper, mvnw

Gradle Wrapper, gradlew

What Gradle adds to this table is that the build description is code, so there is more flexibility, and also more room to put work in the wrong phase. Its price is that the build file is not a data format that an IDE or a scanner can read without running Gradle. The pages that follow place the same ideas side by side with the other tools; the first one is Comparing Ant, Maven & Gradle.

Gradle 8.14 and older builds

None of the concepts on this page changed between 8.14 and 9.8.1, and a build of this shape works in both. The differences that matter for the scripts shown here, taken from the release notes and upgrade guides (not run here, because Gradle 8.14 was not available):

  • Gradle 9.0 needs JVM 17 or newer to run, where 8.14 runs on JVM 8 and newer; code can still be compiled for older Java versions through toolchains.

  • The configuration cache became stable in 8.1 and is the preferred mode in 9.0, but it still has to be enabled in both lines; it is announced as the default for Gradle 10.

  • The assignment mainClass = "..." (with =) in the Kotlin DSL works since Gradle 8.2, where it became the default; older Kotlin scripts use mainClass.set("...").

  • Gradle 9.0 removed jcenter() and other long-deprecated APIs; a build that uses them needs changes before it runs on 9.x.

Where the books are outdated

The book Gradle in Action (2014) was written for Gradle 1.7. Its model of build, project and task is still right, but several constructs it shows no longer exist in Gradle 9.8.1.

Book statement or practice Current status Source

task name << { }

Removed in Gradle 5.0. Use doLast { } and tasks.register.

Upgrading from Gradle 4.x to 5.0

The configurations compile, runtime, testCompile and testRuntime

Removed in Gradle 7.0. Use implementation, api, compileOnly, runtimeOnly, testImplementation and testRuntimeOnly.

Upgrading from Gradle 6.x to 7.0

apply plugin: 'x' and no plugin portal

The plugins {} block, and the Gradle Plugin Portal for community plugins.

Plugin Basics

Groovy DSL only

The Kotlin DSL (stable since 5.0) is the default for gradle init since 8.2.

Gradle 8.2 release notes

The daemon is opt-in (--daemon)

The daemon is on by default since Gradle 3.0.

Gradle 3.0 release notes

Run on JDK 1.5 or newer

JVM 17 or newer to run Gradle 9.x; older JDKs only through toolchains.

Upgrading to Gradle 9.0.0