effective-pom* inherits the parent POM's dependencies after they have already been interpolated against the parent's own properties. A child POM that overrides a property used in an inherited dependency version has no effect, and the wrong version is selected.
Maven builds the child's effective model by inheriting the raw dependency elements and interpolating them against the child's merged properties.
Repro
test/fixtures/differential-corpus.edn:
[{:name :tools-logging
:deps {org.clojure/tools.logging {:mvn/version "1.3.0"}}}]
GRENADINE_ORACLE_CASES=0 make oracle
Mismatch: :tools-logging
tools.deps: org.clojure/clojure 1.10.3, spec.alpha 0.2.194, core.specs.alpha 0.2.56
grenadine: org.clojure/clojure 1.8.0, no spec.alpha, no core.specs.alpha
Cause
org.clojure/pom.contrib:1.1.0 declares the inherited dependency through a property:
<properties>
<clojure.version>1.8.0</clojure.version>
</properties>
<dependencies>
<dependency>
<groupId>org.clojure</groupId>
<artifactId>clojure</artifactId>
<version>${clojure.version}</version>
</dependency>
</dependencies>
org.clojure/tools.logging:1.3.0 overrides that property:
<parent>
<groupId>org.clojure</groupId>
<artifactId>pom.contrib</artifactId>
<version>1.1.0</version>
</parent>
<properties>
<clojure.version>1.10.3</clojure.version>
</properties>
src/grenadine/pom.cljc:335:
dependencies
(merge-ordered (or (:deps parent) []) declared-dependencies)
(:deps parent) comes back from the recursive effective-pom* call already interpolated with the parent's clojure.version of 1.8.0. The child's override reaches raw-properties at line 288 but never reaches the inherited dependency.
parent-management at line 316 has the same shape. Inherited <dependencyManagement> entries are interpolated in the parent's scope before the child can override the property.
Scope
Every org.clojure/* contrib library inherits org.clojure/clojure from pom.contrib this way, and each pins its own clojure.version. org.clojure/core.async 1.8.741 resolves org.clojure/clojure to 1.9.0 instead of 1.11.4 for the same reason.
More generally this affects any inherited dependency or managed dependency whose group, artifact, or version comes from a property the child overrides.
Suggested fix
Return the parent's uninterpolated <dependencies> and <dependencyManagement> from effective-pom*, and interpolate the merged set once against the child's interpolation-context.
Note on the differential corpus
test/fixtures/differential-corpus.edn holds 6 entries, and random-deps in test/grenadine/oracle.clj draws its fuzz cases from that same corpus, so the fuzz pass only explores combinations of those 6 dependency maps. Widening the corpus to 20 popular libraries surfaced this on the first run. A larger corpus pulled from Clojars would catch this class of bug earlier.
effective-pom*inherits the parent POM's dependencies after they have already been interpolated against the parent's own properties. A child POM that overrides a property used in an inherited dependency version has no effect, and the wrong version is selected.Maven builds the child's effective model by inheriting the raw dependency elements and interpolating them against the child's merged properties.
Repro
test/fixtures/differential-corpus.edn:[{:name :tools-logging :deps {org.clojure/tools.logging {:mvn/version "1.3.0"}}}]Cause
org.clojure/pom.contrib:1.1.0declares the inherited dependency through a property:org.clojure/tools.logging:1.3.0overrides that property:src/grenadine/pom.cljc:335:(:deps parent)comes back from the recursiveeffective-pom*call already interpolated with the parent'sclojure.versionof 1.8.0. The child's override reachesraw-propertiesat line 288 but never reaches the inherited dependency.parent-managementat line 316 has the same shape. Inherited<dependencyManagement>entries are interpolated in the parent's scope before the child can override the property.Scope
Every
org.clojure/*contrib library inheritsorg.clojure/clojurefrompom.contribthis way, and each pins its ownclojure.version.org.clojure/core.async 1.8.741resolvesorg.clojure/clojureto 1.9.0 instead of 1.11.4 for the same reason.More generally this affects any inherited dependency or managed dependency whose group, artifact, or version comes from a property the child overrides.
Suggested fix
Return the parent's uninterpolated
<dependencies>and<dependencyManagement>fromeffective-pom*, and interpolate the merged set once against the child'sinterpolation-context.Note on the differential corpus
test/fixtures/differential-corpus.ednholds 6 entries, andrandom-depsintest/grenadine/oracle.cljdraws its fuzz cases from that same corpus, so the fuzz pass only explores combinations of those 6 dependency maps. Widening the corpus to 20 popular libraries surfaced this on the first run. A larger corpus pulled from Clojars would catch this class of bug earlier.