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

[[capabilities]]
= Capabilities

In a dependency graph, it's common for multiple implementations of the same API to be accidentally included, especially with libraries like logging frameworks where different bindings are selected by various transitive dependencies.
[[sec:handling-mutually-exclusive-deps]]
Since these implementations typically reside at different group, artifact, and version (GAV) coordinates, build tools often can't detect the conflict.

To address this, Gradle introduces the concept of **capability**.

[[sec:capabilities]]
== Understanding capabilities

A capability is essentially a way to declare that different components (dependencies) offer the same functionality.

It’s **illegal** for Gradle to include more than one component providing the same capability in a single dependency graph.
If Gradle detects two components providing the same capability (e.g., different bindings for a logging framework), it will fail the build with an error, indicating the conflicting modules.
This ensures that conflicting implementations are resolved, avoiding issues on the classpath.

For instance, suppose you have dependencies on two different libraries for database connection pooling:

[source,kotlin]
----
dependencies {
    implementation("com.zaxxer:HikariCP:4.0.3")  // A popular connection pool
    implementation("org.apache.commons:commons-dbcp2:2.8.0")  // Another connection pool
}

configurations.all {
    resolutionStrategy.capabilitiesResolution.withCapability("database:connection-pool") {
        select("com.zaxxer:HikariCP")
    }
}
----

In this case, both `HikariCP` and `commons-dbcp2` provide the same functionality (connection pooling).
Gradle will fail if both are on the classpath.

Since only one should be used, Gradle’s resolution strategy allows you to *select* `HikariCP`, resolving the conflict.

[[sec:declaring-capabilities-coordinates]]
== Understanding capability coordinates

A **capability** is identified by a `(group, module, version)` triplet.

Every component defines an implicit capability based on its GAV coordinates: group, artifact, and version.

For instance, the `org.apache.commons:commons-lang3:3.8` module has an implicit capability with the group `org.apache.commons`, name `commons-lang3`, and version `3.8`:

[source,kotlin]
----
dependencies {
    implementation("org.apache.commons:commons-lang3:3.8")
}
----

It's important to note that capabilities are **versioned**.

[[sec:declaring-component-capabilities]]
== Declaring component capabilities

To detect conflicts early, it's useful to declare *component capabilities* through rules, allowing conflicts to be caught during the build instead of at runtime.

One common scenario is when a component is relocated to different coordinates in a newer release.

For example, the ASM library was published under `asm:asm` until version `3.3.1`, and then relocated to `org.ow2.asm:asm` starting with version `4.0.`
Including both versions on the classpath is illegal because they provide the same feature, under different coordinates.

Since each component has an implicit capability based on its GAV coordinates, we can address this conflict by using a rule that declares the `asm:asm` module as providing the `org.ow2.asm:asm` capability:

====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/kotlin",files="build.gradle.kts[tags=fix_asm]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/groovy",files="build.gradle[tags=fix_asm]"]
====

With this rule in place, the build will fail if both `asm:asm` ( < = `3.3.1`) and `org.ow2.asm:asm` (`4.0+`) are present in the dependency graph.

NOTE: Gradle won’t resolve the conflict automatically, but this helps you realize that the problem exists. It’s recommended to package such rules into plugins for use in builds, allowing users to decide which version to use or to fix the classpath conflict.

[[sec:declaring-capabilities-external-modules]]
== Declaring capabilities for external modules

Gradle allows you to declare capabilities not only for components you build but also for external components that don’t define them.

For example, consider the following dependencies in your build file:

====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/kotlin",files="build.gradle.kts[tags=dependencies]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/groovy",files="build.gradle[tags=dependencies]"]
====

As it stands, it's not obvious that this setup results in two logging frameworks on the classpath.
Specifically, `zookeeper` brings in `log4j`, but we want to use `log4j-over-slf4j`.

To proactively detect this conflict, we can define a rule stating that both frameworks provide the same capability:

====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/kotlin",files="build.gradle.kts[tags=declare_capability]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/groovy",files="build.gradle[tags=declare_capability]"]
====

This ensures that Gradle detects the conflict and fails with a clear error message:

====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/tests",files="dependencyReport.out"]
====

[[sec:declaring-additional-capabilities-for-a-local-component]]
== Declaring capabilities for a local component

Every component has an _implicit capability_ matching its GAV coordinates.
However, you can also declare additional explicit capabilities, which is useful when a library published under different GAV coordinates serves as an _alternate implementation_ of the same API:

====
include::sample[dir="snippets/dependencyManagement/modelingFeatures-outgoingCapabilities/kotlin",files="build.gradle.kts[tags=declare-outgoing-capabilities]"]
include::sample[dir="snippets/dependencyManagement/modelingFeatures-outgoingCapabilities/groovy",files="build.gradle[tags=declare-outgoing-capabilities]"]
====

Capabilities must be attached to _outgoing configurations_, which are <<declaring_configurations.adoc#sec:resolvable-consumable-configs,consumable configurations>> of a component.

In this example, we declare two capabilities:

1. `com.acme:my-library:1.0` - the _implicit capability_ of the library.
2. `com.other:module:1.1` -  an additional capability assigned to this library.

It's important to declare the implicit capability explicitly because once you define any _explicit_ capability, _all_ capabilities must be declared, including the _implicit_ one.

The second capability can either be specific to this library or match a capability provided by an external component.
If `com.other:module` appears elsewhere in the dependency graph, the build will fail, and consumers <<component_capabilities.adoc#sec:selecting-between-candidates,must choose which module to use>>.

Capabilities are published in Gradle Module Metadata but have no equivalent in POM or Ivy metadata files.
As a result, when publishing such a component, Gradle warns that this feature is only supported for Gradle consumers:

----
Maven publication 'maven' contains dependencies that cannot be represented in a published pom file.
  - Declares capability com.acme:my-library:1.0
  - Declares capability com.other:module:1.1
----

[[sec:selecting-between-candidates]]
== Selecting between candidates

At some point, a dependency graph is going to include either _incompatible modules_, or modules which are _mutually exclusive_.

For example, you may have different logger implementations, and you need to choose one binding.
<<component_capabilities.adoc#capabilities,Capabilities>> help _understand_ the conflict, then Gradle provides you with tools to solve the conflicts.

[[sec:selecting-preferred-capability-provider]]
=== Selecting between different capability candidates

In the relocation example above, Gradle was able to tell you that you have two versions of the same API on classpath: an "old" module and a "relocated" one.
We can solve the conflict by automatically choosing the component which has the highest capability version:

====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/kotlin",files="build.gradle.kts[tags=use_highest_asm]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/groovy",files="build.gradle[tags=use_highest_asm]"]
====

However, choosing the highest capability version conflict resolution is not always suitable.

For a logging framework, for example, it doesn't matter what version of the logging frameworks we use.
In this case, we explicitly select `slf4j` as the preferred option:

====
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/kotlin",files="build.gradle.kts[tags=use_slf4j]"]
include::sample[dir="snippets/dependencyManagement/managingTransitiveDependencies-declaringCapabilities/groovy",files="build.gradle[tags=use_slf4j]"]
====

This approach works also well if you have multiple `slf4j` bindings on the classpath; bindings are basically different logger implementations, and you need only one.
However, the selected implementation may depend on the configuration being resolved.

For instance, in testing environments, the lightweight `slf4j-simple` logging implementation might be sufficient, while in production, a more robust solution like `logback` may be preferable.

Resolution can only be made in favor of a module that is found in the dependency graph.
The `select` method accepts only a module from the current set of candidates.
If the desired module is not part of the conflict, you can choose not to resolve that particular conflict, effectively leaving it unresolved.
Another conflict in the graph may have the module you want to select.

If no resolution is provided for all conflicts on a given capability, the build will fail because the module chosen for resolution was not found in the graph.
Additionally, calling `select(null)` will result in an error and should be avoided.

For more information, refer to the link:{javadocPath}/org/gradle/api/artifacts/ResolutionStrategy.html#capabilitiesResolution[capabilities resolution API].
