Gradle 構(gòu)建基礎(chǔ)

2022-08-03 10:17 更新

Projects 和 tasks

projects 和 tasks是 Gradle 中最重要的兩個(gè)概念。

任何一個(gè) Gradle 構(gòu)建都是由一個(gè)或多個(gè) projects 組成。每個(gè) project 包括許多可構(gòu)建組成部分。 這完全取決于你要構(gòu)建些什么。舉個(gè)例子,每個(gè) project 或許是一個(gè) jar 包或者一個(gè) web 應(yīng)用,它也可以是一個(gè)由許多其他項(xiàng)目中產(chǎn)生的 jar 構(gòu)成的 zip 壓縮包。一個(gè) project 不必描述它只能進(jìn)行構(gòu)建操作。它也可以部署你的應(yīng)用或搭建你的環(huán)境。不要擔(dān)心它像聽上去的那樣龐大。 Gradle 的 build-by-convention 可以讓您來具體定義一個(gè) project 到底該做什么。

每個(gè) project 都由多個(gè) tasks 組成。每個(gè) task 都代表了構(gòu)建執(zhí)行過程中的一個(gè)原子性操作。如編譯,打包,生成 javadoc,發(fā)布到某個(gè)倉庫等操作。

到目前為止,可以發(fā)現(xiàn)我們可以在一個(gè) project 中定義一些簡單任務(wù),后續(xù)章節(jié)將會(huì)闡述多項(xiàng)目構(gòu)建和多項(xiàng)目多任務(wù)的內(nèi)容。

Hello world

你可以通過在命令行運(yùn)行 gradle 命令來執(zhí)行構(gòu)建,gradle 命令會(huì)從當(dāng)前目錄下尋找 build.gradle 文件來執(zhí)行構(gòu)建。我們稱 build.gradle 文件為構(gòu)建腳本。嚴(yán)格來說這其實(shí)是一個(gè)構(gòu)建配置腳本,后面你會(huì)了解到這個(gè)構(gòu)建腳本定義了一個(gè) project 和一些默認(rèn)的 task。

要嘗試這一點(diǎn),請(qǐng)創(chuàng)建以下名為 build.gradle 的構(gòu)建腳本。

第一個(gè)構(gòu)建腳本

build.gradle

task hello {
    doLast {
        println 'Hello world!'
    }
}

然后在該文件所在目錄執(zhí)行 ?gradle -q hello?

?-q? 參數(shù)的作用是什么?

該文檔的示例中很多地方在調(diào)用 gradle 命令時(shí)都加了? -q ?參數(shù)。該參數(shù)用來控制 gradle 的日志級(jí)別,可以保證只輸出我們需要的內(nèi)容。具體可參閱本文檔第十八章 日志來了解更多參數(shù)和信息。

執(zhí)行腳本

Output of gradle -q hello
> gradle -q hello
Hello world!

上面的腳本定義了一個(gè)叫做 hello 的 task,并且給它添加了一個(gè)動(dòng)作。當(dāng)執(zhí)行 gradle hello 的時(shí)候, Gralde 便會(huì)去調(diào)用 hello 這個(gè)任務(wù)來執(zhí)行給定操作。這些操作其實(shí)就是一個(gè)用 groovy 書寫的閉包。

如果你覺得它看上去跟 Ant 中的 targets 很像,沒錯(cuò)確實(shí)是這樣。Gradle 的 tasks 就相當(dāng)于 Ant 中的 targets。不過你會(huì)發(fā)現(xiàn)他功能更加強(qiáng)大。我們只是換了一個(gè)比 target 更形象的另外一個(gè)術(shù)語。不幸的是這恰巧與 Ant 中的術(shù)語有些沖突。ant 命令中有諸如 javac、copy、tasks。所以當(dāng)該文檔中提及 tasks 時(shí),除非特別指明 ant task。否則指的均是指 Gradle 中的 tasks。

快速定義任務(wù)

用一種更簡潔的方式來定義上面的 hello 任務(wù)。

快速定義任務(wù)

build.gradle

task hello {
    println 'Hello world!'
}

上面的腳本又一次采用閉包的方式來定義了一個(gè)叫做 hello 的任務(wù),本文檔后續(xù)章節(jié)中我們將會(huì)更多的采用這種風(fēng)格來定義任務(wù)。

 注:<<在Gradle4.x中被棄用,在Gradle 5.0中被移除,詳情見:Gradle 4.x官網(wǎng)

例:task <<{ println 'Hello world!'}    解決方法:直接去掉或使用doLast解決。

代碼即腳本

Gradle 腳本采用 Groovy 書寫,作為開胃菜,看下下面這個(gè)例子。

在 gradle 任務(wù)中采用 groovy

build.gradle

task upper << {
    String someString = 'mY_nAmE'
    println "Original: " + someString
    println "Upper case: " + someString.toUpperCase()
}
Output of gradle -q upper
> gradle -q upper
Original: mY_nAmE
Upper case: MY_NAME

