Skip to content

Groovy Gradle Integration — Build Automation and Custom Tasks

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Groovy Gradle Integration. We cover key concepts, practical examples, and best practices to help you master this topic.

Groovy is the default scripting language for Gradle build automation, enabling custom tasks, plugins, and build logic for Android and Java projects with concise DSL syntax.

What You'll Learn

  • Writing Gradle build scripts in Groovy
  • Creating custom tasks and plugins
  • Dependency management
  • Build lifecycle configuration

Why It Matters

Every Android and Java project uses Gradle for building, testing, and deploying. Doda Browser uses Gradle with Groovy scripts to manage its multi-module build pipeline across Android, desktop, and server targets.

Real-World Use

Android app builds, Java library publishing, multi-module project configuration, continuous integration pipelines, and custom plugin development all rely on Gradle's Groovy DSL.

flowchart LR
    A["Gradle Build"] --> B["Projects"]
    B --> C["Tasks"]
    C --> D["Plugins"]
    D --> E["Artifacts"]
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#f1f5f9,stroke:#94a3b8,color:#64748b

Gradle Build Script Basics

A Gradle build script is a Groovy file named build.gradle:

plugins {
    id 'java'
    id 'application'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.google.guava:guava:32.1.3-jre'
    testImplementation 'junit:junit:4.13.2'
}

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

Run this with gradle build or gradle run.

Custom Tasks

Define tasks with the tasks.register syntax:

tasks.register('hello') {
    doLast {
        println 'Hello from Gradle!'
    }
}

tasks.register('printVersion') {
    doLast {
        println "Version: ${project.version}"
    }
}

Execute with gradle hello or gradle printVersion.

Task Dependencies and Ordering

tasks.register('taskA') {
    doLast { println 'Task A' }
}

tasks.register('taskB') {
    dependsOn tasks.named('taskA')
    doLast { println 'Task B' }
}

tasks.register('taskC') {
    dependsOn tasks.named('taskB')
    finalizedBy tasks.named('cleanup')
    doLast { println 'Task C' }
}

tasks.register('cleanup') {
    doLast { println 'Cleanup done' }
}

Running gradle taskC executes A, B, C, then cleanup.

Custom Plugin Development

class VersionPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.ext {
            major = 1
            minor = 0
            patch = 0
        }
        project.tasks.register('showVersion') {
            doLast {
                println "${project.ext.major}.${project.ext.minor}.${project.ext.patch}"
            }
        }
    }
}

apply plugin: VersionPlugin

Plugins encapsulate reusable build logic across projects.

Multi-Module Projects

// settings.gradle
rootProject.name = 'my-app'
include 'core', 'web', 'mobile'

// core/build.gradle
dependencies {
    implementation 'com.google.inject:guice:5.1.0'
}

// web/build.gradle
dependencies {
    implementation project(':core')
    implementation 'org.eclipse.jetty:jetty-server:11.0.16'
}

// mobile/build.gradle
dependencies {
    implementation project(':core')
}

Multi-module builds scale to large codebases with clear separation.

Dependency Configurations

dependencies {
    implementation 'org.apache.commons:commons-lang3:3.13.0'
    api 'com.google.code.gson:gson:2.10.1'
    compileOnly 'org.projectlombok:lombok:1.18.30'
    runtimeOnly 'ch.qos.logback:logback-classic:1.4.11'
    testImplementation 'org.mockito:mockito-core:5.6.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
}

Each configuration serves a specific scope in the build lifecycle.

Build Lifecycle Hooks

gradle.beforeProject { project ->
    println "Configuring ${project.name}"
}

gradle.afterProject { project ->
    println "Finished ${project.name}"
}

gradle.taskGraph.whenReady { graph ->
    println "Tasks to execute: ${graph.allTasks.size()}"
}

gradle.buildFinished { result ->
    println "Build ${result.failure ? 'failed' : 'succeeded'}"
}

