# Understanding variant selection

version 7.6

In other dependency management engines, like Apache Maven™, dependencies and artifacts are bound to a component that is published at a particular GAV (group-artifact-version) coordinates. The set of dependencies for this component are always the same, regardless of which artifact may be used from the component.

If the component does have multiple artifacts, each one is identified by a cumbersome classifier. There are no common semantics associated with classifiers and that makes it difficult to guarantee a globally consistent dependency graph. This means that nothing prevents multiple artifacts for a single component (e.g., `jdk7` and `jdk8` classifiers) from appearing in a classpath and causing hard to diagnose problems.

## Maven component model

Figure 1. The Maven component model

## Gradle component model

Figure 2. The Gradle component model

Gradle’s dependency management engine is variant aware.

In addition to a component, Gradle has the concept of variants of a component. Variants correspond to the different ways a component can be used, such as for Java compilation or native linking or documentation. Artifacts are attached to a variant and each variant can have a different set of dependencies:

How does Gradle know which variant to choose when there’s more than one? Variants are matched by use of attributes, which provide semantics to the variants and help the engine to produce a consistent resolution result.

Gradle differentiates between two kind of components: - local components (like projects), built from sources - external components, published to repositories

For local components, variants are mapped to consumable configurations. For external components, variants are defined by published Gradle Module Metadata or are derived from Ivy/Maven metadata.

| Variants vs configurations Variants and configurations are sometimes used interchangeably in the documentation, DSL or API for historical reasons. All components provide variants and those variants may be backed by a consumable configuration. Not all configurations are variants because they may be used for declaring or resolving dependencies.

## Variant attributes

Attributes are type-safe key-value pairs that are defined by the consumer (for a resolvable configuration) and the producer (for each variant).

The consumer can define any number of attributes. Each attribute helps narrow the possible variants that can be selected. Attribute values do not need to be exact matches.

The variant can also define any number of attributes. The attributes should describe how the variant is intended to be used. For example, Gradle uses an attribute named `org.gradle.usage` to describe with how a component is used by the consumer (for compilation, for runtime execution, etc). It is not unusual for a variant to have more attributes than the consumer needs to provide to select it.

## Variant attribute matching

| About producer variants The variant name is mostly for debugging purposes and error messages. The name does not participate variant matching—​only its attributes do.

There are no restrictions on the number of variants a component can define. Usually, a component has at least an implementation variant, but it could also expose test fixtures, documentation or source code. A component may also expose different variants for different consumers for the same usage. For example, when compiling, a component could have different headers for Linux vs Windows vs macOS.

Gradle performs variant aware selection by matching the attributes requested by the consumer against attributes defined by the producer. The selection algorithm is detailed in another section.

| There are two exceptions to this rule that bypass variant aware resolution: - when a producer has no variants, a default artifact is chosen. - when a consumer explicitly selects a configuration by name, the artifacts of the configuration are chosen.

## A simple example

Let’s consider an example where a consumer is trying to use a library for compilation.

First, the consumer needs to explain how it’s going to use the result of dependency resolution. This is done by setting attributes on the resolvable configuration of the consumer.

The consumer wants to resolve a variant that matches: - `org.gradle.usage=JAVA_API`

Second, the producer needs to expose the different variants of the component.

The producer component exposes 2 variants: - its API (named `apiElements`) with attribute `org.gradle.usage=JAVA_API` - its runtime (named `runtimeElements`) with attribute `org.gradle.usage=JAVA_RUNTIME`

Finally, Gradle selects the appropriate variant by looking at the variant attributes: - the consumer wants a variant with attributes `org.gradle.usage=JAVA_API` - the producer has a matching variant (`apiElements`) - the producer has a non-matching variant (`runtimeElements`)

Gradle provides the artifacts and dependencies from the `apiElements` variant to the consumer.

## A more complicated example

In the real world, consumers and producers have more than one attribute.

A Java Library project in Gradle will involve several different attributes

- `org.gradle.usage` that describes how the variant is used

- `org.gradle.dependency.bundling` that describes how the variant handles dependencies (shadow jar vs fat jar vs regular jar)

- `org.gradle.libraryelements`, that describes the packaging of the variant (classes or jar)

- `org.gradle.jvm.version` that describes the minimal version of Java this variant targets

- `org.gradle.jvm.environment` that describes the type of JVM this variant targets

Let’s consider an example where the consumer wants to run tests with a library on Java 8 and the producer supports two different Java versions (Java 8 and Java 11).

First, the consumer needs to explain which version of the Java it needs.

The consumer wants to resolve a variant that: - can be used at runtime (has `org.gradle.usage=JAVA_RUNTIME`) - can be run on at least Java 8 (`org.gradle.jvm.version=8`)

