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

:metadata-file-spec: https://github.com/gradle/gradle/blob/master/platforms/documentation/docs/src/docs/design/gradle-module-metadata-latest-specification.md

[[sec:understanding-gradle-module-md]]
= Understanding Gradle Module Metadata

Gradle Module Metadata is a format used to serialize the Gradle component model.
It is similar to https://maven.apache.org/pom.html[Apache Maven™'s POM file] or https://ant.apache.org/ivy/[Apache Ivy™ ivy.xml] files.
The goal of metadata files is to provide _to consumers_ a reasonable model of what is published on a repository.

Gradle Module Metadata is a unique format aimed at improving dependency resolution by making it multi-platform and variant-aware.

In particular, Gradle Module Metadata supports:

- <<rich_versions.adoc#rich-version-constraints,rich version constraints>>
- <<dependency_constraints.adoc#sec:adding-constraints-transitive-deps,dependency constraints>>
- <<component_capabilities.adoc#declaring-component-capabilities,component capabilities>>
- <<variant_model.adoc#understanding-variant-selection,variant-aware resolution>>

Publication of Gradle Module Metadata will enable better dependency management for your consumers:

- early discovery of problems by detecting <<component_capabilities.adoc#declaring-component-capabilities,incompatible modules>>
- consistent selection of <<cross_project_publications.adoc#targeting-different-platforms,platform-specific dependencies>>
- native <<dependency_version_alignment.adoc#version_alignment, dependency version alignment>>
- automatically getting dependencies for specific <<feature_variants.adoc#feature_variants,features of your library>>

Gradle Module Metadata is automatically published when using the <<publishing_maven.adoc#publishing_maven,Maven Publish plugin>> or the <<publishing_ivy.adoc#publishing_ivy,Ivy Publish plugin>>.

The specification for Gradle Module Metadata can be found {metadata-file-spec}[here].

[[sub:mapping-with-other-formats]]
== Mapping with other formats

Gradle Module Metadata is automatically published on Maven or Ivy repositories.
However, it doesn't replace the _pom.xml_ or _ivy.xml_ files: it is published alongside those files.
This is done to maximize compatibility with third-party build tools.

Gradle does its best to map Gradle-specific concepts to Maven or Ivy.
When a build file uses features that can only be represented in Gradle Module Metadata, Gradle will warn you at publication time.
The table below summarizes how some Gradle specific features are mapped to Maven and Ivy:

.Mapping of Gradle specific concepts to Maven and Ivy
|===
|Gradle|Maven|Ivy|Description

|<<dependency_constraints.adoc#sec:adding-constraints-transitive-deps,dependency constraints>>
|`<dependencyManagement>` dependencies
|Not published
|Gradle dependency constraints are _transitive_, while Maven's dependency management block _isn't_

|<<rich_versions.adoc#rich-version-constraints,rich version constraints>>
|Publishes the _requires_ version
|Published the _requires_ version
|

|<<component_capabilities.adoc#declaring-component-capabilities,component capabilities>>
|Not published
|Not published
|Component capabilities are unique to Gradle

|<<feature_variants.adoc#feature_variants,Feature variants>>
|Variant artifacts are uploaded, dependencies are published as _optional_ dependencies
|Variant artifacts are uploaded, dependencies are not published
|Feature variants are a good replacement for optional dependencies

|<<publishing_customization.adoc#sec:publishing-custom-components,Custom component types>>
|Artifacts are uploaded, dependencies are those described by the mapping
|Artifacts are uploaded, dependencies are ignored
|Custom component types are probably not consumable from Maven or Ivy in any case. They usually exist in the context of a custom ecosystem.

|===

=== Disabling metadata compatibility publication warnings

If you want to suppress warnings, you can use the following APIs to do so:

* For Maven, see the `suppress*` methods in link:{groovyDslPath}/org.gradle.api.publish.maven.MavenPublication.html#org.gradle.api.publish.maven.MavenPublication:suppressAllPomMetadataWarnings()[MavenPublication]
* For Ivy, see the `suppress*` methods in link:{groovyDslPath}/org.gradle.api.publish.ivy.IvyPublication.html#org.gradle.api.publish.ivy.IvyPublication:suppressAllIvyMetadataWarnings()[IvyPublication]


