# Writing Build Scripts

version 7.3

This chapter looks at some of the details of writing a build script.

## The Gradle build language

Gradle provides a domain specific language, or DSL, for describing builds. This build language is available in Groovy and Kotlin.

A Groovy build script can contain any Groovy language element.[1] A Kotlin build script can contain any Kotlin language element. Gradle assumes that each build script is encoded using UTF-8.

## The Project API

Build scripts describe your build by configuring projects. A project is an abstract concept, but you typically map a Gradle project to a software component that needs to be built, like a library or an application. Each build script you have is associated with an object of type Project and as the build script executes, it configures this `Project`.

In fact, almost all top-level properties and blocks in a build script are part of the `Project` API. To demonstrate, take a look at this example build script that prints the name of its project, which is accessed via the Project.name property:

Example 1. Accessing property of the Project object
build.gradle

    println name
    println project.name

build.gradle.kts

    println(name)
    println(project.name)

Output of `gradle -q check`

    > 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 other statement uses the `project` property available to any build script, which returns the associated `Project` object. Only if you define a property or a method which has the same name as a member of the `Project` object, would you need to use the `project` property.

### Standard project properties

The `Project` object provides some standard properties, which are available in your build script. The following table lists a few of the commonly used ones.

Table 1. Project Properties
Name | Type | Default Value
--- | --- | ---
`project` | Project | The `Project` instance
`name` | `String` | The name of the project directory.
`path` | `String` | The absolute path of the project.
`description` | `String` | A description for the project.
`projectDir` | `File` | The directory containing the build script.
`buildDir` | `File` | `projectDir/build`
`group` | `Object` | `unspecified`
`version` | `Object` | `unspecified`
`ant` | AntBuilder | An `AntBuilder` instance

| Script with other targets The build scripts described here target `Project` objects. There are also settings scripts and init scripts that respectively target Settings and Gradle objects.

## The script API

When Gradle executes a Groovy build script (`.gradle`), it compiles the script into a class which implements Script. This means that all of the properties and methods declared by the `Script` interface are available in your script.

When Gradle executes a Kotlin build script (`.gradle.kts`), it compiles the script into a subclass of KotlinBuildScript. This means that all of the visible properties and functions declared by the `KotlinBuildScript` type are available in your script. Also see the KotlinSettingsScript and KotlinInitScript types respectively for settings scripts and init scripts.

## Declaring variables

There are two kinds of variables that can be declared in a build script: local variables and extra properties.

### Local variables

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

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

Example 2. Using local variables
build.gradle

    def dest = 'dest'

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

build.gradle.kts

    val dest = "dest"

    tasks.register<Copy>("copy") {
        from("source")
        into(dest)
    }

### Extra properties

All enhanced objects in Gradle’s domain model can hold extra user-defined properties. This includes, but is not limited to, projects, tasks, and source sets.

Extra properties can be added, read and set via the owning object’s `ext` property. Alternatively, an `ext` block can be used to add multiple properties at once.

Extra properties can be added, read and set via the owning object’s `extra` property. Alternatively, they can be addressed via Kotlin delegated properties using `by extra`.

Example 3. Using extra properties
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') {
        doLast {
            println springVersion
            println emailNotification
            sourceSets.matching { it.purpose == "production" }.each { println it.name }
        }
    }

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") {
        doLast {
            println(springVersion)
            println(emailNotification)
            sourceSets.matching { it.extra["purpose"] == "production" }.forEach { println(it.name) }
        }
    }

Output of `gradle -q printProperties`

    > gradle -q printProperties
    3.1.0.RELEASE
    build@master.org
    main
    plugin

In this example, an `ext` block adds two extra properties to the `project` object. Additionally, a property named `purpose` is added to each source set by setting `ext.purpose` to `null` (`null` is a permissible value). Once the properties have been added, they can be read and set like predefined properties.

In this example, two extra properties are added to the `project` object using `by extra`. Additionally, a property named `purpose` is added to each source set by setting `extra["purpose"]` to `null` (`null` is a permissible value). Once the properties have been added, they can be read and set on `extra`.

By requiring special syntax for adding a property, Gradle can fail fast when an attempt is made to set a (predefined or extra) property but the property is misspelled or does not exist. Extra properties can be accessed from anywhere their owning object can be accessed, giving them a wider scope than local variables. Extra properties on a project are visible from its subprojects.

For further details on extra properties and their API, see the ExtraPropertiesExtension class in the API documentation.

## Configuring arbitrary objects

You can configure arbitrary objects in the following very readable way.

