Fix dependency-reduced-pom exclusion loss for classifier-distinct duplicate deps (Maven 4) - #820
Conversation
|
Note on Maven 4 coverage: this repo's PR matrix runs Maven 3.9.16 only, so the green checks here confirm there is no Maven 3 regression but do not exercise the Maven 4 path this fixes. For the Maven 4 evidence I ran the full
Locally the suite is also 83/0 on both Maven 4.0.0-SNAPSHOT and Maven 3.9.16. |
|
one aspect surprises me: I thought that the change in Maven 4 was tied to Resolver 2 is Maven 4.0 using Resolver 2 not exactly the same way as Maven 3.10 on these conflict-resolved dependencies? @cstamas having your review on this PR would help, please, to be sure that analysis and update to m-shade-p is completely understood and accepted by people who really understand (I don't really understand myself :) ) |
Exactly the same issue in my eye as well. IF there are differences seen between Resolver 1 and Resolver 2, I would like to understand them better. @ascheman can you create some simple reproducer for me to play with it? |
|
@cstamas here you go — standalone reproducer (stock shade 3.6.0, no plugin build needed): https://github.com/aschemaven/mshade-819-reproducer
So @hboutemy's observation holds: same Resolver 2 line, different outcome — the delta is on the Maven side, not the resolver version. Two more data points from the reproducer:
What I don't understand myself yet: why 3.10's collected graph retains the duplicate On test coverage: the existing ITs ( |
|
I independently verified PR #820 against Maven 4.0.0-rc-5 and can confirm it fixes the two remaining failures from #810. Test environment:
Results with this PR applied:
The single skip is the pre-existing Both |
Root Cause Analysis: Why Maven 4 Prunes Differently Than Maven 3.10@cstamas @hboutemy — I dug into why Maven 3.10.0-RC1 (Resolver 2.0.20) retains the duplicate The Configuration DifferenceThe pivotal config property is
Maven 3.10's How This Causes the BugIn Object key = args.pool.toKey(artifact, childRepos, childSelector,
childManager, childTraverser, childFilter);
List<DependencyNode> children = args.pool.getChildren(key);
if (children == null) {
boolean skipResolution = args.skipper.skipResolution(child, parentContext.parents);
// ... resolve and cache
} else {
child.setChildren(children); // pool hit → skipper never consulted
}Maven 3.10 ( Maven 4 ( ConclusionThis is a deliberate Maven 4 design decision — This analysis was generated by an AI agent and may contain inaccuracies. Please verify before relying on it. |
gnodet
left a comment
There was a problem hiding this comment.
Approve
Reviewed with two independent specialized agents (code review + software architecture). Both found zero blocking issues.
What was checked
- Session copy safety:
DefaultRepositorySystemSessioncopy constructor creates an isolated config map; original session is never mutated. The Maven Resolver upgrading guide explicitly blesses this pattern for Mojos. - Forward compatibility: The copy constructor is NOT deprecated in Resolver 2.x (only the no-arg constructor is).
CONFIG_PROP_VERBOSEandVerbosity.STANDARDare stable, non-deprecated API available since Resolver 1.9.8. - Side effects of verbose mode:
Verbosity.STANDARDretains conflict-loser nodes as childless markers with validgetArtifact()/getDependency(). The downstream walk (lines 1315-1366) handles these correctly — thedependencyHasExclusionguard prevents duplicate exclusions, and the classifier-awaregetId()correctly matches across variants. - Root cause: Investigated and filed as a resolver-side issue (apache/maven-resolver#2013). The underlying cause is a cache-transparency violation in
BfDependencyCollectorwhere the pool cache and GACE skipper use different keys. This PR's workaround is correct and semantically appropriate — verbose mode is the right tool for a walk that needs complete transitive visibility.
Clean, minimal, well-documented fix. Nice work! 👏
This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.
| // single variant in the dependency-reduced-pom. | ||
| DefaultRepositorySystemSession verboseSession = | ||
| new DefaultRepositorySystemSession(session.getRepositorySession()); | ||
| verboseSession.setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, ConflictResolver.Verbosity.STANDARD); |
There was a problem hiding this comment.
Verified: ConflictResolver.CONFIG_PROP_VERBOSE and ConflictResolver.Verbosity.STANDARD are stable, non-deprecated API present in both Resolver 1.9.x and 2.0.x. The copy constructor of DefaultRepositorySystemSession is also explicitly blessed for Mojo use in Resolver 2.x (only the no-arg constructor is deprecated).
The underlying root cause (a cache-transparency violation in BfDependencyCollector where pool cache hit/miss changes graph structure) has been filed as apache/maven-resolver#2013. Even if that is fixed, verbose mode is semantically the right choice for this walk.
Under Maven 4, ShadeMojo.updateExcludesInDeps() walks a single
conflict-resolved collect graph. The resolver prunes the duplicate
transitive node ("omitted for duplicate") under one of two
classifier-distinct variants of the same artifact, so the generated
dependency-reduced-pom keeps the <exclusion> on only one variant.
Collect that graph with verbose conflict resolution
(ConflictResolver.CONFIG_PROP_VERBOSE = STANDARD) so the omitted-duplicate
node is retained as a marker; the existing walk then re-attaches the
exclusion to both variants. No behaviour change on Maven 3.9.16.
Covered by the existing dep-reduced-pom-exclusions and
MSHADE-467_parallel-dependency-reduced-pom ITs.
Closes apache#819
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4da6b40 to
1f9588e
Compare
What
Fixes the generated
dependency-reduced-pom.xmldropping an<exclusion>on one of two classifier-distinct duplicate variants of a dependency under Maven 4 (e.g.b:0.2keeps the exclusion,b:0.2:altloses it).Why
ShadeMojo.updateExcludesInDeps()walks a single conflict-resolved collect graph. Under Maven 4 the resolver prunes the duplicate transitive node (omitted for duplicate) under one variant, so the exclusion is re-attached to only that variant. This is stable, intended resolver behaviour (not a resolver bug); shade's own dependency keying is already classifier-aware.Fix
Collect that graph with verbose conflict resolution (
ConflictResolver.CONFIG_PROP_VERBOSE = STANDARD) on a copied session, so the omitted-duplicate node is retained as a marker; the existing walk then re-attaches the exclusion to both variants. Localized to the singlecollectDependenciescall; no behaviour change on Maven 3.Test
Covered by the existing ITs
dep-reduced-pom-exclusionsandMSHADE-467_parallel-dependency-reduced-pom(red on Maven 4 / green on Maven 3 before; green on both after). Full-P run-itsverified locally: 83/0 on both Maven 4.0.0-SNAPSHOT and Maven 3.9.16.Closes #819