// Copyright 2018 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.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.

[[managing_transitive_dependencies]]
= Managing Transitive Dependencies

Resolution behavior for transitive dependencies can be customized to a high degree to meet enterprise requirements.

[[sec:dependency_constraints]]
== Managing versions of transitive dependencies with dependency constraints

Dependency constraints allow you to define the version or the version range of both dependencies declared in the build script and transitive dependencies. It is the preferred method to express constraints that should be applied to all dependencies of a configuration. When Gradle attempts to resolve a dependency to a module version, all <<declaring_dependencies.adoc#sub:declaring_dependency_with_version,dependency declarations with version>>, all transitive dependencies and all dependency constraints for that module are taken into consideration. The highest version that matches all conditions is selected. If no such version is found, Gradle fails with an error showing the conflicting declarations. If this happens you can adjust your dependencies or dependency constraints declarations, or <<#managing_transitive_dependencies,make other adjustments to the transitive dependencies>> if needed. Similar to dependency declarations, dependency constraint declarations are <<managing_dependency_configurations.adoc#managing_dependency_configurations,scoped by configurations>> and can therefore be selectively defined for parts of a build. If a dependency constraint influenced the resolution result, any type of <<customizing_dependency_resolution_behavior.adoc#sec:dependency_resolve_rules,dependency resolve rules>> may still be applied afterwards.

.Define dependency constraints
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/versionsWithConstraints/groovy",files="build.gradle[tags=dependency-constraints]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/versionsWithConstraints/kotlin",files="build.gradle.kts[tags=dependency-constraints]"]
====

In the example, all versions are omitted from the dependency declaration. Instead, the versions are defined in the constraints block. The version definition for `commons-codec:1.11` is only taken into account if `commons-codec` is brought in as transitive dependency, since `commons-codec` is not defined as dependency in the project. Otherwise, the constraint has no effect.

[NOTE]
====
Dependency constraints are not yet published, but that will be added in a future release.
This means that their use currently only targets builds that do not publish artifacts to maven or ivy repositories.
====

Dependency constraints themselves can also be added transitively.

[[sec:excluding_transitive_module_dependencies]]
== Excluding transitive module dependencies

Declared dependencies in a build script can pull in a lot of transitive dependencies. You might decide that you do not want a particular transitive dependency as part of the dependency graph for a good reason.

- The dependency is undesired due to licensing constraints.
- The dependency is not available in any of the declared repositories.
- The metadata for the dependency exists but the artifact does not.
- The metadata provides incorrect coordinates for a transitive dependency.

Transitive dependencies can be excluded on the level of a declared dependency or a configuration. Let's demonstrate both use cases. In the following two examples the build script declares a dependency on Log4J, a popular logging framework in the Java world. The metadata of the particular version of Log4J also defines transitive dependencies.

.Unresolved artifacts for transitive dependencies
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/unresolved/groovy",files="build.gradle[tags=unresolved-transitive-dependencies]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/unresolved/kotlin",files="build.gradle.kts[tags=unresolved-transitive-dependencies]"]
====

If resolved from Maven Central some of the transitive dependencies provide metadata but not the corresponding binary artifact. As a result any task requiring the binary files will fail e.g. a compilation task.

[listing]
----
> gradle -q compileJava

* What went wrong:
Could not resolve all files for configuration ':compileClasspath'.
> Could not find jms.jar (javax.jms:jms:1.1).
  Searched in the following locations:
      https://repo.maven.apache.org/maven2/javax/jms/jms/1.1/jms-1.1.jar
> Could not find jmxtools.jar (com.sun.jdmk:jmxtools:1.2.1).
  Searched in the following locations:
      https://repo.maven.apache.org/maven2/com/sun/jdmk/jmxtools/1.2.1/jmxtools-1.2.1.jar
> Could not find jmxri.jar (com.sun.jmx:jmxri:1.2.1).
  Searched in the following locations:
      https://repo.maven.apache.org/maven2/com/sun/jmx/jmxri/1.2.1/jmxri-1.2.1.jar
----

The situation can be fixed by adding a repository containing those dependencies. In the given example project, the source code does not actually use any of Log4J's functionality that require the JMS (e.g. `link:https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/net/JMSAppender.html[JMSAppender]`) or JMX libraries. It's safe to exclude them from the dependency declaration.

Exclusions need to spelled out as a key/value pair via the attributes `group` and/or `module`. For more information, refer to link:{javadocPath}/org/gradle/api/artifacts/ModuleDependency.html#exclude-java.util.Map-[ModuleDependency.exclude(java.util.Map)].

.Excluding transitive dependency for a particular dependency declaration
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/excludeForDependency/groovy",files="build.gradle[tags=exclude-transitive-dependencies]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/excludeForDependency/kotlin",files="build.gradle.kts[tags=exclude-transitive-dependencies]"]
====

You may find that other dependencies will want to pull in the same transitive dependency that misses the artifacts. Alternatively, you can exclude the transitive dependencies for a particular configuration by calling the method link:{groovyDslPath}/org.gradle.api.artifacts.Configuration.html#org.gradle.api.artifacts.Configuration:exclude(java.util.Map)[Configuration.exclude(java.util.Map)].

.Excluding transitive dependency for a particular configuration
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/excludeForConfiguration/groovy",files="build.gradle[tags=exclude-transitive-dependencies]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/excludeForConfiguration/kotlin",files="build.gradle.kts[tags=exclude-transitive-dependencies]"]
====

[NOTE]
====
As a build script author you often times know that you want to exclude a dependency for all configurations available in the project. You can use the method link:{javadocPath}/org/gradle/api/DomainObjectCollection.html#all-org.gradle.api.Action-[DomainObjectCollection.all(org.gradle.api.Action)] to define a global rule.
====

You might encounter other use cases that don't quite fit the bill of an exclude rule. For example you want to automatically select a version for a dependency with a specific requested version or you want to select a different group for a requested dependency to react to a relocation. Those use cases are better solved by the link:{groovyDslPath}/org.gradle.api.artifacts.ResolutionStrategy.html[ResolutionStrategy] API. Some of these use cases are covered in <<customizing_dependency_resolution_behavior.adoc#customizing_dependency_resolution_behavior,Customizing Dependency Resolution Behavior>>.

[[sec:enforcing_dependency_version]]
== Enforcing a particular dependency version

Gradle resolves any dependency version conflicts by selecting the latest version found in the dependency graph. Some projects might need to divert from the default behavior and enforce an earlier version of a dependency e.g. if the source code of the project depends on an older API of a dependency than some of the external libraries.

[NOTE]
====
Enforcing a version of a dependency requires a conscious decision. Changing the version of a transitive dependency might lead to runtime errors if external libraries do not properly function without them. Consider upgrading your source code to use a newer version of the library as an alternative approach.
====

Let's say a project uses the link:https://hc.apache.org/httpcomponents-client-ga/[HttpClient library] for performing HTTP calls. HttpClient pulls in link:https://commons.apache.org/proper/commons-codec/[Commons Codec] as transitive dependency with version 1.10. However, the production source code of the project requires an API from Commons Codec 1.9 which is not available in 1.10 anymore. A dependency version can be enforced by declaring it in the build script and setting link:{javadocPath}/org/gradle/api/artifacts/ExternalDependency.html#setForce-boolean-[ExternalDependency.setForce(boolean)] to `true`.

.Enforcing a dependency version
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/forceForDependency/groovy",files="build.gradle[tags=force-per-dependency]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/forceForDependency/kotlin",files="build.gradle.kts[tags=force-per-dependency]"]
====

If the project requires a specific version of a dependency on a configuration-level then it can be achieved by calling the method link:{groovyDslPath}/org.gradle.api.artifacts.ResolutionStrategy.html#org.gradle.api.artifacts.ResolutionStrategy:force(java.lang.Object++[]++)[ResolutionStrategy.force(java.lang.Object++[]++)].

.Enforcing a dependency version on the configuration-level
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/forceForConfiguration/groovy",files="build.gradle[tags=force-per-configuration]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/forceForConfiguration/kotlin",files="build.gradle.kts[tags=force-per-configuration]"]
====

[[sub:disabling_resolution_transitive_dependencies]]
== Disabling resolution of transitive dependencies

