Error resolving Gradle reference

I think the key is to choose the highest requested version.

The Gradle docs say “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?.

In the case of ConstraintLayout-Solver, the only requested version was 1.1.3, but EBuild arbitrarily bumped it to 2.0.2 (treating it like *). If it chooses the highest requested version, this would be 1.1.3 and would build successfully. In the case of AppCompat/Annotation, it should also use the highest requested version, which in this case would be 1.1.0.

At that point, if there is a versioning conflict then its up to the developer to decide which versions to use to avoid conflicts. But at least then the developer has control over it. The issue with ConstraintLayout-Solver was that it was a transitive dependency that was being versioned up, so I had no control over the versioning.

Let’s get the nomenclature straight here. there’s 4 ways to specify a version (ignoring beta suffixes):

  • *”— use the latest
  • "3.0: — use the latest, but at least 3.0 (ie fail if you cant get 3.0)
  • "3.0]: — use the latest version up to but no higher than 3.0 (this syntax is mostly useful for ranges (ie “2.0,3.0]”)
  • 3.0)” — use the latest version, excluding 3.0 or higher (e.g. 2.9.9 is fine).

The old/original behavior was that if the original package had a ) or ] (ie “foo:5.0]”), then the dependencies it would pull in got that same limitation, if they weren’t * (ie if foo depended on “bar:2.0” and baz:*, it would restrict “bar:2.0]” to match (to about getting `bar:2.1 that might be potentially tally incompatible.

July 14, 2018 this logic was disabled (iirc based on a bug report from you).

October 14, 2020 it got put back.

it seems we need a mixture of both, so the trick will be t figure out when dependencies should be strictly versioned (“]”), and when not (no “]”).

Can run get me a test case that shows both scenarios in one go, and ideally pointers to what you think should determine one behavior over the other?

that said, reviewing the code and your second test case now.

1 Like

If I’m understanding both EBuild and Gradle correctly, it seems that EBuild needs a fifth way of handling versions for transitive dependencies, which would be to use the highest requested version instead of using the latest version.

For example, say I add dependencies on libraries Foo and Bar. Foo has a dependency on Baz version 1.1.0 and Bar has a dependency on Baz version 1.2.0.

FooBaz:1.1.0
BarBaz:1.2.0

Say the latest Baz available is actually Baz:2.0.0. Currently EBuild would grab Baz:2.0.0 since it’s the highest available version. I’m suggesting that it should grab Baz:1.2.0 since this was the highest requested version for a transitive dependency.

That’s an important distinction from postfixing a “]”, since that (I beieve?) means it should fail if a higher version is requested. But if a higher version is requested, instead of failing it should just be bumped to the higher requested version.

So let’s say EBuild processes Foo first. Foo requests Baz:1.1.0, so now EBuild resolves to get Baz:1.1.0 (not Baz:2.0.0). Then EBuild processes Bar. Bar requests Baz:1.2.0 so EBuild should bump the Baz target to Baz:1.2.0.

If I add Baz as a full dependency to my project, then it would follow the standard rules (ie If I add Baz:1.2.0 then it would resolve to Baz:2.0.0 since I didn’t restrict the upper bound). But for transitive dependencies it seems that the highest requested version needs to be grabbed.

Here’s whats happening exactly:

                        Adding dependency 'com.google.android.gms:play-services-measurement-base:[17.4.3]' from 'com.google.android.gms:play-services-measurement:17.4.3'.
D:                      Package com.google.android.gms:play-services-measurement-base:17.4.3 found in local cache.

so we’re processing strict reference, that was specified as such in the drag file — no adjustments ,Ade by ebuild:

<dependency>
      <groupId>com.google.android.gms</groupId>
      <artifactId>play-services-measurement-base</artifactId>
      <version>[17.4.3]</version>
      <scope>compile</scope>
      <type>aar</type>
    </dependency>

and that one wants to add non-strict com.google.android.gms:play-services-measurement-base:17.4.3. Note because the originating ref was stricter, we get this:

D:                      Restricting dependency com.google.android.gms:play-services-basement from version 17.0.0 to 17.0.0] (because 'com.google.android.gms:play-services-measurement-base:[17.4.3]' was strict).

thats the logic I brought back in October to fix the bug reported here.

problem is, someone else already added a ref to 17.4.3. so now we’re saying “I want 17.0.0, and no higher!”, while this existing reference is saying "I want 17.4.3 (or later).

Note that even if I didn’t add the trictenign, it would still fail, because even “17.4.3 or higher” does n to qualify for “17.0.0, and no higher”.

This yields us a warning, but we’re not giving up quite yet.

W:                      Dependency com.google.android.gms:play-services-basement:17.0.0] has a version number mismatch with existing gradle reference 17.0.0]:com.google.android.gms:play-services-basement.

at this point, I do some extra tests to see if I can recover (but I need to call quits here for today and resume in the morning). suffice to say i do a check that seems wring, and if I remove it it builds past this, but removing said check feels wrong too, so I need to review it more…

that said, I’m willing to commit my change now and have you have a fo at the next build, to see what goes…

I dont see how that’s not bringing back the issue we fixed with the revert from October. We’ll grab the one requested, something else upgrades it, and you have the same problem again — a newer version is used that (might be) incompatible…

Foo says it wants Baz:1.2. we settle for that, and don’t get 2.0. But someone else might still request 2.0, and — without warning — you’re back to square one:

  • allowing then the guy to ask for 2.0 instead — potentially leads to previous behavior, though maybe at a lesser chance
  • not allowing the other guy to ask for 2.0 instead — current behavior

I can see and implement the middle ground (will be quite a bit of work) but I doubt it’ll solve the problem…

The difference is that if it’s using the highest requested version then I have control over it.

In that case it will cause a conflict, but I can resolve this by down-versioning the library requesting 2.0. So if Qux:3.5.0 is requesting Baz:2.0.0 then I can manually downgrade to Qux:3.4.0 (or whatever version I need to, to get back to when they were using a compatible Baz version.)

The issue I was having with ConstraintLayout is that I requested ConstraintLayout:[1.3.0] which requested ConstraintLayout-Solver:1.3.0 but this was internally bumped to 2.0.2, which failed. And there was no way that I could change the version since it was a transitive dependency.

Just saw this – that does seem like a different issue that “highest requested version” issue I was talking about. I’ll have to try this in Gradle to see exactly what happens, because I agree it seems like this one should fail.

Lemme se how the build started now works for you.

gotta sign off for the day, now.

1 Like

Same, thanks Marc!

1 Like

Looks good from my initial test. It correctly handled a build with both the ConstraintLayout:[1.1.3] and Firebase libraries added, which were the two causing separate problems.

1 Like

Cool. lets keep it as is then, and see how it goes, long term…

1 Like

I forgot that this was the initial problem I was trying to solve in this thread :upside_down_face: I have ConstraintLayout locked at 1.1.3 at the moment, but they made signficant performance improvements in 2.0.2, so I’d really like to update. I’m looking back to see if I can figure out what’s causing the “attribute ___ has already been defined” errors.

1 Like

IIrc that happens when conflicting android/androidx packages get referenced…

IIrc that happens when conflicting android/androidx packages get referenced…

Did you solve this problem ?
I have the same issue and searching how to resolve this problem (remove legacy package from reference )??

FWIW, none of the last test cases you sent me in our last thread showed this error. I did add (not shipping yet) some diagnostic code that, if apt fails, checks all XMLs files and reports where the duped come from, b ut since in diode;t have a test case for this partictuialr problem, I could not test that yet.