Build Tools: Gradle, Maven and Ant
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The reference material consulted while preparing these pages is listed in the section bibliography. |
This page is written against Apache Groovy 6.0 (6.0.0, 2026-09-22). Compiling Groovy is done by the compiler classes in the
groovy jar, and every build tool below is a thin way of calling them with the right classpath, source directories and
options. The Ant section was run: Groovy 6.0.0 on JDK 26 (checked on 2026-10-14, on macOS), using the Ant 1.10.18 jars that ship in
the distribution’s lib directory, since no separate Ant installation exists on that machine. The Gradle and Maven snippets are
not run here: no Gradle was available and the Maven build was not executed, so treat their plugin and version numbers as the
shape of a working configuration to be checked against the current releases, not as verified output. The depth for the Gradle
Groovy DSL itself (build scripts, tasks, the Gradle API) is the subject of issue #300; this page only covers how Gradle builds
Groovy code. For the Java side of the same tools see Java build and tooling.
The compiler options a build tool has to set
Whatever the tool, a Groovy build sets the same few things:
-
the Groovy library on the compile classpath (
org.apache.groovy:groovy, the groups changed fromorg.codehaus.groovywith Groovy 4.0), plus any optional module such asgroovy-jsonorgroovy-xmlthat the code imports; -
the source directories (conventionally
src/main/groovyandsrc/test/groovy); -
the file encoding and the target bytecode level, which follow the JDK in use (Groovy 6.0 needs JDK 17 or newer);
-
when Groovy and Java sources reference each other, joint compilation, described next.
Joint compilation
Groovy and Java classes can depend on each other. The Java compiler cannot read .groovy files, so the build first asks the
Groovy compiler to generate Java stubs (declarations without bodies) for the Groovy classes, compiles the Java sources against
those stubs, and finally compiles the Groovy sources against the real Java classes. Gradle does this automatically when Java
files sit in the Groovy source set; Maven with GMavenPlus uses the generateStubs goal; the Ant <groovyc> task does it when
it contains a nested <javac>. The Groovy 6.0 release notes add that AST-generated members now appear in the stubs, so Java
code can call, for example, a getter produced by @Canonical or @Immutable.
Gradle
The groovy plugin adds a Groovy source set on top of the java plugin: compilation tasks compileGroovy and
compileTestGroovy, a groovydoc task, and the directories src/main/groovy and src/test/groovy. Groovy source files are
also looked up for Java classes in the same directory, which is how joint compilation is enabled. A minimal Groovy DSL script
(build.gradle), not run here:
plugins {
id 'groovy'
id 'application'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.apache.groovy:groovy:6.0.0'
implementation 'org.apache.groovy:groovy-json:6.0.0'
testImplementation 'org.spockframework:spock-core:2.5-groovy-6.0'
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
application {
mainClass = 'com.example.greenhouse.Main'
}
tasks.withType(GroovyCompile).configureEach {
options.encoding = 'UTF-8'
groovyOptions.parameters = true // keep parameter names for reflection
}
tasks.named('test') {
useJUnitPlatform() // Spock 2.x runs on the JUnit Platform
}
The same build in the Kotlin DSL (build.gradle.kts), also not run here:
plugins {
groovy
application
}
repositories {
mavenCentral()
}
dependencies {
implementation("org.apache.groovy:groovy:6.0.0")
implementation("org.apache.groovy:groovy-json:6.0.0")
testImplementation("org.spockframework:spock-core:2.5-groovy-6.0")
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
application {
mainClass.set("com.example.greenhouse.Main")
}
tasks.withType<GroovyCompile>().configureEach {
options.encoding = "UTF-8"
groovyOptions.isParameters = true
}
tasks.test {
useJUnitPlatform()
}
Source: Groovy documentation: Gradle
Notes on both scripts:
-
The Groovy used by the build is the one declared in
implementation; there is no separate Gradle setting for the Groovy version. Gradle’s own embedded Groovy, which runs Groovy DSL scripts, is a different (and older) one and is unrelated to the project’s. -
A build that mixes several Groovy modules should not repeat the version. Import the bill of materials, described below.
-
The
groovydoctask writes API documentation from the Groovy sources; it is covered in the Groovydoc page. -
The Kotlin DSL is type safe and gives IDE completion, but each Groovy DSL idiom (
groovyOptions.parameters = true) has a Kotlin spelling (isParameters) that follows Kotlin’s property naming.
The groovy-bom
The org.apache.groovy:groovy-bom artifact is a Maven bill of materials that pins every Groovy module to one version, so a
dependency list names modules without versions. In Gradle it is imported as a platform; in Maven in dependencyManagement
with scope import. Both snippets are not run here:
dependencies {
implementation platform('org.apache.groovy:groovy-bom:6.0.0')
implementation 'org.apache.groovy:groovy'
implementation 'org.apache.groovy:groovy-json'
implementation 'org.apache.groovy:groovy-xml'
}
dependencies {
implementation(platform("org.apache.groovy:groovy-bom:6.0.0"))
implementation("org.apache.groovy:groovy")
implementation("org.apache.groovy:groovy-json")
implementation("org.apache.groovy:groovy-xml")
}
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-bom</artifactId>
<version>6.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Source: Groovy documentation: Download
Maven
Maven’s own compiler plugin knows only Java. Two plugins add Groovy:
-
GMavenPlus (
org.codehaus.gmavenplus:gmavenplus-plugin) runs the standard Groovy compiler as ordinary Maven goals. It is the choice most projects make, and the one the Groovy documentation describes. -
The Groovy-Eclipse compiler (
groovy-eclipse-compiler, used as acompilerIdof the Maven compiler plugin) compiles Groovy and Java together through the Eclipse compiler, so no stub generation is needed. It needs a matchinggroovy-eclipse-batchartifact and lags the Groovy releases, so check that it supports Groovy 6 before adopting it.
A GMavenPlus configuration, not run here (check the plugin’s current release before copying the version):
<dependencies>
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy</artifactId>
<version>6.0.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>5.1.0</version>
<executions>
<execution>
<goals>
<goal>addSources</goal>
<goal>addTestSources</goal>
<goal>generateStubs</goal>
<goal>compile</goal>
<goal>generateTestStubs</goal>
<goal>compileTests</goal>
<goal>removeStubs</goal>
<goal>removeTestStubs</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
With this configuration the Groovy and Java sources can live together in src/main/groovy (or in src/main/java), the stub
goals make the joint compilation work, and mvn package produces a jar containing both. A project that has only Groovy code
can drop the stub goals. GMavenPlus also has goals to run Groovy scripts during the build (execute), which is how the
house pom.xml templates generate build information, and to generate Groovydoc (groovydoc).
Ant
Groovy ships two Ant tasks in the groovy-ant module: <groovyc> (compile) and <groovy> (run a script or inline code).
They are declared with taskdef, giving the Groovy and Ant jars on the classpath. The following build file was run with the
Ant jars from the distribution’s lib directory (java -cp ant-1.10.18.jar:ant-launcher-1.10.18.jar
org.apache.tools.ant.Main -Dgroovy.home=…), after creating src/Hello.groovy with a class Hello that has a static
greet(String) method:
<project name="demo" default="all" basedir=".">
<property name="groovy.lib" value="${groovy.home}/lib"/>
<path id="groovy.path">
<fileset dir="${groovy.lib}"
includes="groovy-6.0.0.jar,groovy-ant-6.0.0.jar,groovy-templates-6.0.0.jar,groovy-xml-6.0.0.jar"/>
<fileset dir="${groovy.lib}" includes="ant-*.jar,antlr4-runtime*.jar,*asm*.jar,picocli*.jar"/>
</path>
<taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpathref="groovy.path"/>
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" classpathref="groovy.path"/>
<target name="compile">
<mkdir dir="classes"/>
<groovyc srcdir="src" destdir="classes" classpathref="groovy.path"/>
</target>
<target name="run" depends="compile">
<groovy classpath="classes">
println Hello.greet(properties['greeting'] ?: 'Ant')
println project.name
</groovy>
</target>
<target name="all" depends="run"/>
</project>
compile:
[groovyc] Compiling 1 source file to .../classes
run:
[groovy] Hello, Ant
[groovy] demo
all:
BUILD SUCCESSFUL
Inside the <groovy> task the script sees the Ant project, its properties, the current target and task, and can
call Ant tasks through the built-in ant builder. For joint compilation, nest a <javac> element in <groovyc>; the task
then generates stubs, runs javac and finishes the Groovy compilation. A real Ant installation was not available; the run
above used only the bundled jars, which is the same Ant code with a more manual startup.
AntBuilder
groovy.ant.AntBuilder (in groovy-ant) is a builder that turns Ant tasks into Groovy method calls: each task is a
method, its attributes are named parameters and nested elements are nested closures. It needs no build file, so it is a
convenient way to use copy, zip, javac or exec from a script. The tasks print their usual Ant log lines. It is not
imported by default.
import groovy.ant.AntBuilder
def ant = new AntBuilder()
ant.mkdir(dir: 'out')
ant.echo(message: 'hello from AntBuilder')
ant.copy(todir: 'out') {
fileset(dir: 'src', includes: '**/*.txt')
}
ant.property(name: 'greeting', value: 'hi')
println ant.project.getProperty('greeting')
ant.zip(destfile: 'out/src.zip', basedir: 'src')
println new File('out/src.zip').exists()
println new File('out').list().sort()
try {
ant.fail(message: 'boom')
} catch (org.apache.tools.ant.BuildException e) {
println "caught: ${e.message}"
}
[echo] hello from AntBuilder
[copy] Copying 1 file to .../out
hi
[zip] Building zip: .../out/src.zip
true
[a.txt, src.zip]
caught: boom
Ant failures surface as BuildException, which a script can catch. ant.project gives the underlying Ant Project, whose
properties are the way to pass values between tasks. Besides scripts, Gradle builds have used ant (the same builder) to
reuse Ant tasks.
Gant (legacy)
Gant was a Groovy front end to Ant: build files were Groovy scripts with targets as closures and Ant tasks as method calls.
It is a legacy tool: it was written for Groovy 1.x and 2.x, and Gradle and the builders above replaced it, so it should not
be adopted for new work and is not shown running here. When an old Gant build must be migrated, the usual target is to turn
its targets into Gradle tasks or into plain AntBuilder scripts.
Pitfalls
-
Groovy 4.0 moved the artifacts from
org.codehaus.groovytoorg.apache.groovy. A build that still has the old group resolves an old Groovy, or fails to find the new modules. -
The Groovy version in the build must be a version that can run on the JDK used. Groovy 6.0 requires JDK 17 or newer.
-
Spock publishes a build per Groovy line, such as
2.5-groovy-6.0; a mismatched Spock artifact fails at test start-up. -
Optional modules (
groovy-json,groovy-xml,groovy-sql,groovy-templatesand others) are separate jars; having the maingroovyjar alone is not enough for the classes they provide. -
AntBuilderand the<groovy>task run in the build’s JVM and classloader; a long-lived build daemon can keep classes loaded between runs. -
Do not put credentials in a build file. Pass repository credentials from the environment or the build tool’s secret store.
Where the book is outdated
Books from the 1.x and 2.x era show Ant and Gant for build automation and describe AntBuilder in the default packages. In
current Groovy AntBuilder is in groovy.ant and needs an import, Gant is no longer maintained for modern Groovy, and
Gradle (and Maven with GMavenPlus) are the usual tools. They also use the old org.codehaus.groovy group, which was replaced
by org.apache.groovy in 4.0.