How do I set an upper bound on Gradle dynamic versions? - gradle

I can't find any explicit documentation on Gradle dynamic version syntax -- the examples in the official docs are 1.+ and 2.+, neither of which appears to have an upper bound.
Say I have 1.0-SNAPSHOT and 2.0-SNAPSHOT in my repository, and I want a certain project to pull in the first or any future stable 1.x, but not the second.
I've tried both Maven syntax ([1.0,2.0)) and Ivy syntax ([1.0,2.0[). Both of these pull in 2.0-SNAPSHOT. Why? Is 2.0-SNAPSHOT considered "less than" 2.0?
On that assumption, I tried the obvious hacks: [1.0,2.0-SNAPSHOT) and [1.0,2.0-SNAPSHOT[, but both of those just fail dependency resolution.
How can I tell Gradle I only want version 1.x?

Looks like the answer is that + includes an implicit upper bound. So 1.+ means "any version that starts with 1."
This doesn't seem to be anywhere in the Gradle docs, but it is documented for Ivy:
end the revision with a +
selects the latest sub-revision of the dependency module. For instance,
if the dependency module exists in revision 1.0.3, 1.0.7 and 1.1.2, "1.0.+" will select 1.0.7.

Related

Gradle dependency library updated by another library

My gradle has for some time had a dependency on the (amazing) Android library Picasso. It has always been set to version 2.5.2
implementation 'com.squareup.picasso:picasso:2.5.2'
I recently updated all my Firebase libraries from a fairly old version to the latest. At which point something odd happened.
My Picasso method calls began to error
Picasso.with(context)
Which I know from this SO article results from a change to Picasso.
cannot find symbol method with() using picasso library android and I need to change to
Picasso.get()
OK not a big deal, but it got me wondering. Obviously Firebase uses the latest version of Picasso and is making my project use the latest version as well. My question is why is my local gradle file ignored and the newer version of Picasso defaulted to?
Off the top of my head: Since you declare a specific version that requirement is not flexible. To allow for a newer version if available a + declaration is required. My guess is that another dependency is also dependent on Picasso after the updates. Gradle, when given a redundant dependency, will select the newer version.
This is in alignment with what you said, if I understand correctly. If Firebase uses a newer Picasso version, because it requires that version, then Gradle is given two versions to choose one from. This will always result in the newer version being chosen. At least this is default behavior afaik.
It seems to me that you already know Picasso is used by Firebase. If you want to see where which dependency comes from however, you can look into build scans:
gradle build --scan
https://scans.gradle.com/?_ga=2.166196030.1236003146.1565212874-222812074.1565212874
A little bit more advanced dependency management:
1) Set Gradle behavior on dependency conflict:
https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.ResolutionStrategy.html
2) Declare version constraints (see Rich version declaration):
https://docs.gradle.org/current/userguide/declaring_dependencies.html
You can check the official doc:
Gradle resolves version conflicts by picking the highest version of a module. Build scans and the dependency insight report are immensely helpful in identifying why a specific version was selected.

How to compile against lower bound in Maven dependency version range

Is there a way, in Maven, to declare a dependency version range and have it resolve against the lower bound for the compile phase of the build?
eg. I declare a dependency using version range [1.2.0,1.999.999]. I would like for the compile phase to use version 1.2.0, specifically, but for the deployed POM to still show my compatible version range as [1.2.0,1.999.999].
My project is a library. For a non-library project I would just pin a specific version.
I see your point, but I am not sure this is the right idea.
First of all, version ranges are not very popular nowadays. People tend to avoid them because the build is not reproducible. AFAIK, they are not really deprecated, though.
Using version ranges to show compatibility is unexpected. Maybe a comment in the POM would be better.

Version ranges in gradle

