The initialization phase in the Gradle Build lifecycle finds the root project and subprojects included in your project root directory using the settings file.

author gradle 6

Then, for each project included in the settings file, Gradle creates a Project instance.

Gradle then looks for a corresponding build script file, which is used in the configuration phase.

Build Scripts

Every Gradle build comprises one or more projects; a root project and subprojects.

A project typically corresponds to a software component that needs to be built, like a library or an application. It might represent a library JAR, a web application, or a distribution ZIP assembled from the JARs produced by other projects.

On the other hand, it might represent a thing to be done, such as deploying your application to staging or production environments.

Gradle scripts are written in either Groovy DSL or Kotlin DSL (domain-specific language).

A build script configures a project and is associated with an object of type Project.

Build

As the build script executes, it configures Project.

The build script is either a *.gradle file in Groovy or a *.gradle.kts file in Kotlin.

Build scripts configure Project objects and their children.

The Project object

The Project object is part of the Gradle API:

  • In the Groovy DSL, the Project object documentation is found here.

  • In the Kotlin DSL, the Project object documentation is found here.

Many top-level properties and blocks in a build script are part of the Project API.

For example, the following build script uses the Project.name property to print the name of the project:

build.gradle.kts
println(name)
println(project.name)
build.gradle
println name
println project.name
$ gradle -q check
project-api
project-api

Both println statements print out the same property.

The first uses the top-level reference to the name property of the Project object. The second statement uses the project property available to any build script, which returns the associated Project object.

Standard project properties

The Project object exposes a standard set of properties in your build script.

The following table lists a few commonly used properties:

Name Type Description

name

String

The name of the project directory.

path

String

The fully qualified name of the project.

description

String

A description for the project.

dependencies

DependencyHandler

Returns the dependency handler of the project.

repositories

RepositoryHandler

Returns the repository handler of the project.

layout

ProjectLayout

Provides access to several important locations for a project.

group

Object

The group of this project.

version

Object

The version of this project.

The following table lists a few commonly used methods:

Name Description

uri()

Resolves a file path to a URI, relative to the project directory of this project.

task()

Creates a Task with the given name and adds it to this project.

Build Script structure

The Build script is composed of { …​ }, a special object in both Groovy and Kotlin. This object is called a lambda in Kotlin or a closure in Groovy.

Simply put, the plugins{ } block is a method invocation in which a Kotlin lambda object or Groovy closure object is passed as the argument. It is the short form for:

plugins(function() {
    id("plugin")
})

Blocks are mapped to Gradle API methods.

The code inside the function is executed against a this object called a receiver in Kotlin lambda and a delegate in Groovy closure. Gradle determines the correct this object and invokes the correct corresponding method. The this of the method invocation id("plugin") object is of type PluginDependenciesSpec.

The build script is essentially composed of Gradle API calls built on top of the DSLs. Gradle executes the script line by line, top to bottom.

Let’s take a look at an example and break it down:

build.gradle.kts
plugins {   (1)
    id("application")
}

repositories {  (2)
    mavenCentral()
}

dependencies {  (3)
    testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.3")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
    implementation("com.google.guava:guava:32.1.1-jre")
}

application {   (4)
    mainClass = "com.example.Main"
}

tasks.named<Test>("test") { (5)
    useJUnitPlatform()
}

tasks.named<Javadoc>("javadoc").configure {
    exclude("app/Internal*.java")
    exclude("app/internal/*")
}

tasks.register<Zip>("zip-reports") {
    from("Reports/")
    include("*")
    archiveFileName.set("Reports.zip")
    destinationDirectory.set(file("/dir"))
}
build.gradle
plugins {   (1)
    id 'application'
}

repositories {  (2)
    mavenCentral()
}

dependencies {  (3)
    testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.3'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    implementation 'com.google.guava:guava:32.1.1-jre'
}

application {   (4)
    mainClass = 'com.example.Main'
}

tasks.named('test', Test) { (5)
    useJUnitPlatform()
}

