Gradle 提供與 Ant 的絕佳整合。您可以在 Gradle 建置中使用個別 Ant 任務或整個 Ant 建置。事實上,您會發現,在 Gradle 建置指令碼中使用 Ant 任務比使用 Ant 的 XML 格式更容易且更強大。您甚至可以將 Gradle 僅用作強大的 Ant 任務指令碼工具。

Ant 可以分為兩層。第一層是 Ant 語言。它提供 build.xml 檔案的語法、目標的處理、巨集定義等特殊建構。換句話說,除了 Ant 任務和類型之外的所有內容。Gradle 了解此語言,並允許您將 Ant build.xml 直接匯入 Gradle 專案。然後,您可以使用 Ant 建置的目標,就像它們是 Gradle 任務一樣。

Ant 的第二層是其豐富的 Ant 任務和類型,例如 javaccopyjar。對於此層,Gradle 僅透過依賴 Groovy 和絕佳的 AntBuilder 來提供整合。

最後,由於建置指令碼是 Groovy 指令碼,因此您始終可以將 Ant 建置執行為外部程序。您的建置指令碼可能包含以下陳述:"ant clean compile".execute().[1]

您可以將 Gradle 的 Ant 整合用作將您的建置從 Ant 移轉到 Gradle 的路徑。例如,您可以從匯入現有的 Ant 建置開始。然後,您可以將相依性宣告從 Ant 指令碼移到您的建置檔案。最後,您可以將您的任務移到您的建置檔案,或用 Gradle 的某些外掛程式取代它們。此程序可以隨著時間分批完成,而且您可以在整個過程中擁有可運作的 Gradle 建置。

Ant 整合與 組態快取 並不完全相容。使用 Task.ant 來在任務動作中執行 Ant 任務可能會運作,但匯入 Ant 建置不受支援。

在您的建置中使用 Ant 任務和類型

在您的建置指令碼中,Gradle 提供一個名為 ant 的屬性。這是對 AntBuilder 執行個體的參考。這個 AntBuilder 用於從您的建置指令碼存取 Ant 任務、類型和屬性。Ant 的 build.xml 格式與 Groovy 之間有一個非常簡單的對應,如下所述。

您透過呼叫 AntBuilder 執行個體上的方法來執行 Ant 任務。您使用任務名稱作為方法名稱。例如,您透過呼叫 ant.echo() 方法來執行 Ant echo 任務。Ant 任務的屬性會傳遞為方法的地圖參數。以下是 echo 任務的範例。請注意,我們也可以混合 Groovy 程式碼和 Ant 任務標記。這可能會非常強大。

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 的輸出
> gradle hello

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

BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed

您透過將巢狀文字傳遞為任務方法呼叫的參數,來將其傳遞給 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 的輸出
> 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
        }
    }
}

您可以在「Groovy in Action」8.4 或 Groovy Wiki 中找到更多關於 AntBuilder 的資訊。

在您的建置中使用自訂 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 的輸出
> 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 的輸出
> 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 的輸出
> 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 的輸出
> 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 的輸出
> 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 輸出中。預設情況下,這些對應如下

表 1. Ant 訊息優先順序對應
Ant 訊息優先順序 Gradle 記錄層級

詳細

偵錯

偵錯

偵錯

資訊

資訊

警告

警告

錯誤

錯誤

微調 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 的輸出
> 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 層級記錄,並會在預設情況下被抑制。

API

Ant 整合是由 AntBuilder 提供。


1. 在 Groovy 中,你可以執行字串。如需進一步了解如何使用 Groovy 執行外部程序,請參閱「Groovy in Action」9.3.2 或 Groovy wiki