gitnewb
gitnewb

Reputation: 13

Are sub-dependencies shared in Gradle?

If library L is a dependency of both X and Y projects, and your MainModule has:

implementation X
implementation Y

Is the same L shared between X and Y? What if X needs a different version of L than Y?

Upvotes: 1

Views: 113

Answers (1)

jannis
jannis

Reputation: 5230

It's neatly described in Gradle documentation: TL;DR In case of a version conflict the default is to select the dependency with the highest version. But note that version conflict resolution can be customized.

Version conflict resolution

A version conflict occurs when two components:

  • Depend on the same module, let’s say com.google.guava:guava

  • But on different versions, let’s say 20.0 and 25.1-android

    • Our project itself depends on com.google.guava:guava:20.0

    • Our project also depends on com.google.inject:guice:4.2.2 which itself depends on com.google.guava:guava:25.1-android

Resolution strategy

Given the conflict above, there exist multiple ways to handle it, either by selecting a version or failing the resolution. Different tools that handle dependency management have different ways of handling these type of conflicts.

Apache Maven uses a nearest first strategy.

Maven will take the shortest path to a dependency and use that version. In case there are multiple paths of the same length, the first one wins.

This means that in the example above, the version of guava will be 20.0 because the direct dependency is closer than the guice dependency.

The main drawback of this method is that it is ordering dependent. Keeping order in a very large graph can be a challenge. For example, what if the new version of a dependency ends up having its own dependency declarations in a different order than the previous version?

With Maven, this could have unwanted impact on resolved versions.

Apache Ivy is a very flexible dependency management tooling. It offers the possibility to customize dependency resolution, including conflict resolution.

This flexibility comes with the price of making it hard to reason about.

Gradle will consider all requested versions, wherever they appear in the dependency graph. Out of these versions, it will select the highest one.

As you have seen, Gradle supports a concept of rich version declaration, so what is the highest version depends on the way versions were declared:

  • If no ranges are involved, then the highest version that is not rejected will be selected.

    • If a version declared as strictly is lower than that version, selection will fail.
  • If ranges are involved:

    • If there is a non range version that falls within the specified ranges or is higher than their upper bound, it will be selected.

    • If there are only ranges, the highest existing version of the range with the highest upper bound will be selected.

    • If a version declared as strictly is lower than that version, selection will fail.

Note that in the case where ranges come into play, Gradle requires metadata to determine which versions do exist for the considered range. This causes an intermediate lookup for metadata, as described in How Gradle retrieves dependency metadata?.

-- https://docs.gradle.org/current/userguide/dependency_resolution.html#sec:version-conflict

Upvotes: 1

Related Questions