tasks.named('javadoc', Javadoc).configure {
    exclude 'app/Internal*.java'
    exclude 'app/internal/*'
}

tasks.register('zip-reports', Zip) {
    from 'Reports/'
    include '*'
    archiveFileName = 'Reports.zip'
    destinationDirectory = file('/dir')
}
1 Apply plugins to the build.
2 Define the locations where dependencies can be found.
3 Add dependencies.
4 Set properties.
5 Register and configure tasks.

1. Apply plugins to the build

Plugins are used to extend Gradle. They are also used to modularize and reuse project configurations.

Plugins can be applied using the PluginDependenciesSpec plugins script block.

The plugins block is preferred:

build.gradle.kts
plugins {   (1)
    id("application")
}
build.gradle
plugins {   (1)
    id 'application'
}

In the example, the application plugin, which is included with Gradle, has been applied, describing our project as a Java application.

2. Define the locations where dependencies can be found

A project generally has a number of dependencies it needs to do its work. Dependencies include plugins, libraries, or components that Gradle must download for the build to succeed.

The build script lets Gradle know where to look for the binaries of the dependencies. More than one location can be provided:

build.gradle.kts
repositories {  (2)
    mavenCentral()
}
build.gradle
repositories {  (2)
    mavenCentral()
}

In the example, the guava library and the JetBrains Kotlin plugin (org.jetbrains.kotlin.jvm) will be downloaded from the Maven Central Repository.

3. Add dependencies

A project generally has a number of dependencies it needs to do its work. These dependencies are often libraries of precompiled classes that are imported in the project’s source code.

Dependencies are managed via configurations and are retrieved from repositories.

Use the DependencyHandler returned by Project.getDependencies() method to manage the dependencies. Use the RepositoryHandler returned by Project.getRepositories() method to manage the repositories.

build.gradle.kts
dependencies {  (3)
    testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.3")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
    implementation("com.google.guava:guava:32.1.1-jre")
}
build.gradle
dependencies {  (3)
    testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.3'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    implementation 'com.google.guava:guava:32.1.1-jre'
}

In the example, the application code uses Google’s guava libraries. Guava provides utility methods for collections, caching, primitives support, concurrency, common annotations, string processing, I/O, and validations.

4. Set properties

A plugin can add properties and methods to a project using extensions.

The Project object has an associated ExtensionContainer object that contains all the settings and properties for the plugins that have been applied to the project.

In the example, the application plugin added an application property, which is used to detail the main class of our Java application:

build.gradle.kts
application {   (4)
    mainClass = "com.example.Main"
}
build.gradle
application {   (4)
    mainClass = 'com.example.Main'
}

5. Register and configure tasks

Tasks perform some basic piece of work, such as compiling classes, or running unit tests, or zipping up a WAR file.

While tasks are typically defined in plugins, you may need to register or configure tasks in build scripts.

Registering a task adds the task to your project.

You can register tasks in a project using the TaskContainer.register(java.lang.String) method:

build.gradle.kts
tasks.register<Zip>("zip-reports") {
    from("Reports/")
    include("*")
    archiveFileName.set("Reports.zip")
    destinationDirectory.set(file("/dir"))
}
build.gradle
tasks.register('zip-reports', Zip) {
    from 'Reports/'
    include '*'
    archiveFileName = 'Reports.zip'
    destinationDirectory = file('/dir')
}

You may have seen usage of the TaskContainer.create(java.lang.String) method which should be avoided.

tasks.create<Zip>("zip-reports") { }
register(), which enables task configuration avoidance, is preferred over create().

You can locate a task to configure it using the TaskCollection.named(java.lang.String) method:

build.gradle.kts
tasks.named<Test>("test") { (5)
    useJUnitPlatform()
}
build.gradle
tasks.named('test', Test) { (5)
    useJUnitPlatform()
}

The example below configures the Javadoc task to automatically generate HTML documentation from Java code:

build.gradle.kts
tasks.named<Javadoc>("javadoc").configure {
    exclude("app/Internal*.java")
    exclude("app/internal/*")
}
build.gradle
tasks.named('javadoc', Javadoc).configure {
    exclude 'app/Internal*.java'
    exclude 'app/internal/*'
}

Build Scripting

A build script is made up of zero or more statements and script blocks:

println(project.layout.projectDirectory);

Statements can include method calls, property assignments, and local variable definitions:

version = '1.0.0.GA'

A script block is a method call which takes a closure/lambda as a parameter:

configurations {
}

The closure/lambda configures some delegate object as it executes:

repositories {
    google()
}

A build script is also a Groovy or a Kotlin script:

build.gradle.kts
tasks.register("upper") {
    doLast {
        val someString = "mY_nAmE"
        println("Original: $someString")
        println("Upper case: ${someString.toUpperCase()}")
    }
}
build.gradle
tasks.register('upper') {
    doLast {
        String someString = 'mY_nAmE'
        println "Original: $someString"
        println "Upper case: ${someString.toUpperCase()}"
    }
}
$ gradle -q upper
Original: mY_nAmE
Upper case: MY_NAME

It can contain elements allowed in a Groovy or Kotlin script, such as method definitions and class definitions:

build.gradle.kts
tasks.register("count") {
    doLast {
        repeat(4) { print("$it ") }
    }
}
build.gradle
tasks.register('count') {
    doLast {
        4.times { print "$it " }
    }
}
$ gradle -q count
0 1 2 3 

Flexible task registration

Using the capabilities of the Groovy or Kotlin language, you can register multiple tasks in a loop:

build.gradle.kts
repeat(4) { counter ->
    tasks.register("task$counter") {
        doLast {
            println("I'm task number $counter")
        }
    }
}
build.gradle
4.times { counter ->
    tasks.register("task$counter") {
        doLast {
            println "I'm task number $counter"
        }
    }
}
$ gradle -q task1
I'm task number 1

Gradle Types

In Gradle, types, properties, and providers are foundational for managing and configuring build logic:

  • Types: Gradle defines types (like Task, Configuration, File, etc.) to represent build components. You can extend these types to create custom tasks or domain objects.

  • Properties: Gradle properties (e.g., Property<T>, ListProperty<T>, SetProperty<T>) are used for build configuration. They allow lazy evaluation, meaning their values are calculated only when needed, enhancing flexibility and performance.

  • Providers: A Provider<T> represents a value that is computed or retrieved lazily. Providers are often used with properties to defer value computation until necessary. This is especially useful for integrating dynamic, runtime values into your build.

You can learn more about this in Understanding Gradle Types.

Declare Variables

Build scripts can declare two variables: local variables and extra properties.

Local Variables

Declare local variables with the val keyword. Local variables are only visible in the scope where they have been declared. They are a feature of the underlying Kotlin language.

Declare local variables with the def keyword. Local variables are only visible in the scope where they have been declared. They are a feature of the underlying Groovy language.

build.gradle.kts
val dest = "dest"

tasks.register<Copy>("copy") {
    from("source")
    into(dest)
}
build.gradle
def dest = 'dest'

tasks.register('copy', Copy) {
    from 'source'
    into dest
}

Extra Properties

Gradle’s enhanced objects, including projects, tasks, and source sets, can hold user-defined properties.

Add, read, and set extra properties via the owning object’s extra property. Alternatively, you can access extra properties via Kotlin delegated properties using by extra.

Add, read, and set extra properties via the owning object’s ext property. Alternatively, you can use an ext block to add multiple properties simultaneously.

build.gradle.kts
plugins {
    id("java-library")
}

val springVersion by extra("3.1.0.RELEASE")
val emailNotification by extra { "build@master.org" }

sourceSets.all { extra["purpose"] = null }

sourceSets {
    main {
        extra["purpose"] = "production"
    }
    test {
        extra["purpose"] = "test"
    }
    create("plugin") {
        extra["purpose"] = "production"
    }
}

