// Copyright (C) 2024 Gradle, Inc.
//
// Licensed under the Creative Commons Attribution-Noncommercial-ShareAlike 4.0 International License.;
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://creativecommons.org/licenses/by-nc-sa/4.0/
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

[[variant-attributes]]
= Variants and Attributes

**Variants** represent different versions or aspects of a component, like `api` vs `implementation`.
**Attributes** define which variant is selected based on the consumer's requirements.

For example, a library may have an `api` and an `implementation` variant.
Here, the consumer wants an external `implementation` variant:

[source,kotlin]
----
configurations {
    implementation {
        attributes {
            attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL))
        }
    }
}
----

For example, a build might have `debug` and `release` variants.
This selects the `debug` variant based on the attribute.

[source,kotlin]
----
configurations {
    compileClasspath {
        attributes {
            attribute(TargetConfiguration.TARGET_ATTRIBUTE, objects.named("debug"))
        }
    }
}
----

**Attributes** help Gradle match the right **variant** by comparing the requested attributes with what's available:

[source,kotlin]
----
attribute(TargetConfiguration.TARGET_ATTRIBUTE, objects.named("debug"))
----

This sets the `TargetConfiguration.TARGET_ATTRIBUTE` to `"debug"`, meaning Gradle will attempt to resolve dependencies that have a "debug" variant, instead of other available variants (like "release").

[[sec:standard-attributes]]
== Standard attributes defined by Gradle

As a user of Gradle, attributes are often hidden as implementation details.
But it might be useful to understand the _standard attributes_ defined by Gradle and its core plugins.

As a plugin author, these attributes, and the way they are defined, can serve as a basis for <<#sec:declaring-attributes,building your own set of attributes>> in your ecosystem plugin.

=== Ecosystem-independent standard attributes

[%header%autowidth,compact]
|===
| Attribute name | Description | Values | compatibility and disambiguation rules

| link:{javadocPath}/org/gradle/api/attributes/Usage.html#USAGE_ATTRIBUTE[`org.gradle.usage`]
| Indicates main purpose of variant
| `Usage` values built from constants defined in link:{javadocPath}/org/gradle/api/attributes/Usage.html[Usage]
| Following ecosystem semantics (e.g. `java-runtime` can be used in place of `java-api` but not the opposite)

| link:{javadocPath}/org/gradle/api/attributes/Category.html#CATEGORY_ATTRIBUTE[`org.gradle.category`]
| Indicates the category of this software component
| `Category` values built from constants defined in link:{javadocPath}/org/gradle/api/attributes/Category.html[Category]
| Following ecosystem semantics (e.g. `library` is default on the JVM, no compatibility otherwise)

| link:{javadocPath}/org/gradle/api/attributes/LibraryElements.html#LIBRARY_ELEMENTS_ATTRIBUTE[`org.gradle.libraryelements`]
| Indicates the contents of a `org.gradle.category=library` variant
| `LibraryElements` values built from constants defined in link:{javadocPath}/org/gradle/api/attributes/LibraryElements.html[LibraryElements]
| Following ecosystem semantics(e.g. in the JVM world, `jar` is the default and is compatible with `classes`)

| link:{javadocPath}/org/gradle/api/attributes/DocsType.html#DOCS_TYPE_ATTRIBUTE[`org.gradle.docstype`]
| Indicates the contents of a `org.gradle.category=documentation` variant
| `DocsType` values built from constants defined in link:{javadocPath}/org/gradle/api/attributes/DocsType.html[DocsType]
| No default, no compatibility

| link:{javadocPath}/org/gradle/api/attributes/Bundling.html#BUNDLING_ATTRIBUTE[`org.gradle.dependency.bundling`]
| Indicates how dependencies of a variant are accessed.
| `Bundling` values built from constants defined in link:{javadocPath}/org/gradle/api/attributes/Bundling.html[Bundling]
| Following ecosystem semantics (e.g. in the JVM world, `embedded` is compatible with `external`)

| link:{javadocPath}/org/gradle/api/attributes/VerificationType.html#VERIFICATION_TYPE_ATTRIBUTE[`org.gradle.verificationtype`]
| Indicates what kind of verification task produced this output.
| `VerificationType` values built from constants defined in link:{javadocPath}/org/gradle/api/attributes/VerificationType.html[VerificationType]
| No default, no compatibility
|===

[[sec:verification-category]]
When the `Category` attribute is present with the incubating value `org.gradle.category=verification` on a variant, that variant is considered to be a verification-time only variant.

These variants are meant to contain only the results of running verification tasks, such as test results or code coverage reports.
They are *not publishable*, and will produce an error if added to a component which is published.

[%header%autowidth,compact]
|===
| Attribute name | Description | Values | compatibility and disambiguation rules

