Gradle學習記錄009 Gradle構建的生命週期

學習Gradle構建的生命週期(lifecycle)。該學習記錄基於Gradle官方網站資料。本篇參考鏈接如下:

https://docs.gradle.org/current/userguide/build_lifecycle.html

1.Gradle構建的三個階段

初始化階段(Initialization)

Gradle支持多工程,在初始化階段,Gradle會決定需要哪個或哪些工程,並且為每一個工程構造一個工程實例。

配置階段(Configuration)

在這個階段每一個工程實例都會被配置,每個工程的所有構建腳本都會被執行。(官網是這樣介紹,但是筆者懷疑應該是每個與配置相關的腳本會被執行。後續學習展開。)

執行階段(Execution)

Gradle會判斷那些在配置階段被創建,被選擇,被配置好的任務中哪些是可以執行的。接受命令行輸入的任務名稱或者參數,執行相關任務。

2.Settings文件

settings.gradle文件通常與build.gradle文件處於工程根目錄。是Gradle構建的設定文件。settings文件在初始化階段執行。多工程構建必須在主工程根目錄有settings文件,因為settings文件會決定那個工程是構建的一部分。單工程構建時,settings文件是可選的。有時單工程構建會通過settings文件引入一些類庫。

下面的例子完美展現了Gradle構建的三個階段。

settings.gradle

// 初始化階段執行此語句

println 'This is executed during the initialization phase.'

build.gradle

// 配置階段執行此語句

println 'This is executed during the configuration phase.'

task configured {

// 配置階段執行此語句

println 'This is also executed during the configuration phase.'

}

task test {

doLast {

// 執行階段執行此語句

println 'This is executed during the execution phase.'

}

}

task testBoth {

doFirst {

// 執行階段執行此語句

println 'This is executed first during the execution phase.'

}

doLast {

// 執行階段執行此語句

println 'This is executed last during the execution phase.'

}

// 配置階段執行此語句

println 'This is executed during the configuration phase as well.'

}

輸出:

$ gradle test testBoth> Configure project :This is executed during the configuration phase.This is also executed during the configuration phase.This is executed during the configuration phase as well.> Task :testThis is executed during the execution phase.> Task :testBothThis is executed first during the execution phase.This is executed last during the execution phase.BUILD SUCCESSFUL in 1s2 actionable tasks: 2 executed

3.多工程構建

工程的位置

多工程構建通常都是樹形結構。這個樹形結構的層級關係通常由各個工程所在的物理位置決定,但是也可以通過settings.gradle文件進行指定。

樹形結構的構建

  • 層級關係結構

下面的示例表明這個多工程構建由三層結構組成。第一層是根工程;第二層是project1,project2和project3;第三層是project2:child和project3:child1。跟工程目錄下有project1,project2和project3;project2工程目錄下有child工程;同樣project3工程目錄下有child1工程。

settings.gradle

include 'project1', 'project2:child', 'project3:child1'

  • 水平結構關係

下面的示例表明這個多工程構建是水平的。project3與project4必須和根工程處於同一級目錄。但是它們仍然被認為是settings.gradle文件所在工程的子工程。

settings.gradle

includeFlat 'project3', 'project4'

更改樹形結構多工程構建的節點

settings文件中對多工程構建的設置被稱為描述。我們可以很容易的更改描述來實現各種需求。(下面的示例沒有試驗成功,需要後續學習展開)

settings.gradle

rootProject.name = 'main'

project(':projectChild').projectDir = new File(settingsDir, '../projectChild')

project(':projectChild').buildFileName = 'projectA.gradle'

includeFlat 'projectChild'

4.初始化

Gradle允許在任何子工程內執行Gradle腳本。如果當前工程內沒有settings.gradle文件,它會按照以下的步驟來執行:

  1. 在同級目錄下如果有master文件夾(水平的多工程構建一般會有這個文件夾),則去該文件夾查找settings.gradle文件。
  2. 如果沒找到,則查找上級目錄
  3. 如果仍沒找到,按照單工程構建來執行腳本
  4. 如果找到了settings.gradle文件,Gradle會檢測當前工程是不是settings文件裡描述的多工程構建的一部分。如果是則按照多工程構建執行。如果不是則按照單工程構建執行。

Gradle必須定位settings.gradle文件的原因是它必須通過setinngs.gradle才能知道當前構建是多工程還是單工程的。

