從 Gradle 5.1 開始,我們建議在建立任務時使用設定避免 API。

任務設定避免 API
設定避免 API 會避免設定不會用於建置的任務,這可能會對總設定時間產生重大影響。
例如,在執行 compile
任務(套用 java
外掛)時,其他不相關的任務(例如 clean
、test
、javadocs
)將不會執行。
若要避免建立和設定建置不需要的任務,我們可以改為註冊該任務。
註冊任務時,建置會知道該任務。可以設定任務,並且可以傳遞對任務的參照,但任務物件本身尚未建立,而且其動作尚未執行。已註冊的任務將保持此狀態,直到建置中的某個項目需要已實例化的任務物件為止。如果永遠不需要任務物件,則任務將保持註冊狀態,並且將避免建立和設定任務的成本。
在 Gradle 中,您可以使用 TaskContainer.register(java.lang.String) 註冊任務。register(…)
方法不會傳回任務實例,而是傳回 TaskProvider,這是任務的參照,可以在許多可能使用一般任務物件的地方使用(例如,在建立任務相依性時)。
指南
延後任務建立
有效避免任務設定需要建立作者將 TaskContainer.create(java.lang.String) 的實例變更為 TaskContainer.register(java.lang.String)。
較舊版本的 Gradle 只支援 create(…)
API。create(…)
API 在呼叫時會急切建立並設定任務,應避免使用。
單獨使用 register(…) 可能不足以完全避免所有任務設定。您可能需要變更其他透過名稱或類型設定任務的程式碼,請參閱下方說明。
|
延後任務設定
急切 API 例如 DomainObjectCollection.all(org.gradle.api.Action) 和 DomainObjectCollection.withType(java.lang.Class, org.gradle.api.Action) 會立即建立並設定任何已註冊的任務。若要延後任務設定,您必須移轉到等效的避免設定 API。請參閱 下表 以找出最佳替代方案。
參照已註冊的任務
您可以透過 TaskProvider 物件處理已註冊的任務,而非參照任務物件。可以透過多種方式取得 TaskProvider,包括 TaskContainer.register(java.lang.String) 和 TaskCollection.named(java.lang.String) 方法。
呼叫 Provider.get() 或使用 TaskCollection.getByName(java.lang.String) 根據名稱查詢任務,將會建立並設定任務。
像 Task.dependsOn(java.lang.Object…) 和 ConfigurableFileCollection.builtBy(java.lang.Object...) 這類方法會以與 Task 相同的方式處理 TaskProvider,因此您不需要取消封裝 Provider
,顯式依賴關係才能繼續運作。
您必須使用避免設定的等效方式,才能根據名稱設定任務。請參閱 下表 以找出最佳替代方案。
參照任務實例
如果您需要存取 Task 實例,可以使用 TaskCollection.named(java.lang.String) 和 Provider.get()。這將會建立並設定任務,但所有內容應會像使用急切 API 時一樣運作。
避免設定的任務排序
呼叫排序方法本身不會建立任務。這些方法所做的只是宣告關聯性。
這些關係的存在可能會在建置程序的後續階段間接導致任務建立。 |
當需要建立任務關係時 (例如,dependsOn
、finalizedBy
、mustRunAfter
、shouldRunAfter
),可以區分軟關係和強關係。它們對組態階段中任務建立的影響不同
-
Task.mustRunAfter(…) 和 Task.shouldRunAfter(…) 代表軟關係,只能變更現有任務的順序,但無法觸發它們的建立。
-
Task.dependsOn(…) 和 Task.finalizedBy(…) 代表強關係,強制執行參考的任務,即使它們原本不會被建立。
-
如果任務未執行,無論它是由 Task.register(…) 或 Task.create(…) 建立,定義的關係都不會在組態時間觸發任務建立。
-
如果任務已執行,所有強烈關聯的任務都必須在組態時間建立和組態,因為它們可能有其他
dependsOn
或finalizedBy
關係。這會遞移發生,直到任務圖包含所有強關係。
遷移指南
遷移準則
-
在遷移期間使用
help
任務作為基準。
help
任務是評量遷移程序的完美候選者。在僅使用組態避免 API 的建置中,建置掃描 顯示組態期間未建立任何任務,且僅建立已執行的任務。 -
僅在組態動作內變更目前的任務。
由於任務組態動作現在可以立即、稍後或永遠不會執行,變更目前的任務以外的任何內容都可能導致組建行為不確定。請考慮以下程式碼val check by tasks.registering tasks.register("verificationTask") { // Configure verificationTask // Run verificationTask when someone runs check check.get().dependsOn(this) }
def check = tasks.register("check") tasks.register("verificationTask") { verificationTask -> // Configure verificationTask // Run verificationTask when someone runs check check.get().dependsOn verificationTask }
執行
gradle check
任務應執行verificationTask
,但在此範例中不會執行。這是因為verificationTask
和check
之間的相依性僅在verificationTask
實現時才會發生。為避免此類問題,您只能修改與組態動作相關聯的任務。其他任務應在其自己的組態動作中修改val check by tasks.registering val verificationTask by tasks.registering { // Configure verificationTask } check { dependsOn(verificationTask) }
def check = tasks.register("check") def verificationTask = tasks.register("verificationTask") { // Configure verificationTask } check.configure { dependsOn verificationTask }
未來,Gradle 會將此類反模式視為錯誤並產生例外狀況。
-
確保建立一個良好的驗證組建邏輯計畫。
通常,一個簡單的build
任務呼叫就足以驗證您的組建邏輯。不過,有些組建可能需要額外的驗證 - 了解組建的行為並確保您有一個良好的驗證計畫。 -
避免以名稱參照任務。
通常,以名稱參照任務是一種脆弱的模式,應避免使用。儘管任務名稱在TaskProvider
上可用,但應盡力使用強類型模型中的參照。 -
盡可能使用新的任務 API。
急切實現某些任務可能會導致其他任務的連鎖實現。使用TaskProvider
有助於建立間接關係,以防範遞移實現。 -
如果您嘗試從新 API 的組態區塊存取某些 API,這些 API 可能會被禁止。
例如,Project.afterEvaluate()
無法在組態使用新 API 註冊的任務時呼叫。由於afterEvaluate
用於延遲組態Project
,將延遲組態與新 API 混用可能會導致難以診斷的錯誤,因為使用新 API 註冊的任務並不總是組態的,但afterEvaluate
區塊可能總是會執行。
遷移步驟
遷移程序的第一部分是逐步檢閱程式碼,並手動將急切任務建立和組態遷移到使用組態迴避 API。
-
遷移影響所有任務 (
tasks.all {}
) 或依類型進行子集遷移 (tasks.withType(…) {}
) 的任務組態。
這將導致您的建置急切建立較少的由外掛程式註冊的任務。 -
遷移由名稱組態的任務。
這將導致您的建置急切建立較少的由外掛程式註冊的任務。例如,使用TaskContainer#getByName(String, Closure)
的邏輯應轉換為TaskContainer#named(String, Action)
。這也包括 透過 DSL 區塊進行任務組態。 -
將任務建立遷移到
register(…)
。
此時,您應變更任何任務建立 (使用create(…)
或類似方法) 改用註冊。
在進行這些變更後,您應會看到組態時間急切建立的任務數量有所改善。
遷移疑難排解
-
正在實現哪些任務? 使用 建置掃描 透過下列步驟進行疑難排解
-
使用
--scan
旗標執行 Gradle 指令。 -
導覽至組態效能索引標籤
-
將顯示所有必要的資訊
-
當建立或未建立每個任務時存在的總任務數。
-
立即建立
代表使用急切任務 API 建立的任務。 -
在組態期間建立
代表使用組態迴避 API 建立的任務,但已明確 (透過TaskProvider#get()
) 或隱含地使用急切任務查詢 API 實現。 -
立即建立
和在組態期間建立
數字都被視為「不良」數字,應盡可能將其減至最低。 -
在任務執行期間建立
代表在建立執行任務圖表之後建立的任務。此時建立的任何任務都不會作為圖表的一部分執行。理想情況下,此數字應為零。 -
在任務圖表計算期間建立
代表在建立執行任務圖表時建立的任務。理想情況下,此數字應等於執行的任務數。 -
未建立
代表在此建置階段中避免的任務。理想情況下,此數字應盡可能大。
-
-
下一節有助於回答任務在哪裡實現的問題。對於每個腳本、外掛程式或生命週期回呼,最後一欄代表立即或在組態期間建立的任務。理想情況下,此欄應為空。
-
專注於腳本、外掛或生命週期回呼,將會顯示所建立任務的細目。
-
-
移轉陷阱
-
注意隱藏的急切任務實現。有許多方法可以急切配置任務。
例如,使用任務名稱和 DSL 程式區塊來配置任務,將會立即建立任務(使用 Groovy DSL 時)// Given a task lazily created with tasks.register("someTask") // Some time later, the task is configured using a DSL block someTask { // This causes the task to be created and this configuration to be executed immediately }
改用
named()
方法來取得任務參考並進行配置tasks.named("someTask") { // ... // Beware of the pitfalls here }
類似地,Gradle 有語法糖,允許透過名稱來參照任務,而無需明確的查詢方法。這也會導致立即建立任務
tasks.register("someTask") // Sometime later, an eager task is configured like task anEagerTask { // The following will cause "someTask" to be looked up and immediately created dependsOn someTask }
有幾種方法可以避免這種過早建立
-
使用
TaskProvider
變數。當在同一個建置腳本中多次參照任務時很有用。val someTask by tasks.registering task("anEagerTask") { dependsOn(someTask) }
def someTask = tasks.register("someTask") task anEagerTask { dependsOn someTask }
-
將使用者任務移轉到新的 API。
tasks.register("someTask") tasks.register("anEagerTask") { dependsOn someTask }
-
延遲查詢任務。當任務不是由同一個外掛建立時很有用。
tasks.register("someTask") task("anEagerTask") { dependsOn(tasks.named("someTask")) }
tasks.register("someTask") task anEagerTask { dependsOn tasks.named("someTask") }
-
要使用的延遲 API
API | 備註 |
---|---|
傳回 |
|
傳回 |
|
可以使用。如果串連 |
|
傳回 |
要避免的急切 API
API | 備註 |
---|---|
|
不要使用簡寫符號。改用 |
改用 |
|
不要使用。 |
|
不要使用。 |
|
避免呼叫此方法。行為可能會在未來變更。 |
|
改用 |
|
改用 |
|
請改用 |
|
如果您是根據名稱進行比對,請改用 |
|
改用 |
|
請改用 |
|
請改用 |
|
請改用 |
|
避免呼叫此方法。在大部分情況下, |
|
請勿使用。 |
|
|
避免執行此操作,因為它需要建立和設定所有任務。 |
|
避免呼叫此方法。行為可能會在未來變更。 |