Gradle 理解:configuration、dependency

一、什麼是 dependency 的 configuration

不同的 configuration 用來引用不同領域(或不同用途)的 dependencies

如:implementation、api、testRuntime。

或者說 configuration 用來管理一批 dependencies。

二、configuration 的繼承與複合

一個 configuration 可以繼承另一個 configuration。子 configuration 會繼承父 configuration 的所有 dependencies。

如 a 繼承了 b(通過 extendsFrom 方法來繼承):

configurations {
   a.extendsFrom b
}

在 java 插件中,compileClasspath 繼承了 implementation,擁有了其聲明的所有 dependencies。compileJava task 會用到 compileClasspath
在這裏插入圖片描述

三、可解析和可消費 configuration

configuration 可用於三個方面:

  • 單純地聲明 dependencies
  • 被消費者使用,將其管理的一組 dependencies 解析爲文件
  • 被生產者使用,公開其管理的 artifacts 及其 dependencies 以供其他項目使用

一個可被解析的 configuration 的 canBeResolved 屬性爲 true。
一個可被消費的 configuration 的 canBeConsumed 屬性爲 true。

configurations {
	a {	canBeResloved = true }
	
	b { canBeConsumed = true }
}

一個可被解析 configuration 往往繼承了一個或多個不可被解析 configuration。它們的關係類似於抽象類和子類:不可被解析 configuration 不能實例化,可解析 configuration 是它的子類,可實例化(將 dependencies 解析爲文件)。

所以可被解析 configuration 用於管理 dependency,相應的,可被消費 configuration 用於管理 artifact。

canBeResolved、canBeConsumed 屬性默認都爲 true。
java 插件中,implementation 的 canBeResolved、canBeConsumed 屬性都爲 false。

四、自定義 configuration

4.1 現有 configuration 的繼承

configurations {
	// 聲明一個自定義 configuration
    gdConfiguration
    // 使 implementation 繼承於它
    implementation.extendsFrom(gdConfiguration)
}

dependencies {
    // 使用自定義 configuration: gdConfiguration
    // mylib 中的類可以在 app 中正常訪問
    gdConfiguration project(":mylib")
}

4.2 自定義 configuration 的解析

configurations {
   jasper
}

dependencies {
   jasper 'com.github.bumptech.glide:glide:4.9.0'
}

task preCompileJasper {
   doLast {
       // asPath 會觸發 resolve
       println("configurations.jasper.asPath: ${configurations.jasper.asPath}")
   }
}

執行 preCompileJasper:

> Task :app:preCompileJasper
configurations.jasper.asPath: ~/.gradle/caches/modules-2/files-2.1/com.github.bumptech.glide/glide/4.9.0/c34f3a0b710a00c62a62683f5f756e6af34183a8/glide-4.9.0.aar:...

asPath 會下載引用的依賴(即 resolve),返回下載後的文件地址。
asPath 即 FileCollection 的 getAsPath() 方法。

如果設置 jasper 的 canBeResolved 屬性爲 false,執行 preCompileJasper 會失敗:

> Task :app:preCompileJasper FAILED
* What went wrong:
Execution failed for task ':app:preCompileJasper'.
> Resolving configuration 'jasper' directly is not allowed

五、dependencies 的種類

5.1 module dependencies

module dependencies 是最常見的 dependencies,指向倉庫中的某個 module。

dependencies {
    runtimeOnly group: 'org.springframework', name: 'spring-core', version: '2.5'
    runtimeOnly 'org.springframework:spring-core:2.5',
            'org.springframework:spring-aop:2.5'
    runtimeOnly(
        [group: 'org.springframework', name: 'spring-core', version: '2.5'],
        [group: 'org.springframework', name: 'spring-aop', version: '2.5']
    )
    runtimeOnly('org.hibernate:hibernate:3.0.5') {
        transitive = true
    }
    runtimeOnly group: 'org.hibernate', name: 'hibernate', version: '3.0.5', transitive: true
    runtimeOnly(group: 'org.hibernate', name: 'hibernate', version: '3.0.5') {
        transitive = true
    }
}

可以使用一個 String 或一個 Map 來聲明 module dependencies,還可以加上一個配置閉包來補充配置。

map 的所有 key 見 dependencyHandler

5.2 文件 dependencies

可以直接將一個文件看作 dependency。

dependencies {
    antContrib files('ant/antcontrib.jar')
    externalLibs files('libs/commons-lang.jar', 'libs/log4j.jar')
    deploymentTools(fileTree('tools') { include '*.exe' })
    implementation fileTree(include: ['*.jar'], dir: 'libs')
}

這裏注意,一種常見的 aar 的引入方式是:

implementation(name: 'library_1.2', ext: 'aar')

這其實不是一個文件 dependencies,而是一個 module dependency,需要將 aar 的目錄設爲 flatDir 倉庫。

如果引用 jar 包時加上 aar,那其實也不用每個 aar 都單獨聲明:

implementation fileTree(include: ['*.jar'], dir: 'libs')

當聲明一個文件 dependency 時,可以通過 builtBy 來聲明生產這個文件的 task:

configurations {
    myConfiguration
}

dependencies {
    myConfiguration files("$buildDir/classes") {
        builtBy 'myCompile'
    }
}

task myCompile() {
    doLast {
        println "myCompile exec"
    }
}

task list(dependsOn: configurations.myConfiguration) {
    doLast {
        // dependsOn 會觸發 resolve
        // collect 也會觸發 resolve
        println "classpath = ${configurations.myConfiguration.collect { File file -> file.name }}"
    }
}

這樣,在 resolve 這個 configuration 時,它 builtBy 的 task 會先執行。

執行 list:

$ ./gradlew list
> Task :app:myCompile
myCompile exec

> Task :app:list
classpath = [classes]

如上,會先執行 myCompile,再執行 list。

5.3 Project dependencies

在 project 自己的 build.gradle 中聲明:

dependencies {
   implementation project(':shared')
}

在其他 project 的 build.gradle 中聲明:

project(":app") {
    dependencies {
        implementation project(':shared')
    }
}

一個 module dependencies 可以被該 module 的本地源碼代替,即通過 project dependencies 代替 module dependencies,具體可查看 composite_builds

5.4 Gradle 特定 dependencies

Gradle 提供了幾個特定的 dependencies。

dependencies {
   implementation gradleApi() // 聲明一個當前 Gradle 版本 api 的依賴,用於開發自定義 Gradle task 或 plugin
   implementation localGroovy() // 聲明一個當前 Gradle 版本使用的 Groovy 的依賴,用於開發自定義 Gradle task 或 plugin
   testImplementation gradleTestKit() // 聲明一個當前 Gradle 版本使用的 TestKit 的依賴,用於測試 Gradle plugin 或 build script
}

六、文檔化 dependencies

Gradle 4.6 及以後支持 because 關鍵字,用於說明使用某個 dependencies 的原因:

dependencies {
    implementation('org.ow2.asm:asm:7.1') {
        because 'we require a JDK 9 compatible bytecode generator'
    }
}

在使用 dependency insight 時,分析報告中會含有 because 的內容。

./gradlew :app:dependencyInsight --configuration debugCompileClasspath --dependency asm
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章