Gradle 支援功能的概念:通常情況下,單一程式庫可以分割成多個相關但不同的程式庫,其中每個功能可以用在主程式庫旁邊。
功能允許元件公開多個相關程式庫,每個程式庫都可以宣告自己的相依性。這些程式庫公開為變異,類似於主程式庫公開其 API 和執行時期的變異方式。
這允許許多不同的情況(清單不完整)
-
(更好的)Maven 選擇性相依性替代方案
-
主程式庫建置時支援執行時期功能的不同互斥實作;使用者必須選擇一個,且僅能選擇一個,每個此類功能的實作
-
主程式庫建置時支援選擇性執行時期功能,每個功能都需要不同的相依性
-
主程式庫附帶補充功能,例如測試固定裝置
-
主程式庫附帶主成品,而啟用其他功能需要其他成品
透過功能選擇功能
宣告依賴於某個元件通常是透過提供一組座標 (群組、成品、版本,也稱為 GAV 座標) 來完成的。這讓引擎得以判斷我們正在尋找的元件,但此類元件可能會提供不同的變異。變異通常會根據使用方式來選擇。例如,我們可能會選擇不同的變異來編譯元件 (在這種情況下,我們需要元件的 API) 或執行程式碼 (在這種情況下,我們需要元件的執行時間)。元件的所有變異都會提供許多功能,這些功能會使用 GAV 座標以類似的方式表示。
功能會以 GAV 座標表示,但您必須將其視為功能說明
-
"我提供 SLF4J 繫結"
-
"我提供 MySQL 的執行時間支援"
-
"我提供 Groovy 執行時間"
而且一般來說,在圖形中擁有兩個提供相同東西的元件會造成問題 (它們會衝突)。
這是個重要的概念,因為
-
預設情況下,變異會提供與其元件的 GAV 座標對應的功能
-
依賴圖形中沒有兩個變異可以提供相同的功能
-
只要提供不同的功能,就可以選取單一元件的許多變異
典型的元件只會提供具有預設功能的變異。例如,Java 函式庫會公開兩個變異 (API 和執行時間),它們提供相同的功能。因此,在依賴圖形中同時擁有單一元件的API和執行時間會造成錯誤。
不過,假設您需要元件的執行時間和測試固定裝置執行時間。只要函式庫的執行時間和測試固定裝置執行時間變異宣告不同的功能,這樣就是允許的。
如果我們這樣做,消費者就必須宣告兩個依賴關係
-
一個在「主要」功能上,也就是函式庫
-
一個在「測試固定裝置」功能上,透過要求其功能
雖然解析引擎獨立於生態系統支援多變異元件,但目前只能使用 Java 外掛程式來使用功能。 |
註冊功能
透過套用 java-library
外掛,可以宣告功能。下列程式碼說明如何宣告名為 mongodbSupport
的功能
sourceSets {
create("mongodbSupport") {
java {
srcDir("src/mongodb/java")
}
}
}
java {
registerFeature("mongodbSupport") {
usingSourceSet(sourceSets["mongodbSupport"])
}
}
sourceSets {
mongodbSupport {
java {
srcDir 'src/mongodb/java'
}
}
}
java {
registerFeature('mongodbSupport') {
usingSourceSet(sourceSets.mongodbSupport)
}
}
Gradle 會自動為您設定許多事項,其方式與 Java 函式庫外掛 設定組態的方式非常類似。
相依性範圍組態的建立方式與主功能相同
-
組態
mongodbSupportApi
,用於宣告此功能的 API 相依性 -
組態
mongodbSupportImplementation
,用於宣告此功能的實作相依性 -
組態
mongodbSupportRuntimeOnly
,用於宣告此功能的執行時期相依性 -
組態
mongodbSupportCompileOnly
,用於宣告此功能的編譯時期相依性 -
組態
mongodbSupportCompileOnlyApi
,用於宣告此功能的編譯時期 API 相依性
此外,可消耗組態的建立方式與主功能相同
-
組態
mongodbSupportApiElements
,供消費者用來擷取此功能的成品和 API 相依性 -
組態
mongodbSupportRuntimeElements
,供消費者用來擷取此功能的成品和執行時期相依性
功能應具有同名的來源組。Gradle 會建立一個 Jar
工作,以將從功能來源組建置的類別打包,並使用與功能的 kebab-case 名稱對應的分類器。
註冊功能時,請勿使用主來源組。此行為將在 Gradle 的未來版本中被棄用。 |
大多數使用者只需要關注相依性範圍組態,即可宣告此功能的特定相依性
dependencies {
"mongodbSupportImplementation"("org.mongodb:mongodb-driver-sync:3.9.1")
}
dependencies {
mongodbSupportImplementation 'org.mongodb:mongodb-driver-sync:3.9.1'
}
依慣例,Gradle 會將功能名稱對應到功能,其群組和版本分別與主元件的群組和版本相同,但其名稱是主元件名稱加上 -
,後接 kebab-case 功能名稱。
例如,如果元件的群組是 org.gradle.demo
,其名稱是 provider
,其版本是 1.0
,且功能名稱是 mongodbSupport
,則功能的變體將具有 org.gradle.demo:provider-mongodb-support:1.0
功能。
如果您自行選擇功能名稱或為變體新增更多功能,建議遵循相同的慣例。
發佈功能
根據元資料檔案格式,發佈功能可能會造成資料遺失
-
使用 Gradle 模組元資料,所有內容都會發佈,消費者將能充分享受功能
-
使用 POM 元資料 (Maven),功能會以選用相依性發佈,而功能的成品會以不同的分類器發佈
-
使用 Ivy 元資料,功能會以額外組態發佈,這些組態不會由
default
組態延伸
僅支援使用 maven-publish
和 ivy-publish
外掛發佈功能。Java 函式庫外掛會負責為您註冊額外的變體,因此不需要額外的組態,只需要一般的發佈
plugins {
`java-library`
`maven-publish`
}
// ...
publishing {
publications {
create("myLibrary", MavenPublication::class.java) {
from(components["java"])
}
}
}
plugins {
id 'java-library'
id 'maven-publish'
}
// ...
publishing {
publications {
myLibrary(MavenPublication) {
from components.java
}
}
}
新增 javadoc 和來源 JAR
類似於 主要的 Javadoc 和來源 JAR,您可以設定新增的功能,以便為 Javadoc 和來源產生 JAR。
java {
registerFeature("mongodbSupport") {
usingSourceSet(sourceSets["mongodbSupport"])
withJavadocJar()
withSourcesJar()
}
}
java {
registerFeature('mongodbSupport') {
usingSourceSet(sourceSets.mongodbSupport)
withJavadocJar()
withSourcesJar()
}
}
相依於功能
如前所述,發佈時功能可能會造成資料遺失。因此,消費者只能在下列情況下相依於功能
-
使用專案相依性 (在多專案建置中)
-
有 Gradle 模組元資料可用,也就是說發佈者必須已發佈
-
在 Ivy 世界中,透過宣告相依於與功能相符的組態
消費者可以透過宣告必要的相容性,指定需要生產者的特定功能。例如,如果生產者宣告「MySQL 支援」功能如下
group = "org.gradle.demo"
sourceSets {
create("mysqlSupport") {
java {
srcDir("src/mysql/java")
}
}
}
java {
registerFeature("mysqlSupport") {
usingSourceSet(sourceSets["mysqlSupport"])
}
}
dependencies {
"mysqlSupportImplementation"("mysql:mysql-connector-java:8.0.14")
}
group = 'org.gradle.demo'
sourceSets {
mysqlSupport {
java {
srcDir 'src/mysql/java'
}
}
}
java {
registerFeature('mysqlSupport') {
usingSourceSet(sourceSets.mysqlSupport)
}
}
dependencies {
mysqlSupportImplementation 'mysql:mysql-connector-java:8.0.14'
}
那麼消費者可以透過執行下列動作,宣告相依於 MySQL 支援功能
dependencies {
// This project requires the main producer component
implementation(project(":producer"))
// But we also want to use its MySQL support
runtimeOnly(project(":producer")) {
capabilities {
requireCapability("org.gradle.demo:producer-mysql-support")
}
}
}
dependencies {
// This project requires the main producer component
implementation(project(":producer"))
// But we also want to use its MySQL support
runtimeOnly(project(":producer")) {
capabilities {
requireCapability("org.gradle.demo:producer-mysql-support")
}
}
}
這會自動在執行時期類別路徑中加入 mysql-connector-java
相依性。如果有超過一個相依性,所有相依性都會加入,這表示功能可用於將有助於功能的相依性分組在一起。
類似地,如果具有功能的外部函式庫已使用 Gradle 模組元資料 發佈,則可以依賴該函式庫提供的功能
dependencies {
// This project requires the main producer component
implementation("org.gradle.demo:producer:1.0")
// But we also want to use its MongoDB support
runtimeOnly("org.gradle.demo:producer:1.0") {
capabilities {
requireCapability("org.gradle.demo:producer-mongodb-support")
}
}
}
dependencies {
// This project requires the main producer component
implementation('org.gradle.demo:producer:1.0')
// But we also want to use its MongoDB support
runtimeOnly('org.gradle.demo:producer:1.0') {
capabilities {
requireCapability("org.gradle.demo:producer-mongodb-support")
}
}
}
處理互斥變異
將功能用作處理功能的方式的主要優點是,您可以精確地處理變異的相容性。規則很簡單
依賴圖形中沒有兩個變異可以提供相同的功能
我們可以利用這一點來確保 Gradle 在使用者錯誤設定相依項時失敗。假設您的函式庫支援 MySQL、Postgres 和 MongoDB,但一次只能選擇其中一個。我們可以透過確保每個功能也提供相同功能來模擬此限制,因此這些功能無法在同一個圖表中一起使用。
java {
registerFeature("mysqlSupport") {
usingSourceSet(sourceSets["mysqlSupport"])
capability("org.gradle.demo", "producer-db-support", "1.0")
capability("org.gradle.demo", "producer-mysql-support", "1.0")
}
registerFeature("postgresSupport") {
usingSourceSet(sourceSets["postgresSupport"])
capability("org.gradle.demo", "producer-db-support", "1.0")
capability("org.gradle.demo", "producer-postgres-support", "1.0")
}
registerFeature("mongoSupport") {
usingSourceSet(sourceSets["mongoSupport"])
capability("org.gradle.demo", "producer-db-support", "1.0")
capability("org.gradle.demo", "producer-mongo-support", "1.0")
}
}
dependencies {
"mysqlSupportImplementation"("mysql:mysql-connector-java:8.0.14")
"postgresSupportImplementation"("org.postgresql:postgresql:42.2.5")
"mongoSupportImplementation"("org.mongodb:mongodb-driver-sync:3.9.1")
}
java {
registerFeature('mysqlSupport') {
usingSourceSet(sourceSets.mysqlSupport)
capability('org.gradle.demo', 'producer-db-support', '1.0')
capability('org.gradle.demo', 'producer-mysql-support', '1.0')
}
registerFeature('postgresSupport') {
usingSourceSet(sourceSets.postgresSupport)
capability('org.gradle.demo', 'producer-db-support', '1.0')
capability('org.gradle.demo', 'producer-postgres-support', '1.0')
}
registerFeature('mongoSupport') {
usingSourceSet(sourceSets.mongoSupport)
capability('org.gradle.demo', 'producer-db-support', '1.0')
capability('org.gradle.demo', 'producer-mongo-support', '1.0')
}
}
dependencies {
mysqlSupportImplementation 'mysql:mysql-connector-java:8.0.14'
postgresSupportImplementation 'org.postgresql:postgresql:42.2.5'
mongoSupportImplementation 'org.mongodb:mongodb-driver-sync:3.9.1'
}
在此,產生器宣告 3 個功能,每個功能支援一個資料庫執行時間
-
mysql-support
提供db-support
和mysql-support
功能 -
postgres-support
提供db-support
和postgres-support
功能 -
mongo-support
提供db-support
和mongo-support
功能
然後,如果使用者嘗試取得 postgres-support
和 mysql-support
功能(這也適用於遞移)
dependencies {
// This project requires the main producer component
implementation(project(":producer"))
// Let's try to ask for both MySQL and Postgres support
runtimeOnly(project(":producer")) {
capabilities {
requireCapability("org.gradle.demo:producer-mysql-support")
}
}
runtimeOnly(project(":producer")) {
capabilities {
requireCapability("org.gradle.demo:producer-postgres-support")
}
}
}
dependencies {
implementation(project(":producer"))
// Let's try to ask for both MySQL and Postgres support
runtimeOnly(project(":producer")) {
capabilities {
requireCapability("org.gradle.demo:producer-mysql-support")
}
}
runtimeOnly(project(":producer")) {
capabilities {
requireCapability("org.gradle.demo:producer-postgres-support")
}
}
}
相依項解析會失敗,並顯示以下錯誤
Cannot choose between org.gradle.demo:producer:1.0 variant mysqlSupportRuntimeElements and org.gradle.demo:producer:1.0 variant postgresSupportRuntimeElements because they provide the same capability: org.gradle.demo:producer-db-support:1.0