[[sec:component_metadata_rules]]
= Fixing metadata with component metadata rules

Each module that is pulled from a repository has metadata associated with it, such as its group, name, version as well as the different variants it provides with their artifacts and dependencies.
Sometimes, this metadata is incomplete or incorrect.
To manipulate such incomplete metadata from within the build script, Gradle offers an API to write _component metadata rules_.
These rules take effect after a module's metadata has been downloaded, but before it is used in dependency resolution.

== Basics of writing a component metadata rule

Component metadata rules are applied in the components (link:{groovyDslPath}/org.gradle.api.artifacts.dsl.ComponentMetadataHandler.html[ComponentMetadataHandler]) section of the dependencies block (link:{groovyDslPath}/org.gradle.api.artifacts.dsl.DependencyHandler.html[DependencyHandler]) of a build script.
The rules can be defined in two different ways:

1. As an action directly when they are applied in the _components_ section
2. As an isolated class implementing the link:{javadocPath}/org/gradle/api/artifacts/ComponentMetadataRule.html[ComponentMetadataRule] interface

While defining rules inline as action can be convenient for experimentation, it is generally recommended to define rules as separate classes.
Rules that are written as isolated classes can be annotated with `@CacheableRule` to cache the results of their application such that they do not need to be re-executed each time dependencies are resolved.

.Example of a configurable component metadata rule
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=config-component-metadata-rule]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=config-component-metadata-rule]"]
====

As can be seen in the examples above, component metadata rules are defined by implementing link:{javadocPath}/org/gradle/api/artifacts/ComponentMetadataRule.html[ComponentMetadataRule] which has a single `execute` method receiving an instance of link:{javadocPath}/org/gradle/api/artifacts/ComponentMetadataContext.html[ComponentMetadataContext] as parameter.
In this example, the rule is also further configured through an link:{javadocPath}/org/gradle/api/ActionConfiguration.html[ActionConfiguration].
This is supported by having a constructor in your implementation of `ComponentMetadataRule` accepting the parameters that were configured and the services that need injecting.

Gradle enforces isolation of instances of `ComponentMetadataRule`.
This means that all parameters must be `Serializable` or known Gradle types that can be isolated.

In addition, Gradle services can be injected into your `ComponentMetadataRule`.
Because of this, the moment you have a constructor, it must be annotated with `@javax.inject.Inject`.
A commonly required service is link:{javadocPath}/org/gradle/api/model/ObjectFactory.html[ObjectFactory] to create instances of strongly typed value objects like a value for setting an link:{javadocPath}/org/gradle/api/attributes/Attribute.html[Attribute].
A service which is helpful for advanced usage of component metadata rules with custom metadata is the link:{javadocPath}/org/gradle/api/artifacts/repositories/RepositoryResourceAccessor.html[RepositoryResourceAccessor].

A component metadata rule can be applied to all modules — `all(rule)` — or to a selected module — `withModule(groupAndName, rule)`.
Usually, a rule is specifically written to enrich metadata of one specific module and hence the `withModule` API should be preferred.

[[sec:component_metadata_rules_details]]
== Which parts of metadata can be modified?

The component metadata rules API is oriented at the features supported by link:https://github.com/gradle/gradle/blob/master/subprojects/docs/src/docs/design/gradle-module-metadata-latest-specification.md[Gradle Module Metadata] and the _dependencies_ API in build scripts.
The main difference between writing rules and defining dependencies and artifacts in the build script is that component metadata rules, following the structure of Gradle Module Metadata, operate on <<variant_model.adoc#understanding-variant-selection,variants>> directly.
On the contrary, in build scripts you often influence the shape of multiple variants at once (e.g. an _api_ dependency is added to the _api_ and _runtime_ variant of a Java library, the artifact produced by the _jar_ task is also added to these two variants).

Variants can be addressed for modification through the following methods:

- `allVariants`: modify all variants of a component
- `withVariant(name)`: modify a single variant identified by its name
- `addVariant(name)` or `addVariant(name, base)`: add a new variant to the component either _from scratch_ or by _copying_ the details of an existing variant (base)

The following details of each variant can be adjusted:

- The <<variant_model.adoc#sec:abm_configuration_attributes,attributes>> that identify the variant — `attributes {}` block
- The <<component_capabilities.adoc#declaring-component-capabilities,capabilities>> the variant provides — `withCapabilities { }` block
- The <<declaring_dependencies.adoc#,dependencies>> of the variant, including <<rich_versions.adoc#,rich versions>> — `withDependencies {}` block
- The <<dependency_constraints.adoc#sec:adding-constraints-transitive-deps,dependency constraints>> of the variant, including <<rich_versions.adoc#,rich versions>> — `withDependencyConstraints {}` block
- The location of the published files that make up the actual content of the variant — `withFiles { }` block

There are also a few properties of the whole component that can be changed:

- The _component level attributes_, currently the only meaningful attribute there is `org.gradle.status`
- The _status scheme_ to influence interpretation of the `org.gradle.status` attribute during version selection
- The _belongsTo_ property for <<component_capabilities.adoc#sec:declaring-capabilities-external-modules,version alignment through virtual platforms>>

Depending on the format of the metadata of a module, it is mapped differently to the variant-centric representation of the metadata:

- If the module has Gradle Module Metadata, the data structure the rule operates on is very similar to what you find in the module's `.module` file.
- If the module was published only with `.pom` metadata, a number of fixed variants is derived as explained in the <<variant_model.adoc#sub:maven-mapping-to-variants,mapping of POM files to variants>> section.
- If the module was published only with an `ivy.xml` file, the _Ivy configurations_ defined in the file can be accessed instead of variants.
  Their dependencies, dependency constraints and files can be modified.
  Additionally, the `addVariant(name, baseVariantOrConfiguration) { }` API can be used to derive variants from _Ivy configurations_ if desired (for example, <<variant_model.adoc#sub:ivy-mapping-to-variants,_compile_ and _runtime_ variants for the Java library plugin>> can be defined with this).

== When to use Component Metadata Rules? ==

In general, if you consider using component metadata rules to adjust the metadata of a certain module, you should check first if that module was published with Gradle Module Metadata (`.module` file) or traditional metadata only (`.pom` or `ivy.xml`).

If a module was published with Gradle Module Metadata, the metadata is likely complete although there can still be cases where something is just plainly wrong.
For these modules you should only use component metadata rules if you have clearly identified a problem with the metadata itself.
If you have an issue with the dependency resolution result, you should first check if you can solve the issue by declaring <<rich_versions.adoc#,dependency constraints with rich versions>>.
In particular, if you are developing a library that you publish, you should remember that dependency constraints, in contrast to component metadata rules, are published as part of the metadata of your own library.
So with dependency constraints, you automatically share the solution of dependency resolution issues with your consumers, while component metadata rules are only applied to your own build.

If a module was published with traditional metadata (`.pom` or `ivy.xml` only, no `.module` file) it is more likely that the metadata is incomplete as features such as variants or dependency constraints are not supported in these formats.
Still, conceptually such modules can contain different variants or might have dependency constraints they just omitted (or wrongly defined as dependencies).
In the next sections, we explore a number existing oss modules with such incomplete metadata and the rules for adding the missing metadata information.

As a rule of thumb, you should contemplate if the rule you are writing also works out of context of your build.
That is, does the rule still produce a correct and useful result if applied in any other build that uses the module(s) it affects?

== Fixing wrong dependency details ==

Lets consider as an example the publication of the Jaxen XPath Engine on link:https://repo1.maven.org/maven2/jaxen/jaxen[Maven central].
The pom of version 1.1.3 declares a number of dependencies in the compile scope which are not actually needed for compilation.
These have been removed in the 1.1.4 pom.
Assuming that we need to work with 1.1.3 for some reason, we can fix the metadata with the following rule:

.Rule to remove unused dependencies of Jaxen metadata
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=jaxen-rule-1]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=jaxen-rule-1]"]
====

Within the `withDependencies` block you have access to the full list of dependencies and can use all methods available on the Java collection interface to inspect and modify that list.
In addition, there are `add(notation, configureAction)` methods accepting the usual notations similar to <<declaring_dependencies.adoc#,declaring dependencies>> in the build script.
Dependency constraints can be inspected and modified the same way in the `withDependencyConstraints` block.

If we take a closer look at the Jaxen 1.1.4 pom, we observe that the _dom4j_, _jdom_ and _xerces_ dependencies are still there but marked as _optional_.
Optional dependencies in poms are not automatically processed by Gradle nor Maven.
The reason is that they indicate that there are <<feature_variants.adoc#,optional feature variants>> provided by the Jaxen library which require one or more of these dependencies, but the information what these features are and which dependency belongs to which is missing.
Such information cannot be represented in pom files, but in Gradle Module Metadata through variants and <<component_capabilities.adoc#declaring-component-capabilities,capabilities>>.
Hence, we can add this information in a rule as well.

.Rule to add optional feature to Jaxen metadata
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=jaxen-rule-2]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=jaxen-rule-2]"]
====

Here, we first use the `addVariant(name, baseVariant)` method to create an additional variant, which we identify as _feature variant_ by defining a new capability _jaxen-dom4j_ to represent the optional dom4j integration feature of Jaxen.
This works similar to <<feature_variants.adoc#sec::declare_feature_variants,defining optional feature variants>> in build scripts.
We then use one of the `add` methods for adding dependencies to define which dependencies this optional feature needs.

In the build script, we can then add a <<feature_variants.adoc#sec::consuming_feature_variants, dependency to the optional feature>> and Gradle will use the enriched metadata to discover the correct transitive dependencies.

.Applying and utilising rules for Jaxen metadata
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=jaxen-dependencies]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=jaxen-dependencies]"]
====

== Making variants published as classified jars explicit ==

While in the previous example, all variants, "main variants" and optional features, were packaged in one jar file, it is common to publish certain variants as separate files.
In particular, when the variants are mutual exclusive — i.e. they are *not* feature variants, but different variants offering alternative choices.
One example *all* pom-based libraries already have are the _runtime_ and _compile_ variants, where Gradle can choose only one depending on the task at hand.
Another of such alternatives discovered often in the Java ecosystems are jars targeting different Java versions.

As example, we look at version 0.7.9 of the asynchronous programming library Quasar published on link:https://repo1.maven.org/maven2/co/paralleluniverse/quasar-core/0.7.9[Maven central].
If we inspect the directory listing, we discover that a `quasar-core-0.7.9-jdk8.jar` was published, in addition to `quasar-core-0.7.9.jar`.
Publishing additional jars with a _classifier_ (here _jdk8_) is common practice in maven repositories.
And while both Maven and Gradle allow you to reference such jars by classifier, they are not mentioned at all in the metadata.
Thus, there is no information that these jars exist and if there are any other differences, like different dependencies, between the variants represented by such jars.

In Gradle Module Metadata, this variant information would be present and for the already published Quasar library, we can add it using the following rule:

.Rule to add Jdk 8 variants to Quasar metadata
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=quasar-rule]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=quasar-rule]"]
====

In this case, it is pretty clear that the classifier stands for a target Java version, which is a <<variant_model.adoc#sec:variant-aware-matching,known Java ecosystem attribute>>.
Because we also need both a _compile_ and _runtime_ for Java 8, we create two new variants but use the existing _compile_ and _runtime_ variants as _base_.
This way, all other Java ecosystem attributes are already set correctly and all dependencies are carried over.
Then we set the `TARGET_JVM_VERSION_ATTRIBUTE` to `8` for both variants, remove any existing file from the new variants with `removeAllFiles()`, and add the jdk8 jar file with `addFile()`.
The `removeAllFiles()` is needed, because the reference to the main jar `quasar-core-0.7.5.jar` is copied from the corresponding base variant.

We also enrich the existing _compile_ and _runtime_ variants with the information that they target Java 7 — `attribute(TARGET_JVM_VERSION_ATTRIBUTE, 7)`.

Now, we can request a Java 8 versions for all of our dependencies on the compile classpath in the build script and Gradle will automatically select the best fitting variant for each library.
In the case of Quasar this will now be the _jdk8Compile_ variant exposing the `quasar-core-0.7.9-jdk8.jar`.

.Applying and utilising rule for Quasar metadata
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=quasar-dependencies]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=quasar-dependencies]"]
====

== Making variants encoded in versions explicit  ==

Another solution to publish multiple alternatives for the same library is the usage of a a versioning pattern as done by the popular Guava library.
Here, each new version is published twice by appending the classifier to the version instead of the jar artifact.
In the case of Guava 28 for example, we can find a _28.0-jre_ (Java 8) and _28.0-android_ (Java 6) version on link:https://repo1.maven.org/maven2/com/google/guava/guava[Maven central].
The advantage of using this pattern when working only with pom metadata is that both variants are discoverable through the version.
The disadvantage is that there is no information what the different version suffixes mean semantically.
So in the case of conflict, Gradle would just pick the highest version when comparing the version strings.