Second, the producer needs to expose the different variants of the component.

Like in the simple example, there is both a API (compilation) and runtime variant. These exist for both the Java 8 and Java 11 version of the component. - its API for Java 8 consumers (named `apiJava8Elements`) with attribute `org.gradle.usage=JAVA_API` and `org.gradle.jvm.version=8` - its runtime for Java 8 consumers (named `runtime8Elements`) with attribute `org.gradle.usage=JAVA_RUNTIME` and `org.gradle.jvm.version=8` - its API for Java 11 consumers (named `apiJava11Elements`) with attribute `org.gradle.usage=JAVA_API` and `org.gradle.jvm.version=11` - its runtime for Java 11 consumers (named `runtime11Elements`) with attribute `org.gradle.usage=JAVA_RUNTIME` and `org.gradle.jvm.version=11`

Finally, Gradle selects the best matching variant by looking at all of the attributes: - the consumer wants a variant with compatible attributes to `org.gradle.usage=JAVA_RUNTIME` and `org.gradle.jvm.version=8` - the variants `runtime8Elements` and `runtime11Elements` have `org.gradle.usage=JAVA_RUNTIME` - the variants `apiJava8Elements` and `apiJava11Elements` are incompatible - the variant `runtime8Elements` is compatible because it can run on Java 8 - the variant `runtime11Elements` is incompatible because it cannot run on Java 8

Gradle provides the artifacts and dependencies from the `runtime8Elements` variant to the consumer.

| Compatibility of variants What if the consumer sets `org.gradle.jvm.version` to 7? Dependency resolution would fail with an error message explaining that there’s no suitable variant. Gradle recognizes that the consumer wants a Java 7 compatible library and the minimal version of Java available on the producer is 8. If the consumer requested `org.gradle.jvm.version=15`, then Gradle knows either the Java 8 or Java 11 variants could work. Gradle select the highest compatible Java version (11).

## Variant selection errors

When selecting the most compatible variant of a component, resolution may fail: * when more than one variant from the producer matches the consumer attributes (ambiguity error) * when no variants from the producer match the consumer attributes (incompatibility error)

### Dealing with ambiguity errors

An ambiguous variant selection looks like the following:

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

All compatible candidate variants are displayed with their attributes.

- Unmatched attributes are presented first, as they might be the missing piece in selecting the proper variant.

- Compatible attributes are presented second as they indicate what the consumer wanted and how these variants do match that request.

- There will not be any incompatible attributes as the variant would not be considered a candidate.

In the example above, the fix does not lie in attribute matching but in capability matching, which are shown next to the variant name. Because these two variants effectively provide the same attributes and capabilities, they cannot be disambiguated. So in this case, the fix is most likely to provide different capabilities on the producer side (`project :lib`) and express a capability choice on the consumer side (`project :ui`).

### Dealing with no matching variant errors

A no matching variant error looks like the following:

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

All compatible candidate variants are displayed with their attributes.

- Incompatible attributes are presented first, as they usually are the key in understanding why a variant could not be selected.

- Other attributes are presented second, this includes required and compatible ones as well as all extra producer attributes that are not requested by the consumer.

Similar to the ambiguous variant error, the goal is to understand which variant should be selected. In some cases, there may not be any compatible variants from the producer (e.g., trying to run on Java 8 with a library built for Java 11).

## Visualizing variant information

### Outgoing variants report

The report task `outgoingVariants` shows the list of variants available for selection by consumers of the project. It displays the capabilities, attributes and artifacts for each variant.

This task is similar to the `dependencyInsight` reporting task.

By default, `outgoingVariants` prints information about all variants. It offers the optional parameter `--variant <variantName>` to select a single variant to display. It also accepts the `--all` flag to include information about legacy and deprecated configurations.

Here is the output of the `outgoingVariants` task on a freshly generated `java-library` project:

    > Task :outgoingVariants
    --------------------------------------------------
    Variant apiElements
    --------------------------------------------------
    Description = API elements for main.

    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
    --------------------------------------------------
    Description = Elements of runtime for main.

    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.

From this you can see the two main variants that are exposed by a java library, `apiElements` and `runtimeElements`. Notice that the main difference is on the `org.gradle.usage` attribute, with values `java-api` and `java-runtime`. As they indicate, this is where the difference is made between what needs to be on the compile classpath of consumers, versus what’s needed on the runtime classpath.

It also shows secondary variants, which are exclusive to Gradle projects and not published. For example, the secondary variant `classes` from `apiElements` is what allows Gradle to skip the JAR creation when compiling against a `java-library` project.