或者

在 gradle 任務(wù)中采用 groovy

build.gradle

task count << {
    4.times { print "$it " }
}
Output of gradle -q count
> gradle -q count
0 1 2 3

任務(wù)依賴

你可以按如下方式創(chuàng)建任務(wù)間的依賴關(guān)系

在兩個(gè)任務(wù)之間指明依賴關(guān)系

build.gradle

task hello << {
    println 'Hello world!'
}
task intro(dependsOn: hello) << {
    println "I'm Gradle"
}

?gradle -q intro ?的輸出結(jié)果

Output of gradle -q intro
\> gradle -q intro
Hello world!
I'm Gradle

添加依賴 task 也可以不必首先聲明被依賴的 task。

延遲依賴

build.gradle

task taskX(dependsOn: 'taskY') << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}

輸出 ?gradle -q taskX?

 \> gradle -q taskX
taskY
taskX

可以看到,taskX 是 在 taskY 之前定義的,這在多項(xiàng)目構(gòu)建中非常有用。

注意:當(dāng)引用的任務(wù)尚未定義的時(shí)候不可使用短標(biāo)記法來運(yùn)行任務(wù)。

動(dòng)態(tài)任務(wù)

借助 Groovy 的強(qiáng)大不僅可以定義簡單任務(wù)還能做更多的事。例如,可以動(dòng)態(tài)定義任務(wù)。

創(chuàng)建動(dòng)態(tài)任務(wù)

build.gradle

4.times { counter ->
    task "task$counter" << {
        println "I'm task number $counter"
    }
}

gradle -q task1 的輸出結(jié)果。

\> gradle -q task1
I'm task number 1

任務(wù)操縱

一旦任務(wù)被創(chuàng)建后,任務(wù)之間可以通過 API 進(jìn)行相互訪問。這也是與 Ant 的不同之處。比如可以增加一些依賴。

通過 API 進(jìn)行任務(wù)之間的通信 - 增加依賴

build.gradle

4.times { counter ->
    task "task$counter" << {
        println "I'm task number $counter"
    }
}
task0.dependsOn task2, task3

gradle -q task0的輸出結(jié)果。

Output of gradle -q task0
\> gradle -q task0
I'm task number 2
I'm task number 3
I'm task number 0

為已存在的任務(wù)增加行為。

通過 API 進(jìn)行任務(wù)之間的通信 - 增加任務(wù)行為

build.gradle

task hello << {
    println 'Hello Earth'
}
hello.doFirst {
    println 'Hello Venus'
}
hello.doLast {
    println 'Hello Mars'
}
hello << {
    println 'Hello Jupiter'
}
Output of gradle -q hello
> gradle -q hello
Hello Venus
Hello Earth
Hello Mars
Hello Jupiter

doFirst 和 doLast 可以進(jìn)行多次調(diào)用。他們分別被添加在任務(wù)的開頭和結(jié)尾。當(dāng)任務(wù)開始執(zhí)行時(shí)這些動(dòng)作會(huì)按照既定順序進(jìn)行。其中 << 操作符 是 doLast 的簡寫方式。

短標(biāo)記法

你早就注意到了吧,沒錯(cuò),每個(gè)任務(wù)都是一個(gè)腳本的屬性,你可以訪問它:

以屬性的方式訪問任務(wù)

build.gradle

task hello << {
    println 'Hello world!'
}
hello.doLast {
    println "Greetings from the $hello.name task."
}

gradle -q hello 的輸出結(jié)果

Output of gradle -q hello
\> gradle -q hello
Hello world!
Greetings from the hello task.

對(duì)于插件提供的內(nèi)置任務(wù)。這尤其方便(例如:complie)

增加自定義屬性

你可以為一個(gè)任務(wù)添加額外的屬性。例如,新增一個(gè)叫做 myProperty 的屬性,用 ext.myProperty 的方式給他一個(gè)初始值。這樣便增加了一個(gè)自定義屬性。

為任務(wù)增加自定義屬性

build.gradle

task myTask {
    ext.myProperty = "myValue"
}

task printTaskProperties << {
    println myTask.myProperty
}

gradle -q printTaskProperties 的輸出結(jié)果

Output of gradle -q printTaskProperties
\> gradle -q printTaskProperties
myValue

自定義屬性不僅僅局限于任務(wù)上,還可以做更多事情。

調(diào)用 Ant 任務(wù)

Ant 任務(wù)是 Gradle 中的一等公民。Gradle 借助 Groovy 對(duì) Ant 任務(wù)進(jìn)行了優(yōu)秀的整合。Gradle 自帶了一個(gè) AntBuilder,在 Gradle 中調(diào)用 Ant 任務(wù)比在 build.xml 中調(diào)用更加的方便和強(qiáng)大。 通過下面的例子你可以學(xué)到如何調(diào)用一個(gè) Ant 任務(wù)以及如何與 Ant 中的屬性進(jìn)行通信。

