diff --git a/ivy/Ivy.scala b/ivy/Ivy.scala index 0a7e07882..937648cc0 100644 --- a/ivy/Ivy.scala +++ b/ivy/Ivy.scala @@ -474,18 +474,23 @@ private object IvySbt */ def mergeDuplicateDefinitions(dependencies: Seq[DependencyDescriptor]): Seq[DependencyDescriptor] = { - val deps = new java.util.LinkedHashMap[ModuleRevisionId, DependencyDescriptor] + // need to preserve basic order of dependencies: can't use dependencies.groupBy + val deps = new java.util.LinkedHashMap[ModuleRevisionId, List[DependencyDescriptor]] for( dd <- dependencies ) { val id = dd.getDependencyRevisionId val updated = deps get id match { - case null => dd - case v => ivyint.MergeDescriptors(v, dd) + case null => dd :: Nil + case v => dd :: v } deps.put(id, updated) } + import collection.JavaConverters._ - deps.values.asScala.toSeq + deps.values.asScala.toSeq.flatMap { dds => + val mergeable = (dds, dds.tail).zipped.forall( ivyint.MergeDescriptors.mergeable _) + if(mergeable) dds.reverse.reduceLeft(ivyint.MergeDescriptors.apply _) :: Nil else dds + } } /** Transforms an sbt ModuleID into an Ivy DefaultDependencyDescriptor.*/ diff --git a/ivy/MergeDescriptors.scala b/ivy/MergeDescriptors.scala index f46f90501..e1c07c780 100644 --- a/ivy/MergeDescriptors.scala +++ b/ivy/MergeDescriptors.scala @@ -15,20 +15,24 @@ import util.extendable.ExtendableItem private[sbt] object MergeDescriptors { + def mergeable(a: DependencyDescriptor, b: DependencyDescriptor): Boolean = + a.isForce == b.isForce && + a.isChanging == b.isChanging && + a.isTransitive == b.isTransitive && + a.getParentRevisionId == b.getParentRevisionId && + a.getNamespace == b.getNamespace && { + val amrid = a.getDependencyRevisionId + val bmrid = b.getDependencyRevisionId + amrid == bmrid + } && { + val adyn = a.getDynamicConstraintDependencyRevisionId + val bdyn = b.getDynamicConstraintDependencyRevisionId + adyn == bdyn + } + def apply(a: DependencyDescriptor, b: DependencyDescriptor): DependencyDescriptor = { - assert(a.isForce == b.isForce) - assert(a.isChanging == b.isChanging) - assert(a.isTransitive == b.isTransitive) - assert(a.getParentRevisionId == b.getParentRevisionId) - val amrid = a.getDependencyRevisionId - val bmrid = b.getDependencyRevisionId - assert(amrid == bmrid) - val adyn = a.getDynamicConstraintDependencyRevisionId - val bdyn = b.getDynamicConstraintDependencyRevisionId - assert(adyn == bdyn) - assert(a.getNamespace == b.getNamespace) - + assert(mergeable(a,b)) new MergedDescriptors(a,b) } } diff --git a/sbt/src/sbt-test/dependency-management/multiple-classifiers/changes/non-mergeable.sbt b/sbt/src/sbt-test/dependency-management/multiple-classifiers/changes/non-mergeable.sbt new file mode 100644 index 000000000..caac80653 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/multiple-classifiers/changes/non-mergeable.sbt @@ -0,0 +1,13 @@ +libraryDependencies ++= Seq( + "org.easytesting" % "fest-assert" % "1.4", + "org.easytesting" % "fest-assert" % "1.4" % "test" intransitive()) + +autoScalaLibrary := false + +TaskKey[Unit]("check") <<= (externalDependencyClasspath in Compile, externalDependencyClasspath in Test) map { (cp, tcp) => + assert(cp.size == 2, "Expected 2 jars on compile classpath, got: " + cp.files.mkString("(", ", ", ")")) + // this should really be 1 because of intransitive(), but Ivy doesn't handle this. + // So, this test can only check that the assertion reported in #582 isn't triggered. + assert(tcp.size == 2, "Expected 2 jar on test classpath, got: " + tcp.files.mkString("(", ", ", ")")) +} + diff --git a/sbt/src/sbt-test/dependency-management/multiple-classifiers/test b/sbt/src/sbt-test/dependency-management/multiple-classifiers/test index b4d923e87..0f60369cd 100644 --- a/sbt/src/sbt-test/dependency-management/multiple-classifiers/test +++ b/sbt/src/sbt-test/dependency-management/multiple-classifiers/test @@ -1,3 +1,9 @@ > check > check-pom + +$ delete build.sbt +$ copy-file changes/non-mergeable.sbt build.sbt + +> reload +> check