Gradle构建工具的学习与使用
http://my.eoe.cn/william_sim/archive/10691.html
?
Gradle 是以 Groovy 語言為基礎(chǔ),面向Java應(yīng)用為主.基于DSL(領(lǐng)域特定語言)語法的自動化構(gòu)建工具。
官網(wǎng):http://www.gradle.org/
前提: 安裝Gradle。安裝過程非常簡單:
1. 下載Gradle
2. 將GRADLE_HOME/bin/gradle加入$PATH。
基本概念(Project 和 Task)
Gradle中有兩個基本的概念:project和task。每個Gradle的構(gòu)建由一個project構(gòu)成,它代表著需要被構(gòu)建的組件或者構(gòu)建的整個項目。每個project由一個或者多個task組成。task代表著Gradle構(gòu)建過程中可執(zhí)行的最小單元。例如當構(gòu)建一個組件時,可能需要先編譯、打包、然后再生成文檔或者發(fā)布等,這其中的每個步驟都可以定義成一個task。
構(gòu)建第一個Task
和Ant運行時讀取build.xml類似,Gradle運行時默認會讀取build.gradle這個文件, 當然你也可以使用參數(shù)"-b"來指定其他的xxx.gradle
下面,讓我們新建一個build.gradle文件,然后輸入如下內(nèi)容:
| 1 2 3 4 5 | task hello {doLast{println "hello world"}} |
這個構(gòu)建的腳本很簡單,就是輸出hello world。為了運行這個構(gòu)建,我們應(yīng)該在當前目錄下執(zhí)行 "gradle hello",即gradle TaskName。
doLast意思是定義一個行為(映射Gradle中的Action類),放在當前task的最后,類似的,還有doFirst, 表示將定義的行為放在當前task最前面,例如
| 1 2 3 4 5 6 7 8 | task hello {doLast{println "Hello world"} doFirst{println "I am xxx"} } |
執(zhí)行g(shù)radle hello, 將輸出
"I am xxx"
"Hello world"
另外,你也可以使用如下更簡潔的方式來定義task:
| 1 2 3 | task hello << {println "hello world" } |
這里也許大家可能會覺得很奇怪,為什么可以用"<<"來定義Task的執(zhí)行內(nèi)容呢,還是讓我們看看Gradle的代碼是怎么實現(xiàn)的吧:
| 123456789 10 11 12 13 14 15 16 17 18 | public abstract class AbstractTask implements TaskInternal, DynamicObjectAware {private List<Action<? super Task>> actions = new ArrayList<Action<? super Task>>();public Task doFirst(Action<? super Task> action) {if (action == null) {throw new InvalidUserDataException("Action must not be null!");}actions.add(0, wrap(action));return this;}public Task doLast(Action<? super Task> action) {if (action == null) {throw new InvalidUserDataException("Action must not be null!");}actions.add(wrap(action));return this;} |
從上面的代碼可以看出,Task類里有個Action的集合actions,當使用doFirst或者doLast時,實際上是將定義的執(zhí)行部分實例化成Action的對象,然后添加到actions集合里。
明白了這一點,接下來讓我們看看為什么可以使用<<定義Task--------Groovy作為強大的支持DSL的動態(tài)語言,早已經(jīng)重載了 << 操作符,使得我們可以方便的使用<<向集合添加元素。
說道這,相信真相已經(jīng)大白了:原來就是使用Groovy的特性,往集合里添加Action而已。對,這就是Gradle的語法,利用Groovy的DSL特性,幫助我們更容易的定義我們的構(gòu)建腳本。
不過也許大家會覺得,這個例子實在是沒有什么代表性,只是一個簡單的 hello world,說明不了什么問題。好吧,別著急,下次我們會繼續(xù)研究Gradle的其他部分,不過先記住:作為一個構(gòu)建工具,Gradle真的很強大哦!
如何使用Task
1. Project和Task
對于build.gradle配置文件,當運行Gradle 時,Gradle會為我們創(chuàng)建一個Project的對象,來映射build.gradle中的內(nèi)容。其中呢,對于不屬于任何Task范疇的代碼,Gradle會創(chuàng)建一個Script類的對象,來執(zhí)行這些代碼;對于Task的定義,Gradle會創(chuàng)建Task對象,并將它會作為project的屬性存在(實際上是通過getTaskName完成的)。ok,看一個簡單的例子:
新建文件basic/build.gradle,然后加入如下部分代碼:
| 1 2 3 4 5 | println "the project name is $name" task hello << {println "the current task name is $name"println "hello world" } |
當運行這個例子時,首先Gradle會創(chuàng)建一個Porject的對象,然后將它和build.gradle的內(nèi)容映射起來。在這個例子中,project包括兩部分:
1)可執(zhí)行腳本定義
按照之前提到的,可執(zhí)行腳本的定義將直接被創(chuàng)建成對應(yīng)的Script類的對象
在這個例子中,Script對應(yīng)的部分就是第一行println的部分,然后執(zhí)行的結(jié)果就是打印出 "the project name is basic"。
默認的,Project的名字是當前build.gradle所在目錄的名字,在這個例子中,build.gradle放在basic目錄下,因此,project的name也就是basic.
2)Task定義
在這個例子中,Gradle將創(chuàng)建一個Task的實例,將其和定義的task內(nèi)容關(guān)聯(lián)起來。另外,按照之前所說的,當Gradle運行時,我們可以使用訪問project屬性的方式去訪問它。
例如,這個例子中,我們可以使用project.hello來訪問這個task。因為這個task hello已經(jīng)成為project的一個屬性,那么當我們使用gradle properties(properties是gradle自帶的一個Task,它能列出當前project級別的所有屬性,可以使用gradle tasks查看更多內(nèi)建的Task)來獲取project級別的屬性列表時,也將會得到'hello'。
另外,有一點要注意的是,在這個例子中,task中使用的$name,并不是Project的name,它是當前Task的name,因為它被使用在Task的scope里。
執(zhí)行Gradle hello,輸出的結(jié)果將是:
| 1 2 3 | current project name is test the current task name is hello hello world |
2. 定義屬性
在Gradle中,我們可以定義以下三種屬性并使用它們:
1)System Properties
System Properties 實際是指的JVM的system properties。我們知道,在運行java程序時,可以使用-D來設(shè)置Java的系統(tǒng)變量,在Gradle中,你也可以做同樣的事情。比如
gradle xxx -DmySystemProp=xxxx
同時,在build.gradle中,應(yīng)該這樣使用這個變量:
| 1 2 3 | task printSysProps << {println System.properties['system'] } |
2)Project Properties
Project Properties是Gradle專門為Project定義的屬性。它的最大優(yōu)點在于當運行g(shù)radle的時候,我們可以使用-P來設(shè)置它的值。比如,
gradle xxx -PmyProjectProp=xxxxx
而在build.gradle中,可以這樣使用這個變量:
| 1 2 3 4 5 | task printProps << {if (project.hasProperty('commandLineProjectProp')) { println commandLineProjectProp} } |
同時,當我們執(zhí)行g(shù)radle properties查看屬性列表時,這個變量的名稱以及值會顯示在結(jié)果中。
3)Ext(ra) Properties
| 1 | 另外,我們還可以為Project或者Task定義Ext屬性,也稱動態(tài)屬性,我們必須使用關(guān)鍵字ext(對應(yīng)ExtraPropertiesExtension的實例)去定義動態(tài)屬性。從這點可以看出,Gradle已經(jīng)為我們設(shè)計了很多不同的類,去做不同的事情,我們只需要遵循Convention,使用他們即可。如果忘記寫ext關(guān)鍵字,gradle運行時則會提示: |
"Dynamic properties are deprecated...."。這是因為以前版本的gradle定義動態(tài)屬性時,不需要加ext關(guān)鍵字的。
對于Project和Task而言,動態(tài)屬性定義的方式完全一樣,只是作用域不一樣。
當定義完成后,我們就可以使用project.prop 或者 task.prop來訪問這些動態(tài)屬性了。下面讓我們看一個例子:
| 123456789 10 | ext.projectProperties="ext projectProperties-value" task printExtProps << {ext.taskProperties="ext.task.properties-value"if (project.hasProperty('projectProperties')){println "ext.projectProperties values is " + projectProperties }if (printExtProps.hasProperty('taskProperties')){println "task has defined ext.taskProperties value is " + taskProperties } } |
注意:,對于ext定義的動態(tài)屬性,并不能通過外部的方式修改它的值,只能在build.gradle中去設(shè)置或者修改它的值。
同時,如果是為project定義的ext動態(tài)屬性,也會顯示在gradle properties的結(jié)果中。
3. Task依賴
當構(gòu)建一個復雜的項目時,不同task之間存在依賴是必然的。比如說,如果想運行'部署'的task,必然要先運行 編譯、打包、檢測服務(wù)器等task,只有當這被些被依賴的task執(zhí)行完成后,才會部署。對于這種行為之間的依賴,Ant、Maven都提供了聲明式的定義,非常簡單。同樣,使用Gradle定義task之間的依賴也是件很容易的事。
例如,定義如下兩個Task,并且在"intro"里加上"dependendsOn"的關(guān)鍵字,如下所示:
| 1 2 3 4 5 6 | task hello << {println 'Hello world!' } task intro(dependsOn: hello) << {println "I'm Gradle" } |
執(zhí)行 "gradle intro",結(jié)果將是:
| 1 2 | Hello World I'm Gradle |
由此可見,當執(zhí)行g(shù)radle intro時,intro依賴的task hello會先被執(zhí)行。除此之外,dependensOn也支持定義多個task的依賴,使用[]括起來即可。例如
| 1 | task A(dependensOn:['B','C','D']) <<{ xxx } |
除了使用dependensOn跟字符串來定義依賴,我們也可以使用taskX.dependensOn taskY這種形式:
| 1 2 3 4 5 6 7 | task taskX << {println 'taskX' } task taskY << {println 'taskY' } taskX.dependsOn taskY |
或者,也可以使用閉包:
| 1 2 3 4 5 6 7 8 9 | task taskX << {println 'taskX' } taskX.dependsOn {tasks.findAll { task -> task.name.startsWith('lib') } } task lib1 << {println 'lib1' } |
除了之前講的task的部分,Gradle還為我們提供了很多可用的API,更多的細節(jié)大家可以參考下Gradle的文檔。這里我列出一個常用的方法onlyIf。在Gradle里,我們可以使用“OnlyIf()”來決定當前task是否需要被執(zhí)行,例如:新建build.gradle,加入如下代碼:
| 1 2 3 4 | task hello << {println 'hello world' } hello.onlyIf { !project.hasProperty('skipHello') } |
當我們直接執(zhí)行"gradle hello"時,沒有任何結(jié)果,當我們執(zhí)行"gradle hello -PskipHello=xxxx"時,會輸出"hello world"。
4. 總結(jié)
總體而言,用DSL的代碼而不是xml來定義構(gòu)建的過程,還是很fancy的。
多項目構(gòu)建
對于多系統(tǒng)、多項目的情況,很多構(gòu)建工具都已經(jīng)提供了不錯的支持,像maven、ant。Gradle除了借鑒了ant或者maven的繼承的方式定義子項目,也提供了一種更為方便的集中配置的方式,大大減少了構(gòu)建帶來的復雜度。除此之外,Gradle還提供了清晰的Project樹模型來映射多項目的組織結(jié)構(gòu)。下面,讓我們了解一下如何使用Gradle構(gòu)建多項目。
1. 多項目定義及結(jié)構(gòu)
在Gradle中,使用文件settings.gradle定義當前項目的子項目,格式如下所示:
| 1 | include 'sub-project1', 'sub-project2', 'sub-project3', |
它表示在當前的項目下建立三個子項目,分別為'sub-project1', 'sub-project2', 'sub-project3'。默認情況下,每個子項目的名稱對應(yīng)著當前操作系統(tǒng)目錄下的一個子目錄。
當Gradle運行時,會根據(jù)settings.gradle的配置情況,構(gòu)建一個單根節(jié)點的項目樹。其中的每個子節(jié)點代表一個項目(Project),每個項目都有一個唯一的路徑表示它在當前樹中的位置,路徑的定義方式類似:
| 1 | Root:<Level1-子節(jié)點>:<Level2-子節(jié)點>:<Level3-子節(jié)點> |
也可以簡寫成“:::”。借助這種路徑的定義方式,我們可以在build.gradle去訪問不同的子項目。另外,對于單項目,實際上是一種特殊的、只存在根節(jié)點,沒有子節(jié)點的項目樹。
例如,我們有個產(chǎn)品A,包括以下幾個組件core,web,mobile。分別代表"核心邏輯"、"網(wǎng)站"、“手機客戶端”。 因為每個組件是獨立的部分,這個時候最好我們能定義多個子項目,讓每個子項目分別管理自己的構(gòu)建。于是我們可以這樣定義A/settings.gradle
| 1 | include 'core', 'web', 'mobile' |
按照之前描述的,core組件對應(yīng)A/core目錄,web組件對應(yīng)A/web目錄,mobile組件對應(yīng)A/mobile目錄。接下來,我們就可以在每個組件內(nèi)部,定義build.gradle負責管理當前組件的構(gòu)建。
Gradle提供了一個內(nèi)建的task 'gradle projects',可以 幫助我們查看當前項目所包含的子項目,下面讓我們看看gradle projects的輸出結(jié)果:
| 1 2 3 4 5 6 7 8 9 | $ gradle projects :projects ------------------------------------------------------------ Root project ------------------------------------------------------------ Root project 'A' +--- Project ':core' +--- Project ':mobile' --- Project ':web |
結(jié)果一目了然,首先是Root級別的項目A,然后是A下面的子項目'core', 'mobile', 'mobile'
最終的文件以及目錄結(jié)構(gòu)如下所示:
| 1 2 3 4 5 6 7 8 9 | A--settings.gradle--build.gradle--core--build.gradle--web--build.gradle--mobile--build.gradle |
如果你不喜歡這種默認的結(jié)構(gòu),也可以按照如下方式定義子項目的名稱和物理目錄結(jié)構(gòu):
| 1 2 3 4 5 6 7 8 | include(':core) project(':core').projectDir = new File(settingsDir, 'core-xxx') include(':web) project(':web').projectDir = new File(settingsDir, 'web-xxx') include(':mobile) project(':mobile').projectDir = new File(settingsDir, 'mobile-xxx') |
在這個例子中,子項目core實際上對應(yīng)的物理目錄為A/core-xxx,web實際上對應(yīng)的是A/web-xxx,mobile也類似。
雖然我們更改了子項目的物理目錄結(jié)構(gòu),不過由于我們在build.gradle中使用的是類似 “ :”的方式訪問對應(yīng)的子項目,所以目錄結(jié)構(gòu)的改變,對我們Gradle的構(gòu)建腳本并不會產(chǎn)生影響。
接下來,考慮一個更復雜的情況,隨著產(chǎn)品的發(fā)展,mobile這個組件慢慢的劃分成了Android和IOS兩個部分,這時我們只需要在目錄A/mobile下定義新的settings.gradle,并加入如下部分:
include 'android', 'ios'
現(xiàn)在,mobile組件下將存在兩個新的子項目 "android"和"ios"
于是,這時候'gradle projects'的目錄結(jié)構(gòu)就變成
| 123456789 10 11 12 | A--settings.gradle--core--build.gradle--web--build.gradle--mobile--settings.gradle--ios--build.gradle--android--build.gradle |
2. 多項目的集中配置
對于大多數(shù)構(gòu)建工具,對于子項目的配置,都是基于繼承的方式。Gradle除了提供繼承的方式來設(shè)置子項目,還提供了另外一種集中的配置方式,方便我們統(tǒng)一管理子項目的信息。下面看一個例子,打開A/build.gradle,輸入如下部分:
| 1 2 3 4 5 6 7 8 9 | allprojects {task hello << {task -> println "I'm $task.project.name" } } subprojects {hello << {println "- I am the sub project of A"} } project(':core').hello << {println "- I'm the core component and provide service for other parts." } |
對于上面所示的代碼,已經(jīng)很表意了:
allprojects{xxx} 這段代碼表示,對于所有的project,Gradle都將定義一個名稱是hello的Task { println "I'm $task.project.name"} 。
subprojects{xxxx}的這段代碼表示,對于所有的子project,將在名稱為hello的Task上追加Action {println "- I am the sub project of A"}
注意:關(guān)于Task和Action的關(guān)系,請看我之前寫的本系列的第一部分。
project(':core')的這段代碼表示,對于名稱為core的project,將在名稱為hello的Task上追加Action { println "- I'm the core component and provide service for other parts." }
3. 多項目的Task執(zhí)行
之前我們已經(jīng)了解了多項目的結(jié)構(gòu)以及如何通過路徑去訪問子項目?,F(xiàn)在讓我們看看如何使用Gradle來執(zhí)行多項目。
在Gradle中,當在當前項目上執(zhí)行g(shù)radle 時,gradle會遍歷當前項目以及其所有的子項目,依次執(zhí)行所有的同名Task,注意:子項目的遍歷順序并不是按照setting.gradle中的定義順序,而是按照子項目的首字母排列順序。
基于剛才的例子,如果我們在根目錄下,執(zhí)行g(shù)radle hello,那么所有子項目的“hello” Task都會被執(zhí)行。如果我們在mobile目錄下執(zhí)行g(shù)radle hello,那么mobile、android以及IOS的“hello” Task都會被執(zhí)行。關(guān)于該例子的運行結(jié)果,這里就不貼出來了。大家如果有興趣的話可以試試。
4. 總結(jié)
這篇文章主要描述了使用Gradle管理多項目的知識。相比Ant或者Maven,Gradle提供了更靈活的配置方式。更重要的是,Gradle還提供了很多內(nèi)建的Task幫助我們查看或者管理項目。這次就先聊到這里,下次我們來看看Gradle的生命周期。
from eoe.cn
聲明:eoe文章著作權(quán)屬于作者,受法律保護,轉(zhuǎn)載時請務(wù)必以超鏈接形式附帶如下信息
原文作者: william_sim
原文地址: http://my.eoe.cn/william_sim/archive/10691.html
總結(jié)
以上是生活随笔為你收集整理的Gradle构建工具的学习与使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Eclipse 安装Gradle插件
- 下一篇: 解决Could not reserve