# Build Script Basics

version 6.5

This chapter introduces you to the basics of writing Gradle build scripts. For a quick hands-on introduction, try the Creating New Gradle Builds guide.

## Projects and tasks

Everything in Gradle sits on top of two basic concepts: projects and tasks.

Every Gradle build is made up of one or more projects. What a project represents depends on what it is that you are doing with Gradle. For example, a project might represent a library JAR or a web application. It might represent a distribution ZIP assembled from the JARs produced by other projects. A project does not necessarily represent a thing to be built. It might represent a thing to be done, such as deploying your application to staging or production environments. Don’t worry if this seems a little vague for now. Gradle’s build-by-convention support adds a more concrete definition for what a project is.

Each project is made up of one or more tasks. A task represents some atomic piece of work which a build performs. This might be compiling some classes, creating a JAR, generating Javadoc, or publishing some archives to a repository.

For now, we will look at defining some simple tasks in a build with one project. Later chapters will look at working with multiple projects and more about working with projects and tasks.

## Hello world

You run a Gradle build using the `gradle` command. The `gradle` command looks for a file called `build.gradle` in the current directory.[1] We call this `build.gradle` file a build script, although strictly speaking it is a build configuration script, as we will see later. The build script defines a project and its tasks.

To try this out, create the following build script named `build.gradle`.

You run a Gradle build using the `gradle` command. The `gradle` command looks for a file called `build.gradle.kts` in the current directory.[2] We call this `build.gradle.kts` file a build script, although strictly speaking it is a build configuration script, as we will see later. The build script defines a project and its tasks.

To try this out, create the following build script named `build.gradle.kts`.

Example 1. Your first build script
build.gradle

    task hello {
        doLast {
            println 'Hello world!'
        }
    }

build.gradle.kts

    tasks.register("hello") {
        doLast {
            println("Hello world!")
        }
    }

In a command-line shell, move to the containing directory and execute the build script with `gradle -q hello`:

| What does `-q` do? Most of the examples in this user guide are run with the `-q` command-line option. This suppresses Gradle’s log messages, so that only the output of the tasks is shown. This keeps the example output in this user guide a little clearer. You don’t need to use this option if you don’t want to. See Logging for more details about the command-line options which affect Gradle’s output.

Example 2. Execution of a build script
Output of `gradle -q hello`

    > gradle -q hello
    include::{snippetsPath}/tutorial/hello/tests/hello.out

What’s going on here? This build script defines a single task, called `hello`, and adds an action to it. When you run `gradle hello`, Gradle executes the `hello` task, which in turn executes the action you’ve provided. The action is simply a block containing some code to execute.

If you think this looks similar to Ant’s targets, you would be right. Gradle tasks are the equivalent to Ant targets, but as you will see, they are much more powerful. We have used a different terminology than Ant as we think the word task is more expressive than the word target. Unfortunately this introduces a terminology clash with Ant, as Ant calls its commands, such as `javac` or `copy`, tasks. So when we talk about tasks, we always mean Gradle tasks, which are the equivalent to Ant’s targets. If we talk about Ant tasks (Ant commands), we explicitly say Ant task.

## Build scripts are code

Gradle’s build scripts give you the full power of Groovy and Kotlin. As an appetizer, have a look at this:

Example 3. Using Groovy or Kotlin in Gradle’s tasks
build.gradle

    task upper {
        doLast {
            String someString = 'mY_nAmE'
            println "Original: $someString"
            println "Upper case: ${someString.toUpperCase()}"
        }
    }

build.gradle.kts

    tasks.register("upper") {
        doLast {
            val someString = "mY_nAmE"
            println("Original: $someString")
            println("Upper case: ${someString.toUpperCase()}")
        }
    }

Output of `gradle -q upper`

    > gradle -q upper
    include::{snippetsPath}/tutorial/upper/tests/upper.out

or

Example 4. Using Groovy or Kotlin in Gradle’s tasks
build.gradle

    task count {
        doLast {
            4.times { print "$it " }
        }
    }

build.gradle.kts

    tasks.register("count") {
        doLast {
            repeat(4) { print("$it ") }
        }
    }

Output of `gradle -q count`

    > gradle -q count
    include::{snippetsPath}/tutorial/count/tests/count.out

## Task dependencies

As you probably have guessed, you can declare tasks that depend on other tasks.

Example 5. Declaration of task that depends on other task
build.gradle

    task hello {
        doLast {
            println 'Hello world!'
        }
    }
    task intro {
        dependsOn hello
        doLast {
            println "I'm Gradle"
        }
    }

