Working with Variant Attributes
As explained in the section on variant aware matching, attributes give semantics to variants and are used to perform the selection between them.
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 building your own set of attributes in your eco system plugin.
Standard attributes defined by Gradle
Gradle defines a list of standard attributes used by Gradle’s core plugins.
Ecosystem-independent standard attributes
Attribute name | Description | Values | compatibility and disambiguation rules |
---|---|---|---|
Indicates main purpose of variant |
|
Following ecosystem semantics (e.g. |
|
Indicates the category of this software component |
|
Following ecosystem semantics (e.g. |
|
Indicates the contents of a |
|
Following ecosystem semantics(e.g. in the JVM world, |
|
Indicates the contents of a |
|
No default, no compatibility |
|
Indicates how dependencies of a variant are accessed. |
|
Following ecosystem semantics (e.g. in the JVM world, |
Attribute name | Description | Values | compatibility and disambiguation rules |
---|---|---|---|
|
Component level attribute, derived |
Based on a status scheme, with a default one existing based on the source repository. |
Based on the scheme in use |
JVM ecosystem specific attributes
In addition to the ecosystem independent attributes defined above, the JVM ecosystem adds the following attribute:
Attribute name | Description | Values | compatibility and disambiguation rules |
---|---|---|---|
Indicates the JVM version compatibility. |
Integer using the version after the |
Defaults to the JVM version used by Gradle, lower is compatible with higher, prefers highest compatible. |
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:
Attribute name | Description | Values | compatibility and disambiguation rules |
---|---|---|---|
Indicates if the binary was built with debugging symbols |
Boolean |
N/A |
|
Indicates if the binary was built with optimization flags |
Boolean |
N/A |
|
Indicates the target architecture of the binary |
|
None |
|
Indicates the target operating system of the binary |
|
None |
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 compatibility and 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:
// An attribute of type `String`
def myAttribute = Attribute.of("my.attribute.name", String)
// An attribute of type `Usage`
def myUsage = Attribute.of("my.usage.attribute", Usage)
// An attribute of type `String`
val myAttribute = Attribute.of("my.attribute.name", String::class.java)
// An attribute of type `Usage`
val myUsage = Attribute.of("my.usage.attribute", Usage::class.java)
Currently, only attribute types of String
, or anything extending Named
is supported.
Attributes must be declared in the attribute schema found on the dependencies
handler:
dependencies.attributesSchema {
// registers this attribute to the attributes schema
attribute(myAttribute)
attribute(myUsage)
}
dependencies.attributesSchema {
// registers this attribute to the attributes schema
attribute(myAttribute)
attribute(myUsage)
}
Then configurations can be configured to set values for attributes:
configurations {
myConfiguration {
attributes {
attribute(myAttribute, 'my-value')
}
}
}
configurations {
create("myConfiguration") {
attributes {
attribute(myAttribute, "my-value")
}
}
}
For attributes which type extends Named
, the value of the attribute must be created via the object factory:
configurations {
myConfiguration {
attributes {
attribute(myUsage, project.objects.named(Usage, 'my-value'))
}
}
}
configurations {
"myConfiguration" {
attributes {
attribute(myUsage, project.objects.named(Usage::class.java, "my-value"))
}
}
}
Attribute compatibility rules
Attributes let the engine select compatible variants. However, there are cases where a provider may not have exactly what the consumer wants, but still something that it can use. For example, if the consumer is asking for the API of a library, there’s a possibility that the producer doesn’t have such a variant, but only a runtime variant. This is typical of libraries published on 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). To deal with this, Gradle provides attribute compatibility rules. The role of a compatibility rule is to explain what variants are compatible with what the consumer asked for.
Attribute compatibility rules have to be registered via the attribute matching strategy that you can obtain from the attributes schema.
Attribute disambiguation rules
Because multiple values for an attribute can be compatible with the requested attribute, Gradle needs to choose between the candidates. This is done by implementing an attribute disambiguation rule.
Attribute disambiguation rules have to be registered via the attribute matching strategy that you can obtain from the attributes schema.