Skip to content
Gradle DSL
step 1/4

Reading — step 1 of 4

Learn

~2 min readSpock, Gradle, Modern Groovy

Gradle is THE most-used build tool for the JVM (and increasingly outside it). Build files are Groovy scripts. Understanding the DSL is essential for any JVM dev.

Anatomy of a build.gradle

plugins {
    id 'java'
    id 'application'
}

group = 'com.example'
version = '1.0.0'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.slf4j:slf4j-api:2.0.9'
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
}

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

tasks.test {
    useJUnitPlatform()
}

Every line is Groovy. The blocks (plugins { }, dependencies { }) are closures with delegates. id 'java' invokes a method id with arg 'java'.

Tasks

Gradle's unit of work is a task. Plugins (like java) add tasks (compileJava, test, jar, assemble, etc.).

Custom task:

task hello {
    doLast {
        println "hello from gradle"
    }
}

// In Gradle 7+:
tasks.register('hello') {
    doLast {
        println "hello"
    }
}

Run with gradle hello or ./gradlew hello.

Task with type:

tasks.register('greet', GreetTask) {
    name = 'Ada'
    greeting = 'Hello'
}

class GreetTask extends DefaultTask {
    @Input String name
    @Input String greeting = 'Hi'

    @TaskAction
    void execute() {
        println "$greeting, $name!"
    }
}

Dependencies between tasks

tasks.register('first') { ... }
tasks.register('second') {
    dependsOn 'first'
    ...
}

// Or:
tasks.named('compileJava') {
    dependsOn 'generateProto'
}

Configuration vs execution

Gradle splits into:

  • Configuration phase — top-level code in build.gradle runs to define tasks
  • Execution phase — selected tasks' doFirst/doLast/@TaskAction blocks run

This split confuses newcomers. Code OUTSIDE a doLast runs every time you invoke gradle, even for unrelated tasks. Code INSIDE only runs when that task executes.

Multi-project builds

A settings.gradle lists subprojects:

rootProject.name = 'monorepo'
include 'lib-foo'
include 'lib-bar'
include 'app-web'

Each subproject has its own build.gradle.

Shared config in allprojects { ... } or subprojects { ... }:

allprojects {
    repositories { mavenCentral() }
}

subprojects {
    apply plugin: 'java'
    dependencies {
        testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
    }
}

Plugins

  • java, java-library, application — Java basics
  • groovy — adds Groovy compilation
  • kotlin('jvm') — Kotlin
  • org.springframework.boot — Spring Boot
  • com.github.johnrengelman.shadow — fat JARs
  • jacoco — code coverage

Kotlin DSL

Gradle now supports build.gradle.kts (Kotlin DSL) — better IDE support, slightly more verbose. Many new projects use it. The Groovy DSL is still widely supported.

Why Groovy was chosen

  • Closures + delegates → declarative-looking code with full programming power
  • No type ceremony for build scripts
  • IDE smart enough (mostly) thanks to @DelegatesTo

Compared to Maven's pure-XML or Bazel's Starlark, Gradle's Groovy DSL is the most flexible at the cost of tooling complexity.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…