Gradle 的依賴管理引擎是變體感知的。
在前一節中,Gradle 建構了一個已解析依賴關係的圖形。在圖形解析期間,Gradle 必須根據您的建置需求,選擇每個依賴關係的正確變體。

變體代表組件可以使用的不同方式,例如用於 Java 編譯、原生連結或文件。每個變體可能都有自己的工件和依賴關係。
Gradle 使用屬性來決定選擇哪個變體。這些屬性為每個變體添加上下文,描述它們應該在何時使用。
組件
在大多數情況下,組件代表模組的版本。
一個組件
-
包含變體
-
變體包含一個或多個工件
-
工件包含零個或多個依賴關係
-
由元數據描述
-

在上面的範例中,org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1
是組件。它有一個模組 org.jetbrains.kotlinx:kotlinx-serialization-json
和一個版本 1.5.1

變體屬性
變體代表組件的不同版本或方面,例如 api
與 implementation
或 jar
與 classes
。
在上面的範例中,org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1
有四個變體:jvm
、android
、js
和 native
。
屬性是由消費者和生產者在變體選擇期間使用的類型安全鍵值對:attribute : value
。
在上面的範例中,org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1
的變體有兩個重要的屬性需要注意
變體 | 屬性 1 | 屬性 2 |
---|---|---|
JVM |
|
|
Android |
|
|
Javascript |
|
|
Native |
|
|
然而,在 org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1
的元數據中,也有更多屬性描述每個變體,例如 org.gradle.libraryelements: jar
或 org.gradle.category: library
"variants": [
...
{
"name": "jsLegacyRuntimeElements-published",
"attributes": {
"org.gradle.category": "library",
"org.gradle.usage": "kotlin-runtime",
"org.jetbrains.kotlin.js.compiler": "legacy",
"org.jetbrains.kotlin.platform.type": "js"
}
},
{
"name": "jvmRuntimeElements-published",
"attributes": {
"org.gradle.category": "library",
"org.gradle.libraryelements": "jar",
"org.gradle.usage": "java-runtime",
"org.jetbrains.kotlin.platform.type": "jvm"
}
},
...
]
您可以在變體和屬性中了解更多關於變體和屬性的資訊。
Gradle 使用兩種屬性類型來匹配可用的變體與建置所需的一個
-
消費者屬性:定義可解析配置所請求的變體的期望特性。
-
生產者屬性:每個變體都有一組描述其用途的屬性。
變體屬性匹配
對於一個組件可以定義多少個變體沒有限制。一個典型的組件將至少包含一個實作變體,但也可能提供額外的變體,例如測試夾具、文件或原始碼。此外,一個組件可以為相同的用途提供不同的變體,具體取決於消費者。例如,在編譯期間,組件可以為 Linux、Windows 和 macOS 提供不同的標頭。
Gradle 透過匹配消費者指定的屬性與生產者定義的屬性來執行變體感知選擇。此過程的詳細資訊將在下面一節中介紹。

