New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implicit capabilities not always applied/detected #11300
Comments
Thanks for reporting @tbroyer. It indeed looks like a bug. The build scan also shows that two components with the same capability were selected:
Another thing to observe is that we do not de-duplicate capabilities. In the seconds link you can see the We will look into this. |
I also noticed that accessing the capabilities will realize them and trigger the expected conflict: dependencies {
components {
all {
allVariants {
withCapabilities {
capabilities.forEach {
println("$id -> ${it.group}:${it.name}:${it.version}")
}
}
}
}
}
} or more simply: dependencies {
components {
withModule("log4j:log4j") {
withCapabilities {
// do nothing, calling withCapabilities is enough to realize the implicit capabilities
}
}
}
} |
Fwiw, with Gradle 5.6, the only workaround I've found is to use a capability different from |
Thanks for all the additional info @tbroyer. I am looking into this now and this seems to be an ordering issue concerning only implicit capabilities. So you can "fix" it by working only with explicit capabilities but also by order.
In your sample, if you change the order of these two declarations it works. Which is bad (but the reason we did not catch this I suppose). Working on a fix now. |
…11334) In general conflict detection for implicit capabilities is skipped for performance optimization. However, if the corresponding capability is explicitly declared by another component that was visited *before*, we need to do the conflict detection between the component with the implicit capability and the one visited earlier. See also: #11300
Fix is on Keeping this open in case there is a |
…11334) In general conflict detection for implicit capabilities is skipped for performance optimization. However, if the corresponding capability is explicitly declared by another component that was visited *before*, we need to do the conflict detection between the component with the implicit capability and the one visited earlier. See also: #11300
Picked to |
Documentation (https://docs.gradle.org/5.6.4/userguide/managing_transitive_dependencies.html#sec:capabilities, https://docs.gradle.org/6.0/userguide/dependency_capability_conflict.html) says that “Each component defines an implicit capability corresponding to its GAV coordinates (group, artifact, version)” but this isn't actually always the case, or at least it doesn't always work to detect conflicts between dependencies.
I've only experienced it with
log4j:log4j
, but I doubt this is specific to that dependency/capability.Expected Behavior
There should be a failure due to a conflict in capabilities.
Current Behavior
No conflict is detected, and both dependencies appear in the output (output of the
dependencies
anddependencyInsight
tasks, and anything that lists or copies dependencies, e.g. building ZIPs, or Deb/RPM packages).Context
I've noticed that our application ends up packaging several logging frameworks, including "conflicting" ones, such as jcl-over-slf4j and commons-logging, or log4j-over-slf4j and log4j (and log4j-1.2-api). Some of them were partially handled using exclusions (inherited from the time when the project was using Maven, before migrating it to Gradle), but that wasn't enough (and didn't resist badly handled upgrades that didn't update the exclusions).
I've started using capabilities to declare conflicts between those dependencies, and then capability resolution rules to resolve conflicts (we're using Log4j 2 through the SLF4J API). This worked well, except for one case:
log4j:log4j
vs.org.apache.logging.log4j:log4j-1.2-api
.Steps to Reproduce
Here's the simplest repro case I could come with:
then run
gradle dependencies --configuration=runtimeClasspath
.The bug won't manifest if declaring a direct dependency on
log4j:log4j
, only through transitive dependencies (in our project,log4j:log4
comes transitively fromeu.medsea.mimeutil:mime-util
in one subproject, andorg.apache.logging.log4j:log4j-1.2-api
as a direct dependency of another subproject, all being "assembled" in yet another subproject, possibly transitively through other subprojects).It doesn't manifest either for
org.slf4j:jcl-over-slf4j
andcommons-logging:commons-logging
(when declaring thatjcl-over-slf4j
provides thecommons-logging
capability), or other similar conflicts.Workarounds:
log4j:log4j
provides thelog4j:log4j
capabilitylog4j:log4j-capability
)log4j:log4j
provides theorg.apache.logging.log4j:log4j-1.2-api
capability, rather than the reverse.Your Environment
Build scan URL: https://scans.gradle.com/s/n5272o3emcjbs
The text was updated successfully, but these errors were encountered: