变体和属性
变体 代表组件的不同版本或方面,例如 api
与 implementation
。 属性 定义根据消费者的需求选择哪个变体。
例如,一个库可能有一个 api
和一个 implementation
变体。 在这里,消费者想要一个外部 implementation
变体
configurations {
implementation {
attributes {
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
}
}
}
例如,一个构建可能具有 debug
和 release
变体。 这基于属性选择 debug
变体。
configurations {
compileClasspath {
attributes {
attribute(TargetConfiguration.TARGET_ATTRIBUTE, objects.named("debug"))
}
}
}
属性 通过比较请求的属性与可用的属性,帮助 Gradle 匹配正确的 变体
attribute(TargetConfiguration.TARGET_ATTRIBUTE, objects.named("debug"))
这会将 TargetConfiguration.TARGET_ATTRIBUTE
设置为 "debug"
,这意味着 Gradle 将尝试解析具有 "debug" 变体的依赖项,而不是其他可用的变体(如 "release")。
Gradle 定义的标准属性
作为 Gradle 的用户,属性通常作为实现细节隐藏。 但是,了解 Gradle 及其核心插件定义的标准属性可能很有用。
作为插件作者,这些属性及其定义方式可以作为在您的生态系统插件中构建您自己的属性集的基础。
独立于生态系统的标准属性
属性名称 | 描述 | 值 | 兼容性和消除歧义规则 |
---|---|---|---|
指示变体的主要用途 |
从 Usage 中定义的常量构建的 |
遵循生态系统语义(例如, |
|
指示此软件组件的类别 |
从 Category 中定义的常量构建的 |
遵循生态系统语义(例如, |
|
指示 |
从 LibraryElements 中定义的常量构建的 |
遵循生态系统语义(例如,在 JVM 世界中, |
|
指示 |
从 DocsType 中定义的常量构建的 |
无默认值,不兼容 |
|
指示如何访问变体的依赖项。 |
从 Bundling 中定义的常量构建的 |
遵循生态系统语义(例如,在 JVM 世界中, |
|
指示哪种验证任务产生了此输出。 |
从 VerificationType 中定义的常量构建的 |
无默认值,不兼容 |
当 Category
属性存在于变体上且具有孵化值 org.gradle.category=verification
时,该变体被认为是仅用于验证时的变体。
这些变体旨在仅包含运行验证任务的结果,例如测试结果或代码覆盖率报告。 它们是 不可发布的,如果添加到已发布的组件中,将产生错误。
属性名称 | 描述 | 值 | 兼容性和消除歧义规则 |
---|---|---|---|
|
组件级别属性,派生 |
基于 状态方案,默认方案基于源仓库。 |
基于正在使用的方案 |
JVM 生态系统特定属性
除了上面定义的独立于生态系统的属性之外,JVM 生态系统还添加了以下属性
属性名称 | 描述 | 值 | 兼容性和消除歧义规则 |
---|---|---|---|
指示 JVM 版本兼容性。 |
整数,Java 1.4 及更早版本使用 |
默认为 Gradle 使用的 JVM 版本,较低版本与较高版本兼容,首选最高兼容版本。 |
|
指示变体针对特定 JVM 环境进行了优化。 |
常用值是 |
如果多个变体可用,则该属性用于优先选择一个变体而不是另一个变体,但通常所有值都兼容。 默认值为 |
|
指示生成此输出的 TestSuite 的名称。 |
值是 Suite 的名称。 |
无默认值,不兼容 |
JVM 生态系统还包含许多关于不同属性的兼容性和消除歧义规则。 愿意了解更多的读者可以查看 org.gradle.api.internal.artifacts.JavaEcosystemSupport
的代码。
原生生态系统特定属性
除了上面定义的独立于生态系统的属性之外,原生生态系统还添加了以下属性
属性名称 | 描述 | 值 | 兼容性和消除歧义规则 |
---|---|---|---|
指示二进制文件是否使用调试符号构建 |
布尔值 |
不适用 |
|
指示二进制文件是否使用优化标志构建 |
布尔值 |
不适用 |
|
指示二进制文件的目标架构 |
从 MachineArchitecture 中定义的常量构建的 |
无 |
|
指示二进制文件的目标操作系统 |
从 OperatingSystemFamily 中定义的常量构建的 |
无 |
Gradle 插件生态系统特定属性
对于 Gradle 插件开发,自 Gradle 7.0 起支持以下属性。 Gradle 插件变体可以通过此属性指定与 Gradle API 版本的兼容性。
属性名称 | 描述 | 值 | 兼容性和消除歧义规则 |
---|---|---|---|
指示 Gradle API 版本兼容性。 |
有效的 Gradle 版本字符串。 |
默认为当前运行的 Gradle,较低版本与较高版本兼容,首选最高兼容版本。 |
使用标准属性
对于此示例,假设您正在创建一个库,其中包含用于不同 JVM 版本的不同变体。
plugins {
id("java-library")
}
configurations {
named("apiElements") {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
}
}
}
plugins {
id 'java-library'
}
configurations {
apiElements {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
}
}
}
在消费者项目(使用该库的项目)中,您可以在声明依赖项时指定 JVM 版本属性。
plugins {
id("application")
}
dependencies {
implementation(project(":lib")) {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
}
}
}
plugins {
id 'application'
}
dependencies {
implementation(project(':lib')) {
attributes {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
}
}
}
通过定义和使用 JVM 版本属性,您可以确保您的库及其消费者与指定的 JVM 版本兼容。 本质上,这确保 Gradle 解析为与所需 JVM 版本匹配的变体。
查看和调试属性
dependencyInsight
任务对于检查特定依赖项及其属性(包括它们如何解析)非常有用
$ ./gradlew dependencyInsight --configuration compileClasspath --dependency com.example:your-library
> Task :dependencyInsight
com.example:your-library:1.0 (compileClasspath)
variant "apiElements" [
org.gradle.api.attributes.Attribute: org.gradle.api.attributes.Usage = [java-api]
org.gradle.api.attributes.Attribute: org.gradle.api.attributes.Usage = [java-runtime]
org.gradle.api.attributes.Attribute: org.gradle.api.attributes.JavaLanguageVersion = [1.8]
]
variant "runtimeElements" [
org.gradle.api.attributes.Attribute: org.gradle.api.attributes.Usage = [java-runtime]
org.gradle.api.attributes.Attribute: org.gradle.api.attributes.JavaLanguageVersion = [1.8]
]
Selection reasons:
- By constraint from configuration ':compileClasspath'
- Declared in build.gradle.kts
Resolved to:
com.example:your-library:1.0 (runtime)
Additional Information:
- Dependency declared in the 'implementation' configuration
- No matching variants found for the requested attributes in the 'compileClasspath' configuration
声明自定义属性
使用 自定义属性 扩展 Gradle 时,重要的是要考虑它们的长期影响,特别是如果您计划发布库。 自定义属性允许您在插件中集成 变体感知依赖管理,但使用这些属性的库还必须确保消费者可以正确解释它们。 这通常通过应用相应的插件来完成,该插件定义了兼容性和消除歧义规则。
如果您的插件是公开可用的,并且库发布到公共仓库,那么引入新属性将成为一项重要责任。 已发布的属性必须保持受支持,或者在插件的未来版本中具有兼容性层,以确保向后兼容性。
以下是在 Gradle 插件中声明和使用自定义属性的示例
// Define a custom attribute
val myAttribute = Attribute.of("com.example.my-attribute", String::class.java)
configurations {
create("myConfig") {
// Set custom attribute
attributes {
attribute(myAttribute, "special-value")
}
}
}
dependencies {
// Apply the custom attribute to a dependency
add("myConfig","com.google.guava:guava:31.1-jre") {
attributes {
attribute(myAttribute, "special-value")
}
}
}
// Define a custom attribute
def myAttribute = Attribute.of("com.example.my-attribute", String)
// Create a custom configuration
configurations {
create("myConfig") {
// Set custom attribute
attributes {
attribute(myAttribute, "special-value")
}
}
}
dependencies {
// Apply the custom attribute to a dependency
add("myConfig", "com.google.guava:guava:31.1-jre") {
attributes {
attribute(myAttribute, "special-value")
}
}
}
在此示例中: - 定义了一个自定义属性 my-attribute
。 - 该属性在自定义配置 (myConfig
) 上设置。 - 添加依赖项时,自定义属性将应用于匹配配置。
如果发布具有此属性的库,请确保使用者应用理解和处理 my-attribute
的插件。
在构建脚本或插件中创建属性
属性是类型化的。 可以通过 Attribute<T>.of
方法创建属性
// An attribute of type `String`
val myAttribute = Attribute.of("my.attribute.name", String::class.java)
// An attribute of type `Usage`
val myUsage = Attribute.of("my.usage.attribute", Usage::class.java)
// An attribute of type `String`
def myAttribute = Attribute.of("my.attribute.name", String)
// An attribute of type `Usage`
def myUsage = Attribute.of("my.usage.attribute", Usage)
属性类型支持大多数 Java 原始类; 例如 String
和 Integer
。 或任何扩展 org.gradle.api.Named
的内容。
属性应始终在 dependencies
处理程序上的属性模式中声明
dependencies.attributesSchema {
// registers this attribute to the attributes schema
attribute(myAttribute)
attribute(myUsage)
}
dependencies.attributesSchema {
// registers this attribute to the attributes schema
attribute(myAttribute)
attribute(myUsage)
}
为了使用兼容性和消除歧义规则,必须在模式中注册属性,这些规则可以解决 属性匹配 期间多个可选变体之间的歧义。
每个配置都有一个属性容器。 可以配置属性以设置值
configurations {
create("myConfiguration") {
attributes {
attribute(myAttribute, "my-value")
}
}
}
configurations {
myConfiguration {
attributes {
attribute(myAttribute, 'my-value')
}
}
}
对于类型扩展 Named
的属性,属性的值必须通过对象工厂创建
configurations {
"myConfiguration" {
attributes {
attribute(myUsage, project.objects.named(Usage::class.java, "my-value"))
}
}
}
configurations {
myConfiguration {
attributes {
attribute(myUsage, project.objects.named(Usage, 'my-value'))
}
}
}
处理属性匹配
在 Gradle 中,属性匹配 和 属性消除歧义 是解析具有不同属性的依赖项的关键机制。
属性匹配 允许 Gradle 根据预定义的规则选择兼容的依赖项变体,即使没有完全匹配的变体。 另一方面,当存在多个兼容选项时,属性消除歧义 帮助 Gradle 选择最合适的变体。
属性兼容性规则
属性让引擎选择兼容的变体。 在某些情况下,生产者可能没有完全消费者要求的内容,但具有可以使用的变体。
此示例定义并注册自定义兼容性规则,以确保根据依赖项与特定 Java 版本的兼容性来选择依赖项
// Define the compatibility rule class
class TargetJvmVersionCompatibilityRule : AttributeCompatibilityRule<Int> {
// Implement the execute method which will check compatibility
override fun execute(details: CompatibilityCheckDetails<Int>) {
// Switch case to check the consumer value for supported Java versions
when (details.consumerValue) {
8, 11 -> details.compatible() // Compatible with Java 8 and 11
else -> details.incompatible()
}
}
}
// Register the compatibility rule within the dependencies block
dependencies {
attributesSchema {
// Add the compatibility rule for the TargetJvmVersion attribute
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE) {
// Add the defined compatibility rule to this attribute
compatibilityRules.add(TargetJvmVersionCompatibilityRule::class.java)
}
}
}
// Define the compatibility rule
class TargetJvmVersionCompatibilityRule implements AttributeCompatibilityRule<Integer> {
@Override
void execute(CompatibilityCheckDetails<Integer> details) {
switch (details.consumerValue) {
case 8:
case 11:
details.compatible() // Compatible with Java 8 and 11
break
default:
details.incompatible("Unsupported Java version: ${details.consumerValue}")
}
}
}
// Register a compatibility rule
dependencies {
attributesSchema {
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE) {
compatibilityRules.add(TargetJvmVersionCompatibilityRule)
}
}
}
Gradle 提供了 属性兼容性规则,可以为每个属性定义这些规则。 兼容性规则的作用是解释哪些属性值基于消费者的要求是兼容的。
属性兼容性规则必须通过 属性模式 注册。
属性消除歧义规则
当依赖项的多个变体与消费者请求的属性兼容时,Gradle 需要决定选择哪个变体。 在兼容选项中确定“最佳”候选者的过程称为 属性消除歧义。
在 Gradle 中,不同的变体可能满足消费者的请求,但并非所有变体都是相同的。 例如,您可能拥有多个版本的库,这些库与消费者请求的 Java 版本兼容。 消除歧义有助于 Gradle 根据其他标准选择最合适的变体。
您可以定义消除歧义规则,以指导 Gradle 在找到多个候选者时选择最合适的变体。 这可以通过实现 属性消除歧义规则 来完成
import org.gradle.api.attributes.Attribute
import org.gradle.api.attributes.AttributeMatchingStrategy
// Define custom attribute
val javaLanguageVersion = Attribute.of("org.gradle.jvm.version", String::class.java)
// Register disambiguation rules
configurations.all {
attributes {
// Define the attribute matching strategy
attribute(javaLanguageVersion, "1.8") {
// Set up disambiguation logic
disambiguationStrategy {
// Example disambiguation: Prefer newer versions
preferNewer()
}
}
}
}
-
属性定义:创建或引用要应用消除歧义规则的属性。 这里,使用了
javaLanguageVersion
。 -
注册消除歧义规则:使用
attributes
代码块中的disambiguationStrategy
应用消除歧义策略。 此示例设置了一个简单的规则来优先选择较新版本。 -
消除歧义逻辑:
preferNewer()
方法是您的自定义逻辑的占位符。 您可以根据您的要求实现更复杂的规则。
属性消除歧义规则必须通过您可以从 属性模式 获取的 属性匹配策略 注册,属性模式是 DependencyHandler 的成员。
从 Maven/Ivy 映射到 Gradle 变体
Maven 和 Ivy 都没有变体的概念,变体仅由 Gradle 模块元数据原生支持。 Gradle 仍然可以通过使用不同的变体派生策略来与 Maven 和 Ivy 一起工作。
Gradle 模块元数据是为发布在 Maven、Ivy 和其他类型的仓库上的模块提供的元数据格式。 它类似于 pom.xml
或 ivy.xml
元数据文件,但此格式包含有关变体的详细信息。
有关更多信息,请参阅 {metadata-file-spec}[Gradle 模块元数据规范]。
Maven POM 元数据到变体的映射
发布到 Maven 仓库的模块在由 Gradle 解析时会自动转换为变体感知模块。
Gradle 无法知道发布的是哪种组件
-
代表 Gradle 平台的 BOM
-
用作超级 POM 的 BOM
-
既是平台又是库的 POM
Java 项目在 Gradle 中使用的默认策略是派生 8 个不同的变体
-
两个 “library” 变体(属性
org.gradle.category
=library
)-
compile
变体映射<scope>compile</scope>
依赖项。 此变体等效于 Java Library 插件 的apiElements
变体。 此作用域的所有依赖项都被视为 API 依赖项。 -
runtime
变体映射<scope>compile</scope>
和<scope>runtime</scope>
依赖项。 此变体等效于 Java Library 插件 的runtimeElements
变体。 这些作用域的所有依赖项都被视为 运行时依赖项。-
在这两种情况下,
<dependencyManagement>
依赖项都未转换为约束
-
-
-
代表组件的 sources jar 的 “sources” 变体
-
代表组件的 javadoc jar 的 “javadoc” 变体
-
从
<dependencyManagement>
代码块派生的四个 “platform” 变体(属性org.gradle.category
=platform
)-
platform-compile
变体将<scope>compile</scope>
依赖项管理依赖项映射为 依赖约束。 -
platform-runtime
变体将<scope>compile</scope>
和<scope>runtime</scope>
依赖项管理依赖项映射为 依赖约束。 -
enforced-platform-compile
类似于platform-compile
,但所有约束都是强制的 -
enforced-platform-runtime
类似于platform-runtime
,但所有约束都是强制的
-
您可以通过查看手册的 导入 BOM 部分来了解有关平台和强制平台变体的更多信息。 默认情况下,每当您声明 Maven 模块上的依赖项时,Gradle 都会查找 library
变体。 但是,使用 platform
或 enforcedPlatform
关键字,Gradle 现在正在查找 “platform” 变体之一,这使您可以从 POM 文件导入约束,而不是依赖项。
Ivy 文件到变体的映射
Gradle 没有为 Ivy 文件实现内置的派生策略。 Ivy 是一种灵活的格式,允许您发布任意文件并且可以高度自定义。
如果您想为 Ivy 实现 compile 和 runtime 变体的派生策略,您可以使用 组件元数据规则。 组件元数据规则 API 允许您 访问 Ivy 配置 并基于它们创建变体。 如果您知道您正在使用的所有 Ivy 模块都是使用 Gradle 发布的,并且没有进一步自定义 ivy.xml
文件,则可以将以下规则添加到您的构建中
abstract class IvyVariantDerivationRule @Inject internal constructor(objectFactory: ObjectFactory) : ComponentMetadataRule {
private val jarLibraryElements: LibraryElements
private val libraryCategory: Category
private val javaRuntimeUsage: Usage
private val javaApiUsage: Usage
init {
jarLibraryElements = objectFactory.named(LibraryElements.JAR)
libraryCategory = objectFactory.named(Category.LIBRARY)
javaRuntimeUsage = objectFactory.named(Usage.JAVA_RUNTIME)
javaApiUsage = objectFactory.named(Usage.JAVA_API)
}
override fun execute(context: ComponentMetadataContext) {
// This filters out any non Ivy module
if(context.getDescriptor(IvyModuleDescriptor::class) == null) {
return
}
context.details.addVariant("runtimeElements", "default") {
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, jarLibraryElements)
attribute(Category.CATEGORY_ATTRIBUTE, libraryCategory)
attribute(Usage.USAGE_ATTRIBUTE, javaRuntimeUsage)
}
}
context.details.addVariant("apiElements", "compile") {
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, jarLibraryElements)
attribute(Category.CATEGORY_ATTRIBUTE, libraryCategory)
attribute(Usage.USAGE_ATTRIBUTE, javaApiUsage)
}
}
}
}
dependencies {
components { all<IvyVariantDerivationRule>() }
}
abstract class IvyVariantDerivationRule implements ComponentMetadataRule {
final LibraryElements jarLibraryElements
final Category libraryCategory
final Usage javaRuntimeUsage
final Usage javaApiUsage
@Inject
IvyVariantDerivationRule(ObjectFactory objectFactory) {
jarLibraryElements = objectFactory.named(LibraryElements, LibraryElements.JAR)
libraryCategory = objectFactory.named(Category, Category.LIBRARY)
javaRuntimeUsage = objectFactory.named(Usage, Usage.JAVA_RUNTIME)
javaApiUsage = objectFactory.named(Usage, Usage.JAVA_API)
}
void execute(ComponentMetadataContext context) {
// This filters out any non Ivy module
if(context.getDescriptor(IvyModuleDescriptor) == null) {
return
}
context.details.addVariant("runtimeElements", "default") {
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, jarLibraryElements)
attribute(Category.CATEGORY_ATTRIBUTE, libraryCategory)
attribute(Usage.USAGE_ATTRIBUTE, javaRuntimeUsage)
}
}
context.details.addVariant("apiElements", "compile") {
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, jarLibraryElements)
attribute(Category.CATEGORY_ATTRIBUTE, libraryCategory)
attribute(Usage.USAGE_ATTRIBUTE, javaApiUsage)
}
}
}
}
dependencies {
components { all(IvyVariantDerivationRule) }
}
该规则基于每个 ivy 模块的 compile
配置创建 apiElements
变体,并基于 default
配置创建 runtimeElements
变体。 对于每个变体,它都设置了相应的 Java 生态系统属性。 变体的依赖项和工件取自底层配置。 如果并非所有使用的 Ivy 模块都遵循此模式,则可以调整规则或仅将其应用于选定的模块集。
对于所有没有变体的 Ivy 模块,Gradle 都有一个回退选择方法。 Gradle 不 执行变体感知解析,而是选择 default
配置或显式命名的配置。