build.gradle.kts

    tasks.register("hello") {
        doLast {
            println("Hello world!")
        }
    }
    tasks.register("intro") {
        dependsOn("hello")
        doLast {
            println("I'm Gradle")
        }
    }

Output of `gradle -q intro`

    > gradle -q intro
    include::{snippetsPath}/tutorial/intro/tests/intro.out

To add a dependency, the corresponding task does not need to exist.

Example 6. Lazy dependsOn - the other task does not exist (yet)
build.gradle

    task taskX {
        dependsOn 'taskY'
        doLast {
            println 'taskX'
        }
    }
    task taskY {
        doLast {
            println 'taskY'
        }
    }

build.gradle.kts

    tasks.register("taskX") {
        dependsOn("taskY")
        doLast {
            println("taskX")
        }
    }
    tasks.register("taskY") {
        doLast {
            println("taskY")
        }
    }

Output of `gradle -q taskX`

    > gradle -q taskX
    include::{snippetsPath}/tutorial/lazyDependsOn/tests/lazyDependsOn.out

The dependency of `taskX` to `taskY` may be declared before `taskY` is defined. This freedom is very important for multi-project builds. Task dependencies are discussed in more detail in Adding dependencies to a task.

Please notice that you can’t use shortcut notation when referring to a task that is not yet defined.

## Dynamic tasks

The power of Groovy or Kotlin can be used for more than defining what a task does. For example, you can also use it to dynamically create tasks.

Example 7. Dynamic creation of a task
build.gradle

    4.times { counter ->
        task "task$counter" {
            doLast {
                println "I'm task number $counter"
            }
        }
    }

build.gradle.kts

    repeat(4) { counter ->
        tasks.register("task$counter") {
            doLast {
                println("I'm task number $counter")
            }
        }
    }

Output of `gradle -q task1`

    > gradle -q task1
    include::{snippetsPath}/tutorial/dynamic/tests/dynamic.out

## Manipulating existing tasks

Once tasks are created they can be accessed via an API. For instance, you could use this to dynamically add dependencies to a task, at runtime. Ant doesn’t allow anything like this.

Example 8. Accessing a task via API - adding a dependency
build.gradle

    4.times { counter ->
        task "task$counter" {
            doLast {
                println "I'm task number $counter"
            }
        }
    }
    task0.dependsOn task2, task3

build.gradle.kts

    repeat(4) { counter ->
        tasks.register("task$counter") {
            doLast {
                println("I'm task number $counter")
            }
        }
    }
    tasks.named("task0") { dependsOn("task2", "task3") }

Output of `gradle -q task0`

    > gradle -q task0
    include::{snippetsPath}/tutorial/dynamicDepends/tests/dynamicDepends.out

Or you can add behavior to an existing task.

Example 9. Accessing a task via API - adding behaviour
build.gradle

    task hello {
        doLast {
            println 'Hello Earth'
        }
    }
    hello.doFirst {
        println 'Hello Venus'
    }
    hello.configure {
        doLast {
            println 'Hello Mars'
        }
    }
    hello.configure {
        doLast {
            println 'Hello Jupiter'
        }
    }

build.gradle.kts

    val hello by tasks.registering {
        doLast {
            println("Hello Earth")
        }
    }
    hello {
        doFirst {
            println("Hello Venus")
        }
    }
    hello {
        doLast {
            println("Hello Mars")
        }
    }
    hello {
        doLast {
            println("Hello Jupiter")
        }
    }

Output of `gradle -q hello`

    > gradle -q hello
    include::{snippetsPath}/tutorial/helloEnhanced/tests/helloEnhanced.out

The calls `doFirst` and `doLast` can be executed multiple times. They add an action to the beginning or the end of the task’s actions list. When the task executes, the actions in the action list are executed in order.

## Groovy DSL shortcut notations

There is a convenient notation for accessing an existing task. Each task is available as a property of the build script:

Example 10. Accessing task as a property of the build script
build.gradle

    task hello {
        doLast {
            println 'Hello world!'
        }
    }
    hello.doLast {
        println "Greetings from the $hello.name task."
    }

Output of `gradle -q hello`

    > gradle -q hello
    include::{snippetsPath}/tutorial/helloWithShortCut/tests/helloWithShortCut.out

This enables very readable code, especially when using the tasks provided by the plugins, like the `compile` task.

## Extra task properties

You can add your own properties to a task. To add a property named `myProperty`, set `ext.myProperty` to an initial value. From that point on, the property can be read and set like a predefined task property.