By default Gradle resolves all transitive dependencies specified by the dependency metadata. Sometimes this behavior may not be desirable e.g. if the metadata is incorrect or defines a large graph of transitive dependencies. You can tell Gradle to disable transitive dependency management for a dependency by setting link:{javadocPath}/org/gradle/api/artifacts/ModuleDependency.html#setTransitive-boolean-[ModuleDependency.setTransitive(boolean)] to `false`. As a result only the main artifact will be resolved for the declared dependency.

.Disabling transitive dependency resolution for a declared dependency
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/disableForDependency/groovy",files="build.gradle[tags=transitive-per-dependency]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/disableForDependency/kotlin",files="build.gradle.kts[tags=transitive-per-dependency]"]
====

[NOTE]
====
Disabling transitive dependency resolution will likely require you to declare the necessary runtime dependencies in your build script which otherwise would have been resolved automatically. Not doing so might lead to runtime classpath issues.
====

A project can decide to disable transitive dependency resolution completely. You either don't want to rely on the metadata published to the consumed repositories or you want to gain full control over the dependencies in your graph. For more information, see link:{javadocPath}/org/gradle/api/artifacts/Configuration.html#setTransitive-boolean-[Configuration.setTransitive(boolean)].

.Disabling transitive dependency resolution on the configuration-level
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/disableForConfiguration/groovy",files="build.gradle[tags=transitive-per-configuration]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/disableForConfiguration/kotlin",files="build.gradle.kts[tags=transitive-per-configuration]"]
====

[[sec:bom_import]]
== Importing version recommendations from a Maven BOM

Gradle provides support for importing https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Importing_Dependencies[bill of materials (BOM) files], which are effectively `.pom` files that use `<dependencyManagement>` to control the dependency versions of direct and transitive dependencies. The BOM support in Gradle works similar to using `<scope>import</scope>` when depending on a BOM in Maven. In Gradle however, it is done via a regular dependency declaration on the BOM:

.Depending on a BOM to import its dependency constraints
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/constraintsFromBOM/groovy",files="build.gradle[tags=dependency-on-bom]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/constraintsFromBOM/kotlin",files="build.gradle.kts[tags=dependency-on-bom]"]
====

In the example, the versions of `gson` and `dom4j` are provided by the Spring Boot BOM. This way, if you are developing for a platform like Spring Boot, you do not have to declare any versions yourself but can rely on the versions the platform provides.

Gradle treats all entries in the `<dependencyManagement>` block of a BOM similar to <<#sec:dependency_constraints,Gradle's dependency constraints>>. This means that any version defined in the `<dependencyManagement>` block can impact the dependency resolution result. In order to qualify as a BOM, a `.pom` file needs to have `<packaging>pom</packaging>` set.

However often BOMs are not only providing versions as recommendations, but also a way to override any other version found in the graph. You can enable this behavior by using the `enforcedPlatform` keyword, instead of `platform`, when importing the BOM:

.Importing a BOM, making sure the versions it defines override any other version found
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/enforcedConstraintsFromBOM/groovy",files="build.gradle[tags=dependency-on-bom]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/enforcedConstraintsFromBOM/kotlin",files="build.gradle.kts[tags=dependency-on-bom]"]
====

[[sec:version_alignment]]
== Dependency version alignment

Dependency version alignment allows different modules belonging to the same logical group (a _platform_) to have identical versions in a dependency graph.

=== Handling inconsistent module versions
Gradle supports aligning versions of modules which belong to the same "platform".
It is often preferable, for example, that the API and implementation modules of a component are using the same version.
However, because of the game of transitive dependency resolution, it is possible that different modules belonging to the same platform end up using different versions.
For example, your project may depend on the `jackson-databind` and `vert.x` libraries, as illustrated below:

.Declaring dependencies
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/dependencyAlignment/groovy",files="build.gradle[tags=dependencies]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/dependencyAlignment/kotlin",files="build.gradle.kts[tags=dependencies]"]
====

Because `vert.x` depends on `jackson-core`, we would actually resolve the following dependency versions:

- `jackson-core` version `2.9.5` (brought by `vertx-core`)
- `jackson-databind` version `2.9.5` (by conflict resolution)
- `jackson-annotation` version `2.9.0` (dependency of `jackson-databind:2.9.5`)