What are the possible ways of specifying version ranges in gradle dependencies? I saw some 1.+ notation but I have not found a document which really says what is possible and what is not. Furthermore, I do not know whether the Maven ranges can be used as well.
Can somebody give me a short overview so that I can understand the rules?
The book "Gradle Dependency Management" states on p. 12 and 13 that, in addition to the +-notation (2.1.+ means the range from 2.1.0 inclusive to 2.2.0 exclusive) you can use the Ivy notation for open and closed intervals of the form
[1.0,2.0]
[1.0,2.0[
or also
[1.0, )
for "all versions starting from 1.0".
Preferred alternative
Specify the version range using Ivy notation. Here are some examples copied from this web page:
[1.0, 2.0]: all versions >= 1.0 and <= 2.0
[1.0, 2.0[: all versions >= 1.0 and < 2.0
[1.0, ) : all versions >= 1.0 // avoid. Unbound is dangerous!
Troublesome alternative
Use '+' in the major, minor or patch number. This approach has at least two issues:
If you're building a lib and generating a pom file, the pom will be incompatible with maven, unless you apply some workaround to resolve the version and prevent the pom dependency to use '+' in the version element. See this Gradle github issue.
The meaning of '+' is prone to confusion. Well, maybe not, but ask around to see if all your peers know exactly the difference between 1.1.+ and 1.1+ in a gradle dependency.
Ideal alternative
Avoid dynamic dependencies (using '+' or version ranges) altogether. Instead, use a fixed version dependency and update the version often with good testing. Here's why:
In the old days, backwards compatibility was sacred. That's not true anymore. New versions often move/remove/rename classes and functions.
If your dependency is dynamic (especially with '+' or unbound range), the next build may pick a new version that is incompatible with your project. The incompatibility may be detected only at rutime.
Version X of your library as built today might be different from version X of your library built tomorrow if one its dependencies is dynamic and a new version is released overnight. This level of uncertainty is not desirable for libraries.

Why did a specific jar file get included?

I have a project that uses gradle and mavenCentral() (plus mavenLocal()). It has enough dependencies that I can't go through them one by one.
Given the name of a .jar file in build/install/x/lib, how do I find out the chain of transitive dependencies that caused it to be included?
update: I discovered gradle dependencies. The output shows:
org.apache.commons:commons-jexl:2.1.1
\---- commons-logging:commons-logging:1.1.1 -> 1.1.3
What does this mean? 1.1.1 is the version I expect, and 1.1.3 is the version I seem to actually end up using. Looking at the pom for commons-jexl it looks like it does indeed list logging:1.1.1 as a requirement. What's going on? Is there a way for me to tell it to avoid certain versions, or force it to use the version it was set to?
The problem in my case is that it's including a -SNAPSHOT version and I'd rather it didn't. In fact I probably want it to just use the version numbers I'm asking for instead of the most recent it can find.
Dependencies of gradle-managed project have their own dependencies (they're called transitive). It may happen (and happens quite often) that two different dependencies has the same (group and module) dependency but in the different version). This is the case with commons-logging:commons-logging. In this case there are two transitive dependencies one versioned with 1.1.1 and the second one with 1.1.3. If both of the libraries will be included in the final artifact it may result in a conflict and exception. To prevent such situation gradle tries to resolve mentioned version resolution problems by picking (by default) the latest version. It's indicated with the right arrow -> see here. You can exclude transitive dependencies from a particular dependency. This chapter of manual might be useful.

How does gradle evaluate dynamic dependencies?

The Gradle documentation is very sparse on how dynamic dependencies are resolved.
There are two styles of dynamic dependency declaration: lib:20.+ and lib:20.0+.
Are they equivalent?
Do 20.1 , 21 and 20.0.1 match these declarations?
Typically, I want to get fixes (x.y versions with x fixed) automatically and manually update to the next major version which can include breaking changes.
I have finally found an answer, in the Ivy documentation of all places :
Revision Matches
1.0.+ all revisions starting with '1.0.', like 1.0.1, 1.0.5, 1.0.a
1.1+ all revisions starting with '1.1', like 1.1, 1.1.5, but also 1.10, 1.11
source : http://ant.apache.org/ivy/history/latest-milestone/settings/version-matchers.html

Resources