| `org.gradle.status`
| Component level attribute, derived
| Based on <<component_metadata_rules.adoc#sec:custom-status-scheme,a status scheme>>, with a default one existing based on the source repository.
| Based on the scheme in use
|===

[[sec:jvm-default-attributes]]
=== JVM ecosystem specific attributes

In addition to the ecosystem independent attributes defined above, the JVM ecosystem adds the following attribute:

[%header%autowidth,compact]
|===
| Attribute name | Description | Values | compatibility and disambiguation rules

| link:{javadocPath}/org/gradle/api/attributes/java/TargetJvmVersion.html#TARGET_JVM_VERSION_ATTRIBUTE[`org.gradle.jvm.version`]
| Indicates the JVM version compatibility.
| Integer using the version after the `1.` for Java 1.4 and before, the major version for Java 5 and beyond.
| Defaults to the JVM version used by Gradle, lower is compatible with higher, prefers highest compatible.

| link:{javadocPath}/org/gradle/api/attributes/java/TargetJvmEnvironment.html#TARGET_JVM_ENVIRONMENT_ATTRIBUTE[`org.gradle.jvm.environment`]
| Indicates that a variant is optimized for a certain JVM environment.
| Common values are `standard-jvm` and `android`. Other values are allowed.
| The attribute is used to prefer one variant over another if multiple are available, but in general all values are compatible. The default is `standard-jvm`.

| link:{javadocPath}/org/gradle/api/attributes/TestSuiteName.html#TEST_SUITE_NAME_ATTRIBUTE[`org.gradle.testsuite.name`]
| Indicates the name of the link:{javadocPath}/org/gradle/testing/base/TestSuite.html[TestSuite] that produced this output.
| Value is the name of the Suite.
| No default, no compatibility

| link:{javadocPath}/org/gradle/nativeplatform/MachineArchitecture.html#ARCHITECTURE_ATTRIBUTE[`org.gradle.native.architecture`]
| Indicates that native binaries are included requiring the defined target architecture
| `MachineArchitecture` values built from constants defined in link:{javadocPath}/org/gradle/nativeplatform/MachineArchitecture.html[MachineArchitecture]
| None

| link:{javadocPath}/org/gradle/nativeplatform/OperatingSystemFamily.html#OPERATING_SYSTEM_ATTRIBUTE[`org.gradle.native.operatingSystem`]
| Indicates that native binaries are included requiring the defined operating system
| `OperatingSystemFamily` values built from constants defined in link:{javadocPath}/org/gradle/nativeplatform/OperatingSystemFamily.html[OperatingSystemFamily]
| None
|===

The JVM ecosystem also contains a number of compatibility and disambiguation rules over the different attributes.
The reader willing to know more can take a look at the code for `org.gradle.api.internal.artifacts.JavaEcosystemSupport`.

=== Native ecosystem specific attributes

In addition to the ecosystem independent attributes defined above, the native ecosystem adds the following attributes:

[%header%autowidth,compact]
|===
| Attribute name | Description | Values | compatibility and disambiguation rules

| link:{javadocPath}/org/gradle/language/cpp/CppBinary.html#DEBUGGABLE_ATTRIBUTE[`org.gradle.native.debuggable`]
| Indicates if the binary was built with debugging symbols
| Boolean
| N/A

| link:{javadocPath}/org/gradle/language/cpp/CppBinary.html#OPTIMIZED_ATTRIBUTE[`org.gradle.native.optimized`]
| Indicates if the binary was built with optimization flags
| Boolean
| N/A

| link:{javadocPath}/org/gradle/nativeplatform/MachineArchitecture.html#ARCHITECTURE_ATTRIBUTE[`org.gradle.native.architecture`]
| Indicates the target architecture of the binary
| `MachineArchitecture` values built from constants defined in link:{javadocPath}/org/gradle/nativeplatform/MachineArchitecture.html[MachineArchitecture]
| None

| link:{javadocPath}/org/gradle/nativeplatform/OperatingSystemFamily.html#OPERATING_SYSTEM_ATTRIBUTE[`org.gradle.native.operatingSystem`]
| Indicates the target operating system of the binary
| `OperatingSystemFamily` values built from constants defined in link:{javadocPath}/org/gradle/nativeplatform/OperatingSystemFamily.html[OperatingSystemFamily]
| None
|===

[[sec:gradle-plugins-default-attributes]]
=== Gradle plugin ecosystem specific attributes

For Gradle plugin development, the following attribute is supported since Gradle 7.0.
A Gradle plugin variant can specify compatibility with a Gradle API version through this attribute.

[%header%autowidth,compact]
|===
| Attribute name | Description | Values | compatibility and disambiguation rules

