// Copyright (C) 2023 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]]
= Working with Variant Attributes

As explained in the section on <<variant_model.adoc#sec:variant-aware-matching,variant aware matching>>, attributes give semantics to variants and are used by Gradle's dependency management engine to select the best matching variant.

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 eco system plugin.

[[sec:standard_attributes]]
== Standard attributes defined by Gradle

Gradle defines a list of standard attributes used by Gradle's core plugins.

=== Ecosystem-independent standard attributes

.Ecosystem-independent standard variant 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]]

[WARNING]
====
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.
====

.Ecosystem-independent standard component attributes
[%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
|===

[[sub:jvm_default_attributes]]
=== JVM ecosystem specific attributes

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

.JVM ecosystem standard component attributes
[%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/TestSuiteType.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/api/attributes/TestSuiteTargetName.html#TEST_SUITE_TARGET_NAME_ATTRIBUTE[`org.gradle.testsuite.target.name`]
| Indicates the name of the link:{javadocPath}/org/gradle/testing/base/TestSuiteTarget.html[TestSuiteTarget] that produced this output.
| Value is the name of the Target.
| No default, no compatibility

| link:{javadocPath}/org/gradle/api/attributes/TestSuiteType.html#TEST_SUITE_TYPE_ATTRIBUTE[`org.gradle.testsuite.type`]
| Indicates the type of test suite (unit test, integration test, performance test, etc.)
| `TestSuiteType` values built from constants defined in link:{javadocPath}/org/gradle/api/attributes/TestSuiteType.html[TestSuiteType] or other custom values for user-defined test suite types.
| No default, no compatibility
|===

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:

.Native ecosystem standard component 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
|===

[[sub: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.

.Gradle plugin ecosystem standard component attributes
[%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:declaring_attributes]]
== Declaring custom attributes

If you are extending Gradle, e.g. by writing a plugin for another ecosystem, declaring custom attributes could be an option if you want to support variant-aware dependency management features in your plugin.
However, you should be cautious if you also attempt to publish libraries.
Semantics of new attributes are usually defined through a plugin, which can carry <<#sec:abm_compatibility_rules,compatibility>> and <<sec:abm_disambiguation_rules,disambiguation>> rules.
Consequently, builds that consume libraries published for a certain ecosystem, also need to apply the corresponding plugin to interpret attributes correctly.
If your plugin is intended for a larger audience, i.e. if it is openly available and libraries are published to public repositories, defining new attributes effectively extends the semantics of Gradle Module Metadata and comes with responsibilities.
E.g., support for attributes that are already published should not be removed again, or should be handled in some kind of compatibility layer in future versions of the plugin.

== Creating attributes in a build script or plugin

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

.Define attributes
====
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 must be declared in the _attribute schema_ found on the `dependencies` handler:

.Registering attributes on the attributes schema
====
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]"]
====

Then configurations can be configured to set values for attributes:

.Setting attributes on configurations
====
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_:

.Named attributes
====
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]"]
====

== Attribute matching

[[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.

For example, if the consumer is asking for the API of a library and the producer doesn't have an exactly matching variant, the _runtime_ variant could be considered compatible.
This is typical of libraries published to external repositories.
In this case, we know that even if we don't have an exact match (API), we can still compile against the runtime variant (it contains _more_ than what we need to compile but it's still ok to use).

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/AttributeMatchingStrategy.html[attribute matching strategy] that you can obtain from the link:{javadocPath}/org/gradle/api/attributes/AttributesSchema.html[attributes schema].

[[sec:abm_disambiguation_rules]]
=== Attribute disambiguation rules

Since multiple values for an attribute can be _compatible_, Gradle needs to choose the "best" candidate between all compatible candidates. This is called "disambiguation".

This is done by implementing an link:{javadocPath}/org/gradle/api/attributes/AttributeDisambiguationRule.html[attribute disambiguation rule].

Attribute disambiguation rules have to be registered via the link:{javadocPath}/org/gradle/api/attributes/AttributeMatchingStrategy.html[attribute matching strategy] that you can obtain from the link:{javadocPath}/org/gradle/api/attributes/AttributesSchema.html[attributes schema], which is a member of link:{javadocPath}/org/gradle/api/artifacts/dsl/DependencyHandler.html[DependencyHandler].
[[sec:abm_algorithm]]
=== Variant attribute matching algorithm

Finding the best variant can get complicated when there are many different variants available for a component and many different attributes. Gradle's dependency resolution engine performs the following algorithm when finding the best result (or failing):

1. Each candidate's attribute value is compared to the consumer's requested attribute value. A candidate is considered compatible if its value matches the consumer's value exactly, passes the attribute's compatibility rule or is not provided.
2. If only one candidate is considered compatible, that candidate wins.
3. If several candidates are compatible, but one of the candidates matches all of the same attributes as the other candidates, Gradle chooses that candidate. This is the candidate with the "longest" match.
4. If several candidates are compatible and are compatible with an equal number of attributes, Gradle needs to disambiguate the candidates.
    a. For each requested attribute, if a candidate does not have a value matching the disambiguation rule, it's eliminated from consideration.
    b. If the attribute has a known precedence, Gradle will stop as soon as there is a single candidate remaining.
    c. If the attribute does not have a known precedence, Gradle must consider all attributes.
5. If several candidates still remain, Gradle will start to consider "extra" attributes to disambiguate between multiple candidates. Extra attributes are attributes that were not requested by the consumer but are present on at least one candidate. These extra attributes are considered in precedence order.
    a. If the attribute has a known precedence, Gradle will stop as soon as there is a single candidate remaining.
    b. After all extra attributes with precedence are considered, the remaining candidates can be chosen if they are compatible with all of the non-ordered disambiguation rules.
6. If several candidates still remain, Gradle will consider extra attributes again. A candidate can be chosen if it has the fewest number of extra attributes.

If at any step no candidates remain compatible, resolution fails.
Additionally, Gradle outputs a list of all compatible candidates from step 1 to help with debugging variant matching failures.

Plugins and ecosystems can influence the selection algorithm by implementing compatibility rules, disambiguation rules and telling Gradle the precedence of attributes. Attributes with a higher precedence are used to eliminate compatible matches in order.

For example, in the Java ecosystem, the `org.gradle.usage` attribute has a higher precedence than `org.gradle.libraryelements`. This means that if two candidates were available with compatible values for both `org.gradle.usage` and `org.gradle.libraryelements`, Gradle will choose the candidate that passes the disambiguation rule for `org.gradle.usage`.
