Gradle Tasks

Gradle tasks are the fundamental units of work in a build process. They can be anything from compiling source code, running tests, packaging binaries, and to deploying applications. Understanding how to define, configure, and execute tasks is essential for leveraging Gradle's power.

1. Creating Simple Tasks

A task in Gradle is a single piece of work, such as compiling classes, running tests, or packaging applications.

Syntax

Tasks can be defined using the task keyword followed by a task name and an optional configuration block.

Example

// Define a simple task
task hello {
    doLast {
        println 'Hello, World!'
    }
}

In this example, the hello task prints "Hello, World!" to the console.

2. Configuring Tasks

Tasks can have multiple actions using doFirst and doLast methods. Actions added with doFirst will execute before the task's main action, and those added with doLast will execute after.

Example

task greet {
    doFirst {
        println 'Starting task...'
    }
    doLast {
        println 'Hello, World!'
    }
    doLast {
        println 'Task completed.'
    }
}

This task will print "Starting task...", "Hello, World!", and "Task completed." in sequence.

Task Properties

Tasks can have properties to configure their behavior. For example, a Copy task has from and into properties.

Example

task copyFiles(type: Copy) {
    from 'src/main/resources'
    into 'build/resources'
}

This task copies files from src/main/resources to build/resources.

3. Task Dependencies

Tasks can depend on other tasks, ensuring that dependent tasks are executed before the task itself.

Example

task compile {
    doLast {
        println 'Compiling source code...'
    }
}

task build {
    dependsOn compile
    doLast {
        println 'Building project...'
    }
}

In this example, the build task depends on the compile task, ensuring that compile runs before build.

4. Task Inputs and Outputs

Tasks can define inputs and outputs to determine if they are up-to-date and if they need to be executed again.

Example

task compile {
    inputs.file 'src/main/java/MyClass.java'
    outputs.file 'build/classes/MyClass.class'

    doLast {
        println 'Compiling MyClass.java...'
        // Compilation logic here
    }
}

This task specifies that MyClass.java is the input and MyClass.class is the output. If the input file changes, the task will be executed again.

5. Incremental Tasks

Incremental tasks only process inputs that have changed since the last execution.

Example

task compile(type: IncrementalTask) {
    inputs.dir 'src/main/java'
    outputs.dir 'build/classes'

    doLast {
        println 'Compiling changed files...'
        // Incremental compilation logic here
    }
}

This task will only compile files that have changed since the last execution.

6. Built-in Tasks

Common Built-in Tasks

Gradle provides several built-in tasks for common build activities.

  • clean: Deletes the build directory.
  • assemble: Assembles the outputs of the project.
  • check: Runs all checks, including tests.
  • build: Assembles and tests the project.

Example

./gradlew clean build

This command cleans the project and then builds it.

7. Custom Tasks

Custom tasks allow you to define specific actions that are not covered by the standard Gradle tasks.

Example

task customTask {
    doLast {
        println 'Executing custom task...'
    }
}

This task prints "Executing custom task..." to the console.

8. Task Configuration Avoidance

Gradle provides a way to avoid configuring tasks until they are needed, improving build performance.

Example

tasks.register('lazyTask') {
    doLast {
        println 'Executing lazy task...'
    }
}

The lazyTask is not configured until it is executed.

9. Parallel Execution

Gradle can execute tasks in parallel to reduce build time. This requires specifying the --parallel option when running tasks.

Example

./gradlew build --parallel

10. Finalized By

A task can specify another task to run after it, regardless of whether it succeeds or fails.

Example

task clean {
    doLast {
        println 'Cleaning project...'
    }
}

task build {
    doLast {
        println 'Building project...'
    }
    finalizedBy clean
}

In this example, the clean task will run after the build task, regardless of the build's success.

Complete Example

Let's create a complete example to demonstrate various aspects of Gradle tasks.

Project Structure

gradle-tasks-example/
|-- build.gradle
|-- settings.gradle
|-- src/
    |-- main/
        |-- java/
            |-- com/
                |-- example/
                    |-- App.java
        |-- resources/
            |-- application.properties
    |-- test/
        |-- java/
            |-- com/
                |-- example/
                    |-- AppTest.java

build.gradle

plugins {
    id 'java'
    id 'application'
}

group 'com.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.2'
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
}

application {
    mainClass = 'com.example.App'
}

tasks.register('hello') {
    doLast {
        println 'Hello, World!'
    }
}

task copyFiles(type: Copy) {
    from 'src/main/resources'
    into 'build/resources'
}

task compileJava {
    dependsOn 'copyFiles'
    inputs.file 'src/main/java/com/example/App.java'
    outputs.dir 'build/classes'

    doLast {
        println 'Compiling Java files...'
        // Java compilation logic here
    }
}

tasks.build {
    dependsOn compileJava
    doLast {
        println 'Building the project...'
    }
}

settings.gradle

rootProject.name = 'gradle-tasks-example'

src/main/java/com/example/App.java

package com.example;

public class App {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

src/test/java/com/example/AppTest.java

package com.example;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertTrue;

public class AppTest {
    @Test
    public void testApp() {
        assertTrue(true);
    }
}

Conclusion

Understanding Gradle tasks is essential for effectively managing build processes. Tasks can be simple or complex, with dependencies, inputs, outputs, and incremental execution. Utilizing built-in tasks and creating custom tasks allows for flexible and efficient builds. For more detailed information, visit the official Gradle documentation

Happy learning!

Comments