i have a gradle dependency that has been maven rel...
# community-support
j
i have a gradle dependency that has been maven relocated to a new group id, and the new group id also provides the old group as a capability. depending on the old group id (and that group id only) gives the following error, even though only one dependency is declared:
Copy code
% ./gradlew run
> Task :compileJava FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileJava'.
> Could not resolve all files for configuration ':compileClasspath'.
   > Could not resolve org.lz4:lz4-java:1.8.1.
     Required by:
         root project :
      > Module 'org.lz4:lz4-java' has been rejected:
           Cannot select module with conflict on capability 'org.lz4:lz4-java:1.8.1' also provided by [at.yawk.lz4:lz4-java:1.8.1(apiElements)]
   > Could not resolve at.yawk.lz4:lz4-java:1.8.1.
     Required by:
         root project : > org.lz4:lz4-java:1.8.1
      > Module 'at.yawk.lz4:lz4-java' has been rejected:
           Cannot select module with conflict on capability 'org.lz4:lz4-java:1.8.1' also provided by [org.lz4:lz4-java:1.8.1(compile)]
poc: https://github.com/yawkat/test-case-gradle-rename-capability is this a bug or is the metadata not supposed to be used this way?
v
Looks fine to me from a cursory look. Especially if the packages inside the artifact did not change. The documentation even encourages to use capabilities like this at https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven:relocation_duplicate_dependencies. So I have a strong feeling that this indeed is a bug.
t
The docs encourages using capabilities to detect possible conflicts, which means error out with a conflict at resolution time, rather than possibly have two artifacts with duplicate (and possibly different) classes at compile-time or runtime. My understanding is that you still need to resolve the conflict. In this specific case, it should be enough to replace the dependency with the new coordinates. In other cases (you depend on the new coordinates, but you also have a transitive dependency on an older version at the old coordinates, before the relocation) you'll want to use a capability resolution rule to tell Gradle to use the new artifact, at the new coordinates. https://docs.gradle.org/current/userguide/component_capabilities.html#sec:selecting-between-candidates So to me it looks like the expected behavior. Could Gradle do better when the conflict arises specifically with the relocated artifact? Sure, but that's more a request for enhancement than a bug (you're expected to update the coordinates when you update the version, not to use the relocation)
j
i think it makes no sense to get a conflict when you declare a single dependency. a relocated dependency should behave like the new dependency, not like having the old and the new.
also i dont think theres anything i could do on the publishing side here. the relocation has to exist, and the new artifact needs the gradle capability so that gradle properly detects the case where both org.lz4lz4 java1.7.0 and at.yawk.lz4lz4 java1.8.1 are on the classpath and only selects the latter one. and if i couldnt have done anything on the publishing side, that means that org.lz4lz4 java1.8.1 is essentially unusable with gradle. sure people can just use the new group id, but that kind of defeats the purpose of a relocation.
v
@Thomas Broyer there is no conflict
If one would have a dependency on an old version under old coordinates and new version under new coordinates I agree
But the sole dependency (added it to my play project) was to the relocated version of the old coordinates.
Gradle automatically follows the relocation and uses the artifact from the new coordinates, but then fails due to the capability confilct. And that does not sound sensible to me.
so that gradle properly detects the case where both org.lz4lz4 java1.7.0 and at.yawk.lz4lz4 java1.8.1 are on the classpath and only selects the latter one.
This will not work automatically. In that case the will get a capability conflict and define how to resolve it. Gradle can for example now know whether the new coordinates start with v1 again and always the new coordinates are used or whether the versions continue to count and then new version should be used. So in that situation the user indeed has to define how to resolve the conflict. But by only depending on the relocated artifact, that does imho not make sense.
šŸ‘ 1
t
Depends what one thinks the purpose of the relocation is. My opinion: you should never directly use (depend on) it. It's there so people (browsing Central) and tools (think dependabot/renovatebot, or the various Gradle plugins assisting you in keeping your dependencies up-to-date) know / can discover there's a new version, but that new version lives at different coordinates, so when you update, you change both the version and the coordinates. IIRC, Maven warns when you depend on a relocation, telling you to update the coordinates.
AFAICT, on the publishing side, one alternative would be to use dependency constraints in the new artifact, updating the old one to the relocated version, instead of using the capabilities. I.e. at.yawk.lz4lz4 java1.8.1 has a dependency constraint on org.lz4lz4 java1.8.1, so if you have both org.lz4lz4 java1.7.0 and at.yawk.lz4lz4 java1.8.1, then org.lz4lz4 java1.7.0 gets automatically upgraded to 1.8.1, which relocates to the new coordinates so you only have a single JAR in your classpath in the end.
j
hmm well even if i add dependency constraints to new versions of at.yawk.lz4, i guess it will still break since it'll pull in the 1.8.1 with the capabilities, which i cant fix anymore
t
That's why I said "instead of using the capabilities", and yes in your case it's too late (unless you publish another relocation for a newer version, where the relocated version uses dependency constraints; not sure I'd recommend doing that though)
šŸ‘ 1
j
i wonder what will happen if a gradle project depends on a maven project which in turn depends on org.lz4lz4 java1.8.1. i guess this issue could "poison" an entire dependency tree for gradle
t
So IMO: • depend on the new coordinates, never on the relocation • open a request for enhancement for Gradle to be smarter about relocations and conflicting capabilities (but I suspect there might be many edge cases), or possibly that they update their stance and instead recommend the dependency constraint path.
v
Why change the publishing? The publishing with the capabilities is fine and exactly what the Gradle documentation recommends.
He follows the recommended ways by Gradle and it does not work, so it imho is a Gradle bug. It does not make much sense if Gradle automatically follows the relocation to take the new jar and then whine on the capability conflict, especially as there is only one jar in the end coming from one dependency.
Imho the publishing should stay like it is, the capability should stay, also on new releases, and the bug in Gradle should be reported and fixed.
i wonder what will happen if a gradle project depends on a maven project which in turn depends on org.lz4lz4 java1.8.1. i guess this issue could "poison" an entire dependency tree for gradle
Doesn't matter, Maven is not in play at resolution time. At resolution time only Gradle is at work and the same like now will happen. It resolves
org.lz4:lz4-java:1.8.1
, finds and follows the relocation, there finds the capability and will fail.
I of course agree to @Thomas Broyer that it is better to use the new coordinates if there was a relocation, but if the old coordinates are used, it should still work, especially in this case that is exactly following the Gradle documentation.
j
i guess i better add a big fat warning to the release notes before people start depending on the relocation
and make their projects unusable for gradle users
v
It's not unusable, just not as convenient as it could be
It should be the same situation if you depend on v1 old coordinates and v2 new coordinates, you also get a capability conflict that you need to resolve.
I'd hope that also works here, though it should not be necessary.
j
could you be so kind as to make a simple snippet to resolve the conflict so i can document this? if i write it it probably won't be idiomatic. otherwise i'll ask @melix
v
selecting the highest version here does not work of course as both have the same version:
Copy code
configurations.configureEach {
    resolutionStrategy {
        capabilitiesResolution {
            withCapability("org.lz4:lz4-java") {
                selectHighestVersion()
            }
        }
    }
}
That would be in the typical situation (old version in old coords and new version in new coords). But in that situation you can for example filter by group, this works, just tested:
Copy code
configurations.configureEach {
    resolutionStrategy {
        capabilitiesResolution {
            withCapability("org.lz4:lz4-java") {
                select(candidates.first { (it.id as ModuleComponentIdentifier).group == "at.yawk.lz4" })
            }
        }
    }
}
But you should imho still report a Gradle bug
šŸ‘ 1
thanks for the help!