.Disabling publication warnings
====
include::sample[dir="snippets/dependencyManagement/modelingFeatures-outgoingCapabilities/kotlin",files="build.gradle.kts[tags=ignore-pom-warnings]"]
include::sample[dir="snippets/dependencyManagement/modelingFeatures-outgoingCapabilities/groovy",files="build.gradle[tags=ignore-pom-warnings]"]
====

[[sub:interactions-other-build-tools]]
== Interactions with other build tools

Because Gradle Module Metadata is not widely spread and because it aims at <<#sub:mapping-with-other-formats,maximizing compatibility with other tools>>, Gradle does a couple of things:

- Gradle Module Metadata is systematically published alongside the normal descriptor for a given repository (Maven or Ivy)
- the `pom.xml` or `ivy.xml` file will contain a _marker comment_ which tells Gradle that Gradle Module Metadata exists for this module

The goal of the marker is _not_ for other tools to parse module metadata: it's for Gradle users only.
It explains to Gradle that a _better_ module metadata file exists and that it should use it instead.
It doesn't mean that consumption from Maven or Ivy would be broken either, only that it works in <<#sub:mapping-with-other-formats,degraded mode>>.

NOTE: This must be seen as a _performance optimization_: instead of having to do 2 network requests, one to get Gradle Module Metadata, then one to get the POM/Ivy file in case of a miss, Gradle will first look at the file which is most likely to be present, then only perform a 2nd request if the module was actually published with Gradle Module Metadata.

If you know that the modules you depend on are always published with Gradle Module Metadata, you can optimize the network calls by configuring the metadata sources for a repository:

.Resolving Gradle Module Metadata only
====
include::sample[dir="snippets/dependencyManagement/modelingFeatures-crossProjectPublications-advanced-published/kotlin/producer",files="build.gradle.kts[tags=gradle_metadata_source]"]
include::sample[dir="snippets/dependencyManagement/modelingFeatures-crossProjectPublications-advanced-published/groovy/producer",files="build.gradle[tags=gradle_metadata_source]"]
====

[[sub:gmm-validation]]
== Gradle Module Metadata validation

Gradle Module Metadata is validated before being published.

The following rules are enforced:

* Variant names must be unique,
* Each variant must have at least <<variant_attributes.adoc#variant_attributes,one attribute>>,
* Two variants cannot have the <<variant_model.adoc#understanding-variant-selection,exact same attributes and capabilities>>,
* If there are dependencies, at least one, across all variants, must carry <<rich_versions.adoc#rich-version-constraints,version information>>.

These rules ensure the quality of the metadata produced, and help confirm that consumption will not be problematic.

[[sub:gmm-reproducible]]
== Gradle Module Metadata reproducibility

The task generating the module metadata files is currently never marked `UP-TO-DATE` by Gradle due to the way it is implemented.
However, if neither build inputs nor build scripts changed, the task result is effectively up-to-date: it always produces the same output.

If users desire to have a unique `module` file per build invocation, it is possible to link an identifier in the produced metadata to the build that created it.
Users can choose to enable this unique identifier in their `publication`:

.Configuring the build identifier of a publication
====
include::sample[dir="snippets/publishing/javaLibrary/kotlin",files="build.gradle.kts[tags=enable-build-id]"]
include::sample[dir="snippets/publishing/javaLibrary/groovy",files="build.gradle[tags=enable-build-id]"]
====

With the changes above, the generated Gradle Module Metadata file will always be different, forcing downstream tasks to consider it out-of-date.

[[sub:disabling-gmm-publication]]
== Disabling Gradle Module Metadata publication

There are situations where you might want to disable publication of Gradle Module Metadata:

- the repository you are uploading to rejects the metadata file (unknown format)
- you are using Maven or Ivy specific concepts which are not properly mapped to Gradle Module Metadata

In this case, disabling the publication of Gradle Module Metadata is done simply by disabling the task which generates the metadata file:

.Disabling publication of Gradle Module Metadata
====
include::sample[dir="snippets/dependencyManagement/modelingFeatures-crossProjectPublications-advanced-published/kotlin/producer",files="build.gradle.kts[tags=disable_gradle_metadata_publication]"]
include::sample[dir="snippets/dependencyManagement/modelingFeatures-crossProjectPublications-advanced-published/groovy/producer",files="build.gradle[tags=disable_gradle_metadata_publication]"]
====
