Help me understand Gradle's conflict resolution

For starters I should say that I’m coming from the Maven world, so I carry that baggage with me. I have a project that is dependent on Spring and I’m trying to figure out why Gradle is resolving “conflicts” in transitive dependencies.

For example, the ‘spring-beans’ library is referenced from multiple locations. In one it is referenced at ‘3.2.7.RELEASE’ and in another it is referenced in a range from ‘3.0.7.RELEASE’ to ‘4.0.0.RELEASE’. In Maven we end up using ‘3.2.7.RELEASE’ since it is explicitly requested and it fits in the specified range. Gradle however resolves the “conflict” differently.

If you are using ‘mavenCentral()’ as your repository then Gradle selects ‘3.2.9.RELEASE’ and if you are using ‘jcenter()’ it selects ‘4.0.0.M3’. This result surprised me not only because it was selecting different versions, but also in one case it was selecting a milestone build in favor of a release build. Looking at the selected versions however we see that in each case they turn out to be the most recently updated version in each repository. So this must be what is meant by “newest version”. I can see that this is literally true, but it still doesn’t match the expected semantics of version numbers. Gradle must have some understanding of these rules (since it doesn’t select a version outside the specified range), but it doesn’t apply them in selecting the version.

I’d like to understand why it works this way. The behavior I’d like to see is that it just uses the specified version (since it falls in the accepted range), but if it insists on selecting a new one I’d really like it to not be dependent on which central repository I choose. I’d like to use jcenter, but I can’t because I really don’t want to use some random milestone build (you could argue that the fault here lies with SpringSource for publishing those, but…). Anyway, I’m really just trying to understand the reasoning here. I know that I can force specific versions (and will if necessary), but that too isn’t ideal (I don’t like having knowledge of second level dependencies).

It’s actually a bit messier than I thought. Since I’m using ‘mavenLocal()’ in my list of repositories all it takes is for some other project to download different versions into my local maven cache and now it wants to switch to them. Since sometimes these versions have only poms and no jars (this is the case for the milestone builds) I end up with build errors.

I’m really hoping that I’m misunderstanding something here because if this continues I’m going to have to revert back to using Maven (which isn’t my preference).

The default conflict resolution strategy is “highest”. For how to force a version, see the user guide and the samples in the full Gradle distribution. 4.0.0.M3 is a release version from a Maven perspective. It’s entirely possible that different repos contain different (highest) versions. To avoid the Maven Local pollution issues, don’t use Maven Local from Gradle builds. Recent Gradle versions should be able to deal with modules in Maven Local that only have POMs (i.e. search for artifacts should continue in subsequent repos).