Example 11. Adding extra properties to a task
build.gradle

    task myTask {
        ext.myProperty = "myValue"
    }

    task printTaskProperties {
        doLast {
            println myTask.myProperty
        }
    }

build.gradle.kts

    tasks.register("myTask") {
        extra["myProperty"] = "myValue"
    }

    tasks.register("printTaskProperties") {
        doLast {
            println(tasks["myTask"].extra["myProperty"])
        }
    }

Output of `gradle -q printTaskProperties`

    > gradle -q printTaskProperties
    include::{snippetsPath}/tutorial/extraProperties/tests/extraTaskProperties.out

Extra properties aren’t limited to tasks. You can read more about them in Extra properties.

## Using Ant Tasks

Ant tasks are first-class citizens in Gradle. Gradle provides excellent integration for Ant tasks by simply relying on Groovy. Groovy is shipped with the fantastic `AntBuilder`. Using Ant tasks from Gradle is as convenient and more powerful than using Ant tasks from a `build.xml` file. And it is usable from Kotlin too. From the example below, you can learn how to execute Ant tasks and how to access Ant properties:

Example 12. Using AntBuilder to execute ant.loadfile target
build.gradle

    task loadfile {
        doLast {
            def files = file('./antLoadfileResources').listFiles().sort()
            files.each { File file ->
                if (file.isFile()) {
                    ant.loadfile(srcFile: file, property: file.name)
                    println " *** $file.name ***"
                    println "${ant.properties[file.name]}"
                }
            }
        }
    }

build.gradle.kts

    tasks.register("loadfile") {
        doLast {
            val files = file("./antLoadfileResources").listFiles().sorted()
            files.forEach { file ->
                if (file.isFile) {
                    ant.withGroovyBuilder {
                        "loadfile"("srcFile" to file, "property" to file.name)
                    }
                    println(" *** ${file.name} ***")
                    println("${ant.properties[file.name]}")
                }
            }
        }
    }

Output of `gradle -q loadfile`

    > gradle -q loadfile
    include::{snippetsPath}/tutorial/antLoadfile/tests/antLoadfile.out

There is lots more you can do with Ant in your build scripts. You can find out more in Ant.

## Using methods

Gradle scales in how you can organize your build logic. The first level of organizing your build logic for the example above, is extracting a method.

Example 13. Using methods to organize your build logic
build.gradle

    task checksum {
        doLast {
            fileList('./antLoadfileResources').each { File file ->
                ant.checksum(file: file, property: "cs_$file.name")
                println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
            }
        }
    }

    task loadfile {
        doLast {
            fileList('./antLoadfileResources').each { File file ->
                ant.loadfile(srcFile: file, property: file.name)
                println "I'm fond of $file.name"
            }
        }
    }

    File[] fileList(String dir) {
        file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
    }

build.gradle.kts

    tasks.register("checksum") {
        doLast {
            fileList("./antLoadfileResources").forEach { file ->
                ant.withGroovyBuilder {
                    "checksum"("file" to file, "property" to "cs_${file.name}")
                }
                println("$file.name Checksum: ${ant.properties["cs_${file.name}"]}")
            }
        }
    }

    tasks.register("loadfile") {
        doLast {
            fileList("./antLoadfileResources").forEach { file ->
                ant.withGroovyBuilder {
                    "loadfile"("srcFile" to file, "property" to file.name)
                }
                println("I'm fond of ${file.name}")
            }
        }
    }

    fun fileList(dir: String): List<File> =
        file(dir).listFiles { file: File -> file.isFile }.sorted()

Output of `gradle -q loadfile`

    > gradle -q loadfile
    include::{snippetsPath}/tutorial/antLoadfileWithMethod/tests/antLoadfileWithMethod.out

Later you will see that such methods can be shared among subprojects in multi-project builds. If your build logic becomes more complex, Gradle offers you other very convenient ways to organize it. We have devoted a whole chapter to this. See Organizing Gradle Projects.

## Default tasks

Gradle allows you to define one or more default tasks that are executed if no other tasks are specified.

Example 14. Defining a default task
build.gradle

    defaultTasks 'clean', 'run'

    task clean {
        doLast {
            println 'Default Cleaning!'
        }
    }

    task run {
        doLast {
            println 'Default Running!'
        }
    }

    task other {
        doLast {
            println "I'm not a default task!"
        }
    }

build.gradle.kts

    defaultTasks("clean", "run")

    task("clean") {
        doLast {
            println("Default Cleaning!")
        }
    }

    tasks.register("run") {
        doLast {
            println("Default Running!")
        }
    }

    tasks.register("other") {
        doLast {
            println("I'm not a default task!")
        }
    }