Turning this into proper variants is a bit more tricky, as Gradle first selects a version of a module and then selects the best fitting variant.
So the concept that variants are encoded as versions is not supported directly.
However, since both variants are always published together we can assume that the files are physically located in the same repository.
And since they are published with Maven repository conventions, we know the location of each file if we know module name and version.
We can write the following rule:

.Rule to add Jdk 6 and Jdk 8 variants to Guava metadata
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=guava-rule]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=guava-rule]"]
====

Similar to the previous example, we add runtime and compile variants for both Java versions.
In the `withFile` block however, we now also specify a relative path for the corresponding jar file which allows Gradle to find the file no matter if it has selected a _-jre_ or _-android_ version.
The path is always relative to the location of the metadata (in this case `pom`) file of the selection module version.
So with this rules, both Guava 28 "versions" carry both the _jdk6_ and _jdk8_ variants.
So it does not matter to which one Gradle resolves.
The variant, and with it the correct jar file, is determined based on the requested `TARGET_JVM_VERSION_ATTRIBUTE` value.

.Applying and utilising rule for Guava metadata
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=guava-dependencies]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=guava-dependencies]"]
====

== Adding variants for native jars

Jars with classifiers are also used to separate parts of a library for which multiple alternatives exists, for example native code, from the main artifact.
This is for example done by the Lightweight Java Game Library (LWGJ), which publishes several platform specific jars to link:https://repo1.maven.org/maven2/org/lwjgl/lwjgl/3.2.3[Maven central] from which always one is needed, in addition to the main jar, at runtime.
It is not possible to convey this information in pom metadata as there is no concept of putting multiple artifacts in relation through the metadata.
In Gradle Module Metadata, each variant can have arbitrary many files and we can leverage that by writing the following rule:

.Rule to add native runtime variants to LWGJ metadata
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=lwgj-rule]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=lwgj-rule]"]
====

This rule is quite similar to the Quasar library example above.
Only this time we have five different runtime variants we add and nothing we need to change for the compile variant.
The runtime variants are all based on the existing _runtime_ variant and we do not change any existing information.
All Java ecosystem attributes, the dependencies and the main jar file stay part of each of the runtime variants.
We only set the additional attributes `OPERATING_SYSTEM_ATTRIBUTE` and `ARCHITECTURE_ATTRIBUTE` which are defined as part of Gradle's <<building_cpp_projects.adoc#,native support>>.
And we add the corresponding native jar file so that each runtime variant now carries two files: tha main jar and the native jar.

In the build script, we can now request a specific variant and Gradle will fail with a selection error if more information is needed to make a decision.

.Applying and utilising rule for LWGJ metadata
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=lwgj-dependencies]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=lwgj-dependencies]"]
====

.Gradle fails to select a variant because a machine architecture needs to be chosen
----
> Could not resolve all files for configuration ':runtimeClasspath'.
   > Could not resolve org.lwjgl:lwjgl:3.2.3.
     Required by:
         project :
      > Cannot choose between the following variants of org.lwjgl:lwjgl:3.2.3:
          - natives-windows-runtime
          - natives-windows-x86-runtime
----

== Making different flavors of a library available through capabilities

Because it is difficult to model <<feature_variants.adoc#,optional feature variants>> as separate jars with pom metadata, libraries sometimes compose different jars with a different feature set.
That is, instead of composing your flavor of the library from different feature variants, you select one of the pre-composed variants (offering everything in one jar).
One such library is the well-known dependency injection framework Guice, published on link:https://repo1.maven.org/maven2/com/google/inject/guice/4.2.2[Maven central], which offers a complete flavor (the main jar) and a reduced variant without aspect-oriented programming support (`guice-4.2.2-no_aop.jar`).
That second variant with a classifier is not mentioned in the pom metadata.
With the following rule, we create compile and runtime variants based on that file and make it selectable through a capability named `com.google.inject:guice-no_aop`.

.Rule to add no_aop feature variant to Guice metadata
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=guice-rule]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=guice-rule]"]
====

The new variants also have the dependency on the standardized aop interfaces library `aopalliance:aopalliance` removed, as this is clearly not needed by these variants.
Again, this is information that can not be expressed in pom metadata.
We can now select a `guice-no_aop` variant and will get the correct jar file *and* the correct dependencies.

.Applying and utilising rule for Guice metadata
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=guice-dependencies]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=guice-dependencies]"]
====

== Adding missing capabilities to detect conflicts