| link:{javadocPath}/org/gradle/api/attributes/plugin/GradlePluginApiVersion.html#GRADLE_PLUGIN_API_VERSION_ATTRIBUTE[`org.gradle.plugin.api&#8209;version`]
| Indicates the Gradle API version compatibility.
| Valid Gradle version strings.
| Defaults to the currently running Gradle, lower is compatible with higher, prefers highest compatible.
|===

[[sec:using-standard-attribute]]
== Using a standard attribute

For this example, let’s assume you are creating a library with different variants for different JVM versions.

====
include::sample[dir="snippets/dependencyManagement/customizingResolution-attributeSimple/kotlin",files="lib/build.gradle.kts[tags=attributes]"]
include::sample[dir="snippets/dependencyManagement/customizingResolution-attributeSimple/groovy",files="lib/build.gradle[tags=attributes]"]
====

In the consumer project (that uses the library), you can specify the JVM version attribute when declaring dependencies.

====
include::sample[dir="snippets/dependencyManagement/customizingResolution-attributeSimple/kotlin",files="consumer/build.gradle.kts[tags=attributes]"]
include::sample[dir="snippets/dependencyManagement/customizingResolution-attributeSimple/groovy",files="consumer/build.gradle[tags=attributes]"]
====

By defining and using the JVM version attribute, you ensure that your library and its consumers are compatible with the specified JVM version.
Essentially, this ensures that Gradle resolves to the variant that matches the desired JVM version.

[[sec:view-debug-attributes]]
== Viewing and debugging attributes

The `dependencyInsight` task is useful for inspecting specific dependencies and their attributes, including how they are resolved:

[source,bash]
----
$ ./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
----

[[sec:declaring-attributes]]
== Declaring custom attributes

When extending Gradle with *custom attributes*, it’s important to consider their long-term impact, especially if you plan to publish libraries.
Custom attributes allow you to integrate *variant-aware dependency management* in your plugin, but libraries using these attributes must also ensure consumers can interpret them correctly.
This is typically done by applying the corresponding plugin, which defines compatibility and disambiguation rules.

If your plugin is publicly available and libraries are published to public repositories, introducing new attributes becomes a significant responsibility.
Published attributes must remain supported or have a <<#sec:abm-compatibility-rules,compatibility layer>> in future versions of the plugin to ensure backward compatibility.

Here’s an example of declaring and using custom attributes in a Gradle plugin:

====
include::sample[dir="snippets/dependencyManagement/customizingResolution-attributeSimple/kotlin",files="lib/build.gradle.kts[tags=custom-attributes]"]
include::sample[dir="snippets/dependencyManagement/customizingResolution-attributeSimple/groovy",files="lib/build.gradle[tags=custom-attributes]"]
====

In this example:
- A custom attribute `my-attribute` is defined.
- The attribute is set on a custom configuration (`myConfig`).
- When adding a dependency, the custom attribute is applied to match the configuration.

If publishing a library with this attribute, ensure that consumers apply the plugin that understands and handles `my-attribute`.

[[sec:creating-attributes]]
== Creating attributes in a build script or plugin

Attributes are _typed_.
An attribute can be created via the `Attribute<T>.of` method:

====
include::sample[dir="snippets/dependencyManagement/attributeMatching/kotlin",files="build.gradle.kts[tags=define_attribute]"]
include::sample[dir="snippets/dependencyManagement/attributeMatching/groovy",files="build.gradle[tags=define_attribute]"]
====

Attribute types support most Java primitive classes; such as `String` and `Integer`.
Or anything extending `org.gradle.api.Named`.

Attributes should always be declared in the _attribute schema_ found on the `dependencies` handler:

====
include::sample[dir="snippets/dependencyManagement/attributeMatching/kotlin",files="build.gradle.kts[tags=register-attributes]"]
include::sample[dir="snippets/dependencyManagement/attributeMatching/groovy",files="build.gradle[tags=register-attributes]"]
====

Registering an attribute with the schema is required in order to use Compatibility and Disambiguation rules that can resolve ambiguity between multiple selectable variants during <<#sec:attribute-matching,Attribute Matching>>.

Each configuration has a container of attributes.  Attributes can be configured to set values:

====
include::sample[dir="snippets/dependencyManagement/attributeMatching/kotlin",files="build.gradle.kts[tags=attributes-on-configurations]"]
include::sample[dir="snippets/dependencyManagement/attributeMatching/groovy",files="build.gradle[tags=attributes-on-configurations]"]
====

For attributes which type extends `Named`, the value of the attribute **must** be created via the _object factory_:

====
include::sample[dir="snippets/dependencyManagement/attributeMatching/kotlin",files="build.gradle.kts[tags=named-attributes]"]
include::sample[dir="snippets/dependencyManagement/attributeMatching/groovy",files="build.gradle[tags=named-attributes]"]
====

[[sec:attribute-matching]]
== Dealing with attribute matching