tasks.register("printProperties") {
    val springVersion = springVersion
    val emailNotification = emailNotification
    val productionSourceSets = provider {
        sourceSets.matching { it.extra["purpose"] == "production" }.map { it.name }
    }
    doLast {
        println(springVersion)
        println(emailNotification)
        productionSourceSets.get().forEach { println(it) }
    }
}
build.gradle
plugins {
    id 'java-library'
}

ext {
    springVersion = "3.1.0.RELEASE"
    emailNotification = "build@master.org"
}

sourceSets.all { ext.purpose = null }

sourceSets {
    main {
        purpose = "production"
    }
    test {
        purpose = "test"
    }
    plugin {
        purpose = "production"
    }
}

tasks.register('printProperties') {
    def springVersion = springVersion
    def emailNotification = emailNotification
    def productionSourceSets = provider {
        sourceSets.matching { it.purpose == "production" }.collect { it.name }
    }
    doLast {
        println springVersion
        println emailNotification
        productionSourceSets.get().each { println it }
    }
}
$ gradle -q printProperties
3.1.0.RELEASE
build@master.org
main
plugin

This example adds two extra properties to the project object via by extra. Additionally, this example adds a property named purpose to each source set by setting extra["purpose"] to null. Once added, you can read and set these properties via extra.

This example adds two extra properties to the project object via an ext block. Additionally, this example adds a property named purpose to each source set by setting ext.purpose to null. Once added, you can read and set all these properties just like predefined ones.

Gradle requires special syntax for adding a property so that it can fail fast. For example, this allows Gradle to recognize when a script attempts to set a property that does not exist. You can access extra properties anywhere where you can access their owning object. This gives extra properties a wider scope than local variables. Subprojects can access extra properties on their parent projects.

For more information about extra properties, see ExtraPropertiesExtension in the API documentation.

Configure Arbitrary Objects

The example greet() task shows an example of arbitrary object configuration:

build.gradle.kts
class UserInfo(
    var name: String? = null,
    var email: String? = null
)

tasks.register("greet") {
    val user = UserInfo().apply {
        name = "Isaac Newton"
        email = "isaac@newton.me"
    }
    doLast {
        println(user.name)
        println(user.email)
    }
}
build.gradle
class UserInfo {
    String name
    String email
}

tasks.register('greet') {
    def user = configure(new UserInfo()) {
        name = "Isaac Newton"
        email = "isaac@newton.me"
    }
    doLast {
        println user.name
        println user.email
    }
}
$ gradle -q greet
Isaac Newton
isaac@newton.me

Closure Delegates

Each closure has a delegate object. Groovy uses this delegate to look up variable and method references to nonlocal variables and closure parameters. Gradle uses this for configuration closures, where the delegate object refers to the object being configured.

build.gradle
dependencies {
    assert delegate == project.dependencies
    testImplementation('junit:junit:4.13')
    delegate.testImplementation('junit:junit:4.13')
}

Default imports

To make build scripts more concise, Gradle automatically adds a set of import statements to scripts.

As a result, instead of writing throw new org.gradle.api.tasks.StopExecutionException(), you can write throw new StopExecutionException() instead.

Gradle implicitly adds the following imports to each script:

import org.gradle.*
import org.gradle.api.*
import org.gradle.api.artifacts.*
import org.gradle.api.artifacts.capability.*
import org.gradle.api.artifacts.component.*
import org.gradle.api.artifacts.dsl.*
import org.gradle.api.artifacts.ivy.*
import org.gradle.api.artifacts.maven.*
import org.gradle.api.artifacts.query.*
import org.gradle.api.artifacts.repositories.*
import org.gradle.api.artifacts.result.*
import org.gradle.api.artifacts.transform.*
import org.gradle.api.artifacts.type.*
import org.gradle.api.artifacts.verification.*
import org.gradle.api.attributes.*
import org.gradle.api.attributes.java.*
import org.gradle.api.attributes.plugin.*
import org.gradle.api.cache.*
import org.gradle.api.capabilities.*
import org.gradle.api.component.*
import org.gradle.api.configuration.*
import org.gradle.api.credentials.*
import org.gradle.api.distribution.*
import org.gradle.api.distribution.plugins.*
import org.gradle.api.execution.*
import org.gradle.api.file.*
import org.gradle.api.flow.*
import org.gradle.api.initialization.*
import org.gradle.api.initialization.definition.*
import org.gradle.api.initialization.dsl.*
import org.gradle.api.initialization.resolve.*
import org.gradle.api.invocation.*
import org.gradle.api.java.archives.*
import org.gradle.api.jvm.*
import org.gradle.api.launcher.cli.*
import org.gradle.api.logging.*
import org.gradle.api.logging.configuration.*
import org.gradle.api.model.*
import org.gradle.api.plugins.*
import org.gradle.api.plugins.antlr.*
import org.gradle.api.plugins.catalog.*
import org.gradle.api.plugins.jvm.*
import org.gradle.api.plugins.quality.*
import org.gradle.api.plugins.scala.*
import org.gradle.api.problems.*
import org.gradle.api.project.*
import org.gradle.api.provider.*
import org.gradle.api.publish.*
import org.gradle.api.publish.ivy.*
import org.gradle.api.publish.ivy.plugins.*
import org.gradle.api.publish.ivy.tasks.*
import org.gradle.api.publish.maven.*
import org.gradle.api.publish.maven.plugins.*
import org.gradle.api.publish.maven.tasks.*
import org.gradle.api.publish.plugins.*
import org.gradle.api.publish.tasks.*
import org.gradle.api.reflect.*
import org.gradle.api.reporting.*
import org.gradle.api.reporting.components.*
import org.gradle.api.reporting.dependencies.*
import org.gradle.api.reporting.dependents.*
import org.gradle.api.reporting.model.*
import org.gradle.api.reporting.plugins.*
import org.gradle.api.resources.*
import org.gradle.api.services.*
import org.gradle.api.specs.*
import org.gradle.api.tasks.*
import org.gradle.api.tasks.ant.*
import org.gradle.api.tasks.application.*
import org.gradle.api.tasks.bundling.*
import org.gradle.api.tasks.compile.*
import org.gradle.api.tasks.diagnostics.*
import org.gradle.api.tasks.diagnostics.configurations.*
import org.gradle.api.tasks.incremental.*
import org.gradle.api.tasks.javadoc.*
import org.gradle.api.tasks.options.*
import org.gradle.api.tasks.scala.*
import org.gradle.api.tasks.testing.*
import org.gradle.api.tasks.testing.junit.*
import org.gradle.api.tasks.testing.junitplatform.*
import org.gradle.api.tasks.testing.testng.*
import org.gradle.api.tasks.util.*
import org.gradle.api.tasks.wrapper.*
import org.gradle.api.toolchain.management.*
import org.gradle.authentication.*
import org.gradle.authentication.aws.*
import org.gradle.authentication.http.*
import org.gradle.build.event.*
import org.gradle.buildconfiguration.tasks.*
import org.gradle.buildinit.*
import org.gradle.buildinit.plugins.*
import org.gradle.buildinit.tasks.*
import org.gradle.caching.*
import org.gradle.caching.configuration.*
import org.gradle.caching.http.*
import org.gradle.caching.local.*
import org.gradle.concurrent.*
import org.gradle.external.javadoc.*
import org.gradle.ide.visualstudio.*
import org.gradle.ide.visualstudio.plugins.*
import org.gradle.ide.visualstudio.tasks.*
import org.gradle.ide.xcode.*
import org.gradle.ide.xcode.plugins.*
import org.gradle.ide.xcode.tasks.*
import org.gradle.ivy.*
import org.gradle.jvm.*
import org.gradle.jvm.application.scripts.*
import org.gradle.jvm.application.tasks.*
import org.gradle.jvm.tasks.*
import org.gradle.jvm.toolchain.*
import org.gradle.language.*
import org.gradle.language.assembler.*
import org.gradle.language.assembler.plugins.*
import org.gradle.language.assembler.tasks.*
import org.gradle.language.base.*
import org.gradle.language.base.artifact.*
import org.gradle.language.base.compile.*
import org.gradle.language.base.plugins.*
import org.gradle.language.base.sources.*
import org.gradle.language.c.*
import org.gradle.language.c.plugins.*
import org.gradle.language.c.tasks.*
import org.gradle.language.cpp.*
import org.gradle.language.cpp.plugins.*
import org.gradle.language.cpp.tasks.*
import org.gradle.language.java.artifact.*
import org.gradle.language.jvm.tasks.*
import org.gradle.language.nativeplatform.*
import org.gradle.language.nativeplatform.tasks.*
import org.gradle.language.objectivec.*
import org.gradle.language.objectivec.plugins.*
import org.gradle.language.objectivec.tasks.*
import org.gradle.language.objectivecpp.*
import org.gradle.language.objectivecpp.plugins.*
import org.gradle.language.objectivecpp.tasks.*
import org.gradle.language.plugins.*
import org.gradle.language.rc.*
import org.gradle.language.rc.plugins.*
import org.gradle.language.rc.tasks.*
import org.gradle.language.scala.tasks.*
import org.gradle.language.swift.*
import org.gradle.language.swift.plugins.*
import org.gradle.language.swift.tasks.*
import org.gradle.maven.*
import org.gradle.model.*
import org.gradle.nativeplatform.*
import org.gradle.nativeplatform.platform.*
import org.gradle.nativeplatform.plugins.*
import org.gradle.nativeplatform.tasks.*
import org.gradle.nativeplatform.test.*
import org.gradle.nativeplatform.test.cpp.*
import org.gradle.nativeplatform.test.cpp.plugins.*
import org.gradle.nativeplatform.test.cunit.*
import org.gradle.nativeplatform.test.cunit.plugins.*
import org.gradle.nativeplatform.test.cunit.tasks.*
import org.gradle.nativeplatform.test.googletest.*
import org.gradle.nativeplatform.test.googletest.plugins.*
import org.gradle.nativeplatform.test.plugins.*
import org.gradle.nativeplatform.test.tasks.*
import org.gradle.nativeplatform.test.xctest.*
import org.gradle.nativeplatform.test.xctest.plugins.*
import org.gradle.nativeplatform.test.xctest.tasks.*
import org.gradle.nativeplatform.toolchain.*
import org.gradle.nativeplatform.toolchain.plugins.*
import org.gradle.normalization.*
import org.gradle.platform.*
import org.gradle.platform.base.*
import org.gradle.platform.base.binary.*
import org.gradle.platform.base.component.*
import org.gradle.platform.base.plugins.*
import org.gradle.plugin.devel.*
import org.gradle.plugin.devel.plugins.*
import org.gradle.plugin.devel.tasks.*
import org.gradle.plugin.management.*
import org.gradle.plugin.use.*
import org.gradle.plugins.ear.*
import org.gradle.plugins.ear.descriptor.*
import org.gradle.plugins.ide.*
import org.gradle.plugins.ide.api.*
import org.gradle.plugins.ide.eclipse.*
import org.gradle.plugins.ide.idea.*
import org.gradle.plugins.signing.*
import org.gradle.plugins.signing.signatory.*
import org.gradle.plugins.signing.signatory.pgp.*
import org.gradle.plugins.signing.type.*
import org.gradle.plugins.signing.type.pgp.*
import org.gradle.process.*
import org.gradle.swiftpm.*
import org.gradle.swiftpm.plugins.*
import org.gradle.swiftpm.tasks.*
import org.gradle.testing.base.*
import org.gradle.testing.base.plugins.*
import org.gradle.testing.jacoco.plugins.*
import org.gradle.testing.jacoco.tasks.*
import org.gradle.testing.jacoco.tasks.rules.*
import org.gradle.testkit.runner.*
import org.gradle.util.*
import org.gradle.vcs.*
import org.gradle.vcs.git.*
import org.gradle.work.*
import org.gradle.workers.*

Next Step: Learn how to use Tasks >>