Example 4. Configuring arbitrary objects
build.gradle

    import java.text.FieldPosition

    tasks.register('configure') {
        doLast {
            def pos = configure(new FieldPosition(10)) {
                beginIndex = 1
                endIndex = 5
            }
            println pos.beginIndex
            println pos.endIndex
        }
    }

build.gradle.kts

    import java.text.FieldPosition

    tasks.register("configure") {
        doLast {
            val pos = FieldPosition(10).apply {
                beginIndex = 1
                endIndex = 5
            }
            println(pos.beginIndex)
            println(pos.endIndex)
        }
    }

Output of `gradle -q configure`

    > gradle -q configure
    1
    5

## Configuring arbitrary objects using an external script

You can also configure arbitrary objects using an external script.

| Only supported from a Groovy script Configuring arbitrary objects using an external script is not yet supported by the Kotlin DSL. See gradle/kotlin-dsl#659 for more information.

Example 5. Configuring arbitrary objects using a script
build.gradle

    tasks.register('configure') {
        doLast {
            def pos = new java.text.FieldPosition(10)
            // Apply the script
            apply from: 'other.gradle', to: pos
            println pos.beginIndex
            println pos.endIndex
        }
    }

other.gradle

    // Set properties.
    beginIndex = 1
    endIndex = 5

Output of `gradle -q configure`

    > gradle -q configure
    1
    5

## Some Groovy basics

| Looking for some Kotlin basics, the Kotlin reference documentation and Kotlin Koans should be useful to you.

The Groovy language provides plenty of features for creating DSLs, and the Gradle build language takes advantage of these. Understanding how the build language works will help you when you write your build script, and in particular, when you start to write custom plugins and tasks.

### Groovy JDK

Groovy adds lots of useful methods to the standard Java classes. For example, `Iterable` gets an `each` method, which iterates over the elements of the `Iterable`:

Example 6. Groovy JDK methods
build.gradle

    // Iterable gets an each() method
    configurations.runtimeClasspath.each { File f -> println f }

Have a look at https://groovy-lang.org/gdk.html for more details.

### Property accessors

Groovy automatically converts a property reference into a call to the appropriate getter or setter method.

Example 7. Property accessors
build.gradle

    // Using a getter method
    println project.buildDir
    println getProject().getBuildDir()

    // Using a setter method
    project.buildDir = 'target'
    getProject().setBuildDir('target')

### Optional parentheses on method calls

Parentheses are optional for method calls.

Example 8. Method call without parentheses
build.gradle

    test.systemProperty 'some.prop', 'value'
    test.systemProperty('some.prop', 'value')

### List and map literals

Groovy provides some shortcuts for defining `List` and `Map` instances. Both kinds of literals are straightforward, but map literals have some interesting twists.

For instance, the “`apply`” method (where you typically apply plugins) actually takes a map parameter. However, when you have a line like “`apply plugin:'java'`”, you aren’t actually using a map literal, you’re actually using “named parameters”, which have almost exactly the same syntax as a map literal (without the wrapping brackets). That named parameter list gets converted to a map when the method is called, but it doesn’t start out as a map.

Example 9. List and map literals
build.gradle

    // List literal
    test.includes = ['org/gradle/api/**', 'org/gradle/internal/**']

    List<String> list = new ArrayList<String>()
    list.add('org/gradle/api/**')
    list.add('org/gradle/internal/**')
    test.includes = list

    // Map literal.
    Map<String, String> map = [key1:'value1', key2: 'value2']

    // Groovy will coerce named arguments
    // into a single map argument
    apply plugin: 'java'

### Closures as the last parameter in a method

The Gradle DSL uses closures in many places. You can find out more about closures here. When the last parameter of a method is a closure, you can place the closure after the method call:

Example 10. Closure as method parameter
build.gradle

    repositories {
        println "in a closure"
    }
    repositories() { println "in a closure" }
    repositories({ println "in a closure" })

### Closure delegate

Each closure has a `delegate` object, which Groovy uses to look up variable and method references which are not local variables or parameters of the closure. Gradle uses this for configuration closures, where the `delegate` object is set to the object to be configured.

Example 11. Closure delegates
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 the Gradle scripts. This means that instead of using `throw new org.gradle.api.tasks.StopExecutionException()` you can just type `throw new StopExecutionException()` instead.

Listed below are the imports added to each script:

Gradle default imports

    import org.gradle.*
    import org.gradle.api.*
    import org.gradle.api.artifacts.*
    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.capabilities.*
    import org.gradle.api.component.*
    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.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.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.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.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.authentication.*
    import org.gradle.authentication.aws.*
    import org.gradle.authentication.http.*
    import org.gradle.build.event.*
    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.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.*

1. Any language element except for statement labels.