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 使用两种类型的属性来匹配可用的变体,以满足构建的需求
-
消费者属性:定义可解析配置请求的变体的期望特性。
-
生产者属性:每个变体都有一组属性来描述其用途。
变体属性匹配
对于一个组件可以定义的变体数量没有限制。一个典型的组件将至少包含一个实现变体,但也可能提供额外的变体,例如测试 fixtures、文档或源代码。此外,一个组件可以为相同的用途提供不同的变体,具体取决于消费者。例如,在编译期间,组件可以为 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 Library 项目将涉及多个属性
-
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 提供了内置的 task 来可视化变体选择过程,并显示涉及的生产者和消费者属性。
输出变体报告
报告 task outgoingVariants
显示了项目消费者可选择的变体列表。它显示了每个变体的能力、属性和工件。
此 task 类似于 dependencyInsight
报告 task。
默认情况下,outgoingVariants
打印有关所有变体的信息。它提供了可选参数 --variant <variantName>
来选择要显示的单个变体。它还接受 --all
标志以包含有关旧版和已弃用配置的信息,或 --no-all
以排除此信息。
这是在新生成的 java-library
项目上 outgoingVariants
task 的输出
> 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 library 公开的两个主要变体,apiElements
和 runtimeElements
。请注意,主要区别在于 org.gradle.usage
属性,其值为 java-api
和 java-runtime
。正如它们所指示的那样,这就是区分消费者编译类路径上需要的内容与运行时类路径上需要的内容的地方。
它还显示了辅助变体,这些变体是 Gradle 项目独有的,并且未发布。例如,来自 apiElements
的辅助变体 classes
允许 Gradle 在针对 java-library
项目 进行编译时跳过 JAR 创建。
可解析配置报告
Gradle 还提供了一个补充报告 task,称为 resolvableConfigurations
,它显示了项目的可解析配置,即可添加和解析依赖的配置。该报告将列出它们的属性以及它们扩展的任何配置。它还将列出受 兼容性规则 或 消除歧义规则 在解析期间影响的任何属性的摘要。
默认情况下,resolvableConfigurations
打印有关所有纯可解析配置的信息。这些配置被标记为可解析,但未标记为可消费。虽然某些可解析配置也被标记为可消费,但这些是旧版配置,应该不在构建脚本中添加依赖。此报告提供了可选参数 --configuration <configurationName>
来选择要显示的单个配置。它还接受 --all
标志以包含有关旧版和已弃用配置的信息,或 --no-all
以排除此信息。最后,它接受 --recursive
标志以在扩展配置部分中列出传递扩展而不是直接扩展的配置。或者,可以使用 --no-recursive
来排除此信息。
这是在新生成的 java-library
项目上 resolvableConfigurations
task 的输出
> 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
,以及它们对应的测试配置。