Gradle 提供與 Ant 的整合。

Gradle 與 Ant 整合,讓您可以在 Gradle 建置中使用個別的 Ant 任務或整個 Ant 建置。在 Gradle 建置腳本中使用 Ant 任務通常比使用 Ant 的 XML 格式更簡單且更強大。Gradle 也可以作為強大的 Ant 任務腳本工具使用。

Ant 可以分為兩個層次

  1. 層次 1:Ant 語言。它提供了 build.xml 檔案的語法、目標的處理、特殊結構(如 macrodefs)等等。換句話說,這個層次包含除了 Ant 任務和類型之外的所有內容。Gradle 了解這種語言,並讓您將 Ant build.xml 直接匯入到 Gradle 專案中。然後,您可以像使用 Gradle 任務一樣使用 Ant 建置的目標。

  2. 層次 2:Ant 任務和類型,例如 javaccopyjar。對於這個層次,Gradle 使用 Groovy 和 AntBuilder 提供整合。

由於建置腳本是 Kotlin 或 Groovy 腳本,您可以將 Ant 建置作為外部程序執行。您的建置腳本可能包含如下語句:"ant clean compile".execute()[1]

Gradle 的 Ant 整合讓您可以順利地將建置從 Ant 遷移到 Gradle

  1. 首先匯入您現有的 Ant 建置。

  2. 然後,將您的相依性宣告從 Ant 腳本轉換到您的建置檔案。

  3. 最後,將您的任務移動到您的建置檔案,或將它們替換為 Gradle 的外掛。

這個遷移過程可以逐步執行,並且您可以在整個轉換過程中維護一個功能正常的 Gradle 建置。

Ant 整合與配置快取不完全相容。在任務動作中使用 Task.ant 執行 Ant 任務可能有效,但不支援匯入 Ant 建置。

Ant 整合由 AntBuilder API 提供。

使用 Ant 任務和類型

Gradle 在您的建置腳本中提供一個名為 ant 的屬性。這是對 AntBuilder 實例的參考。

AntBuilder 用於從您的建置腳本存取 Ant 任務、類型和屬性。

您可以透過呼叫 AntBuilder 實例上的方法來執行 Ant 任務。您使用任務名稱作為方法名稱

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 任務。

Ant 任務的屬性作為 Map 參數傳遞給方法。以下是 echo 任務的範例

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 任務標記。這可能非常強大。

您可以將巢狀文字作為任務方法呼叫的參數傳遞給 Ant 任務。在本範例中,我們將 echo 任務的訊息作為巢狀文字傳遞

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 任務。巢狀元素的定義方式與任務相同,方法是呼叫與我們要定義的元素同名的方法

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

您可以使用與存取任務相同的方式存取 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 任務

若要使自訂任務在您的建置中可用,請使用 taskdef(通常更容易)或 typedef Ant 任務,就像在 build.xml 檔案中一樣。然後,您可以像參考內建 Ant 任務一樣參考自訂 Ant 任務

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 的相依性管理來組裝自訂任務的類別路徑。若要執行此操作,您需要為類別路徑定義自訂配置,並向其新增一些相依性。這在宣告相依性中有更詳細的描述

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

若要使用類別路徑配置,請使用自訂配置的 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 目標都會被視為 Gradle 任務。這表示您可以像操作 Gradle 任務一樣操作和執行 Ant 目標

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 目標的任務

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 目標新增行為

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 目標也可能依賴於 Gradle 任務

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 目標產生的任務,以避免與現有的 Gradle 任務發生命名衝突。若要執行此操作,請使用 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 任務可以使用該屬性。

您可以直接在 AntBuilder 實例上設定屬性。Ant 屬性也可以作為 Map 使用,您可以變更它。

您也可以使用 Ant property 任務

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 任務在執行時會設定屬性。有幾種方法可以取得這些屬性的值。您可以直接從 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 任務以 INFO 優先順序記錄訊息,這表示若要從 Gradle 公開這些訊息,建置必須以設定為 INFO 的日誌層級執行,這可能會記錄比預期更多的輸出。

相反地,如果 Ant 任務以過高的層級記錄訊息,則抑制這些訊息將需要以更高的日誌層級(例如 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 中,您可以執行字串。