在其他依赖项管理引擎(如 Apache Maven™)中,依赖项和工件绑定到在特定 GAV(组工件版本)坐标处发布的组件。无论该组件使用哪个工件,该组件的依赖项集始终相同。

如果组件确实有多个工件,则每个工件都由一个繁琐的分类器来识别。没有与分类器相关的通用语义,这使得很难保证全局一致的依赖图。这意味着没有什么可以阻止单个组件(例如,分类器)的多个工件jdk7出现jdk8在类路径中并导致难以诊断的问题。

Maven 组件模型

组件模型maven
图 1. Maven 组件模型

Gradle 组件模型

组件模型等级
图 2. Gradle 组件模型

Gradle 的依赖管理引擎是变体感知的

除了组件之外,Gradle 还有组件变体的概念。变体对应于组件的不同使用方式,例如 Java 编译或本机链接或文档。工件附加到一个变体,每个变体可以有一组不同的依赖项。

当有多个变体时,Gradle 如何知道选择哪种变体?变体通过使用属性进行匹配,属性为变体提供语义并帮助引擎产生一致的解析结果。

Gradle 区分两种组件:

  • 从源构建的本地组件(如项目)

  • 外部组件,发布到存储库

对于本地组件,变体被映射到消耗品配置。对于外部组件,变体由已发布的 Gradle 模块元数据定义或源自 Ivy/Maven 元数据

变体与配置

由于历史原因,变体和配置有时在文档、DSL 或 API 中可以互换使用。

所有组件都提供变体,并且这些变体可由消耗品配置支持。并非所有配置都是变体,因为它们可用于声明或解决依赖关系。

变体属性

属性是类型安全的键值对,由消费者(对于可解析配置)和生产者(对于每个变体)定义。

消费者可以定义任意数量的属性。每个属性都有助于缩小可选择的可能变体范围。属性值不需要完全匹配。

该变体还可以定义任意数量的属性。属性应描述变体的用途。例如,Gradle 使用名为 的属性org.gradle.usage来描述使用者如何使用组件(用于编译、运行时执行等)。变体具有的属性多于消费者选择它所需的属性的情况并不罕见。

变体属性匹配

关于生产者变体

变体名称主要用于调试目的和错误消息。该名称不参与变体匹配——只有它的属性参与。

组件可以定义的变体数量没有限制。通常,一个组件至少有一个实现变体,但它也可以公开测试装置、文档或源代码。组件还可以为相同用途的不同消费者公开不同的变体。例如,在编译时,Linux、Windows 和 macOS 的组件可能具有不同的标头。

Gradle通过将消费者请求的属性与生产者定义的属性进行匹配来执行变体感知选择。选择算法将在另一节中详细介绍。

此规则有两个例外,可以绕过变体感知解决方案:

  • 当生产者没有变体时,将选择默认工件。

  • 当消费者按名称显式选择配置时,就会选择该配置的工件。

一个简单的例子

让我们考虑一个消费者尝试使用库进行编译的示例。

首先,消费者需要解释它将如何使用依赖解析的结果。这是通过在消费者的可解析配置上设置属性来完成的。

消费者想要解析匹配的变体:org.gradle.usage=java-api

其次,生产者需要公开组件的不同变体。

生产者组件公开了 2 个变体:

  • 它的 API(名为apiElements)带有属性org.gradle.usage=java-api

  • 它的运行时(名为runtimeElements)带有属性org.gradle.usage=java-runtime

最后,Gradle 通过查看变体属性来选择适当的变体:

  • 消费者想要一个具有属性的变体org.gradle.usage=java-api

  • 生产者有一个匹配的变体 ( apiElements)

  • 生产者有一个不匹配的变体 ( runtimeElements)

apiElementsGradle向消费者提供变体的工件和依赖项。

一个更复杂的例子

在现实世界中,消费者和生产者具有不止一种属性。

Gradle 中的 Java 库项目将涉及几个不同的属性:

  • org.gradle.usage描述了如何使用该变体

  • org.gradle.dependency.bundling描述变体如何处理依赖关系(shadow jar、fat jar 和常规 jar)

  • org.gradle.libraryelements,描述变体的包装(类或 jar)

  • org.gradle.jvm.version描述此变体目标的最小 Java 版本

  • org.gradle.jvm.environment描述了该变体目标的 JVM 类型

让我们考虑一个示例,其中使用者希望使用 Java 8 上的库运行测试,而生产者支持两个不同的 Java 版本(Java 8 和 Java 11)。

首先,消费者需要解释其需要哪个版本的 Java。

消费者想要解决一个变体:

  • 可以在运行时使用(有org.gradle.usage=java-runtime

  • 至少可以在Java 8上运行( org.gradle.jvm.version=8)

其次,生产者需要公开组件的不同变体。

就像这个简单的例子一样,有一个 API(编译)和运行时变体。这些组件的 Java 8 和 Java 11 版本均存在。

  • 它针对 Java 8 消费者(名为apiJava8Elements)的 API 具有属性org.gradle.usage=java-apiorg.gradle.jvm.version=8

  • Java 8 消费者(名为runtime8Elements)的运行时具有属性org.gradle.usage=java-runtimeorg.gradle.jvm.version=8

  • 其针对 Java 11 消费者(名为apiJava11Elements)的 API 具有属性org.gradle.usage=java-apiorg.gradle.jvm.version=11

  • Java 11 消费者(名为runtime11Elements)的运行时具有属性org.gradle.usage=java-runtimeorg.gradle.jvm.version=11

最后,Gradle 通过查看所有属性来选择最佳匹配变体:

  • 消费者想要一个具有兼容属性的变org.gradle.usage=java-runtimeorg.gradle.jvm.version=8

  • 变体runtime8Elementsruntime11Elements具有 `org.gradle.usage=java-runtime

  • 变体apiJava8Elements并且apiJava11Elements不兼容

  • 该变体runtime8Elements是兼容的,因为它可以在 Java 8 上运行

  • 该变体runtime11Elements不兼容,因为它无法在 Java 8 上运行

runtime8ElementsGradle向消费者提供变体的工件和依赖项。

变体的兼容性

如果消费者设置org.gradle.jvm.version为7怎么办?

依赖项解析将失败,并显示一条错误消息,说明没有合适的变体。 Gradle 认识到消费者需要 Java 7 兼容库,而生产者可用的最低Java 版本是 8。

如果消费者提出请求org.gradle.jvm.version=15,Gradle 就会知道 Java 8 或 Java 11 变体都可以工作。 Gradle 选择最高兼容的 Java 版本(11)。

变体选择错误

当选择最兼容的组件变体时,解析可能会失败:

  • 当生产者的多个变体与消费者属性匹配时(歧义错误)

  • 当生产者没有任何变体与消费者属性匹配时(不兼容错误)

处理歧义错误

不明确的变体选择如下所示:

> 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'

所有兼容的候选变体都会显示其属性。

  • 首先呈现不匹配的属性,因为它们可能是选择正确变体时缺失的部分。

  • 其次是兼容的属性,因为它们表明消费者想要什么以及这些变体如何满足该请求。

  • 不会有任何不兼容的属性,因为变体不会被视为候选。

在上面的示例中,修复不在于属性匹配,而在于功能匹配,这显示在变体名称旁边。由于这两个变体有效地提供了相同的属性和功能,因此无法消除它们的歧义。因此在这种情况下,修复很可能在生产者端提供不同的功能(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'

取决于变体选择算法中发生错误的阶段。

所有潜在兼容的候选变体都与其属性一起显示。

  • 首先呈现不兼容的属性,因为它们通常是理解为什么无法选择变体的关键。

  • 其次呈现其他属性,这包括请求的兼容的属性以及消费者未请求的所有额外生产者属性。

与模糊变体错误类似,目标是了解应选择哪个变体。在某些情况下,生产者可能没有任何兼容的变体(例如,尝试使用为 Java 11 构建的库在 Java 8 上运行)。

处理不兼容的变体错误

不兼容的变体错误类似于以下示例,其中消费者想要选择带有 的变体color=green,但唯一可用的变体具有color=blue

> Could not resolve all task 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 task 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 task 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'

可视化变体信息

传出变体报告

报告任务outgoingVariants显示可供项目使用者选择的变体列表。它显示每个变体的功能、属性和工件。

dependencyInsight 此任务与报告任务类似。

默认情况下,outgoingVariants打印有关所有变体的信息。它提供可选参数--variant <variantName>来选择要显示的单个变体。它还接受该--all标志以包含有关旧版和已弃用配置的信息,或--no-all排除此信息。

outgoingVariants以下是新生成的项目上的任务输出java-library

> 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-apijava-runtime。正如他们所指出的,这就是消费者的编译类路径上需要的内容与运行时类路径上需要的内容之间的区别所在。

它还显示了次要变体,这些变体是 Gradle 项目独有的且未发布。例如,第二个变体classes允许Gradle 在针对项目apiElements进行编译时跳过 JAR 创建。java-library

有关无效耗材配置的信息

一个项目不能有多个具有相同属性和功能的配置。在这种情况下,该项目将无法构建。

为了能够可视化此类问题,传出的变体报告以宽松的方式处理这些错误。这允许报告显示有关问题的信息。

可解析的配置报告

Gradle 还提供了一个名为 的免费报告任务,resolvableConfigurations该任务显示项目的可解析配置,这些配置可以添加依赖项并进行解析。该报告将列出它们的属性以及它们扩展的任何配置。它还将列出解析过程中将受兼容性规则消歧规则影响的任何属性的摘要。

默认情况下,resolvableConfigurations打印有关所有纯可解析配置的信息。这些配置被标记为可解析但未标记为可消耗。尽管一些可解析的配置也被标记为可消耗的,但这些是遗留配置,不应构建脚本中添加依赖项。此报告提供可选参数--configuration <configurationName>来选择要显示的单个配置。它还接受该--all标志以包含有关旧版和已弃用配置的信息,或--no-all排除此信息。最后,它接受--recursive标志以在扩展配置部分中列出那些间接扩展而不是直接扩展的配置。或者,--no-recursive可用于排除此信息。

resolvableConfigurations以下是新生成的项目上的任务输出java-library

> 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

从中可以看到用于解决依赖关系的两个主要配置 和compileClasspathruntimeClasspath以及它们对应的测试配置。

从 Maven/Ivy 到 Gradle 变体的映射

Maven 和 Ivy 都没有变体的概念,只有 Gradle 模块元数据原生支持变体。 Gradle 仍然可以通过使用不同的变体派生策略与 Maven 和 Ivy 配合使用。

与 Gradle 模块元数据的关系

Gradle 模块元数据是在 Maven、Ivy 和其他类型的存储库上发布的模块的元数据格式。它类似于pom.xmlivy.xml元数据文件,但此格式包含有关变体的详细信息。

有关更多信息,请参阅Gradle 模块元数据规范。

Maven POM 元数据到变体的映射

在 Maven 存储库上发布的模块会自动转换为变体感知模块。

Gradle 无法知道发布了哪种组件:

  • 代表 Gradle 平台的 BOM

  • 用作超级 POM 的 BOM

  • 既是平台又是库的POM

Gradle 中 Java 项目使用的默认策略是派生 8 种不同的变体:

  • 两个“库”变体(属性org.gradle.category= library

    • compile体映射<scope>compile</scope>依赖关系。此变体相当于Java 库插件apiElements的变体。此范围内的所有依赖项均被视为API 依赖项

    • runtime变体映射了<scope>compile</scope><scope>runtime</scope>依赖项。此变体相当于Java 库插件runtimeElements的变体。这些范围的所有依赖项都被视为运行时依赖项

      • 在这两种情况下,<dependencyManagement>依赖关系都不会转换为约束

  • 代表组件源 jar 的“源”变体

  • 表示组件的 javadoc jar 的“javadoc”变体

  • 从该<dependencyManagement>块派生的四个“平台”变体(属性org.gradle.category= platform):

    • 该变体将依赖关系管理依赖关系platform-compile映射为依赖关系约束<scope>compile</scope>

    • platform-runtime变体将<scope>compile</scope><scope>runtime</scope>依赖管理依赖关系映射为依赖约束

    • 类似于enforced-platform-compileplatform-compile所有约束都是强制的

    • 类似于enforced-platform-runtimeplatform-runtime所有约束都是强制的

您可以通过查看手册的导入 BOM部分来了解有关平台和强制平台变体的使用的更多信息。默认情况下,每当您声明对 Maven 模块的依赖项时,Gradle 都会查找library变体。然而,使用platformorenforcedPlatform关键字,Gradle 现在正在寻找“平台”变体之一,它允许您从 POM 文件导入约束,而不是依赖项。

Ivy 文件到变体的映射

Gradle 没有为 Ivy 文件实现内置的派生策略。 Ivy 是一种灵活的格式,允许您发布任意文件并且可以进行大量自定义。

如果您想为 Ivy 的编译运行时变体实现派生策略,您可以使用组件元数据规则来实现。组件元数据规则 API 允许您访问 Ivy 配置并基于它们创建变体。如果您知道您正在使用的所有 Ivy 模块都已使用 Gradle 发布,而无需进一步自定义文件ivy.xml,则可以将以下规则添加到您的构建中:

build.gradle.kts
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>() }
}
build.gradle
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) }
}

该规则apiElements根据配置创建一个变体,并根据每个 ivy 模块的配置创建compile一个变体。对于每个变体,它设置相应的Java生态系统属性。变体的依赖关系和工件取自底层配置。如果并非所有消耗的 Ivy 模块都遵循此模式,则可以调整该规则或仅将其应用于选定的一组模块。runtimeElementsdefault

对于所有没有变体的 Ivy 模块,Gradle 有一个后备选择方法。 Gradle执行变体感知解析,而是选择default配置或显式命名的配置。