mirror of https://github.com/sbt/sbt.git
Fixes #1639. Fixes cached resolution interacting with force()
When conflicts are found for a given module, a forced one is selected before conflict manager kicks in. The problem is that DependencyDescriptor seems to mark transitive forced dependency as forced as well, so the actual forced dependency are sometimes not prioritized. To work around this, I’ve introduced a mixin called SbtDefaultDependencyDescriptor, which carries around ModuleID to detect direct dependencies.
This commit is contained in:
parent
54032ddd5f
commit
09bca754b5
|
|
@ -4,7 +4,7 @@
|
|||
package sbt
|
||||
|
||||
import Resolver.PluginPattern
|
||||
import ivyint.{ CachedResolutionResolveEngine, CachedResolutionResolveCache }
|
||||
import ivyint.{ CachedResolutionResolveEngine, CachedResolutionResolveCache, SbtDefaultDependencyDescriptor }
|
||||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
|
@ -570,7 +570,9 @@ private[sbt] object IvySbt {
|
|||
/** Transforms an sbt ModuleID into an Ivy DefaultDependencyDescriptor.*/
|
||||
def convertDependency(moduleID: DefaultModuleDescriptor, dependency: ModuleID, parser: CustomXmlParser.CustomParser): DefaultDependencyDescriptor =
|
||||
{
|
||||
val dependencyDescriptor = new DefaultDependencyDescriptor(moduleID, toID(dependency), dependency.isForce, dependency.isChanging, dependency.isTransitive)
|
||||
val dependencyDescriptor = new DefaultDependencyDescriptor(moduleID, toID(dependency), dependency.isForce, dependency.isChanging, dependency.isTransitive) with SbtDefaultDependencyDescriptor {
|
||||
def dependencyModuleId = dependency
|
||||
}
|
||||
dependency.configurations match {
|
||||
case None => // The configuration for this dependency was not explicitly specified, so use the default
|
||||
parser.parseDepsConfs(parser.getDefaultConf, dependencyDescriptor)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import module.id.{ ModuleRevisionId, ModuleId => IvyModuleId }
|
|||
import report.{ ArtifactDownloadReport, ConfigurationResolveReport, ResolveReport }
|
||||
import resolve.{ IvyNode, IvyNodeCallers }
|
||||
import IvyNodeCallers.{ Caller => IvyCaller }
|
||||
import ivyint.SbtDefaultDependencyDescriptor
|
||||
|
||||
object IvyRetrieve {
|
||||
def reports(report: ResolveReport): Seq[ConfigurationResolveReport] =
|
||||
|
|
@ -77,11 +78,14 @@ object IvyRetrieve {
|
|||
case x if nonEmptyString(x).isDefined => x
|
||||
}
|
||||
val ddOpt = Option(caller.getDependencyDescriptor)
|
||||
val (extraAttributes, isForce, isChanging, isTransitive) = ddOpt match {
|
||||
case Some(dd) => (toExtraAttributes(dd.getExtraAttributes), dd.isForce, dd.isChanging, dd.isTransitive)
|
||||
case None => (Map.empty[String, String], false, false, true)
|
||||
val (extraAttributes, isForce, isChanging, isTransitive, isDirectlyForce) = ddOpt match {
|
||||
case Some(dd: SbtDefaultDependencyDescriptor) =>
|
||||
val mod = dd.dependencyModuleId
|
||||
(toExtraAttributes(dd.getExtraAttributes), mod.isForce, mod.isChanging, mod.isTransitive, mod.isForce)
|
||||
case Some(dd) => (toExtraAttributes(dd.getExtraAttributes), dd.isForce, dd.isChanging, dd.isTransitive, false)
|
||||
case None => (Map.empty[String, String], false, false, true, false)
|
||||
}
|
||||
new Caller(m, callerConfigurations, extraAttributes, isForce, isChanging, isTransitive)
|
||||
new Caller(m, callerConfigurations, extraAttributes, isForce, isChanging, isTransitive, isDirectlyForce)
|
||||
}
|
||||
val revId = dep.getResolvedId
|
||||
val moduleId = toModuleID(revId)
|
||||
|
|
|
|||
|
|
@ -190,7 +190,8 @@ final class Caller(
|
|||
val callerExtraAttributes: Map[String, String],
|
||||
val isForceDependency: Boolean,
|
||||
val isChangingDependency: Boolean,
|
||||
val isTransitiveDependency: Boolean) {
|
||||
val isTransitiveDependency: Boolean,
|
||||
val isDirectlyForceDependency: Boolean) {
|
||||
override def toString: String =
|
||||
s"$caller"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,6 +36,9 @@ private[sbt] class CachedResolutionResolveCache() {
|
|||
val mds =
|
||||
if (mrid0.getOrganisation == sbtOrgTemp) Vector(md0)
|
||||
else buildArtificialModuleDescriptors(md0, prOpt) map { _._1 }
|
||||
|
||||
updateReportCache.remove(md0.getModuleRevisionId)
|
||||
directDependencyCache.remove(md0.getModuleRevisionId)
|
||||
mds foreach { md =>
|
||||
updateReportCache.remove(md.getModuleRevisionId)
|
||||
directDependencyCache.remove(md.getModuleRevisionId)
|
||||
|
|
@ -186,6 +189,7 @@ private[sbt] trait CachedResolutionResolveEngine extends ResolveEngine {
|
|||
val miniGraphPath = depDir / "module"
|
||||
val cachedDescriptor = getSettings.getResolutionCacheManager.getResolvedIvyFileInCache(md0.getModuleRevisionId)
|
||||
val cache = cachedResolutionResolveCache
|
||||
cache.directDependencyCache.remove(md0.getModuleRevisionId)
|
||||
val mds = cache.buildArtificialModuleDescriptors(md0, projectResolver)
|
||||
def doWork(md: ModuleDescriptor): Either[ResolveException, UpdateReport] =
|
||||
{
|
||||
|
|
@ -292,10 +296,13 @@ private[sbt] trait CachedResolutionResolveEngine extends ResolveEngine {
|
|||
val name = head.module.name
|
||||
log.debug(s"- conflict in $rootModuleConf:$organization:$name " + (conflicts map { _.module }).mkString("(", ", ", ")"))
|
||||
def useLatest(lcm: LatestConflictManager): (Vector[ModuleReport], Vector[ModuleReport], String) =
|
||||
conflicts find { m =>
|
||||
(conflicts find { m =>
|
||||
m.callers.exists { _.isDirectlyForceDependency }
|
||||
} orElse (conflicts find { m =>
|
||||
m.callers.exists { _.isForceDependency }
|
||||
} match {
|
||||
})) match {
|
||||
case Some(m) =>
|
||||
log.debug(s"- forced dependency: $m")
|
||||
(Vector(m), conflicts filterNot { _ == m } map { _.copy(evicted = true, evictedReason = Some(lcm.toString)) }, lcm.toString)
|
||||
case None =>
|
||||
val strategy = lcm.getStrategy
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package sbt
|
||||
package ivyint
|
||||
|
||||
import org.apache.ivy.core
|
||||
import core.module.descriptor.DefaultDependencyDescriptor
|
||||
|
||||
trait SbtDefaultDependencyDescriptor { self: DefaultDependencyDescriptor =>
|
||||
def dependencyModuleId: ModuleID
|
||||
}
|
||||
|
|
@ -81,8 +81,8 @@ object CacheIvy {
|
|||
implicit def organizationArtifactReportFormat(implicit sf: Format[String], bf: Format[Boolean], df: Format[Seq[ModuleReport]]): Format[OrganizationArtifactReport] =
|
||||
wrap[OrganizationArtifactReport, (String, String, Seq[ModuleReport])](m => (m.organization, m.name, m.modules), { case (o, n, r) => OrganizationArtifactReport(o, n, r) })
|
||||
implicit def callerFormat: Format[Caller] =
|
||||
wrap[Caller, (ModuleID, Seq[String], Map[String, String], Boolean, Boolean, Boolean)](c => (c.caller, c.callerConfigurations, c.callerExtraAttributes, c.isForceDependency, c.isChangingDependency, c.isTransitiveDependency),
|
||||
{ case (c, cc, ea, fd, cd, td) => new Caller(c, cc, ea, fd, cd, td) })
|
||||
wrap[Caller, (ModuleID, Seq[String], Map[String, String], Boolean, Boolean, Boolean, Boolean)](c => (c.caller, c.callerConfigurations, c.callerExtraAttributes, c.isForceDependency, c.isChangingDependency, c.isTransitiveDependency, c.isDirectlyForceDependency),
|
||||
{ case (c, cc, ea, fd, cd, td, df) => new Caller(c, cc, ea, fd, cd, td, df) })
|
||||
implicit def exclusionRuleFormat(implicit sf: Format[String]): Format[ExclusionRule] =
|
||||
wrap[ExclusionRule, (String, String, String, Seq[String])](e => (e.organization, e.name, e.artifact, e.configurations), { case (o, n, a, cs) => ExclusionRule(o, n, a, cs) })
|
||||
implicit def crossVersionFormat: Format[CrossVersion] = wrap(crossToInt, crossFromInt)
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ def commonSettings: Seq[Def.Setting[_]] =
|
|||
ivyPaths := new IvyPaths( (baseDirectory in ThisBuild).value, Some((baseDirectory in LocalRootProject).value / "ivy-cache")),
|
||||
dependencyCacheDirectory := (baseDirectory in LocalRootProject).value / "dependency",
|
||||
libraryDependencies := Seq(
|
||||
"org.springframework" % "spring-core" % "3.2.2.RELEASE" force() exclude("org.springframework", "spring-asm"),
|
||||
"org.springframework" % "spring-context" % "4.0.3.RELEASE" exclude("org.springframework", "spring-asm")
|
||||
"org.springframework" % "spring-core" % "3.2.2.RELEASE" force() exclude("org.springframework", "spring-asm"),
|
||||
"org.springframework" % "spring-tx" % "3.1.2.RELEASE" force() exclude("org.springframework", "spring-asm"),
|
||||
"org.springframework" % "spring-beans" % "3.2.2.RELEASE" force() exclude("org.springframework", "spring-asm"),
|
||||
"org.springframework" % "spring-context" % "3.1.2.RELEASE" force() exclude("org.springframework", "spring-asm")
|
||||
),
|
||||
scalaVersion := "2.10.4",
|
||||
resolvers += Resolver.sonatypeRepo("snapshots")
|
||||
|
|
@ -25,7 +27,12 @@ lazy val b = project.
|
|||
|
||||
lazy val c = project.
|
||||
dependsOn(a).
|
||||
settings(cachedResolutionSettings: _*)
|
||||
settings(cachedResolutionSettings: _*).
|
||||
settings(
|
||||
libraryDependencies := Seq(
|
||||
"org.springframework" % "spring-core" % "4.0.3.RELEASE" exclude("org.springframework", "spring-asm")
|
||||
)
|
||||
)
|
||||
|
||||
lazy val root = (project in file(".")).
|
||||
aggregate(a, b, c).
|
||||
|
|
@ -37,6 +44,25 @@ lazy val root = (project in file(".")).
|
|||
val acp = (externalDependencyClasspath in Compile in a).value.sortBy {_.data.getName}
|
||||
val bcp = (externalDependencyClasspath in Compile in b).value.sortBy {_.data.getName}
|
||||
val ccp = (externalDependencyClasspath in Compile in c).value.sortBy {_.data.getName}
|
||||
|
||||
if (!(acp exists {_.data.getName contains "spring-core-3.2.2.RELEASE"})) {
|
||||
sys.error("spring-core-3.2.2 is not found")
|
||||
}
|
||||
if (!(bcp exists {_.data.getName contains "spring-core-3.2.2.RELEASE"})) {
|
||||
sys.error("spring-core-3.2.2 is not found")
|
||||
}
|
||||
if (!(ccp exists {_.data.getName contains "spring-core-3.2.2.RELEASE"})) {
|
||||
sys.error("spring-core-3.2.2 is not found")
|
||||
}
|
||||
if (!(acp exists {_.data.getName contains "spring-tx-3.1.2.RELEASE"})) {
|
||||
sys.error("spring-tx-3.1.2 is not found")
|
||||
}
|
||||
if (!(bcp exists {_.data.getName contains "spring-tx-3.1.2.RELEASE"})) {
|
||||
sys.error("spring-tx-3.1.2 is not found")
|
||||
}
|
||||
if (!(ccp exists {_.data.getName contains "spring-tx-3.1.2.RELEASE"})) {
|
||||
sys.error("spring-tx-3.1.2 is not found")
|
||||
}
|
||||
if (acp == bcp && acp == ccp) ()
|
||||
else sys.error("Different classpaths are found:" +
|
||||
"\n - a (consolidated) " + acp.toString +
|
||||
|
|
|
|||
Loading…
Reference in New Issue