Another usage of capabilities is to express that two different modules, for example `log4j` and `log4j-over-slf4j`, provide alternative implementations of the same thing.
By declaring that both provide the same capability, Gradle only accepts one of them in a dependency graph.
This example, and how it can be tackled with a component metadata rule, is described in detail in the  <<component_capabilities.adoc#sec:declaring-capabilities-external-modules,feature modelling>> section.

== Making Ivy modules variant-aware ==

Modules with Ivy metadata, do not have variants by default.
However, _Ivy configurations_ can be mapped to variants as the `addVariant(name, baseVariantOrConfiguration)` accepts any Ivy configuration that was published as base.
This can be used, for example, to define runtime and compile variants.
An example of a corresponding rule can be found <<variant_model.adoc#sub:ivy-mapping-to-variants,here>>.
Ivy details of Ivy configurations (e.g. dependencies and files) can also be modified using the `withVariant(configurationName)` API.
However, modifying attributes or capabilities on Ivy configurations has no effect.

For very Ivy specific use cases, the component metadata rules API also offers access to other details only found in Ivy metadata.
These are available through the link:{javadocPath}/org/gradle/api/artifacts/ivy/IvyModuleDescriptor.html[IvyModuleDescriptor] interface and can be accessed using `getDescriptor(IvyModuleDescriptor)` on the link:{javadocPath}/org/gradle/api/artifacts/ComponentMetadataContext.html[ComponentMetadataContext].
(Note that for other types of metadata this method returns null.)

.Ivy component metadata rule
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=ivy-component-metadata-rule]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=ivy-component-metadata-rule]"]
====

== Modifying metadata on the component level for alignment

While all the examples above made modifications to variants of a component, there is also a limited set of modifications that can be done to the metadata of the component itself.
This information can influence the <<dependency_resolution.adoc#,version selection>> process for a module during dependency resolution, which is performed _before_ one or multiple variants of a component are selected.

The first API available on the component is `belongsTo()` to create virtual platforms for aligning versions of multiple modules without Gradle Module Metadata.
It is explained in detail in the section on <<dependency_version_alignment.adoc#sec:align-versions-unpublished,aligning versions of modules not published with Gradle>>.

[[sec:custom_status_scheme]]
== Modifying metadata on the component level for version selection based on status

Gradle and Gradle Module Metadata also allow attributes to be set on the whole component instead of a single variant.
Each of these attributes carries special semantics as they influence version selection which is done _before_ variant selection.
While variant selection can handle <<variant_attributes.adoc#sec:declaring_attributes,any custom attribute>>, version selection only considers attributes for which specific semantics are implemented.
At the moment, the only attribute with meaning here is `org.gradle.status`.
It is therefore recommended to only modify this attribute, if any, on the component level.
A dedicated API `setStatus(value)` is available for this.
To modify another attribute for all variants of a component `withAllVariants { attributes {} }` should be utilised instead.

A module's status is taken into consideration when a <<single_versions.adoc#,_latest_ version selector>> is resolved.
Specifically, `latest.someStatus` will resolve to the highest module version that has status `someStatus` or a more mature status.
For example, `latest.integration` will select the highest module version regardless of its status (because `integration` is the least mature status as explained below), whereas `latest.release` will select the highest module version with status `release`.

The interpretation of the status can be influenced by changing a module's _status scheme_ through the `setStatusScheme(valueList)` API.
This concept models the different levels of maturity that a module transitions through over time with different publications.
The default status scheme, ordered from least to most mature status, is `integration`, `milestone`, `release`.
The `org.gradle.status` attribute must be set, to one of the values in the components status scheme.
Thus each component always has a status which is determined from the metadata as follows:

- Gradle Module Metadata: the value that was published for the `org.gradle.status` attribute on the component
- Ivy metadata: `status` defined in the ivy.xml, defaults to `integration` if missing
- Pom metadata: `integration` for modules with a SNAPSHOT version, `release` for all others

The following example demonstrates `latest` selectors based on a custom status scheme declared in a component metadata rule that applies to all modules:

.Custom status scheme
====
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/groovy",files="build.gradle[tags=custom-status-scheme]"]
include::sample[dir="snippets/userguide/dependencyManagement/customizingResolution/metadataRule/kotlin",files="build.gradle.kts[tags=custom-status-scheme]"]
====

Compared to the default scheme, the rule inserts a new status `rc` and replaces `integration` with `nightly`.
Existing modules with the state `integration` are mapped to `nightly`.


