目錄
1.構建緩存
💡是否想了解頂級工程團隊用來保持構建快速和高性能的提示和技巧?在此處註冊以獲取我們的構建緩存培訓。
✨此處描述的構建緩存功能與Android插件構建緩存不同。
2.總覽
Gradle 構建緩存是一種緩存機制,旨在通過重用其他構建生成的輸出來節省時間。構建緩存通過以下方式工作:存儲(本地或遠程)構建輸出,並允許構建在確定輸入未更改時從緩存中獲取這些輸出,從而避免了重新生成它們的昂貴工作。
使用構建緩存的第一個功能是任務輸出緩存。本質上,任務輸出緩存利用了與Gradle用於避免以前的本地版本已經產生一組任務輸出的最新檢查相同的情報。但是,任務輸出緩存不僅限於同一個工作空間中的先前版本,還使Gradle可以重用本地計算機上任何位置的任何早期版本的任務輸出。當使用共享的構建緩存進行任務輸出緩存時,這甚至可以在開發人員機器和構建代理之間使用。
除任務外,工件轉換還可以利用構建緩存並類似於任務輸出緩存來重用其輸出。
💡有關學習如何使用構建緩存的動手方法,請嘗試使用“構建緩存”指南。它涵蓋了可以改進緩存的不同方案,並詳細討論了爲構建啓用緩存時需要注意的不同警告。
3.啓用構建緩存
默認情況下,不啓用構建緩存。您可以通過以下兩種方式啓用構建緩存:
在命令行上運行--build-cache
Gradle僅將構建緩存用於此構建。
將org.gradle.caching=true
放在你的gradle.properties中
除非使用顯式禁用,否則Gradle將嘗試對所有構建重用先前構建的輸出--no-build-cache
。
啓用構建緩存後,它將在Gradle用戶主目錄中存儲構建輸出。有關配置此目錄或其他類型的構建緩存的信息,請參閱“配置構建緩存”。
4.任務輸出緩存
除了最新檢查中描述的增量構建之外,Gradle還可以通過將輸入與任務匹配來重用任務以前執行的輸出,從而節省時間。任務輸出可以通過構建緩存在一臺計算機上的構建之間,甚至在不同計算機上運行的構建之間重複使用。
我們關注的用例是用戶擁有整個組織範圍內的遠程構建緩存,該緩存由連續的集成構建定期填充。開發人員和其他持續集成代理應從遠程構建緩存中加載緩存條目。我們希望開發人員不會被允許填充遠程構建緩存,並且所有連續集成構建都會在運行clean
任務後填充構建緩存。
爲了使您的構建能夠很好地使用任務輸出緩存,它必須與增量構建功能一起正常工作。例如,當連續兩次運行構建時,所有帶有輸出的任務都應爲UP-TO-DATE
。在不滿足此先決條件的情況下啓用任務輸出緩存時,您不能期望更快的構建或正確的構建。
啓用構建緩存時,將自動啓用任務輸出緩存,請參閱啓用構建緩存。
4.1.它是什麼樣子的
讓我們從使用Java插件的項目開始,該項目具有一些Java源文件。我們是第一次運行構建。
> gradle --build-cache compileJava
:compileJava
:processResources
:classes
:jar
:assemble
BUILD SUCCESSFUL
我們在輸出中看到本地構建緩存使用的目錄。除此之外,構建與沒有構建緩存的情況相同。讓我們清理並再次運行構建。
> gradle clean
:clean
BUILD SUCCESSFUL
> gradle --build-cache assemble
:compileJava FROM-CACHE
:processResources
:classes
:jar
:assemble
BUILD SUCCESSFUL
現在我們看到,不是執行:compileJava
任務,而是從構建緩存中加載了任務的輸出。由於其他任務不可緩存,因此尚未從構建緩存中加載其他任務。這是由於:classes
和:assemble
是生命週期的任務,:processResources
和:jar
是Copy-like的任務,因爲它通常是更快地執行它們哪些是不可緩存的任務。
5.可緩存的任務
由於任務描述了其所有輸入和輸出,因此Gradle可以計算生成緩存密鑰,該密鑰根據其輸入唯一定義任務的輸出。該構建緩存鍵用於從構建緩存請求先前的輸出或將新的輸出存儲在構建緩存中。如果先前的構建輸出已經被其他人(例如您的持續集成服務器或其他開發人員)存儲在緩存中,則可以避免在本地執行大多數任務。
下列輸入以與最新檢查相同的方式爲任務的構建緩存鍵提供幫助:
-
任務類型及其類路徑
-
輸出屬性的名稱
-
如“自定義任務類型”一節中所述,註釋的屬性名稱和值
-
DSL通過TaskInputs添加的屬性的名稱和值
-
Gradle發行版,buildSrc和插件的類路徑
-
當構建腳本影響任務執行時的內容
任務類型需要使用@CacheableTask批註選擇加入任務輸出緩存。請注意,@CacheableTask不是子類繼承的。自定義任務類型默認情況下不可緩存。
5.1.內置可緩存任務
當前,以下內置Gradle任務是可緩存的:
-
Java工具鏈: JavaCompile和 Javadoc
-
Groovy工具鏈: GroovyCompile, Groovydoc
-
Scala工具鏈: ScalaCompile, PlatformScalaCompile, ScalaDoc
-
本機工具鏈: CppCompile, CCompile, SwiftCompile
-
測試: Test
-
代碼質量任務: Checkstyle, CodeNarc, Pmd
-
JaCoCo: JacocoMerge和 JacocoReport
-
其他任務: AntlrTask, ValidatePlugins, WriteProperties
當前所有其他內置任務都不可緩存。
5.2.第三方插件
有一些第三方插件可以很好地與構建緩存配合使用。最突出的示例是Android插件3.1+和Kotlin插件1.2.21+。對於其他第三方插件,請查看其文檔以瞭解它們是否支持構建緩存。
5.3.聲明任務輸入和輸出
具有可緩存任務的輸入和輸出的完整圖片非常重要,這樣一個構建的結果就可以安全地在其他地方重複使用。
缺少任務輸入會導致不正確的緩存命中,其中,由於兩次執行都使用相同的緩存鍵,因此不同的結果被視爲相同。如果Gradle不能完全捕獲給定任務的所有輸出,則缺少任務輸出會導致構建失敗。錯誤聲明的任務輸入會導致高速緩存未命中,尤其是在包含易失性數據或絕對路徑時。(有關應聲明爲輸入和輸出的信息,請參見“任務輸入和輸出”。)
✨任務路徑不是構建緩存鍵的輸入。這意味着具有不同任務路徑的任務可以重用彼此的輸出,只要Gradle確定執行它們會產生相同的結果即可。
爲了確保正確聲明瞭輸入和輸出,請使用集成測試(例如,使用TestKit)檢查任務爲相同的輸入產生相同的輸出並捕獲該任務的所有輸出文件。我們建議添加測試以確保任務輸入可重定位,即可以將任務從緩存加載到其他構建目錄中(請參閱@PathSensitive)。
爲了處理任務的易失性輸入,請考慮配置輸入標準化。
6.啓用不可緩存任務的緩存
正如我們所看到的,如果內置任務或插件提供的任務可以通過其註釋進行註釋,則它們是可緩存的Cacheable
。但是,如果您想使可緩存任務成爲其不可緩存的類,該怎麼辦?讓我們舉一個具體的例子:您的構建腳本使用一般NpmTask
任務通過委派給NPM(並運行npm run bundle
)來創建JavaScript捆綁包。此過程類似於複雜的編譯任務,但NpmTask
由於過於通用而無法默認緩存:它僅接受參數並使用這些參數運行npm。
這個任務的輸入和輸出很容易弄清楚。輸入是包含JavaScript文件和NPM配置文件的目錄。輸出是此任務生成的捆綁文件。
6.1.使用註釋
我們創建的子類,NpmTask
並使用批註聲明輸入和輸出。
如果可能,最好使用委託而不是創建子類。這是內置的情況下JavaExec
,Exec
,Copy
和Sync
任務,這對一個方法Project
做實際的工作。
如果您是一名現代JavaScript開發人員,那麼您就會知道捆綁可能會很長,值得緩存。爲此,我們需要使用@CacheableTask批註告訴Gradle它允許緩存該任務的輸出。
這足以使任務可緩存在您自己的計算機上。但是,默認情況下,輸入文件由其絕對路徑標識。因此,如果需要在使用不同路徑的多個開發人員或計算機之間共享緩存,則將無法正常工作。因此,我們還需要設置路徑靈敏度。在這種情況下,可以使用輸入文件的相對路徑來標識它們。
注意,可以通過覆蓋基類的getter並對該方法進行註釋來覆蓋基類的屬性註釋。
例子1.定製的可緩存BundleTask
Groovy
build.gradle
@CacheableTask // (1)
class BundleTask extends NpmTask {
@Override @Internal // (2)
ListProperty<String> getArgs() {
super.getArgs()
}
@InputDirectory
@SkipWhenEmpty
@PathSensitive(PathSensitivity.RELATIVE) // (3)
final DirectoryProperty scripts = project.objects.directoryProperty()
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE) // (4)
final ConfigurableFileCollection configFiles = project.files()
@OutputFile
final RegularFileProperty bundle = project.objects.fileProperty()
BundleTask() {
args.addAll("run", "bundle")
bundle.set(project.layout.buildDirectory.file("bundle.js"))
scripts.set(project.layout.projectDirectory.dir("scripts"))
configFiles.from(project.layout.projectDirectory.file("package.json"))
configFiles.from(project.layout.projectDirectory.file("package-lock.json"))
}
}
task bundle(type: BundleTask)
-
(1)添加
@Cacheable
以啓用任務緩存。 -
(2)重寫基類的屬性的getter,以將輸入註釋更改爲
@Internal
。 -
(3)(4)聲明路徑靈敏度
6.2.使用運行時API
如果由於某種原因無法創建新的自定義任務類,則還可以使用運行時API聲明輸入和輸出,從而使任務可緩存。
要爲任務啓用緩存,您需要使用TaskOutputs.cacheIf()方法。
通過運行時API進行的聲明與上述註釋具有相同的效果。請注意,您無法通過運行時API覆蓋文件輸入和輸出。通過指定相同的屬性名稱,可以覆蓋輸入屬性。
示例2.使包任務可緩存
Groovy
build.gradle
task bundle(type: NpmTask) {
args = ['run', 'bundle']
outputs.cacheIf { true }
inputs.dir(file("scripts"))
.withPropertyName("scripts")
.withPathSensitivity(PathSensitivity.RELATIVE)
inputs.files("package.json", "package-lock.json")
.withPropertyName("configFiles")
.withPathSensitivity(PathSensitivity.RELATIVE)
outputs.file("$buildDir/bundle.js")
.withPropertyName("bundle")
}
Kotlin
build.gradle.kts
tasks.register<NpmTask>("bundle") {
args.set(listOf("run", "bundle"))
outputs.cacheIf { true }
inputs.dir(file("scripts"))
.withPropertyName("scripts")
.withPathSensitivity(PathSensitivity.RELATIVE)
inputs.files("package.json", "package-lock.json")
.withPropertyName("configFiles")
.withPathSensitivity(PathSensitivity.RELATIVE)
outputs.file("$buildDir/bundle.js")
.withPropertyName("bundle")
}
7.配置構建緩存
您可以使用settings.gradle
中的Settings.buildCache(org.gradle.api.Action)塊來配置構建緩存。
Gradle支持local
和remote
可以分別配置的構建緩存。當兩個構建緩存都啓用時,Gradle首先嚐試從本地構建緩存加載構建輸出,然後如果找不到構建輸出,則嘗試遠程構建緩存。如果在遠程緩存中找到了輸出,則它們也將存儲在本地緩存中,因此下次將在本地找到它們。Gradle存儲(“pushes”)構建輸出在已啓用且BuildCache.isPush()設置爲true
的任何構建緩存中。
默認情況下,本地構建緩存已啓用推送,而遠程構建緩存已禁用推送。
本地構建緩存已預先配置爲DirectoryBuildCache並默認啓用。可以通過指定要連接到的構建緩存的類型(BuildCacheConfiguration.remote(java.lang.Class))來配置遠程構建緩存。
7.1.內置本地構建緩存
內置的本地構建緩存DirectoryBuildCache使用一個目錄來存儲構建緩存工件。默認情況下,該目錄位於Gradle用戶主目錄中,但其位置是可配置的。
Gradle將通過刪除最近尚未使用的條目來節省磁盤空間,從而定期清理本地緩存目錄。
有關配置選項的更多詳細信息,請參考DirectoryBuildCache的DSL文檔。這是配置示例。
例子3.配置本地緩存
Groovy
settings.gradle
buildCache {
local {
directory = new File(rootDir, 'build-cache')
removeUnusedEntriesAfterDays = 30
}
}
Kotlin
settings.gradle.kts
buildCache {
local {
directory = File(rootDir, "build-cache")
removeUnusedEntriesAfterDays = 30
}
}
7.2.遠程HTTP構建緩存
Gradle具有內置支持,可通過HTTP連接到遠程構建緩存後端。有關協議外觀的更多詳細信息,請參見HttpBuildCache。請注意,通過使用以下配置,本地構建緩存將用於存儲構建輸出,而本地和遠程構建緩存將用於檢索構建輸出。
例子4.從HttpBuildCache加載
Groovy
settings.gradle
buildCache {
remote(HttpBuildCache) {
url = 'https://example.com:8123/cache/'
}
}
Kotlin
settings.gradle.kts
buildCache {
remote<HttpBuildCache> {
url = uri("https://example.com:8123/cache/")
}
}
您可以配置HttpBuildCache用於訪問生成緩存服務器的憑據,如以下示例所示。
例子5.配置遠程HTTP緩存
Grovvy
settings.gradle
buildCache {
remote(HttpBuildCache) {
url = 'https://example.com:8123/cache/'
credentials {
username = 'build-cache-user'
password = 'some-complicated-password'
}
}
}
Kotlin
settings.gradle.kts
buildCache {
remote<HttpBuildCache> {
url = uri("https://example.com:8123/cache/")
credentials {
username = "build-cache-user"
password = "some-complicated-password"
}
}
}
✨當您嘗試將生成緩存後端與HTTPS URL結合使用時,可能會遇到不受信任的SSL證書問題。理想的解決方案是爲某人添加有效的SSL證書到構建緩存後端,但是我們認識到您可能無法做到這一點。在這種情況下,請將HttpBuildCache.isAllowUntrustedServer()設置爲
true
。這是一個方便的解決方法,但是您不應該將其用作長期解決方案。
例子6.允許不受信任的緩存服務器
Groovy
settings.gradle
buildCache {
remote(HttpBuildCache) {
url = 'https://example.com:8123/cache/'
allowUntrustedServer = true
}
}
Kotlin
settings.gradle.kts
buildCache {
remote<HttpBuildCache> {
url = uri("https://example.com:8123/cache/")
isAllowUntrustedServer = true
}
}
7.3.配置用例
遠程構建緩存的推薦用例是,持續集成服務器從乾淨的構建中填充它,而開發人員僅從其加載。然後,配置將如下所示。
示例7. CI推送用例的推薦設置
Groovy
settings.gradle
boolean isCiServer = System.getenv().containsKey("CI")
buildCache {
remote(HttpBuildCache) {
url = 'https://example.com:8123/cache/'
push = isCiServer
}
}
Kotlin
settings.gradle.kts
val isCiServer = System.getenv().containsKey("CI")
buildCache {
remote<HttpBuildCache> {
url = uri("https://example.com:8123/cache/")
isPush = isCiServer
}
}
如果使用buildSrc
目錄,則應確保該目錄使用與主版本相同的版本緩存配置。可以通過將相同的腳本應用於buildSrc/settings.gradle和
settings.gradle
如以下示例所示來實現。
示例8. buildSrc和主版本的一致設置
Groovy
settings.gradle
apply from: new File(settingsDir, 'gradle/buildCacheSettings.gradle')
gradle/buildCacheSettings.gradle
boolean isCiServer = System.getenv().containsKey("CI")
buildCache {
local {
enabled = !isCiServer
}
remote(HttpBuildCache) {
url = 'https://example.com:8123/cache/'
push = isCiServer
}
}
buildSrc/settings.gradle
apply from: new File(settingsDir, '../gradle/buildCacheSettings.gradle')
Kotlin
settings.gradle.kts
apply(from = File(settingsDir, "gradle/buildCacheSettings.gradle.kts"))
gradle/buildCacheSettings.gradle.kts
val isCiServer = System.getenv().containsKey("CI")
buildCache {
local {
isEnabled = !isCiServer
}
remote<HttpBuildCache> {
url = uri("https://example.com:8123/cache/")
isPush = isCiServer
}
}
buildSrc/settings.gradle.kts
apply(from = File(settingsDir, "../gradle/buildCacheSettings.gradle.kts"))
也可以從init腳本配置構建緩存,該腳本可以在命令行中使用,可以添加到Gradle用戶主目錄中,也可以作爲自定義Gradle發行版的一部分
例子9.初始化腳本來配置構建緩存
Groovy
init.gradle
gradle.settingsEvaluated { settings ->
settings.buildCache {
// vvv Your custom configuration goes here
remote(HttpBuildCache) {
url = 'https://example.com:8123/cache/'
}
// ^^^ Your custom configuration goes here
}
}
Kotlin
init.gradle.kts
gradle.settingsEvaluated {
buildCache {
// vvv Your custom configuration goes here
remote<HttpBuildCache> {
url = uri("https://example.com:8123/cache/")
}
// ^^^ Your custom configuration goes here
}
}
7.4.構建緩存和複合構建
Gradle的複合構建功能允許將其他完整的Gradle構建包含到另一個構建中。無論所包含的構建是否自行定義構建緩存配置,此類包含的構建都會從頂級構建中繼承構建緩存配置。
對於任何包含的構建,當前存在的構建緩存配置都將被有效忽略,而有利於頂層構建的配置。這也適用於buildSrc
任何包含的內部版本的任何項目。
8.如何設置HTTP構建緩存後端
Gradle爲構建緩存節點提供Docker映像,該映像可與Gradle Enterprise連接以進行集中管理。也可以不使用功能受限的Gradle Enterprise安裝緩存節點。
9.實施自己的構建緩存
使用不同的構建緩存後端存儲構建輸出(連接到HTTP後端的內置支持未涵蓋在內)要求實現自己的邏輯以連接到自定義構建緩存後端。爲此,可以通過BuildCacheConfiguration.registerBuildCacheService(java.lang.Class,java.lang.Class)註冊自定義構建緩存類型。
Gradle Enterprise包含一個高性能,易於安裝和操作的共享構建緩存後端。