Merge pull request #1720 from sbt/fix/1719

cached resolution: Fixes #1711, #1716, #1719
This commit is contained in:
Josh Suereth 2014-11-10 21:06:24 -05:00
commit d4a8e4079d
6 changed files with 70 additions and 22 deletions

View File

@ -12,7 +12,7 @@ import core.resolve._
import core.module.id.{ ModuleRevisionId, ModuleId => IvyModuleId }
import core.report.{ ResolveReport, ConfigurationResolveReport, DownloadReport }
import core.module.descriptor.{ DefaultModuleDescriptor, ModuleDescriptor, DefaultDependencyDescriptor, DependencyDescriptor, Configuration => IvyConfiguration, ExcludeRule, IncludeRule }
import core.module.descriptor.OverrideDependencyDescriptorMediator
import core.module.descriptor.{ OverrideDependencyDescriptorMediator, DependencyArtifactDescriptor }
import core.{ IvyPatternHelper, LogOptions }
import org.apache.ivy.util.Message
import org.apache.ivy.plugins.latest.{ ArtifactInfo => IvyArtifactInfo }
@ -37,33 +37,38 @@ private[sbt] class CachedResolutionResolveCache() {
}
def directDependencies(md0: ModuleDescriptor): Vector[DependencyDescriptor] =
md0.getDependencies.toVector
def buildArtificialModuleDescriptors(md0: ModuleDescriptor, data: ResolveData, prOpt: Option[ProjectResolver]): Vector[(DefaultModuleDescriptor, Boolean)] =
def buildArtificialModuleDescriptors(md0: ModuleDescriptor, data: ResolveData, prOpt: Option[ProjectResolver], log: Logger): Vector[(DefaultModuleDescriptor, Boolean)] =
{
log.debug(s":: building artificial module descriptors from ${md0.getModuleRevisionId}")
val expanded = expandInternalDependencies(md0, data, prOpt, log)
val rootModuleConfigs = md0.getConfigurations.toArray.toVector
val expanded = expandInternalDependencies(md0, data, prOpt)
expanded map { buildArtificialModuleDescriptor(_, rootModuleConfigs, md0, prOpt) }
expanded map { buildArtificialModuleDescriptor(_, rootModuleConfigs, md0, prOpt, log) }
}
// This expands out all internal dependencies and merge them into a single graph that consists
// only of external dependencies.
// The tricky part is the merger of configurations, even though in most cases we will only see compile->compile when it comes to internal deps.
// Theoretically, there could be a potential for test->test->runtime kind of situation. nextConfMap and remapConfigurations track
// the configuration chains transitively.
def expandInternalDependencies(md0: ModuleDescriptor, data: ResolveData, prOpt: Option[ProjectResolver]): Vector[DependencyDescriptor] =
def expandInternalDependencies(md0: ModuleDescriptor, data: ResolveData, prOpt: Option[ProjectResolver], log: Logger): Vector[DependencyDescriptor] =
{
log.debug(s"::: expanding internal dependencies of module descriptor ${md0.getModuleRevisionId}")
val rootModuleConfigs = md0.getConfigurations.toArray.toVector
val rootNode = new IvyNode(data, md0)
def expandInternalDeps(dep: DependencyDescriptor, confMap: Map[String, Array[String]]): Vector[DependencyDescriptor] =
internalDependency(dep) match {
case Some(internal) =>
log.debug(s""":::: found internal dependency ${internal.getResolvedModuleRevisionId}""")
val allConfigurations: Vector[String] =
(if (confMap.isEmpty) nextConfMap(dep, confMap)
else confMap).values.flatten.toList.distinct.toVector
directDependencies(internal) filter { dd =>
val next = nextConfMap(dep, confMap)
val directs = directDependencies(internal) filter { dd =>
allConfigurations exists { conf => !dd.getDependencyConfigurations(conf).isEmpty }
} flatMap { dd => expandInternalDeps(dd, nextConfMap(dd, confMap)) }
}
directs flatMap { dd => expandInternalDeps(dd, next) }
case _ =>
if (confMap.isEmpty) Vector(dep)
else Vector(remapConfigurations(dep, confMap))
else Vector(remapConfigurations(dep, confMap, log))
}
def internalDependency(dep: DependencyDescriptor): Option[ModuleDescriptor] =
prOpt match {
@ -88,32 +93,44 @@ private[sbt] class CachedResolutionResolveCache() {
}
})
}
def remapConfigurations(dd0: DependencyDescriptor, confMap: Map[String, Array[String]]): DependencyDescriptor =
def remapConfigurations(dd0: DependencyDescriptor, confMap: Map[String, Array[String]], log: Logger): DependencyDescriptor =
{
log.debug(s""":::: remapping configuration of ${dd0} with ${confMap.toList map { case (k, v) => (k, v.toList) }}""")
val dd = new DefaultDependencyDescriptor(md0, dd0.getDependencyRevisionId, dd0.getDynamicConstraintDependencyRevisionId,
dd0.isForce, dd0.isChanging, dd0.isTransitive)
val moduleConfigurations = dd0.getModuleConfigurations.toVector
for {
moduleConf <- dd0.getModuleConfigurations
(rootModuleConf, vs) <- confMap
moduleConf <- moduleConfigurations
(rootModuleConf, vs) <- confMap.toSeq
} if (vs contains moduleConf) {
// moduleConf in dd0 maps to rootModuleConf in dd
log.debug(s""":::: ${dd0}: $moduleConf maps to $rootModuleConf""")
dd0.getDependencyConfigurations(moduleConf) foreach { conf =>
dd.addDependencyConfiguration(rootModuleConf, conf)
}
dd0.getIncludeRules(moduleConf) foreach { rule =>
dd.addIncludeRule(rootModuleConf, rule)
}
dd0.getExcludeRules(moduleConf) foreach { rule =>
dd.addExcludeRule(rootModuleConf, rule)
}
dd0.getDependencyArtifacts(moduleConf) foreach { dad =>
dd.addDependencyArtifact(rootModuleConf, dad)
}
}
log.debug(s""":::: remapped dd: $dd""")
dd
}
directDependencies(md0) flatMap { dep => expandInternalDeps(dep, Map()) }
}
def buildArtificialModuleDescriptor(dd: DependencyDescriptor, rootModuleConfigs: Vector[IvyConfiguration], parent: ModuleDescriptor, prOpt: Option[ProjectResolver]): (DefaultModuleDescriptor, Boolean) =
def buildArtificialModuleDescriptor(dd: DependencyDescriptor, rootModuleConfigs: Vector[IvyConfiguration],
parent: ModuleDescriptor, prOpt: Option[ProjectResolver], log: Logger): (DefaultModuleDescriptor, Boolean) =
{
def excludeRuleString(rule: ExcludeRule): String =
s"""Exclude(${rule.getId},${rule.getConfigurations.mkString(",")},${rule.getMatcher})"""
def includeRuleString(rule: IncludeRule): String =
s"""Include(${rule.getId},${rule.getConfigurations.mkString(",")},${rule.getMatcher})"""
def artifactString(dad: DependencyArtifactDescriptor): String =
s"""Artifact(${dad.getName},${dad.getType},${dad.getExt},${dad.getUrl},${dad.getConfigurations.mkString(",")})"""
val mrid = dd.getDependencyRevisionId
val confMap = (dd.getModuleConfigurations map { conf =>
conf + "->(" + dd.getDependencyConfigurations(conf).mkString(",") + ")"
@ -130,6 +147,13 @@ private[sbt] class CachedResolutionResolveCache() {
case rules => Some(conf + "->(" + (rules map includeRuleString).mkString(",") + ")")
}
})
val explicitArtifacts = (dd.getModuleConfigurations.toVector flatMap { conf =>
dd.getDependencyArtifacts(conf).toVector match {
case Vector() => None
case dads => Some(conf + "->(" + (dads map artifactString).mkString(",") + ")")
}
})
val mes = parent.getAllExcludeRules.toVector
val mesStr = (mes map excludeRuleString).mkString(",")
val os = extractOverrides(parent)
@ -281,7 +305,7 @@ private[sbt] trait CachedResolutionResolveEngine extends ResolveEngine {
val os = cache.extractOverrides(md0)
val options1 = new ResolveOptions(options0)
val data = new ResolveData(this, options1)
val mds = cache.buildArtificialModuleDescriptors(md0, data, projectResolver)
val mds = cache.buildArtificialModuleDescriptors(md0, data, projectResolver, log)
def doWork(md: ModuleDescriptor): Either[ResolveException, UpdateReport] =
{
val options1 = new ResolveOptions(options0)

View File

@ -17,7 +17,7 @@ object Sbt extends Build {
s"all control/$task collections/$task io/$task completion/$task"
def buildSettings = Seq(
organization := "org.scala-sbt",
version := "0.13.7-RC3",
version := "0.13.7-SNAPSHOT",
publishArtifact in packageDoc := false,
scalaVersion := "2.10.4",
publishMavenStyle := false,

View File

@ -8,31 +8,45 @@ def commonSettings: Seq[Def.Setting[_]] =
resolvers += Resolver.sonatypeRepo("snapshots")
)
lazy val classifierTest = project.
settings(commonSettings: _*).
settings(
libraryDependencies := Seq(
"net.sf.json-lib" % "json-lib" % "2.4" classifier "jdk15" intransitive()
)
)
lazy val transitiveTest = project.
settings(commonSettings: _*).
settings(
libraryDependencies := Seq(
"junit" % "junit" % "4.11" % "test"
)
)
lazy val a = project.
dependsOn(classifierTest, transitiveTest % "test->test").
settings(commonSettings: _*).
settings(
updateOptions := updateOptions.value.withCachedResolution(true),
artifact in (Compile, packageBin) := Artifact("demo"),
libraryDependencies := Seq(
"net.sf.json-lib" % "json-lib" % "2.4" classifier "jdk15" intransitive(),
"com.typesafe.akka" %% "akka-remote" % "2.3.4" exclude("com.typesafe.akka", "akka-actor_2.10"),
"net.databinder" %% "unfiltered-uploads" % "0.8.0",
"commons-io" % "commons-io" % "1.3",
"com.typesafe" % "config" % "0.4.9-SNAPSHOT",
"junit" % "junit" % "4.11" % "test"
"com.typesafe" % "config" % "0.4.9-SNAPSHOT"
)
)
lazy val b = project.
dependsOn(classifierTest, transitiveTest % "test->test").
settings(commonSettings: _*).
settings(
libraryDependencies := Seq(
"net.sf.json-lib" % "json-lib" % "2.4" classifier "jdk15" intransitive(),
"com.typesafe.akka" %% "akka-remote" % "2.3.4" exclude("com.typesafe.akka", "akka-actor_2.10"),
"net.databinder" %% "unfiltered-uploads" % "0.8.0",
"commons-io" % "commons-io" % "1.3",
"com.typesafe" % "config" % "0.4.9-SNAPSHOT",
"junit" % "junit" % "4.11" % "test"
"com.typesafe" % "config" % "0.4.9-SNAPSHOT"
)
)
@ -51,6 +65,9 @@ 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} filterNot { _.data.getName == "demo_2.10.jar"}
val atestcp = (externalDependencyClasspath in Test in a).value.sortBy {_.data.getName}
val btestcp = (externalDependencyClasspath in Test in b).value.sortBy {_.data.getName}
val ctestcp = (externalDependencyClasspath in Test in c).value.sortBy {_.data.getName} filterNot { _.data.getName == "demo_2.10.jar"}
if (ctestcp exists { _.data.getName contains "junit-4.11.jar" }) {
sys.error("junit found when it should be excluded: " + ctestcp.toString)
@ -59,6 +76,11 @@ lazy val root = (project in file(".")).
else sys.error("Different classpaths are found:" +
"\n - a (cached) " + acp.toString +
"\n - b (plain) " + bcp.toString +
"\n - c (inter-project) " + ccp.toString)
"\n - c (inter-project) " + ccp.toString)
if (atestcp == btestcp) ()
else sys.error("Different classpaths are found:" +
"\n - a test (cached) " + atestcp.toString +
"\n - b test (plain) " + btestcp.toString)
}
)