變體名稱主要用於除錯和錯誤訊息。它在變體匹配中不起作用;只有變體的屬性才用於匹配過程。 |
變體屬性匹配演算法
Gradle 的依賴解析引擎執行以下變體匹配演算法,以找到最佳結果(或失敗)
-
將每個候選者的屬性值與消費者請求的屬性值進行比較。如果候選者的值與消費者的值完全匹配、通過屬性的相容性規則或未提供,則認為該候選者是相容的。
-
如果只有一個候選者被認為是相容的,則該候選者勝出。
-
如果多個候選者相容,但其中一個候選者與其他候選者匹配所有相同的屬性,則 Gradle 選擇該候選者。這是具有「最長」匹配的候選者。
-
如果多個候選者相容且與相同數量的屬性相容,則 Gradle 需要消除候選者的歧義。
-
對於每個請求的屬性,如果候選者沒有與消除歧義規則匹配的值,則將其從考慮中排除。
-
如果屬性具有已知的優先順序,則當只剩下一個候選者時,Gradle 將立即停止。
-
如果屬性沒有已知的優先順序,則 Gradle 必須考慮所有屬性。
-
-
如果仍然剩下多個候選者,則 Gradle 將開始考慮「額外」屬性以消除多個候選者之間的歧義。額外屬性是消費者未請求但在至少一個候選者上存在的屬性。這些額外屬性將按優先順序考慮。
-
如果屬性具有已知的優先順序,則當只剩下一個候選者時,Gradle 將立即停止。
-
在考慮完所有具有優先順序的額外屬性後,如果剩餘的候選者與所有未排序的消除歧義規則相容,則可以選擇它們。
-
-
如果仍然剩下多個候選者,則 Gradle 將再次考慮額外屬性。如果候選者擁有的額外屬性數量最少,則可以選擇它。
如果在任何步驟中都沒有剩餘的相容候選者,則解析失敗。此外,Gradle 會輸出從步驟 1 開始的所有相容候選者的列表,以幫助除錯變體匹配失敗。
外掛程式和生態系統可以透過實作相容性規則、消除歧義規則和定義屬性的優先順序來影響選擇演算法。具有較高優先順序的屬性用於按順序排除候選者。
例如,在 Java 生態系統中,org.gradle.usage
屬性的優先順序高於 org.gradle.libraryelements
。這表示如果兩個候選者都具有與 org.gradle.usage
和 org.gradle.libraryelements
相容的值,則 Gradle 將選擇通過 org.gradle.usage
的消除歧義規則的候選者。
變體感知解析過程有兩個例外
|
一個簡單的範例
讓我們來看一個範例,其中消費者嘗試使用程式庫進行編譯。
首先,消費者詳細說明它將如何使用依賴解析的結果。這是透過在消費者的可解析配置上設定屬性來實現的。
在本例中,消費者想要解析一個與 org.gradle.usage=java-api
匹配的變體。
接下來,生產者公開其組件的不同變體
-
API 變體(命名為
apiElements
),屬性為org.gradle.usage=java-api
-
執行階段變體(命名為
runtimeElements
),屬性為org.gradle.usage=java-runtime
最後,Gradle 評估變體並選擇正確的變體
-
消費者請求一個具有屬性
org.gradle.usage=java-api
的變體 -
生產者的
apiElements
變體與此請求匹配。 -
生產者的
runtimeElements
變體不匹配。
因此,Gradle 選擇 apiElements
變體,並將其工件和依賴關係提供給消費者。
一個複雜的範例
在現實世界的場景中,消費者和生產者通常都使用多個屬性。
例如,Gradle 中的 Java 程式庫專案將涉及多個屬性
-
org.gradle.usage
描述變體的使用方式。 -
org.gradle.dependency.bundling
描述變體如何處理依賴關係(例如,shadow jar、fat jar、regular jar)。 -
org.gradle.libraryelements
描述變體的封裝(例如,classes 或 jar)。 -
org.gradle.jvm.version
描述變體目標的 Java 最低版本。 -
org.gradle.jvm.environment
描述變體目標的 JVM 類型。
讓我們考慮一個場景,其中消費者想要在 Java 8 上使用程式庫運行測試,而生產者支援兩個版本:Java 8 和 Java 11。
步驟 1:消費者指定需求。
消費者想要解析一個變體,該變體
-
可以在執行階段使用 (
org.gradle.usage=java-runtime
)。 -
可以在至少 Java 8 上運行 (
org.gradle.jvm.version=8
)。
步驟 2:生產者公開多個變體。
生產者為 Java 8 和 Java 11 提供 API 和執行階段用途的變體
-
Java 8 的 API 變體(命名為
apiJava8Elements
),屬性為org.gradle.usage=java-api
和org.gradle.jvm.version=8
。 -
Java 8 的執行階段變體(命名為
runtime8Elements
),屬性為org.gradle.usage=java-runtime
和org.gradle.jvm.version=8
。 -
Java 11 的 API 變體(命名為
apiJava11Elements
),屬性為org.gradle.usage=java-api
和org.gradle.jvm.version=11
。 -
Java 11 的執行階段變體(命名為
runtime11Elements
),屬性為org.gradle.usage=java-runtime
和org.gradle.jvm.version=11
。
步驟 3:Gradle 匹配屬性。
Gradle 將消費者請求的屬性與生產者的變體進行比較
-
消費者請求一個具有
org.gradle.usage=java-runtime
和org.gradle.jvm.version=8
的變體。 -
runtime8Elements
和runtime11Elements
都與org.gradle.usage=java-runtime
屬性匹配。 -
API 變體 (
apiJava8Elements
和apiJava11Elements
) 被丟棄,因為它們與org.gradle.usage=java-runtime
不匹配。 -
選擇變體
runtime8Elements
,因為它與 Java 8 相容。 -
變體
runtime11Elements
不相容,因為它需要 Java 11。
Gradle 選擇 runtime8Elements
,並將其工件和依賴關係提供給消費者。
如果消費者設定 org.gradle.jvm.version=7
會發生什麼情況?
在這種情況下,依賴解析將失敗,並顯示錯誤訊息,說明沒有合適的變體。Gradle 知道消費者需要與 Java 7 相容的程式庫,但生產者的最低版本是 8。
如果消費者請求 org.gradle.jvm.version=15
,則 Gradle 可以選擇 Java 8 或 Java 11 變體。然後 Gradle 將選擇最高的相容版本—Java 11。
變體選擇錯誤
當 Gradle 嘗試選擇組件的最相容變體時,解析可能會因為以下原因而失敗
-
歧義錯誤:當生產者的多個變體與消費者的屬性匹配時,導致選擇哪個變體時感到困惑。
-
不相容錯誤:當生產者的任何變體都不與消費者的屬性匹配時,導致解析失敗。
處理歧義錯誤
歧義變體選擇看起來像這樣
> Could not resolve all files for configuration ':compileClasspath'.
> Could not resolve project :lib.
Required by:
project :ui
> Cannot choose between the following variants of project :lib:
- feature1ApiElements
- feature2ApiElements
All of them match the consumer attributes:
- Variant 'feature1ApiElements' capability org.test:test-capability:1.0:
- Unmatched attribute:
- Found org.gradle.category 'library' but wasn't required.
- Compatible attributes:
- Provides org.gradle.dependency.bundling 'external'
- Provides org.gradle.jvm.version '11'
- Required org.gradle.libraryelements 'classes' and found value 'jar'.
- Provides org.gradle.usage 'java-api'
- Variant 'feature2ApiElements' capability org.test:test-capability:1.0:
- Unmatched attribute:
- Found org.gradle.category 'library' but wasn't required.
- Compatible attributes:
- Provides org.gradle.dependency.bundling 'external'
- Provides org.gradle.jvm.version '11'
- Required org.gradle.libraryelements 'classes' and found value 'jar'.
- Provides org.gradle.usage 'java-api'
在這種情況下,所有相容的候選變體都會連同其屬性一起列出
-
不匹配的屬性:首先顯示,這些屬性表示在選擇正確的變體時可能缺少或未對齊的屬性。
-
相容的屬性:接下來顯示,這些屬性突顯候選變體如何與消費者的需求對齊。
-
不相容的屬性:不會顯示,因為不相容的變體已被排除。
在上面的範例中,問題不在於屬性匹配,而在於功能匹配。feature1ApiElements
和 feature2ApiElements
都提供相同的屬性和功能,使得 Gradle 無法區分它們。
為了解決這個問題,您可以修改生產者 (project :lib
) 以提供不同的功能,或在消費者端 (project :ui
) 表達功能選擇,以消除變體之間的歧義。
處理沒有匹配變體錯誤
沒有匹配變體錯誤可能看起來像這樣
> No variants of project :lib match the consumer attributes:
- Configuration ':lib:compile':
- Incompatible attribute:
- Required artifactType 'dll' and found incompatible value 'jar'.
- Other compatible attribute:
- Provides usage 'api'
- Configuration ':lib:compile' variant debug:
- Incompatible attribute:
- Required artifactType 'dll' and found incompatible value 'jar'.
- Other compatible attributes:
- Found buildType 'debug' but wasn't required.
- Provides usage 'api'
- Configuration ':lib:compile' variant release:
- Incompatible attribute:
- Required artifactType 'dll' and found incompatible value 'jar'.
- Other compatible attributes:
- Found buildType 'release' but wasn't required.
- Provides usage 'api'
或
> No variants of project : match the consumer attributes:
- Configuration ':myElements' declares attribute 'color' with value 'blue':
- Incompatible because this component declares attribute 'artifactType' with value 'jar' and the consumer needed attribute 'artifactType' with value 'dll'
- Configuration ':myElements' variant secondary declares attribute 'color' with value 'blue':
- Incompatible because this component declares attribute 'artifactType' with value 'jar' and the consumer needed attribute 'artifactType' with value 'dll'
在這些情況下,會顯示可能相容的候選變體,顯示
-
不相容的屬性:首先列出,以幫助識別為何無法選擇變體。
-
其他屬性:包括請求的和相容的屬性,以及消費者未請求的任何額外生產者屬性。
這裡的目標是了解可以選擇哪個變體(如果有的話)。在某些情況下,生產者可能根本沒有相容的變體(例如,如果消費者需要 dll
,但生產者只提供 jar
,或者如果程式庫是為 Java 11 建置的,但消費者需要 Java 8)。
處理不相容變體錯誤
不相容變體錯誤看起來像以下範例,其中消費者想要選擇具有 color=green
的變體,但唯一可用的變體具有 color=blue
> Could not resolve all dependencies for configuration ':resolveMe'. > Could not resolve project :. Required by: project : > Configuration 'mismatch' in project : does not match the consumer attributes Configuration 'mismatch': - Incompatible because this component declares attribute 'color' with value 'blue' and the consumer needed attribute 'color' with value 'green'
當 Gradle 無法選擇依賴關係的單個變體時,就會發生這種情況,因為明確請求的屬性值與依賴關係的任何變體上的該屬性值不匹配(且不相容)。
當 Gradle 成功選擇同一組件的多個變體時,但所選變體彼此不相容時,會發生此失敗的子類型。
這看起來像以下情況,其中消費者想要選擇組件的兩個不同變體,每個變體都提供不同的功能,這是可以接受的。不幸的是,一個變體具有 color=blue
,另一個變體具有 color=green
> Could not resolve all dependencies for configuration ':resolveMe'. > Could not resolve project :. Required by: project : > Multiple incompatible variants of org.example:nyvu:1.0 were selected: - Variant org.example:nyvu:1.0 variant blueElementsCapability1 has attributes {color=blue} - Variant org.example:nyvu:1.0 variant greenElementsCapability2 has attributes {color=green} > Could not resolve project :. Required by: project : > Multiple incompatible variants of org.example:pi2e5:1.0 were selected: - Variant org.example:pi2e5:1.0 variant blueElementsCapability1 has attributes {color=blue} - Variant org.example:pi2e5:1.0 variant greenElementsCapability2 has attributes {color=green}
處理歧義轉換錯誤
ArtifactTransforms 可用於將工件從一種類型轉換為另一種類型,從而更改其屬性。變體選擇可以使用工件轉換結果中可用的屬性作為候選變體。
如果專案註冊了多個工件轉換,需要使用工件轉換來產生與消費者請求匹配的變體,並且可以使用多個工件轉換來完成此操作,則 Gradle 將因歧義轉換錯誤而失敗,如下所示
> Could not resolve all dependencies for configuration ':resolveMe'. > Found multiple transforms that can produce a variant of project : with requested attributes: - color 'red' - shape 'round' Found the following transforms: - From 'configuration ':roundBlueLiquidElements'': - With source attributes: - color 'blue' - shape 'round' - state 'liquid' - Candidate transform(s): - Transform 'BrokenTransform' producing attributes: - color 'red' - shape 'round' - state 'gas' - Transform 'BrokenTransform' producing attributes: - color 'red' - shape 'round' - state 'solid'
可視化變體資訊
Gradle 提供內建任務來可視化變體選擇過程,並顯示涉及的生產者和消費者屬性。
輸出變體報告
報告任務 outgoingVariants
顯示專案消費者可選擇的變體列表。它顯示每個變體的功能、屬性和工件。
此任務類似於 dependencyInsight
報告任務。
預設情況下,outgoingVariants
列印有關所有變體的資訊。它提供可選參數 --variant <variantName>
以選擇要顯示的單個變體。它也接受 --all
標誌以包含有關舊版和已棄用配置的資訊,或 --no-all
以排除此資訊。
以下是在新產生的 java-library
專案上執行 outgoingVariants
任務的輸出
> Task :outgoingVariants -------------------------------------------------- Variant apiElements -------------------------------------------------- API elements for the 'main' feature. Capabilities - new-java-library:lib:unspecified (default capability) Attributes - org.gradle.category = library - org.gradle.dependency.bundling = external - org.gradle.jvm.version = 11 - org.gradle.libraryelements = jar - org.gradle.usage = java-api Artifacts - build/libs/lib.jar (artifactType = jar) Secondary Variants (*) -------------------------------------------------- Secondary Variant classes -------------------------------------------------- Description = Directories containing compiled class files for main. Attributes - org.gradle.category = library - org.gradle.dependency.bundling = external - org.gradle.jvm.version = 11 - org.gradle.libraryelements = classes - org.gradle.usage = java-api Artifacts - build/classes/java/main (artifactType = java-classes-directory) -------------------------------------------------- Variant mainSourceElements (i) -------------------------------------------------- Description = List of source directories contained in the Main SourceSet. Capabilities - new-java-library:lib:unspecified (default capability) Attributes - org.gradle.category = verification - org.gradle.dependency.bundling = external - org.gradle.verificationtype = main-sources Artifacts - src/main/java (artifactType = directory) - src/main/resources (artifactType = directory) -------------------------------------------------- Variant runtimeElements -------------------------------------------------- Runtime elements for the 'main' feature. Capabilities - new-java-library:lib:unspecified (default capability) Attributes - org.gradle.category = library - org.gradle.dependency.bundling = external - org.gradle.jvm.version = 11 - org.gradle.libraryelements = jar - org.gradle.usage = java-runtime Artifacts - build/libs/lib.jar (artifactType = jar) Secondary Variants (*) -------------------------------------------------- Secondary Variant classes -------------------------------------------------- Description = Directories containing compiled class files for main. Attributes - org.gradle.category = library - org.gradle.dependency.bundling = external - org.gradle.jvm.version = 11 - org.gradle.libraryelements = classes - org.gradle.usage = java-runtime Artifacts - build/classes/java/main (artifactType = java-classes-directory) -------------------------------------------------- Secondary Variant resources -------------------------------------------------- Description = Directories containing the project's assembled resource files for use at runtime. Attributes - org.gradle.category = library - org.gradle.dependency.bundling = external - org.gradle.jvm.version = 11 - org.gradle.libraryelements = resources - org.gradle.usage = java-runtime Artifacts - build/resources/main (artifactType = java-resources-directory) -------------------------------------------------- Variant testResultsElementsForTest (i) -------------------------------------------------- Description = Directory containing binary results of running tests for the test Test Suite's test target. Capabilities - new-java-library:lib:unspecified (default capability) Attributes - org.gradle.category = verification - org.gradle.testsuite.name = test - org.gradle.testsuite.target.name = test - org.gradle.testsuite.type = unit-test - org.gradle.verificationtype = test-results Artifacts - build/test-results/test/binary (artifactType = directory) (i) Configuration uses incubating attributes such as Category.VERIFICATION. (*) Secondary variants are variants created via the Configuration#getOutgoing(): ConfigurationPublications API which also participate in selection, in addition to the configuration itself.
從中您可以看到 java 程式庫公開的兩個主要變體,apiElements
和 runtimeElements
。請注意,主要差異在於 org.gradle.usage
屬性,其值為 java-api
和 java-runtime
。正如它們所指示的那樣,這是在消費者的編譯類路徑上需要什麼與執行階段類路徑上需要什麼之間做出區別的地方。
它還顯示次要變體,這些變體是 Gradle 專案專有的,不會發佈。例如,來自 apiElements
的次要變體 classes
允許 Gradle 在針對 java-library
專案 進行編譯時跳過 JAR 建立。
可解析配置報告
Gradle 還提供了一個補充報告任務,稱為 resolvableConfigurations
,它顯示專案的可解析配置,這些配置是可以添加和解析依賴關係的配置。該報告將列出它們的屬性和它們擴展的任何配置。它還將列出受解析期間的 相容性規則 或 消除歧義規則 影響的任何屬性的摘要。
預設情況下,resolvableConfigurations
列印有關所有純粹可解析配置的資訊。這些配置被標記為可解析,但未標記為可消費。雖然某些可解析配置也被標記為可消費,但這些是舊版配置,不應在建置腳本中添加依賴關係。此報告提供可選參數 --configuration <configurationName>
以選擇要顯示的單個配置。它也接受 --all
標誌以包含有關舊版和已棄用配置的資訊,或 --no-all
以排除此資訊。最後,它接受 --recursive
標誌以在擴展配置部分中列出那些遞迴而不是直接擴展的配置。或者,可以使用 --no-recursive
來排除此資訊。
以下是在新產生的 java-library
專案上執行 resolvableConfigurations
任務的輸出
> Task :resolvableConfigurations -------------------------------------------------- Configuration annotationProcessor -------------------------------------------------- Description = Annotation processors and their dependencies for source set 'main'. Attributes - org.gradle.category = library - org.gradle.dependency.bundling = external - org.gradle.jvm.environment = standard-jvm - org.gradle.libraryelements = jar - org.gradle.usage = java-runtime -------------------------------------------------- Configuration compileClasspath -------------------------------------------------- Description = Compile classpath for source set 'main'. Attributes - org.gradle.category = library - org.gradle.dependency.bundling = external - org.gradle.jvm.environment = standard-jvm - org.gradle.jvm.version = 11 - org.gradle.libraryelements = classes - org.gradle.usage = java-api Extended Configurations - compileOnly - implementation -------------------------------------------------- Configuration runtimeClasspath -------------------------------------------------- Description = Runtime classpath of source set 'main'. Attributes - org.gradle.category = library - org.gradle.dependency.bundling = external - org.gradle.jvm.environment = standard-jvm - org.gradle.jvm.version = 11 - org.gradle.libraryelements = jar - org.gradle.usage = java-runtime Extended Configurations - implementation - runtimeOnly -------------------------------------------------- Configuration testAnnotationProcessor -------------------------------------------------- Description = Annotation processors and their dependencies for source set 'test'. Attributes - org.gradle.category = library - org.gradle.dependency.bundling = external - org.gradle.jvm.environment = standard-jvm - org.gradle.libraryelements = jar - org.gradle.usage = java-runtime -------------------------------------------------- Configuration testCompileClasspath -------------------------------------------------- Description = Compile classpath for source set 'test'. Attributes - org.gradle.category = library - org.gradle.dependency.bundling = external - org.gradle.jvm.environment = standard-jvm - org.gradle.jvm.version = 11 - org.gradle.libraryelements = classes - org.gradle.usage = java-api Extended Configurations - testCompileOnly - testImplementation -------------------------------------------------- Configuration testRuntimeClasspath -------------------------------------------------- Description = Runtime classpath of source set 'test'. Attributes - org.gradle.category = library - org.gradle.dependency.bundling = external - org.gradle.jvm.environment = standard-jvm - org.gradle.jvm.version = 11 - org.gradle.libraryelements = jar - org.gradle.usage = java-runtime Extended Configurations - testImplementation - testRuntimeOnly -------------------------------------------------- Compatibility Rules -------------------------------------------------- Description = The following Attributes have compatibility rules defined. - org.gradle.dependency.bundling - org.gradle.jvm.environment - org.gradle.jvm.version - org.gradle.libraryelements - org.gradle.plugin.api-version - org.gradle.usage -------------------------------------------------- Disambiguation Rules -------------------------------------------------- Description = The following Attributes have disambiguation rules defined. - org.gradle.category - org.gradle.dependency.bundling - org.gradle.jvm.environment - org.gradle.jvm.version - org.gradle.libraryelements - org.gradle.plugin.api-version - org.gradle.usage
從中您可以看到用於解析依賴關係的兩個主要配置,compileClasspath
和 runtimeClasspath
,以及它們對應的測試配置。