It's easy to end up with a set of versions which do not work well together.
To fix this, Gradle supports dependency version alignment, which is supported by the concept of platform. A platform represents a set of modules which "work well together".
Either because they are actually published as a whole (when one of the members of the platform is published, all other modules are also published with the same version), or because someone tested modules and indicates that they work well together (typically, the Spring Platform).

=== Aligning versions natively with Gradle

Gradle natively supports alignment of modules produced by Gradle.
This is a direct consequence of the transitivity of <<sec:dependency_constraints, dependency constraints>>.
So if you have a multi-project build, and that you wish that consumers get the same version of all your modules, Gradle provides a simple way to do this using the <<java_platform_plugin.adoc#,Java Platform Plugin>>.

For example, if you have a project that consists of 3 modules:

- `lib`
- `utils`
- `core`, depending on `lib` and `utils`

And a consumer that declares the following dependencies:

- `core` version 1.0
- `lib` version 1.1

then by default resolution would select `core:1.0` and `lib:1.1`, because `lib` has no dependency on `core`.
We can fix this by adding a new module in our project, a _platform_, that will add constraints on all the modules of your project:

.The platform module
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/dependencyAlignmentWithPlatform/groovy/platform",files="build.gradle[tags=platform]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/dependencyAlignmentWithPlatform/kotlin/platform",files="build.gradle.kts[tags=platform]"]
====

Once this is done, we need to make sure that all modules now _depend on the platform_, like this:

.Declaring a dependency on the platform
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/dependencyAlignmentWithPlatform/groovy/core",files="build.gradle[tags=dependencies]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/dependencyAlignmentWithPlatform/kotlin/core",files="build.gradle.kts[tags=dependencies]"]
====

It is important that the platform contains a constraint on all the components, but also that each component has a dependency on the platform.
By doing this, whenever Gradle will add a dependency to a module of the platform on the graph, it will _also_ include constraints on the other modules of the platform.
This means that if we see another module belonging to the same platform, we will automatically upgrade to the same version.

In our example, it means that we first see `core:1.0`, which brings a platform `1.0` with constraints on `lib:1.0` and `lib:1.0`.
Then we add `lib:1.1` which has a dependency on `platform:1.1`.
By conflict resolution, we select the `1.1` platform, which has a constraint on `core:1.1`.
Then we conflict resolve between `core:1.0` and `core:1.1`, which means that `core` and `lib` are now aligned properly.

NOTE: This behavior is enforced for published components only if you use Gradle Module Metadata.

=== Aligning versions of modules not published with Gradle

Whenever the publisher doesn't use Gradle, like in our Jackson example, we can explain to Gradle that that all Jackson modules "belong to" the same platform and benefit from the same behavior as with native alignment:

.A dependency version alignment rule
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/dependencyAlignment/groovy",files="build.gradle[tags=alignment-rule]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/dependencyAlignment/kotlin",files="build.gradle.kts[tags=alignment-rule]"]
====

By using the `belongsTo` keyword, we declare that all modules belong to the same _virtual platform_, which is treated specially by the engine, in particular with regards to alignment. We can use the rule we just created by registering it:

.Making use of a dependency version alignment rule
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/dependencyAlignment/groovy",files="build.gradle[tags=use_rule]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/dependencyAlignment/kotlin",files="build.gradle.kts[tags=use_rule]"]
====

Then all versions in the example above would align to `2.9.5`. However, Gradle would let you override that choice by specifying a dependency on the Jackson platform:

.Forceful platform downgrade
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/dependencyAlignment/groovy",files="build.gradle[tags=enforced_platform]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/dependencyAlignment/kotlin",files="build.gradle.kts[tags=enforced_platform]"]
====

[[sec:virtual_platform]]
=== Virtual vs published platforms

A platform defined by a component metadata rule for which the `belongsTo` target module isn't published on a repository is called a virtual platform.
A virtual platform is considered specially by the engine and participates in dependency resolution like a published module, but triggers dependency version alignment.
On the other hand, we can find "real" platforms published on public repositories. Typical examples include BOMs, like the Spring BOM. They differ in the sense that a published platform may refer to modules which are effectively different things.
For example the Spring BOM declares dependencies on Spring as well as Apache Groovy. Obviously those things are versioned differently, so it doesn't make sense to align in this case. In other words, if a platform is _published_, Gradle trusts its metadata, and will not try to align dependency versions of this platform.


[[sec:capabilities]]
== Component capabilities

=== Introduction to capabilities

Often a dependency graph would accidentally contain multiple implementations of the same API.
This is particularly common with logging frameworks, where multiple bindings are available, and that one library chooses a binding when another transitive dependency chooses another.
Because those implementations live at different GAV coordinates, the build tool has usually no way to find out that there's a conflict between those libraries.
To solve this, Gradle provides the concept of _capability_.

It's illegal to find two components providing the same _capability_ in a single dependency graph.
Intuitively, it means that if Gradle finds two components that provide the same thing on classpath, it's going to fail with an error indicating what modules are in conflict.
In our example, it means that different bindings of a logging framework provide the same capability.

=== Capability coordinates

A _capability_ is defined by a `(group, module, version)` triplet.
Each component defines an implicit capability corresponding to its GAV coordinates (group, artifact, version).
For example, the `org.apache.commons:commons-lang3:3.8` module has an implicit capability with group `org.apache.commons`, name `commons-lang3` and version `3.8`.
It is important to realize that capabilities are _versioned_.

=== Declaring component capabilities

[NOTE]
====
Capabilities are a core feature of the experimental Gradle metadata file format.
This means that components published with the experimental Gradle metadata file format can declare capabilities, but also that this feature is only natively understood by Gradle.
However, it's possible to declare capabilities on components which were _not_ built by Gradle, as explained in this section.
====

If your build file contains the following dependencies:

.A build file with an implicit conflict of logging frameworks
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/declaringCapabilities/groovy",files="build.gradle[tags=dependencies]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/declaringCapabilities/kotlin",files="build.gradle.kts[tags=dependencies]"]
====

As is, it's pretty hard to figure out that you will end up with two logging frameworks on the classpath.
In fact, `zookeeper` will bring in `log4j`, where what we want to use is `log4j-over-slf4j`.
We can preemptively detect the conflict by adding a rule which will declare that both logging frameworks provide the same capability:

.A build file with an implicit conflict of logging frameworks
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/declaringCapabilities/groovy",files="build.gradle[tags=declare_capability]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/declaringCapabilities/kotlin",files="build.gradle.kts[tags=declare_capability]"]
====

By adding this rule, we will make sure that Gradle _will_ detect conflicts and properly fail:

```
Cannot choose between log4j:log4j:1.2.16 and org.slf4j:log4j-over-slf4j:1.7.10 because they provide the same capability: org.slf4j:slf4j-capability:1.0
```

It does not, however, choose what component to use for you: detecting a conflict is the first step, then you have to fix it.

=== Solving capability conflicts

By default, Gradle will automatically choose the component with the highest version of a capability.
This can be useful whenever a component is relocated at different coordinates in a new release.
For example, the ASM library lived at `asm:asm` coordinates until version `3.3.1`, then changed to `org.ow2.asm:asm` since `4.0`.
It is illegal to have both ASM <=3.3.1 and 4.0+ on the classpath, because they provide the same feature, it's just that the component has been relocated.
Because each component has an implicit capability corresponding to its GAV coordinates, we can fix this by having a rule that will declare that the `asm:asm` module provides the `org.ow2.asm:asm` capability:

.Automatic conflict resolution by capability versioning
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/declaringCapabilities/groovy",files="build.gradle[tags=fix_asm]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/declaringCapabilities/kotlin",files="build.gradle.kts[tags=fix_asm]"]
====

However, fixing by automatic capability version conflict resolution is not always suitable.
In our logging example, it doesn't matter what version of the logging frameworks we use, we should always select the slf4j bridge.

In this case, we can fix it by using dependency substitution:

.Substitute log4j with slf4j
====
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/declaringCapabilities/groovy",files="build.gradle[tags=replacement_rule]"]
include::sample[dir="userguide/dependencyManagement/managingTransitiveDependencies/declaringCapabilities/kotlin",files="build.gradle.kts[tags=replacement_rule]"]
====