利用 AntBuilder 執(zhí)行 ant.loadfile

build.gradle

task loadfile << {
    def files = file('../antLoadfileResources').listFiles().sort()
    files.each { File file ->
        if (file.isFile()) {
            ant.loadfile(srcFile: file, property: file.name)
            println " *** $file.name ***"
            println "${ant.properties[file.name]}"
        }
    }
}

gradle -q loadfile 的輸出結(jié)果

Output of gradle -q loadfile
\> gradle -q loadfile
*** agile.manifesto.txt ***
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration  over contract negotiation
Responding to change over following a plan
 *** gradle.manifesto.txt ***
Make the impossible possible, make the possible easy and make the easy elegant.
(inspired by Moshe Feldenkrais)

在你腳本里還可以利用 Ant 做更多的事情。想了解更多請(qǐng)參閱在 Gradle 中調(diào)用 Ant。

方法抽取

Gradle 的強(qiáng)大要看你如何編寫腳本邏輯。針對(duì)上面的例子,首先要做的就是要抽取方法。

利用方法組織腳本邏輯

build.gradle

task checksum << {
    fileList('../antLoadfileResources').each {File file ->
        ant.checksum(file: file, property: "cs_$file.name")
        println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
    }
}
task loadfile << {
    fileList('../antLoadfileResources').each {File file ->
        ant.loadfile(srcFile: file, property: file.name)
        println "I'm fond of $file.name"
    }
}
File[] fileList(String dir) {
    file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
}

gradle -q loadfile 的輸出結(jié)果

Output of gradle -q loadfile
\> gradle -q loadfile
I'm fond of agile.manifesto.txt
I'm fond of gradle.manifesto.txt

在后面的章節(jié)你會(huì)看到類似出去出來的方法可以在多項(xiàng)目構(gòu)建中的子項(xiàng)目中調(diào)用。無論構(gòu)建邏輯多復(fù)雜,Gradle 都可以提供給你一種簡便的方式來組織它們。

定義默認(rèn)任務(wù)

Gradle 允許在腳本中定義多個(gè)默認(rèn)任務(wù)。

定義默認(rèn)任務(wù)

build.gradle

defaultTasks 'clean', 'run'
task clean << {
    println 'Default Cleaning!'
}
task run << {
    println 'Default Running!'
}
task other << {
    println "I'm not a default task!"
}

gradle -q 的輸出結(jié)果。

Output of gradle -q
\> gradle -q
Default Cleaning!
Default Running!

這與直接調(diào)用 gradle clean run 效果是一樣的。在多項(xiàng)目構(gòu)建中,每個(gè)子項(xiàng)目都可以指定單獨(dú)的默認(rèn)任務(wù)。如果子項(xiàng)目未進(jìn)行指定將會(huì)調(diào)用父項(xiàng)目指定的的默認(rèn)任務(wù)。

Configure by DAG

稍后會(huì)對(duì) Gradle 的配置階段和運(yùn)行階段進(jìn)行詳細(xì)說明 配置階段后,Gradle 會(huì)了解所有要執(zhí)行的任務(wù) Gradle 提供了一個(gè)鉤子來捕獲這些信息。一個(gè)例子就是可以檢查已經(jīng)執(zhí)行的任務(wù)中有沒有被釋放。借由此,你可以為一些變量賦予不同的值。

在下面的例子中,為 distribution 和 release 任務(wù)賦予了不同的 version 值。

依賴任務(wù)的不同輸出

build.gradle

task distribution << {
    println "We build the zip with version=$version"
}
task release(dependsOn: 'distribution') << {
    println 'We release now'
}
gradle.taskGraph.whenReady {taskGraph ->
    if (taskGraph.hasTask(release)) {
        version = '1.0'
    } else {
        version = '1.0-SNAPSHOT'
    }
}

gradle -q distribution 的輸出結(jié)果

Output of gradle -q distribution
\> gradle -q distribution
We build the zip with version=1.0-SNAPSHOT

gradle -q release 的輸出結(jié)果

Output of gradle -q release
\> gradle -q release
We build the zip with version=1.0
We release now

whenReady 會(huì)在已發(fā)布的任務(wù)之前影響到已發(fā)布任務(wù)的執(zhí)行。即使已發(fā)布的任務(wù)不是主要任務(wù)(也就是說,即使這個(gè)任務(wù)不是通過命令行直接調(diào)用)

下一步目標(biāo)

在本章中,我們了解了什么是 task,但這還不夠詳細(xì)。欲知更多請(qǐng)參閱章節(jié)任務(wù)詳述

另外,可以目錄繼續(xù)學(xué)習(xí)Java 構(gòu)建入門依賴管理基礎(chǔ)。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)