In Gradle, **attribute matching** and **attribute disambiguation** are key mechanisms for resolving dependencies with varying attributes.

**Attribute matching** allows Gradle to select compatible dependency variants based on predefined rules, even if an exact match isn't available.
**Attribute disambiguation**, on the other hand, helps Gradle choose the most suitable variant when multiple compatible options exist.

[[sec:abm-compatibility-rules]]
=== Attribute compatibility rules

Attributes let the engine select _compatible variants_.
There are cases where a producer may not have _exactly_ what the consumer requests but has a variant that can be used.

This example defines and registers a custom compatibility rule to ensure that dependencies are selected based on their compatibility with specific Java versions:

====
include::sample[dir="snippets/dependencyManagement/customizingResolution-attributeSimple/kotlin",files="lib/build.gradle.kts[tags=attribute-compatibility]"]
include::sample[dir="snippets/dependencyManagement/customizingResolution-attributeSimple/groovy",files="lib/build.gradle[tags=attribute-compatibility]"]
====

Gradle provides link:{javadocPath}/org/gradle/api/attributes/AttributeCompatibilityRule.html[attribute compatibility rules] that can be defined for each attribute.
The role of a compatibility rule is to explain which attribute values are _compatible_ based on what the consumer asked for.

Attribute compatibility rules have to be registered via the link:{javadocPath}/org/gradle/api/attributes/AttributesSchema.html[attributes schema].

[[sec:abm-disambiguation-rules]]
=== Attribute disambiguation rules

When multiple variants of a dependency are compatible with the consumer's requested attributes, Gradle needs to decide which variant to select.
This process of determining the "best" candidate among compatible options is called **attribute disambiguation**.

In Gradle, different variants might satisfy the consumer's request, but not all are equal.
For example, you might have several versions of a library that are compatible with a Java version requested by the consumer.
Disambiguation helps Gradle choose the most appropriate one based on additional criteria.

You can define disambiguation rules to guide Gradle in selecting the most suitable variant when multiple candidates are found.
This is done by implementing an link:{javadocPath}/org/gradle/api/attributes/AttributeDisambiguationRule.html[AttributeDisambiguationRule] and registering it via the link:{javadocPath}/org/gradle/api/attributes/AttributesSchema.html[attributes schema] within the `dependencies` block:

====
include::sample[dir="snippets/dependencyManagement/customizingResolution-disambiguationRule/kotlin",files="consumer/build.gradle.kts[tags=disambiguation_rule]"]
include::sample[dir="snippets/dependencyManagement/customizingResolution-disambiguationRule/groovy",files="consumer/build.gradle[tags=disambiguation_rule]"]
====

1. **Attribute Selection**: Specify the attribute to disambiguate (e.g., `Usage.USAGE_ATTRIBUTE`, a built-in Gradle attribute).
2. **Rule Registration**: Register the custom disambiguation rule using `disambiguationRules.add()` within the `attributesSchema` block of `dependencies`.
3. **Custom Logic**: Implement the `AttributeDisambiguationRule` interface in a separate class. The `execute` method defines how Gradle selects among candidates. In this example, it prefers the `JAVA_API` usage.

Attribute disambiguation rules must be registered via the link:{javadocPath}/org/gradle/api/attributes/AttributeMatchingStrategy.html[attribute matching strategy], which is accessed through the link:{javadocPath}/org/gradle/api/attributes/AttributesSchema.html[attributes schema] in the link:{javadocPath}/org/gradle/api/artifacts/dsl/DependencyHandler.html[DependencyHandler].
This ensures the rules apply globally to dependency resolution.

[[sec:mapping-maven-ivy-to-variants]]
== 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 {metadata-file-spec}[Gradle Module Metadata specification] for more information.
--

[[sec:maven-mapping-to-variants]]
=== Mapping of Maven POM metadata to variants

Modules published to a Maven repository are automatically converted into variant-aware modules when resolved by Gradle.

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:

* two "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.adoc#java_library_plugin,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.adoc#java_library_plugin,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
* four "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 <<platforms.adoc#sec:bom-import, 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.

[[sec:ivy-mapping-to-variants]]
== 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_rules.adoc#component-metadata-rules,_component_ metadata rule>>.
The _component_ metadata rules API allows you to <<component_metadata_rules.adoc#sec:component-metadata-rules-details,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:

.Deriving compile and runtime variants for Ivy metadata
====
include::sample[dir="snippets/dependencyManagement/customizingResolution-ivyMetadataRule/kotlin",files="build.gradle.kts[tags=ivy-component-metadata-rule]"]
include::sample[dir="snippets/dependencyManagement/customizingResolution-ivyMetadataRule/groovy",files="build.gradle[tags=ivy-component-metadata-rule]"]
====

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 <<variant_aware_resolution.adoc#sec:variant-aware-matching,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.
