增量式構建
- gradle判斷task是否改變過的依據是:這個task的inputs和outputs屬性組成的集合。當一個task鏈被執行時,如果其中某一個task的inputs和outputs沒有發生改變,則認為該task是最新的,該task將被跳過,在執行鏈輸出中可以看到該task被標為
UP-TO-DATE
,下圖為Task中的imputs和outputs屬性
- inputs 屬性應該被賦值為 一個目錄、一個或多個文件、或是一個任意屬性
- outputs 應該被賦值為一個或多個目錄或者一個或多個文件
那么增量式構建使用場景是怎樣的呢?下面是一個例子:
- 例子
假設一個task,makeReleaseVersion,在項目發布部署之前要修改項目版本的release值為true:
task makeReleaseVersion(group: 'versioning' , description: 'Makes Project a release version.') << {
version.release = true
ant.propertyfile(file: versionFile){
entry(key: 'release', type: 'string',operation: '=',value:'true')//利用ant task的propertyfile提供的便利方式來修改屬性文件
}
}
但是意外發生了,在自動化發布的過程中無法連上服務器,在修復故障后,重新運行自動化發布的任務,因為部署任務依賴於修改版本號任務,所以makeReleaseVersion任務會再次被運行,即使版本號已經在上次運行過程中被修改。
為解決這一問題,需要采用gradle提供的增量式構建特性,為該task的inputs和outputs賦值:
task makeReleaseVersion(group: 'versioning' , description: 'Makes Project a release version.') {
inputs.property ('release',version.release)
outputs.file versionFile //由於該任務會修改版本文件,所以它被聲明為outputs
doLast{
version.release = true
ant.propertyfile(file: versionFile){
entry(key: 'release', type: 'string',operation: '=',value:'true')//利用ant task的propertyfile提供的便利方式來修改屬性文件
}
}
}
這樣就實現了增量式構建
- task的inputs和outputs的執行
確保inputs和outputs的值在配置階段是可訪問的,因為它們在配置階段被執行。如果要通過編程獲得輸出,則可以通過upToDateWhen(Closure var1)方法來實現,與常規的inputs和outputs的set方法比,這個方法是在執行階段執行的,如果閉包返回true,則這個task被認為是最新的。
自定義task
在gradle中,默認Task的實現類為DefaultTask,而DefaultTask中空無一物,全部繼承自AbstractTask。當構建task的動作邏輯變得相當復雜時,必然會產生結構化task代碼的需求,這時就可以實現自定義的Task實現類。
下面將makeReleaseVersion封裝為ReleaseVersionTask的自定義Task
class ReleaseVersionTask extends DefaultTask {
@Input Boolean release //通過org.gradle.api.tasks包下的注解來聲明輸入輸出
@OutputFile File destFile
ReleaseVersionTask(){
group = 'versioning'
description = 'Makes Project a release version.'
}
@TaskAction //使用注解聲明執行代碼,
void start(){
project.version.release = true
ant.propertyfile(file: destFile){
entry(key: 'release', type: 'string',operation: '=',value:'true')//利用ant task的propertyfile提供的便利方式來修改屬性文件
}
}
}
注意:@Input注解會在配置期間驗證屬性值,如果為null,gradle會拋出TaskValidationException異常。為了允許輸入null,需要給它添加@Optional注解。
- 使用方法
task makeReleaseVersion(type: ReleaseVersionTask) {
release = version.release
destFile = versionFile
}
Gradle的內置task類型
在Gradle DSL指南中可以找到完整的task參考
下面用一個使用類型為內置的task類型Zip的task
task createDistribution(type: Zip, dependsOn: makeReleaseVersion){
from war.outputs.files //依賴推斷
from(sourceSets*.allSource){
into 'src'
}
from(rootDir){
include versionFile.name
}
}
- task依賴推斷
在上面的createDistribution task中聲明了依賴的task,但是一些task並不直接依賴其他task,比如createDistribution對於war。通過使用一個task的輸出作為另一個task的輸入,Gradle就可以推斷出依賴關系,所依賴的task會自動運行。
task規則
Gradle引入task規則的概念,就是根據task名稱模式執行特定的邏輯。
- task規則命名模式
task名稱的靜態部分和一個占位符組成一個task規則名字,例如increment<Classifier>Version
。java插件的clean任務便利用了task規則,clean<TaskName>
用來刪除指定task的輸出。
- 聲明task規則
- 首先,獲得對TaskContainer的引用,該引用為tasks
- 然后,調用addRule(String,Closure)方法,第一個參數提供了描述信息(比如,task命名模式),第二個參數聲明了要執行的閉包來應用規則。
- 例如這樣:
tasks.addRule("Pattern: increment<Classifier>Version - Increments the project version classifier."){ String taskName -> if(taskName.startsWith('increment') && taskName.endWith('Version')){ task(taskName)<<{ String classifier = (taskName - 'increment'- 'Version').toLowerCase() //接下來便是分支判斷代碼 } } }
- task規則不能像處理任何其它簡單的task或增強的task一樣被獨立分組,task規則會顯示在Rules組下。
構建源代碼目錄buildSrc
事實上在build.gradle文件中不適合寫面向對象的代碼,所以應該把之前的POJO類和自定義task類像項目源代碼一樣組織起來,類似於java源代碼放在src/main/java目錄下、Groovy代碼放在src/main/groovy目錄下,buildSrc用於組織構建代碼源文件。構建代碼包可以放在buildSrc/src/main/groovy或buildSrc/src/main/java下,這樣還方便寫構建代碼的單元測試代碼。