5.在構建的生命週期中響應事件

構建腳本可以在它運行的生命週期內接收通知來完成一些操作。就像java的interceptor或者listener一樣。

工程評價階段前後響應事件

在工程評價階段之前與之後,Gradle都可以接收通知,來完成一些動作。必須輸出log等。關於工程評價階段,個人理解應該是Gradle構建的配置階段的一個子階段。

如下面的示例,為每一個有hasTests屬性並且為true的工程追加了一個test任務。

可以看到執行前的build.gradle文件中並沒有test任務。執行所用的test任務就是在評價工程之後的時間點追加的。

通過輸出結果, 可以確認任務的評價階段其實是在Gradle構建生命週期的配置階段。

allprojects {

afterEvaluate { project ->

println "Adding test task to $project"

project.task('test') {

doLast {

println "Running tests for $project"

}

}

}

}

輸出:

$ gradle test> Configure project :Adding test task to root project 'test'> Configure project :projectAAdding test task to project ':projectA'> Configure project :projectBAdding test task to project ':projectB'> Task :testRunning tests for root project 'test'> Task :projectA:testRunning tests for project ':projectA'> Task :projectB:testRunning tests for project ':projectB'BUILD SUCCESSFUL in 1s3 actionable tasks: 3 executed

下面示例展示了在工程評價階段之後的時間點輸出log。使用afterProject放回會使無論工程評價成功與否,都去執行處理。

為了使ProjectA評價時出錯,修改了一下ProjectA的build.gradle文件,增加了一個人為錯誤。未列在示例中。

主工程的build.gradle文件

allprojects {

afterEvaluate { project ->

println "Adding test task to $project"

project.task('test') {

doLast {

println "Running tests for $project"

}

}

}

}

gradle.afterProject { project ->

if (project.state.failure) {

println "Evaluation of $project FAILED"

} else {

println "Evaluation of $project succeeded"

}

}

輸出:

$ gradle test> Configure project :Evaluation of root project 'test' succeededAdding test task to root project 'test'> Configure project :projectAEvaluation of project ':projectA' FAILEDAdding test task to project ':projectA'FAILURE: Build failed with an exception.* Where:Build file 'C:\study\gradle\test\projectA\build.gradle' line: 1* What went wrong:A problem occurred evaluating project ':projectA'.> Could not find method plugin() for arguments [build_8kheb2om35kx9owucyzjlw3zs$_run_closure1@7da65a09] on project ':projectA' of type org.gradle.api.Project.* Try:Run with --stacktrace option to get the stack trace. Run with --info or --debugoption to get more log output. Run with --scan to get full insights.* Get more help at https://help.gradle.orgBUILD FAILED in 1s

任務追加之後響應事件

Gradle可以在增加任務(taskAdded)之後的時間點接收通知,來完成如為任務增加一些默認值等操作。

tasks.whenTaskAdded { task ->

task.ext.srcDir = 'src/main/java'

}

task a

println "source dir is $a.srcDir"

輸出:

$ gradle a> Configure project :source dir is src/main/java> Task :a UP-TO-DATEBUILD SUCCESSFUL in 1s

任務的執行前後響應事件

Gradle可以在任務執行前與執行後的時間點接收通知。

task ok

task broken(dependsOn: ok) {

doLast {

throw new RuntimeException('broken')

}

}

// 所有的任務執行前都打印executing taskName語句

gradle.taskGraph.beforeTask { Task task ->

println "executing $task ..."

}

// 所有的任務執行後都會判斷成功與否,成功則打印done,失敗則打印FAILED

gradle.taskGraph.afterTask { Task task, TaskState state ->

if (state.failure) {

println "FAILED"

}

else {

println "done"

}

}

輸出:

$ gradle broken> Task :ok UP-TO-DATEexecuting task ':ok' ...done> Task :broken FAILEDexecuting task ':broken' ...FAILEDFAILURE: Build failed with an exception.* Where:Build file 'C:\study\gradle\test\build.gradle' line: 5* What went wrong:Execution failed for task ':broken'.> broken* Try:Run with --stacktrace option to get the stack trace. Run with --info or --debugoption to get more log output. Run with --scan to get full insights.* Get more help at https://help.gradle.orgBUILD FAILED in 2s1 actionable task: 1 executed


分享到:


相關文章: