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

如果该组件确实具有多个工件,则每个工件都由一个笨重的分类器标识。分类器没有共同的语义,这使得难以保证全局一致的依赖关系图。这意味着没有任何东西可以阻止单个组件的多个工件(例如,jdk7jdk8 分类器)出现在类路径中,并导致难以诊断的问题。

Maven 组件模型

component model maven
图 1. Maven 组件模型

Gradle 组件模型

component model 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

Gradle 将 apiElements 变体的工件和依赖项提供给消费者。

一个更复杂的例子

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

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

  • org.gradle.usage 描述了变体的使用方式

  • org.gradle.dependency.bundling 描述了变体如何处理依赖项(影子 jar、胖 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 版本中。

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

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

  • 它的 API 用于 Java 11 消费者(名为 apiJava11Elements),具有属性 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

  • 变体 apiJava8ElementsapiJava11Elements 不兼容

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

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

Gradle 将 runtime8Elements 变体的工件和依赖项提供给消费者。

变体的兼容性

如果消费者将 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 8 上运行使用 Java 11 构建的库)。

处理不兼容变体错误

不兼容变体错误看起来像下面的示例,其中消费者想要选择一个具有 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 库公开的两个主要变体:apiElementsruntimeElements。请注意,主要区别在于 org.gradle.usage 属性,其值分别为 java-apijava-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

从这里您可以看到用于解析依赖项的两个主要配置,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 的“sources”变体

  • 表示组件 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 变体。但是,使用 platformenforcedPlatform 关键字,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) }
}

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

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