在 Gradle 中,依赖解析通常从消费者和生产者的角度来考虑。消费者声明依赖并执行依赖解析,而生产者通过暴露变体来满足这些依赖。
Gradle 的解析引擎遵循一种动态的依赖解析方法,称为变体感知解析,其中消费者使用属性定义需求,这些属性与生产者声明的属性相匹配。
变体感知解析允许 Gradle 自动从生产者那里选择正确的变体,而无需消费者显式指定要使用哪个变体。
例如,如果您正在使用不同的架构(如 arm64
和 i386
),Gradle 可以为每个架构选择库(myLib
)的适当版本
-
生产者
myLib
暴露带有特定属性的变体 (arm64Elements
,i386Elements
)(例如,ArchType.ARM64
,ArchType.I386
)。 -
消费者
myApp
在其可解析配置 (runtimeClasspath
) 中指定所需的属性(例如,ArchType.ARM64
)。 -
如果消费者
myApp
需要arm64
架构的依赖,Gradle 将自动从myLib
生产者中选择arm64Elements
变体,并使用其相应的工件。
代码示例
考虑一个 Java 库,您在其中创建一个名为 instrumentedJars
的新变体,并希望确保它被选择用于测试
-
生产者项目:创建一个专门的
instrumentedJars
变体,并用特定属性标记。 -
消费者项目:配置为请求用于测试的
instrumented-jar
属性。
让我们看一下生产者和消费者的构建文件。
生产者端
1. 创建 instrumented JAR:
我们的 Java 库有一个名为 instrumentedJar
的 task,它生成一个 JAR 文件。我们期望其他项目使用此 JAR 文件。
val instrumentedJar = tasks.register("instrumentedJar", Jar::class) {
archiveClassifier = "instrumented"
}
def instrumentedJar = tasks.register("instrumentedJar", Jar) {
archiveClassifier = "instrumented"
}
2. 创建自定义传出配置:
我们希望在执行测试时使用 instrumented 类,因此我们需要在我们的变体上定义适当的属性。我们创建一个名为 instrumentedJars
的新配置。此配置
-
可以被其他项目使用。
-
无法被解析(即,它旨在用作输出,而不是输入)。
-
具有特定的属性,包括设置为 "instrumented-jar" 的
LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE
,这解释了变体包含的内容。
val instrumentedJars by configurations.creating {
isCanBeConsumed = true
isCanBeResolved = false
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category.LIBRARY))
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, JavaVersion.current().majorVersion.toInt())
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named("instrumented-jar"))
}
}
configurations {
instrumentedJars {
canBeConsumed = true
canBeResolved = false
attributes {
attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category, Category.LIBRARY))
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, Usage.JAVA_RUNTIME))
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, JavaVersion.current().majorVersion.toInteger())
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements, 'instrumented-jar'))
}
}
}
3. 附加工件:
instrumentedJar
task 的输出作为工件添加到 instrumentedJars
配置中。当此变体包含在依赖关系图中时,此工件将在工件解析期间被解析。
artifacts {
add("instrumentedJars", instrumentedJar)
}
artifacts {
instrumentedJars(instrumentedJar)
}
我们在这里所做的是,我们添加了一个新的变体,它可以在运行时使用,但包含 instrumented 类而不是普通类。但是,现在这意味着对于运行时,消费者必须在两个变体之间进行选择
-
runtimeElements
,java-library
插件提供的常规变体 -
instrumentedJars
,我们创建的变体
消费者端
1. 添加依赖:
首先,在消费者端,像任何其他项目一样,我们将 Java 库定义为依赖
dependencies {
testImplementation("junit:junit:4.13")
testImplementation(project(":producer"))
}
dependencies {
testImplementation 'junit:junit:4.13'
testImplementation project(':producer')
}
此时,Gradle 仍将为您的依赖选择默认的 runtimeElements
变体。这是因为 testRuntimeClasspath
配置正在请求具有 jar
库元素属性的工件,而生产者定义了具有不同属性的 instrumentedJars
变体。
2. 调整请求的属性:
修改 testRuntimeClasspath
配置以请求依赖的 "instrumented-jar" 版本。这意味着当 Gradle 为此配置解析依赖时,它将优先选择标记为 "instrumented" 的 JAR 文件
configurations {
testRuntimeClasspath {
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements::class.java, "instrumented-jar"))
}
}
}
configurations {
testRuntimeClasspath {
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements, 'instrumented-jar'))
}
}
}
通过遵循这些步骤,Gradle 将根据配置和属性智能地选择正确的变体,同时还会处理专用变体不可用的情况。