### Resolvable configurations report

Gradle also offers a complimentary report task called `resolvableConfigurations` that displays the resolvable configurations of a project, which are those which can have dependencies added and be resolved. The report will list their attributes and any configurations that they extend. It will also list a summary of any attributes which will be affected by Compatibility Rules or Disambiguation Rules during resolution.

By default, `resolvableConfigurations` prints information about all purely resolvable configurations. These are configurations that are marked resolvable but not marked consumable. Though some resolvable configurations are also marked consumable, these are legacy configurations that should not have dependencies added in build scripts. This report offers the optional parameter `--configuration <configurationName>` to select a single configuration to display. It also accepts the `--all` flag to include information about legacy and deprecated configurations. Finally, it accepts the `--recursive` flag to list in the extended configurations section those configurations which are extended transitively rather than directly.

Here is the output of the `resolvableConfigurations` task on a freshly generated `java-library` project:

    > 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

From this you can see the two main configurations used to resolve dependencies, `compileClasspath` and `runtimeClasspath`, as well as their corresponding test configurations.

## Mapping from Maven/Ivy to Gradle variants

Neither Maven nor Ivy have the concept of variants, which are only natively supported by Gradle Module Metadata. Gradle can still work with Maven and Ivy by using different variant derivation strategies.

| Relationship with Gradle Module Metadata Gradle Module Metadata is a metadata format for modules published on Maven, Ivy and other kinds of repositories. It is similar to the `pom.xml` or `ivy.xml` metadata file, but this format contains details about variants. See the Gradle Module Metadata specification for more information.

### Mapping of Maven POM metadata to variants

Modules published on a Maven repository are automatically converted into variant-aware modules.

There is no way for Gradle to know which kind of component was published: - a BOM that represents a Gradle platform - a BOM used as a super-POM - a POM that is both a platform and a library

The default strategy used by Java projects in Gradle is to derive 8 different variants:

- 2 "library" variants (attribute `org.gradle.category` = `library`)

  - the `compile` variant maps the `<scope>compile</scope>` dependencies. This variant is equivalent to the `apiElements` variant of the Java Library plugin. All dependencies of this scope are considered API dependencies.

  - the `runtime` variant maps both the `<scope>compile</scope>` and `<scope>runtime</scope>` dependencies. This variant is equivalent to the `runtimeElements` variant of the Java Library plugin. All dependencies of those scopes are considered runtime dependencies.

    - in both cases, the `<dependencyManagement>` dependencies are not converted to constraints

- a "sources" variant that represents the sources jar for the component

- a "javadoc" variant that represents the javadoc jar for the component

- 4 "platform" variants derived from the `<dependencyManagement>` block (attribute `org.gradle.category` = `platform`):

  - the `platform-compile` variant maps the `<scope>compile</scope>` dependency management dependencies as dependency constraints.

  - the `platform-runtime` variant maps both the `<scope>compile</scope>` and `<scope>runtime</scope>` dependency management dependencies as dependency constraints.

  - the `enforced-platform-compile` is similar to `platform-compile` but all the constraints are forced

  - the `enforced-platform-runtime` is similar to `platform-runtime` but all the constraints are forced

You can understand more about the use of platform and enforced platforms variants by looking at the importing BOMs section of the manual. By default, whenever you declare a dependency on a Maven module, Gradle is going to look for the `library` variants. However, using the `platform` or `enforcedPlatform` keyword, Gradle is now looking for one of the "platform" variants, which allows you to import the constraints from the POM files, instead of the dependencies.

### Mapping of Ivy files to variants

Gradle has no built-in derivation strategy implemented for Ivy files. Ivy is a flexible format that allows you to publish arbitrary files and can be heavily customized.

If you want to implement a derivation strategy for compile and runtime variants for Ivy, you can do so with component metadata rule. The component metadata rules API allows you to access Ivy configurations and create variants based on them. If you know that all the Ivy modules your are consuming have been published with Gradle without further customizations of the `ivy.xml` file, you can add the following rule to your build:

Example 1. Deriving compile and runtime variants for Ivy metadata
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) }
    }

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>() }
    }

The rule creates an `apiElements` variant based on the `compile` configuration and a `runtimeElements` variant based on the `default` configuration of each ivy module. For each variant, it sets the corresponding Java ecosystem attributes. Dependencies and artifacts of the variants are taken from the underlying configurations. If not all consumed Ivy modules follow this pattern, the rule can be adjusted or only applied to a selected set of modules.

For all Ivy modules without variants, Gradle has a fallback selection method. Gradle does not perform variant aware resolution and instead selects either the `default` configuration or an explicitly named configuration.