Output of `gradle -q`

    > gradle -q
    include::{snippetsPath}/tutorial/defaultTasks/tests/defaultTasks.out

This is equivalent to running `gradle clean run`. In a multi-project build every subproject can have its own specific default tasks. If a subproject does not specify default tasks, the default tasks of the parent project are used (if defined).

## Configure by DAG

As we later describe in full detail (see Build Lifecycle), Gradle has a configuration phase and an execution phase. After the configuration phase, Gradle knows all tasks that should be executed. Gradle offers you a hook to make use of this information. A use-case for this would be to check if the release task is among the tasks to be executed. Depending on this, you can assign different values to some variables.

In the following example, execution of the `distribution` and `release` tasks results in different value of the `version` variable.

Example 15. Different outcomes of build depending on chosen tasks
build.gradle

    task distribution {
        doLast {
            println "We build the zip with version=$version"
        }
    }

    task release {
        dependsOn 'distribution'
        doLast {
            println 'We release now'
        }
    }

    gradle.taskGraph.whenReady { taskGraph ->
        if (taskGraph.hasTask(":release")) {
            version = '1.0'
        } else {
            version = '1.0-SNAPSHOT'
        }
    }

build.gradle.kts

    tasks.register("distribution") {
        doLast {
            println("We build the zip with version=$version")
        }
    }

    tasks.register("release") {
        dependsOn("distribution")
        doLast {
            println("We release now")
        }
    }

    gradle.taskGraph.whenReady {
        version =
            if (hasTask(":release")) "1.0"
            else "1.0-SNAPSHOT"
    }

Output of `gradle -q distribution`

    > gradle -q distribution
    include::{snippetsPath}/tutorial/configByDag/tests/configByDagNoRelease.out

Output of `gradle -q release`

    > gradle -q release
    include::{snippetsPath}/tutorial/configByDag/tests/configByDag.out

The important thing is that `whenReady` affects the release task before the release task is executed. This works even when the release task is not the primary task (i.e., the task passed to the `gradle` command).

| This example works because the `version` value is only read at execution time. When using a similar construct in a real build you must make sure that nowhere is the value read eagerly during configuration. Otherwise your build may use different values for a property between configuration and execution.

## External dependencies for the build script

If your build script needs to use external libraries, you can add them to the script’s classpath in the build script itself. You do this using the `buildscript()` method, passing in a block which declares the build script classpath.

Example 16. Declaring external dependencies for the build script
build.gradle

    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
        }
    }

build.gradle.kts

    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            "classpath"(group = "commons-codec", name = "commons-codec", version = "1.2")
        }
    }

The block passed to the `buildscript()` method configures a ScriptHandler instance. You declare the build script classpath by adding dependencies to the `classpath` configuration. This is the same way you declare, for example, the Java compilation classpath. You can use any of the dependency types except project dependencies.

Having declared the build script classpath, you can use the classes in your build script as you would any other classes on the classpath. The following example adds to the previous example, and uses classes from the build script classpath.

Example 17. A build script with external dependencies
build.gradle

    import org.apache.commons.codec.binary.Base64

    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath group: 'commons-codec', name: 'commons-codec', version: '1.2'
        }
    }

    task encode {
        doLast {
            def byte[] encodedString = new Base64().encode('hello world\n'.getBytes())
            println new String(encodedString)
        }
    }

build.gradle.kts

    import org.apache.commons.codec.binary.Base64

    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            "classpath"(group = "commons-codec", name = "commons-codec", version = "1.2")
        }
    }

    tasks.register("encode") {
        doLast {
            val encodedString = Base64().encode("hello world\n".toByteArray())
            println(String(encodedString))
        }
    }

Output of `gradle -q encode`

    > gradle -q encode
    include::{snippetsPath}/tutorial/externalDependency/tests/externalBuildDependency.out

For multi-project builds, the dependencies declared with a project’s `buildscript()` method are available to the build scripts of all its sub-projects.

Build script dependencies may be Gradle plugins. Please consult Using Gradle Plugins for more information on Gradle plugins.

Every project automatically has a `buildEnvironment` task of type BuildEnvironmentReportTask that can be invoked to report on the resolution of the build script dependencies.

## Further Reading

This chapter only scratched the surface with what’s possible. Here are some other topics that may be interesting:

- Authoring maintainable build scripts

- Organizing your Gradle projects

- Writing Custom tasks

1. There are command line switches to change this behavior. See Command-Line Interface)
2. There are command line switches to change this behavior. See Command-Line Interface)