# Using Ant from Gradle

version 8.14.4-20260127040244+0000

Gradle provides integration with Ant.

Gradle integrates with Ant, allowing you to use individual Ant tasks or entire Ant builds in your Gradle builds. Using Ant tasks in a Gradle build script is often easier and more powerful than using Ant’s XML format. Gradle can also be used as a powerful Ant task scripting tool.

Ant can be divided into two layers:

1. Layer 1: The Ant language. It provides the syntax for the `build.xml` file, the handling of the targets, special constructs like macrodefs, and more. In other words, this layer includes everything except the Ant tasks and types. Gradle understands this language and lets you import your Ant `build.xml` directly into a Gradle project. You can then use the targets of your Ant build as if they were Gradle tasks.

2. Layer 2: The Ant tasks and types, like `javac`, `copy` or `jar`. For this layer, Gradle provides integration using Groovy and the `AntBuilder`.

Since build scripts are Kotlin or Groovy scripts, you can execute an Ant build as an external process. Your build script may contain statements like: `"ant clean compile".execute()`.[1]

Gradle’s Ant integration allows you to migrate your build from Ant to Gradle smoothly:

1. Begin by importing your existing Ant build.

2. Then, transition your dependency declarations from the Ant script to your build file.

3. Finally, move your tasks to your build file or replace them with Gradle’s plugins.

This migration process can be performed incrementally, and you can maintain a functional Gradle build throughout the transition.

| Ant integration is not fully compatible with the configuration cache. Using Task.ant to run Ant task in the task action may work, but importing the Ant build is not supported.

The Ant integration is provided by the AntBuilder API.

## Using Ant tasks and types

Gradle provides a property called `ant` in your build script. This is a reference to an AntBuilder instance.

`AntBuilder` is used to access Ant tasks, types, and properties from your build script.

You execute an Ant task by calling a method on the `AntBuilder` instance. You use the task name as the method name:

build.gradle

    ant.mkdir(dir: "$STAGE")
    ant.copy(todir: "$STAGE/bin") {
        ant.fileset(dir: 'bin', includes: "**")
    }
    ant.gzip(destfile:"build/file-${VERSION}.tar.gz", src: "build/file-${VERSION}.tar")

For example, you execute the Ant `echo` task using the `ant.echo()` method.

The attributes of the Ant task are passed as Map parameters to the method. Below is an example of the `echo` task:

build.gradle.kts

    tasks.register("hello") {
        doLast {
            val greeting = "hello from Ant"
            ant.withGroovyBuilder {
                "echo"("message" to greeting)
            }
        }
    }

build.gradle

    tasks.register('hello') {
        doLast {
            String greeting = 'hello from Ant'
            ant.echo(message: greeting)
        }
    }

    $ gradle hello

    > Task :hello
    [ant:echo] hello from Ant

    BUILD SUCCESSFUL in 0s
    1 actionable task: 1 executed

| You can mix Groovy/Kotlin code and the Ant task markup. This can be extremely powerful.

You pass nested text to an Ant task as a parameter of the task method call. In this example, we pass the message for the `echo` task as nested text:

build.gradle.kts

    tasks.register("hello") {
        doLast {
            ant.withGroovyBuilder {
                "echo"("message" to "hello from Ant")
            }
        }
    }

build.gradle

    tasks.register('hello') {
        doLast {
            ant.echo('hello from Ant')
        }
    }

    $ gradle hello

    > Task :hello
    [ant:echo] hello from Ant

    BUILD SUCCESSFUL in 0s
    1 actionable task: 1 executed

You pass nested elements to an Ant task inside a closure. Nested elements are defined in the same way as tasks by calling a method with the same name as the element we want to define:

build.gradle.kts

    tasks.register("zip") {
        doLast {
            ant.withGroovyBuilder {
                "zip"("destfile" to "archive.zip") {
                    "fileset"("dir" to "src") {
                        "include"("name" to "**.xml")
                        "exclude"("name" to "**.java")
                    }
                }
            }
        }
    }

build.gradle

    tasks.register('zip') {
        doLast {
            ant.zip(destfile: 'archive.zip') {
                fileset(dir: 'src') {
                    include(name: '**.xml')
                    exclude(name: '**.java')
                }
            }
        }
    }

You can access Ant types the same way you access tasks, using the name of the type as the method name. The method call returns the Ant data type, which you can use directly in your build script. In the following example, we create an Ant `path` object, then iterate over the contents of it:

build.gradle.kts

    import org.apache.tools.ant.types.Path

    tasks.register("list") {
        doLast {
            val path = ant.withGroovyBuilder {
                "path" {
                    "fileset"("dir" to "libs", "includes" to "*.jar")
                }
            } as Path
            path.list().forEach {
                println(it)
            }
        }
    }

build.gradle

    tasks.register('list') {
        doLast {
            def path = ant.path {
                fileset(dir: 'libs', includes: '*.jar')
            }
            path.list().each {
                println it
            }
        }
    }

### Using custom Ant tasks

To make custom tasks available in your build, use the `taskdef` (usually easier) or `typedef` Ant task, just as you would in a `build.xml` file. You can then refer to the custom Ant task as you would a built-in Ant task:

build.gradle.kts

    tasks.register("check") {
        val checkstyleConfig = file("checkstyle.xml")
        doLast {
            ant.withGroovyBuilder {
                "taskdef"("resource" to "com/puppycrawl/tools/checkstyle/ant/checkstyle-ant-task.properties") {
                    "classpath" {
                        "fileset"("dir" to "libs", "includes" to "*.jar")
                    }
                }
                "checkstyle"("config" to checkstyleConfig) {
                    "fileset"("dir" to "src")
                }
            }
        }
    }

build.gradle

    tasks.register('check') {
        def checkstyleConfig = file('checkstyle.xml')
        doLast {
            ant.taskdef(resource: 'com/puppycrawl/tools/checkstyle/ant/checkstyle-ant-task.properties') {
                classpath {
                    fileset(dir: 'libs', includes: '*.jar')
                }
            }
            ant.checkstyle(config: checkstyleConfig) {
                fileset(dir: 'src')
            }
        }
    }

You can use Gradle’s dependency management to assemble the classpath for the custom tasks. To do this, you need to define a custom configuration for the classpath and add some dependencies to it. This is described in more detail in Declaring Dependencies:

build.gradle.kts

    val pmd = configurations.create("pmd")

    dependencies {
        pmd(group = "pmd", name = "pmd", version = "4.2.5")
    }

build.gradle

    configurations {
        pmd
    }

    dependencies {
        pmd group: 'pmd', name: 'pmd', version: '4.2.5'
    }

To use the classpath configuration, use the `asPath` property of the custom configuration:

build.gradle.kts

    tasks.register("check") {
        doLast {
            ant.withGroovyBuilder {
                "taskdef"("name" to "pmd",
                          "classname" to "net.sourceforge.pmd.ant.PMDTask",
                          "classpath" to pmd.asPath)
                "pmd"("shortFilenames" to true,
                      "failonruleviolation" to true,
                      "rulesetfiles" to file("pmd-rules.xml").toURI().toString()) {
                    "formatter"("type" to "text", "toConsole" to "true")
                    "fileset"("dir" to "src")
                }
            }
        }
    }

build.gradle

    tasks.register('check') {
        doLast {
            ant.taskdef(name: 'pmd',
                        classname: 'net.sourceforge.pmd.ant.PMDTask',
                        classpath: configurations.pmd.asPath)
            ant.pmd(shortFilenames: 'true',
                    failonruleviolation: 'true',
                    rulesetfiles: file('pmd-rules.xml').toURI().toString()) {
                formatter(type: 'text', toConsole: 'true')
                fileset(dir: 'src')
            }
        }
    }

## Importing an Ant build

You can use the `ant.importBuild()` method to import an Ant build into your Gradle project.

When you import an Ant build, each Ant target is treated as a Gradle task. This means you can manipulate and execute the Ant targets in the same way as Gradle tasks:

build.gradle.kts

    ant.importBuild("build.xml")

build.gradle

    ant.importBuild 'build.xml'

build.xml

    <project>
        <target name="hello">
            <echo>Hello, from Ant</echo>
        </target>
    </project>

    $ gradle hello

    > Task :hello
    [ant:echo] Hello, from Ant

    BUILD SUCCESSFUL in 0s
    1 actionable task: 1 executed

You can add a task that depends on an Ant target:

build.gradle.kts

    ant.importBuild("build.xml")

    tasks.register("intro") {
        dependsOn("hello")
        doLast {
            println("Hello, from Gradle")
        }
    }

build.gradle

    ant.importBuild 'build.xml'

    tasks.register('intro') {
        dependsOn("hello")
        doLast {
            println 'Hello, from Gradle'
        }
    }

    $ gradle intro

    > Task :hello
    [ant:echo] Hello, from Ant

    > Task :intro
    Hello, from Gradle

    BUILD SUCCESSFUL in 0s
    2 actionable tasks: 2 executed

Or, you can add behavior to an Ant target:

build.gradle.kts

    ant.importBuild("build.xml")

    tasks.named("hello") {
        doLast {
            println("Hello, from Gradle")
        }
    }

build.gradle

    ant.importBuild 'build.xml'

    hello {
        doLast {
            println 'Hello, from Gradle'
        }
    }

    $ gradle hello

    > Task :hello
    [ant:echo] Hello, from Ant
    Hello, from Gradle

    BUILD SUCCESSFUL in 0s
    1 actionable task: 1 executed

It is also possible for an Ant target to depend on a Gradle task:

build.gradle.kts

    ant.importBuild("build.xml")

    tasks.register("intro") {
        doLast {
            println("Hello, from Gradle")
        }
    }

build.gradle

    ant.importBuild 'build.xml'

    tasks.register('intro') {
        doLast {
            println 'Hello, from Gradle'
        }
    }

build.xml

    <project>
        <target name="hello" depends="intro">
            <echo>Hello, from Ant</echo>
        </target>
    </project>

    $ gradle hello

    > Task :intro
    Hello, from Gradle

    > Task :hello
    [ant:echo] Hello, from Ant

    BUILD SUCCESSFUL in 0s
    2 actionable tasks: 2 executed

Sometimes, it may be necessary to "rename" the task generated for an Ant target to avoid a naming collision with existing Gradle tasks. To do this, use the AntBuilder.importBuild(java.lang.Object, org.gradle.api.Transformer) method:

build.gradle.kts

    ant.importBuild("build.xml") { antTargetName ->
        "a-" + antTargetName
    }

build.gradle

    ant.importBuild('build.xml') { antTargetName ->
        'a-' + antTargetName
    }

build.xml

    <project>
        <target name="hello">
            <echo>Hello, from Ant</echo>
        </target>
    </project>

    $ gradle a-hello

    > Task :a-hello
    [ant:echo] Hello, from Ant

    BUILD SUCCESSFUL in 0s
    1 actionable task: 1 executed

| While the second argument to this method should be a Transformer, when programming in Groovy you can use a closure instead of an anonymous inner class (or similar) due to Groovy’s support for automatically coercing closures to single-abstract-method types.

## Using Ant properties and references

There are several ways to set an Ant property so that the property can be used by Ant tasks.

You can set the property directly on the `AntBuilder` instance. The Ant properties are also available as a Map, which you can change.

You can also use the Ant `property` task:

build.gradle.kts

    ant.setProperty("buildDir", buildDir)
    ant.properties.set("buildDir", buildDir)
    ant.properties["buildDir"] = buildDir
    ant.withGroovyBuilder {
        "property"("name" to "buildDir", "location" to "buildDir")
    }

build.gradle

    ant.buildDir = buildDir
    ant.properties.buildDir = buildDir
    ant.properties['buildDir'] = buildDir
    ant.property(name: 'buildDir', location: buildDir)

Many Ant tasks set properties when they execute. There are several ways to get the value of these properties. You can get the property directly from the `AntBuilder` instance. The Ant properties are also available as a Map:

build.xml

    <property name="antProp" value="a property defined in an Ant build"/>

build.gradle.kts

    println(ant.getProperty("antProp"))
    println(ant.properties.get("antProp"))
    println(ant.properties["antProp"])

build.gradle

    println ant.antProp
    println ant.properties.antProp
    println ant.properties['antProp']

There are several ways to set an Ant reference:

build.gradle.kts

    ant.withGroovyBuilder { "path"("id" to "classpath", "location" to "libs") }
    ant.references.set("classpath", ant.withGroovyBuilder { "path"("location" to "libs") })
    ant.references["classpath"] = ant.withGroovyBuilder { "path"("location" to "libs") }

build.gradle

    ant.path(id: 'classpath', location: 'libs')
    ant.references.classpath = ant.path(location: 'libs')
    ant.references['classpath'] = ant.path(location: 'libs')

build.xml

    <path refid="classpath"/>

There are several ways to get an Ant reference:

build.xml

    <path id="antPath" location="libs"/>

build.gradle.kts

    println(ant.references.get("antPath"))
    println(ant.references["antPath"])

build.gradle

    println ant.references.antPath
    println ant.references['antPath']

## Using Ant logging

Gradle maps Ant message priorities to Gradle log levels so that messages logged from Ant appear in the Gradle output. By default, these are mapped as follows:

Ant Message Priority | Gradle Log Level
--- | ---
VERBOSE | `DEBUG`
DEBUG | `DEBUG`
INFO | `INFO`
WARN | `WARN`
ERROR | `ERROR`

### Fine-tuning Ant logging

The default mapping of Ant message priority to the Gradle log level can sometimes be problematic. For example, no message priority maps directly to the `LIFECYCLE` log level, which is the default for Gradle. Many Ant tasks log messages at the INFO priority, which means to expose those messages from Gradle, a build would have to be run with the log level set to `INFO`, potentially logging much more output than is desired.

Conversely, if an Ant task logs messages at too high of a level, suppressing those messages would require the build to be run at a higher log level, such as `QUIET.` However, this could result in other desirable outputs being suppressed.

To help with this, Gradle allows the user to fine-tune the Ant logging and control the mapping of message priority to the Gradle log level. This is done by setting the priority that should map to the default Gradle `LIFECYCLE` log level using the AntBuilder.setLifecycleLogLevel(java.lang.String) method. When this value is set, any Ant message logged at the configured priority or above will be logged at least at `LIFECYCLE`. Any Ant message logged below this priority will be logged at `INFO` at most.

For example, the following changes the mapping such that Ant INFO priority messages are exposed at the `LIFECYCLE` log level:

build.gradle.kts

    ant.lifecycleLogLevel = AntBuilder.AntMessagePriority.INFO

    tasks.register("hello") {
        doLast {
            ant.withGroovyBuilder {
                "echo"("level" to "info", "message" to "hello from info priority!")
            }
        }
    }

build.gradle

    ant.lifecycleLogLevel = "INFO"

    tasks.register('hello') {
        doLast {
            ant.echo(level: "info", message: "hello from info priority!")
        }
    }

    $ gradle hello

    > Task :hello
    [ant:echo] hello from info priority!

    BUILD SUCCESSFUL in 0s
    1 actionable task: 1 executed

On the other hand, if the `lifecycleLogLevel` was set to ERROR, Ant messages logged at the WARN priority would no longer be logged at the `WARN` log level. They would now be logged at the `INFO` level and suppressed by default.

1. In Groovy you can execute Strings.