Lifecycle hooks enable instrumentation and cross-cutting concerns.

Common Mistakes

1. Using implementation instead of api

implementation hides transitive dependencies. Use api when consumers need the dependency at compile time.

2. Forgetting to declare repositories

Gradle does not assume any Repository. Always add mavenCentral() or jcenter() before declaring dependencies.

3. Modifying task actions after registration

tasks.register('myTask') {
    doLast { println 'First' }
}
// Wrong — does nothing
myTask.doLast { println 'Second' }
// Right
tasks.named('myTask').configure {
    doLast { println 'Second' }
}

4. Using compile instead of implementation

The compile configuration is deprecated since Gradle 3.0. Use implementation or api.

5. Not using the configuration cache

// Enable in gradle.properties
org.gradle.configuration-cache=true

Without it, every build re-executes configuration logic unnecessarily.

Practice Questions

1. What does the implementation configuration do?

Declares a dependency that is available at compile and runtime for the current module but not exposed to consumers.

2. How do you define a task dependency?

Use dependsOn tasks.named('taskName') inside the task registration block.

3. What is the difference between doFirst and doLast?

doFirst appends an action to the beginning of the task action list, doLast appends to the end.

4. How do you create a multi-module Gradle project?

List subproject directories in settings.gradle with include 'module-a', 'module-b' and give each its own build.gradle.

Challenge: Write a Gradle plugin that generates a build report HTML file with build time, dependency list, and test results.

Solution
class BuildReportPlugin implements Plugin<Project> {
    void apply(Project project) {
        project.tasks.register('buildReport') {
            doLast {
                def report = new File(project.buildDir, 'reports/build.html')
                report.parentFile.mkdirs()
                report.text = """
                <html><body>
                <h1>Build Report</h1>
                <p>Project: ${project.name}</p>
                <p>Dependencies:</p>
                <ul>
                ${project.configurations.compileClasspath.allDependencies.collect {
                    "<li>${it.group}:${it.name}:${it.version}</li>"
                }.join('\n')}
                </ul>
                </body></html>
                """
            }
        }
    }
}

FAQ

{{< faq question="Can I use Kotlin DSL instead of Groovy for Gradle?" >}} Yes, Gradle supports both Groovy DSL (build.gradle) and Kotlin DSL (build.gradle.kts). Kotlin DSL offers better IDE support and type safety. {{< /faq >}}

{{< faq question="What is Gradle wrapper?" >}} The Gradle wrapper (gradlew) is a script that downloads and runs a specific Gradle version, ensuring all developers use the same version without manual installation. {{< /faq >}}

{{< faq question="How do I exclude transitive dependencies?" >}} Use exclude inside the dependency block: implementation('com.example:lib:1.0') { exclude group: 'unwanted-group' }. {{< /faq >}}

{{< faq question="What is the difference between apply plugin and plugins block?" >}} The plugins block (DSL) is the modern approach with type-safe accessors. The apply plugin syntax is legacy but still works. {{< /faq >}}

{{< faq question="How do I publish a library with Gradle?" >}} Apply the maven-publish plugin and configure the publishing block with repositories and publications. {{< /faq >}}

Mini Project

Create a Gradle build script for a library that compiles, runs tests, generates Javadoc, and publishes to a local Maven repository.

plugins {
    id 'java-library'
    id 'maven-publish'
}

java {
    withJavadocJar()
    withSourcesJar()
}

publishing {
    publications {
        maven(MavenPublication) {
            from components.java
        }
    }
    repositories {
        mavenLocal()
    }
}

What's Next

Now that you understand Gradle integration, proceed to learn about Groovy's Meta-Object Protocol.

Topic Description Link
MOP Meta-Object Protocol {{< ref "18-meta-object-protocol" >}}
Testing Spock testing framework {{< ref "15-testing" >}}
Java JVM ecosystem comparison Java

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro