Gradle 提供了与 Ant 的集成。

Gradle 与 Ant 集成,允许你在 Gradle 构建中使用单个 Ant task 或整个 Ant 构建。在 Gradle 构建脚本中使用 Ant task 通常比使用 Ant 的 XML 格式更容易、更强大。Gradle 还可以用作强大的 Ant task 脚本工具。

Ant 可以分为两个层面

  1. 层面 1:Ant 语言。它提供了 build.xml 文件的语法、target 的处理、macrodefs 等特殊结构。换句话说,这个层面包含了 Ant task 和类型之外的所有内容。Gradle 理解这种语言,允许你将 Ant 的 build.xml 直接导入到 Gradle 项目中。然后,你可以像使用 Gradle task 一样使用 Ant 构建中的 target。

  2. 层面 2:Ant task 和类型,例如 javaccopyjar。对于这个层面,Gradle 使用 Groovy 和 AntBuilder 提供了集成。

由于构建脚本是 Kotlin 或 Groovy 脚本,你可以将 Ant 构建作为外部进程执行。你的构建脚本可能包含如下语句:"ant clean compile".execute()[1]

Gradle 的 Ant 集成允许你平滑地将构建从 Ant 迁移到 Gradle

  1. 首先导入现有的 Ant 构建。

  2. 然后,将依赖项声明从 Ant 脚本迁移到构建文件。

  3. 最后,将你的 task 移至构建文件,或将其替换为 Gradle 的 plugin。

这个迁移过程可以逐步进行,并且在整个过渡期间都可以保持一个功能正常的 Gradle 构建。

Ant 集成与配置缓存 (configuration cache) 不完全兼容。在 task action 中使用 Task.ant 运行 Ant task 可能有效,但不支持导入 Ant 构建。

Ant 集成由 AntBuilder API 提供。

使用 Ant task 和类型

Gradle 在构建脚本中提供了一个名为 ant 的属性。这是对一个 AntBuilder 实例的引用。

AntBuilder 用于从构建脚本访问 Ant task、类型和属性。

你可以通过调用 AntBuilder 实例上的方法来执行 Ant task。使用 task 名称作为方法名

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")

例如,你可以使用 ant.echo() 方法执行 Ant 的 echo task。

Ant task 的属性作为 Map 参数传递给方法。下面是 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
你可以混合使用 Groovy/Kotlin 代码和 Ant task 标记。这非常强大。

你可以将嵌套文本作为 task 方法调用的参数传递给 Ant task。在此示例中,我们将 echo task 的消息作为嵌套文本传递

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

你可以在闭包内部将嵌套元素传递给 Ant task。嵌套元素的定义方式与 task 相同,即调用一个与要定义的元素同名的方法

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')
            }
        }
    }
}

你可以像访问 task 一样访问 Ant 类型,使用类型名称作为方法名。方法调用返回 Ant 数据类型,你可以在构建脚本中直接使用它。在以下示例中,我们创建了一个 Ant path 对象,然后遍历其内容

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
        }
    }
}

使用自定义 Ant task

要在构建中使自定义 task 可用,请使用 taskdef(通常更容易)或 typedef Ant task,就像在 build.xml 文件中一样。然后,你可以像引用内置 Ant task 一样引用自定义 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')
        }
    }
}

你可以使用 Gradle 的依赖管理来组装自定义 task 的 classpath。为此,你需要定义一个用于 classpath 的自定义配置,并向其添加一些依赖项。这在 声明依赖项 中有更详细的描述。

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'
}

要使用 classpath 配置,请使用自定义配置的 asPath 属性

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')
        }
    }
}

导入 Ant 构建

你可以使用 ant.importBuild() 方法将 Ant 构建导入到 Gradle 项目中。

导入 Ant 构建时,每个 Ant target 都被视为一个 Gradle task。这意味着你可以像操作和执行 Gradle task 一样操作和执行 Ant target

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

你可以添加一个依赖于 Ant target 的 task

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

或者,你可以向 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

Ant target 也可以依赖于 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

有时,可能需要“重命名”为 Ant target 生成的 task,以避免与现有 Gradle task 发生命名冲突。为此,请使用 AntBuilder.importBuild(java.lang.Object, org.gradle.api.Transformer) 方法

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
虽然此方法的第二个参数应该是 Transformer,但在 Groovy 中编程时,由于 Groovy 支持将闭包自动强制转换为单抽象方法类型,因此可以使用闭包而不是匿名内部类(或类似形式)。

使用 Ant 属性和引用

有几种方法可以设置 Ant 属性,以便 Ant task 可以使用该属性。

你可以直接在 AntBuilder 实例上设置属性。Ant 属性也可以作为 Map 使用,你可以更改它们。

你还可以使用 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)

许多 Ant task 在执行时会设置属性。有几种方法可以获取这些属性的值。你可以直接从 AntBuilder 实例中获取属性。Ant 属性也可以作为 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']

有几种方法可以设置 Ant 引用

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"/>

有几种方法可以获取 Ant 引用

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']

使用 Ant 日志

Gradle 将 Ant 消息优先级映射到 Gradle 日志级别,以便从 Ant 记录的消息出现在 Gradle 输出中。默认情况下,映射如下:

Ant 消息优先级 Gradle 日志级别

VERBOSE

DEBUG

DEBUG

DEBUG

INFO

INFO

WARN

WARN

ERROR

ERROR

微调 Ant 日志

Ant 消息优先级与 Gradle 日志级别的默认映射有时会带来问题。例如,没有消息优先级直接映射到 LIFECYCLE 日志级别,而这是 Gradle 的默认级别。许多 Ant task 以 INFO 优先级记录消息,这意味着要从 Gradle 中暴露这些消息,构建必须在日志级别设置为 INFO 的情况下运行,这可能会记录比期望多得多的输出。

相反,如果 Ant task 以过高的级别记录消息,则抑制这些消息将要求构建在更高的日志级别下运行,例如 QUIET。但这可能会导致其他需要的输出被抑制。

为了解决这个问题,Gradle 允许用户微调 Ant 日志记录,并控制消息优先级到 Gradle 日志级别的映射。这通过使用 AntBuilder.setLifecycleLogLevel(java.lang.String) 方法设置应该映射到默认 Gradle LIFECYCLE 日志级别的优先级来实现。设置此值后,任何以配置的优先级或更高优先级记录的 Ant 消息将至少记录在 LIFECYCLE 级别。低于此优先级的任何 Ant 消息最多将记录在 INFO 级别。

例如,以下更改了映射,使得 Ant INFO 优先级的消息暴露在 LIFECYCLE 日志级别。

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

另一方面,如果将 lifecycleLogLevel 设置为 ERROR,则以 WARN 优先级记录的 Ant 消息将不再记录在 WARN 日志级别。它们现在将记录在 INFO 级别,并且默认被抑制。


1. 在 Groovy 中,你可以执行 String。