From 97163f095157d85f32bd02f599951d8393494d58 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 31 Oct 2014 21:18:22 -0400 Subject: [PATCH 01/16] rename scripted tests --- .../{cached-resolution => cached-resolution-basic}/a/A.java | 0 .../{cached-resolution => cached-resolution-basic}/c/C.java | 0 .../{cached-resolution => cached-resolution-basic}/multi.sbt | 0 .../{cached-resolution => cached-resolution-basic}/test | 0 .../{cached-resolution3 => cached-resolution-exclude}/multi.sbt | 0 .../{cached-resolution3 => cached-resolution-exclude}/test | 0 .../{cached-resolution2 => cached-resolution-force}/multi.sbt | 0 .../{cached-resolution2 => cached-resolution-force}/test | 0 .../{cached-resolution4 => cached-resolution-overrides}/multi.sbt | 0 .../{cached-resolution4 => cached-resolution-overrides}/test | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename sbt/src/sbt-test/dependency-management/{cached-resolution => cached-resolution-basic}/a/A.java (100%) rename sbt/src/sbt-test/dependency-management/{cached-resolution => cached-resolution-basic}/c/C.java (100%) rename sbt/src/sbt-test/dependency-management/{cached-resolution => cached-resolution-basic}/multi.sbt (100%) rename sbt/src/sbt-test/dependency-management/{cached-resolution => cached-resolution-basic}/test (100%) rename sbt/src/sbt-test/dependency-management/{cached-resolution3 => cached-resolution-exclude}/multi.sbt (100%) rename sbt/src/sbt-test/dependency-management/{cached-resolution3 => cached-resolution-exclude}/test (100%) rename sbt/src/sbt-test/dependency-management/{cached-resolution2 => cached-resolution-force}/multi.sbt (100%) rename sbt/src/sbt-test/dependency-management/{cached-resolution2 => cached-resolution-force}/test (100%) rename sbt/src/sbt-test/dependency-management/{cached-resolution4 => cached-resolution-overrides}/multi.sbt (100%) rename sbt/src/sbt-test/dependency-management/{cached-resolution4 => cached-resolution-overrides}/test (100%) diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution/a/A.java b/sbt/src/sbt-test/dependency-management/cached-resolution-basic/a/A.java similarity index 100% rename from sbt/src/sbt-test/dependency-management/cached-resolution/a/A.java rename to sbt/src/sbt-test/dependency-management/cached-resolution-basic/a/A.java diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution/c/C.java b/sbt/src/sbt-test/dependency-management/cached-resolution-basic/c/C.java similarity index 100% rename from sbt/src/sbt-test/dependency-management/cached-resolution/c/C.java rename to sbt/src/sbt-test/dependency-management/cached-resolution-basic/c/C.java diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-basic/multi.sbt similarity index 100% rename from sbt/src/sbt-test/dependency-management/cached-resolution/multi.sbt rename to sbt/src/sbt-test/dependency-management/cached-resolution-basic/multi.sbt diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution/test b/sbt/src/sbt-test/dependency-management/cached-resolution-basic/test similarity index 100% rename from sbt/src/sbt-test/dependency-management/cached-resolution/test rename to sbt/src/sbt-test/dependency-management/cached-resolution-basic/test diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution3/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-exclude/multi.sbt similarity index 100% rename from sbt/src/sbt-test/dependency-management/cached-resolution3/multi.sbt rename to sbt/src/sbt-test/dependency-management/cached-resolution-exclude/multi.sbt diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution3/test b/sbt/src/sbt-test/dependency-management/cached-resolution-exclude/test similarity index 100% rename from sbt/src/sbt-test/dependency-management/cached-resolution3/test rename to sbt/src/sbt-test/dependency-management/cached-resolution-exclude/test diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution2/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-force/multi.sbt similarity index 100% rename from sbt/src/sbt-test/dependency-management/cached-resolution2/multi.sbt rename to sbt/src/sbt-test/dependency-management/cached-resolution-force/multi.sbt diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution2/test b/sbt/src/sbt-test/dependency-management/cached-resolution-force/test similarity index 100% rename from sbt/src/sbt-test/dependency-management/cached-resolution2/test rename to sbt/src/sbt-test/dependency-management/cached-resolution-force/test diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution4/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-overrides/multi.sbt similarity index 100% rename from sbt/src/sbt-test/dependency-management/cached-resolution4/multi.sbt rename to sbt/src/sbt-test/dependency-management/cached-resolution-overrides/multi.sbt diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution4/test b/sbt/src/sbt-test/dependency-management/cached-resolution-overrides/test similarity index 100% rename from sbt/src/sbt-test/dependency-management/cached-resolution4/test rename to sbt/src/sbt-test/dependency-management/cached-resolution-overrides/test From 398e15ab63c5497a1e3e476eb82366c3be29c4db Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 1 Nov 2014 20:45:44 -0400 Subject: [PATCH 02/16] Fixes #1710. Fixed cached resolution mutual eviction When stitching the minigraphs together only exclude the artifacts that were evicted in *all* graphs, instead of some graphs. Consider the following scenario: - Y1 evicts slf4j-api 1.6.6 and picks 1.7.5 - Y2 evicts slf4j-api 1.7.5 and picks 1.6.6 At the root level, we need to use our own judgement and pick 1.7.5. --- .../CachedResolutionResolveEngine.scala | 2 +- .../cached-resolution-conflicts/multi.sbt | 62 +++++++++++++++++++ .../cached-resolution-conflicts/test | 7 +++ 3 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/multi.sbt create mode 100644 sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/test diff --git a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala index 73003bcb7..88dd4241c 100644 --- a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala +++ b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala @@ -321,7 +321,7 @@ private[sbt] trait CachedResolutionResolveEngine extends ResolveEngine { val merged = (modules groupBy { m => (m.module.organization, m.module.name, m.module.revision) }).toSeq.toVector flatMap { case ((org, name, version), xs) => if (xs.size < 2) xs - else Vector(xs.head.copy(evicted = xs exists { _.evicted }, callers = xs flatMap { _.callers })) + else Vector(xs.head.copy(evicted = xs forall { _.evicted }, callers = xs flatMap { _.callers })) } val conflicts = merged filter { m => !m.evicted && m.problem.isEmpty } if (conflicts.size < 2) merged diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/multi.sbt new file mode 100644 index 000000000..3efee9d49 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/multi.sbt @@ -0,0 +1,62 @@ +// https://github.com/sbt/sbt/issues/1710 +lazy val check = taskKey[Unit]("Runs the check") + +def commonSettings: Seq[Def.Setting[_]] = + Seq( + organization := "com.example", + version := "0.1.0", + ivyPaths := new IvyPaths( (baseDirectory in ThisBuild).value, Some((baseDirectory in LocalRootProject).value / "ivy-cache")), + dependencyCacheDirectory := (baseDirectory in LocalRootProject).value / "dependency", + scalaVersion := "2.10.4", + fullResolvers := fullResolvers.value.filterNot(_.name == "inter-project") + ) + +def cachedResolutionSettings: Seq[Def.Setting[_]] = + commonSettings ++ Seq( + updateOptions := updateOptions.value.withCachedResolution(true) + ) + +lazy val X1 = project. + settings(commonSettings: _*). + settings( + libraryDependencies ++= Seq( + "com.example" %% "y1" % "0.1.0" % "compile->compile;runtime->runtime", + "com.example" %% "y2" % "0.1.0" % "compile->compile;runtime->runtime") + ) + +lazy val Y1 = project. + settings(commonSettings: _*). + settings( + name := "y1", + libraryDependencies ++= Seq( + // this includes slf4j 1.7.5 + "com.ning" % "async-http-client" % "1.8.14", + // this includes slf4j 1.6.6 + "com.twitter" % "summingbird-core_2.10" % "0.5.0", + "org.slf4j" % "slf4j-api" % "1.6.6" force() + ) + ) + +lazy val Y2 = project. + settings(commonSettings: _*). + settings( + name := "y2", + libraryDependencies ++= Seq( + // this includes slf4j 1.6.6 + "com.twitter" % "summingbird-core_2.10" % "0.5.0", + // this includes slf4j 1.7.5 + "com.ning" % "async-http-client" % "1.8.14") + ) + +lazy val root = (project in file(".")). + settings( + organization in ThisBuild := "org.example", + version in ThisBuild := "1.0", + check := { + val x1cp = (externalDependencyClasspath in Compile in X1).value.sortBy {_.data.getName} + // sys.error("slf4j-api is not found on X1" + x1cp) + if (!(x1cp exists {_.data.getName contains "slf4j-api"})) { + sys.error("slf4j-api is not found on X1" + x1cp) + } + } + ) diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/test b/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/test new file mode 100644 index 000000000..f91a13d11 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-conflicts/test @@ -0,0 +1,7 @@ +> debug + +> Y1/publishLocal + +> Y2/publishLocal + +> check From c1fc84662f4c533974d2e3f0eeb70ccf6208f6ff Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Tue, 4 Nov 2014 17:48:46 -0500 Subject: [PATCH 03/16] scalariform --- util/collection/src/main/scala/sbt/Settings.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/util/collection/src/main/scala/sbt/Settings.scala b/util/collection/src/main/scala/sbt/Settings.scala index bc4aca4ce..de7d9a8fb 100644 --- a/util/collection/src/main/scala/sbt/Settings.scala +++ b/util/collection/src/main/scala/sbt/Settings.scala @@ -85,7 +85,6 @@ trait Init[Scope] { */ private[sbt] final def validated[T](key: ScopedKey[T], selfRefOk: Boolean): ValidationCapture[T] = new ValidationCapture(key, selfRefOk) - @deprecated("0.13.7", "Use the version with default arguments and default paramter.") final def derive[T](s: Setting[T], allowDynamic: Boolean, filter: Scope => Boolean, trigger: AttributeKey[_] => Boolean): Setting[T] = derive(s, allowDynamic, filter, trigger, false) From ca0f3530f5bfb193cf9fcf10d1d8be6c1c44e1f4 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Tue, 4 Nov 2014 17:56:34 -0500 Subject: [PATCH 04/16] Fixes #1711. Fixes cached resolution pulling in % "test" from subproj deps --- .../CachedResolutionResolveEngine.scala | 6 ++- .../cached-resolution-basic/multi.sbt | 49 ++++++++++++------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala index 88dd4241c..fa8361c95 100644 --- a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala +++ b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala @@ -54,8 +54,10 @@ private[sbt] class CachedResolutionResolveCache() { def expandInternalDeps(dep: DependencyDescriptor): Vector[DependencyDescriptor] = prOpt map { _.getModuleDescriptor(dep.getDependencyRevisionId) match { - case Some(internal) => directDependencies(internal) flatMap expandInternalDeps - case _ => Vector(dep) + case Some(internal) => directDependencies(internal) filter { dd => + !dd.getDependencyConfigurations("compile").isEmpty + } flatMap expandInternalDeps + case _ => Vector(dep) } } getOrElse Vector(dep) val expanded = directDependencies(md0) flatMap expandInternalDeps diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-basic/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-basic/multi.sbt index b5af8b348..40805eff0 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-basic/multi.sbt +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-basic/multi.sbt @@ -4,36 +4,43 @@ def commonSettings: Seq[Def.Setting[_]] = Seq( ivyPaths := new IvyPaths( (baseDirectory in ThisBuild).value, Some((baseDirectory in LocalRootProject).value / "ivy-cache")), dependencyCacheDirectory := (baseDirectory in LocalRootProject).value / "dependency", + scalaVersion := "2.10.4", + resolvers += Resolver.sonatypeRepo("snapshots") + ) + +lazy val a = project. + 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" - ), - scalaVersion := "2.10.4", - resolvers += Resolver.sonatypeRepo("snapshots") - ) - -def consolidatedResolutionSettings: Seq[Def.Setting[_]] = - commonSettings ++ Seq( - updateOptions := updateOptions.value.withConsolidatedResolution(true) - ) - -lazy val a = project. - settings(consolidatedResolutionSettings: _*). - settings( - artifact in (Compile, packageBin) := Artifact("demo") + "com.typesafe" % "config" % "0.4.9-SNAPSHOT", + "junit" % "junit" % "4.11" % "test" + ) ) lazy val b = project. - settings(commonSettings: _*) + 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" + ) + ) lazy val c = project. dependsOn(a). - settings(consolidatedResolutionSettings: _*). + settings(commonSettings: _*). settings( - // libraryDependencies := Seq(organization.value %% "a" % version.value) + updateOptions := updateOptions.value.withCachedResolution(true) ) lazy val root = (project in file(".")). @@ -43,7 +50,11 @@ lazy val root = (project in file(".")). check := { 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 ccp = (externalDependencyClasspath in Compile in c).value.sortBy {_.data.getName} filterNot { _.data.getName == "demo_2.10.jar"} + 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) + } if (acp == bcp && acp == ccp) () else sys.error("Different classpaths are found:" + "\n - a (cached) " + acp.toString + From 760a4ab5d2d1de9c3e6608f9a350f4e92d06fc65 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Tue, 4 Nov 2014 19:01:30 -0500 Subject: [PATCH 05/16] Fixes #1704. Fixes cached resolution + module-level exclusions --- .../sbt/ivyint/CachedResolutionResolveEngine.scala | 7 ++++++- .../cached-resolution-exclude/multi.sbt | 11 +++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala index fa8361c95..4b852f48a 100644 --- a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala +++ b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala @@ -87,8 +87,10 @@ private[sbt] class CachedResolutionResolveCache() { case rules => Some(conf + "->(" + (rules map includeRuleString).mkString(",") + ")") } }) + val mes = parent.getAllExcludeRules.toVector + val mesStr = (mes map excludeRuleString).mkString(",") val os = extractOverrides(parent) - val moduleLevel = s"""dependencyOverrides=${os.mkString(",")}""" + val moduleLevel = s"""dependencyOverrides=${os.mkString(",")};moduleExclusions=$mesStr""" val depsString = s"""$mrid;${confMap.mkString(",")};isForce=${dd.isForce};isChanging=${dd.isChanging};isTransitive=${dd.isTransitive};""" + s"""exclusions=${exclusions.mkString(",")};inclusions=${inclusions.mkString(",")};$moduleLevel;""" val sha1 = Hash.toHex(Hash(depsString)) @@ -102,6 +104,9 @@ private[sbt] class CachedResolutionResolveCache() { os foreach { ovr => md1.addDependencyDescriptorMediator(ovr.moduleId, ovr.pm, ovr.ddm) } + mes foreach { exclude => + md1.addExcludeRule(exclude) + } (md1, IvySbt.isChanging(dd)) } def extractOverrides(md0: ModuleDescriptor): Vector[IvyOverride] = diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-exclude/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-exclude/multi.sbt index 3d3196156..c9233cc5a 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-exclude/multi.sbt +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-exclude/multi.sbt @@ -17,7 +17,11 @@ def cachedResolutionSettings: Seq[Def.Setting[_]] = lazy val a = project. settings(cachedResolutionSettings: _*). settings( - libraryDependencies += "net.databinder" %% "unfiltered-uploads" % "0.8.0" exclude("commons-io", "commons-io") + libraryDependencies += "net.databinder" %% "unfiltered-uploads" % "0.8.0" exclude("commons-io", "commons-io"), + ivyXML := + + + ) lazy val b = project. @@ -32,12 +36,15 @@ lazy val root = (project in file(".")). organization in ThisBuild := "org.example", version in ThisBuild := "1.0", check := { - // sys.error(dependencyCacheDirectory.value.toString) val acp = (externalDependencyClasspath in Compile in a).value.sortBy {_.data.getName} val bcp = (externalDependencyClasspath in Compile in b).value.sortBy {_.data.getName} if (acp exists { _.data.getName contains "commons-io" }) { sys.error("commons-io found when it should be excluded") } + if (acp exists { _.data.getName contains "commons-codec" }) { + sys.error("commons-codec found when it should be excluded") + } + // This is checking to make sure excluded graph is not getting picked up if (!(bcp exists { _.data.getName contains "commons-io" })) { sys.error("commons-io NOT found when it should NOT be excluded") } From af9e520e2817f7bf38cbc3bb474bce4a310a04af Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 6 Nov 2014 00:28:26 -0500 Subject: [PATCH 06/16] expand internal dependencies more accurately --- .../CachedResolutionResolveEngine.scala | 104 +++++++++++++----- 1 file changed, 74 insertions(+), 30 deletions(-) diff --git a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala index 4b852f48a..120e3042c 100644 --- a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala +++ b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala @@ -11,7 +11,7 @@ import org.apache.ivy.core import core.resolve._ import core.module.id.{ ModuleRevisionId, ModuleId => IvyModuleId } import core.report.{ ResolveReport, ConfigurationResolveReport, DownloadReport } -import core.module.descriptor.{ DefaultModuleDescriptor, ModuleDescriptor, DependencyDescriptor, Configuration => IvyConfiguration, ExcludeRule, IncludeRule } +import core.module.descriptor.{ DefaultModuleDescriptor, ModuleDescriptor, DefaultDependencyDescriptor, DependencyDescriptor, Configuration => IvyConfiguration, ExcludeRule, IncludeRule } import core.module.descriptor.OverrideDependencyDescriptorMediator import core.{ IvyPatternHelper, LogOptions } import org.apache.ivy.util.Message @@ -29,42 +29,85 @@ private[sbt] class CachedResolutionResolveCache() { val updateReportCache: concurrent.Map[ModuleRevisionId, Either[ResolveException, UpdateReport]] = concurrent.TrieMap() val resolveReportCache: concurrent.Map[ModuleRevisionId, ResolveReport] = concurrent.TrieMap() val resolvePropertiesCache: concurrent.Map[ModuleRevisionId, String] = concurrent.TrieMap() - val directDependencyCache: concurrent.Map[ModuleRevisionId, Vector[DependencyDescriptor]] = concurrent.TrieMap() val conflictCache: concurrent.Map[(ModuleID, ModuleID), (Vector[ModuleID], Vector[ModuleID], String)] = concurrent.TrieMap() val maxConflictCacheSize: Int = 10000 def clean(md0: ModuleDescriptor, prOpt: Option[ProjectResolver]): Unit = { - val mrid0 = md0.getModuleRevisionId - 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) - } + updateReportCache.clear } def directDependencies(md0: ModuleDescriptor): Vector[DependencyDescriptor] = - directDependencyCache.getOrElseUpdate(md0.getModuleRevisionId, md0.getDependencies.toVector) - - def buildArtificialModuleDescriptors(md0: ModuleDescriptor, prOpt: Option[ProjectResolver]): Vector[(DefaultModuleDescriptor, Boolean)] = + md0.getDependencies.toVector + def buildArtificialModuleDescriptors(md0: ModuleDescriptor, data: ResolveData, prOpt: Option[ProjectResolver]): Vector[(DefaultModuleDescriptor, Boolean)] = { - def expandInternalDeps(dep: DependencyDescriptor): Vector[DependencyDescriptor] = - prOpt map { - _.getModuleDescriptor(dep.getDependencyRevisionId) match { - case Some(internal) => directDependencies(internal) filter { dd => - !dd.getDependencyConfigurations("compile").isEmpty - } flatMap expandInternalDeps - case _ => Vector(dep) - } - } getOrElse Vector(dep) - val expanded = directDependencies(md0) flatMap expandInternalDeps - val rootModuleConfigs = md0.getConfigurations.toVector + val rootModuleConfigs = md0.getConfigurations.toArray.toVector + val expanded = expandInternalDependencies(md0, data, prOpt) expanded map { buildArtificialModuleDescriptor(_, rootModuleConfigs, md0, prOpt) } } - + // 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] = + { + 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) => + val allConfigurations: Vector[String] = + (if (confMap.isEmpty) nextConfMap(dep, confMap) + else confMap).values.flatten.toList.distinct.toVector + directDependencies(internal) filter { dd => + allConfigurations exists { conf => !dd.getDependencyConfigurations(conf).isEmpty } + } flatMap { dd => expandInternalDeps(dd, nextConfMap(dd, confMap)) } + case _ => + if (confMap.isEmpty) Vector(dep) + else Vector(remapConfigurations(dep, confMap)) + } + def internalDependency(dep: DependencyDescriptor): Option[ModuleDescriptor] = + prOpt match { + case Some(pr) => pr.getModuleDescriptor(dep.getDependencyRevisionId) + case _ => None + } + // This creates confMap. The key of the map is rootModuleConf for md0, the value is the dependency configs for dd. + def nextConfMap(dd: DependencyDescriptor, previous: Map[String, Array[String]]): Map[String, Array[String]] = + if (previous.isEmpty) { + ListMap(dd.getModuleConfigurations.toList map { conf => + conf -> (dd.getDependencyConfigurations(conf) flatMap { confName => + if (confName == "*") Array(confName) + else rootNode.getRealConfs(confName) + }) + }: _*) + } else previous map { + case (rootModuleConf, vs) => + rootModuleConf -> (vs flatMap { conf => + dd.getDependencyConfigurations(conf) flatMap { confName => + if (confName == "*") Array(confName) + else rootNode.getRealConfs(confName) + } + }) + } + def remapConfigurations(dd0: DependencyDescriptor, confMap: Map[String, Array[String]]): DependencyDescriptor = + { + val dd = new DefaultDependencyDescriptor(md0, dd0.getDependencyRevisionId, dd0.getDynamicConstraintDependencyRevisionId, + dd0.isForce, dd0.isChanging, dd0.isTransitive) + for { + moduleConf <- dd0.getModuleConfigurations + (rootModuleConf, vs) <- confMap + } if (vs contains moduleConf) { + // moduleConf in dd0 maps to rootModuleConf in dd + dd0.getDependencyConfigurations(moduleConf) foreach { conf => + dd.addDependencyConfiguration(rootModuleConf, conf) + } + dd0.getExcludeRules(moduleConf) foreach { rule => + dd.addExcludeRule(rootModuleConf, rule) + } + } + dd + } + directDependencies(md0) flatMap { dep => expandInternalDeps(dep, Map()) } + } def buildArtificialModuleDescriptor(dd: DependencyDescriptor, rootModuleConfigs: Vector[IvyConfiguration], parent: ModuleDescriptor, prOpt: Option[ProjectResolver]): (DefaultModuleDescriptor, Boolean) = { def excludeRuleString(rule: ExcludeRule): String = @@ -235,9 +278,10 @@ 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 os = cache.extractOverrides(md0) - val mds = cache.buildArtificialModuleDescriptors(md0, projectResolver) + val options1 = new ResolveOptions(options0) + val data = new ResolveData(this, options1) + val mds = cache.buildArtificialModuleDescriptors(md0, data, projectResolver) def doWork(md: ModuleDescriptor): Either[ResolveException, UpdateReport] = { val options1 = new ResolveOptions(options0) From a832b4e6db9dd01eb07b57276fa6edde136d88a3 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 6 Nov 2014 13:56:24 -0500 Subject: [PATCH 07/16] 0.13.7-RC3 --- project/Sbt.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Sbt.scala b/project/Sbt.scala index e4320f873..354342448 100644 --- a/project/Sbt.scala +++ b/project/Sbt.scala @@ -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-RC2", + version := "0.13.7-RC3", publishArtifact in packageDoc := false, scalaVersion := "2.10.4", publishMavenStyle := false, From 4fd5d505f1930cbb8776e74c12809430f95754be Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sun, 9 Nov 2014 23:32:03 -0500 Subject: [PATCH 08/16] fixes #1719. cached resolution: fixes handling of classifier and other explicit artifacts --- .../CachedResolutionResolveEngine.scala | 36 ++++++++++++++----- project/Sbt.scala | 2 +- .../a/A.java | 0 .../c/C.java | 0 .../multi.sbt | 12 +++++-- .../test | 2 ++ 6 files changed, 40 insertions(+), 12 deletions(-) rename sbt/src/sbt-test/dependency-management/{cached-resolution-basic => cached-resolution-classifier}/a/A.java (100%) rename sbt/src/sbt-test/dependency-management/{cached-resolution-basic => cached-resolution-classifier}/c/C.java (100%) rename sbt/src/sbt-test/dependency-management/{cached-resolution-basic => cached-resolution-classifier}/multi.sbt (93%) rename sbt/src/sbt-test/dependency-management/{cached-resolution-basic => cached-resolution-classifier}/test (88%) diff --git a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala index 120e3042c..be2a366a3 100644 --- a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala +++ b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala @@ -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,19 +37,21 @@ 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] = @@ -93,27 +95,36 @@ private[sbt] class CachedResolutionResolveCache() { val dd = new DefaultDependencyDescriptor(md0, dd0.getDependencyRevisionId, dd0.getDynamicConstraintDependencyRevisionId, dd0.isForce, dd0.isChanging, dd0.isTransitive) for { - moduleConf <- dd0.getModuleConfigurations - (rootModuleConf, vs) <- confMap + moduleConf <- dd0.getModuleConfigurations.toVector + (rootModuleConf, vs) <- confMap.toSeq } if (vs contains moduleConf) { // moduleConf in dd0 maps to rootModuleConf in dd 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) + } } 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 +141,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 +299,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) diff --git a/project/Sbt.scala b/project/Sbt.scala index 354342448..0c23ce6d2 100644 --- a/project/Sbt.scala +++ b/project/Sbt.scala @@ -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, diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-basic/a/A.java b/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/a/A.java similarity index 100% rename from sbt/src/sbt-test/dependency-management/cached-resolution-basic/a/A.java rename to sbt/src/sbt-test/dependency-management/cached-resolution-classifier/a/A.java diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-basic/c/C.java b/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/c/C.java similarity index 100% rename from sbt/src/sbt-test/dependency-management/cached-resolution-basic/c/C.java rename to sbt/src/sbt-test/dependency-management/cached-resolution-classifier/c/C.java diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-basic/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/multi.sbt similarity index 93% rename from sbt/src/sbt-test/dependency-management/cached-resolution-basic/multi.sbt rename to sbt/src/sbt-test/dependency-management/cached-resolution-classifier/multi.sbt index 40805eff0..eac83b079 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-basic/multi.sbt +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/multi.sbt @@ -8,13 +8,21 @@ 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 a = project. + dependsOn(classifierTest). 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", @@ -24,10 +32,10 @@ lazy val a = project. ) lazy val b = project. + dependsOn(classifierTest). 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", diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-basic/test b/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/test similarity index 88% rename from sbt/src/sbt-test/dependency-management/cached-resolution-basic/test rename to sbt/src/sbt-test/dependency-management/cached-resolution-classifier/test index 93a09a407..55cfc0713 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-basic/test +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/test @@ -1,3 +1,5 @@ +> a/update + > a/updateClassifiers > a/publishLocal From 867e2b7a180950e1c20156207ea04f6ff9fcfb69 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Mon, 10 Nov 2014 01:10:37 -0500 Subject: [PATCH 09/16] fixes #1711, #1716. Fixes configuration remapping The actual change is one line at line 64. nextConfMap takes dep instead of dd. --- .../CachedResolutionResolveEngine.scala | 18 ++++++++---- .../cached-resolution-classifier/multi.sbt | 28 ++++++++++++++----- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala index be2a366a3..15c3e3b79 100644 --- a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala +++ b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala @@ -57,15 +57,18 @@ private[sbt] class CachedResolutionResolveCache() { 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 { @@ -90,15 +93,17 @@ 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.toVector + 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) } @@ -112,6 +117,7 @@ private[sbt] class CachedResolutionResolveCache() { dd.addDependencyArtifact(rootModuleConf, dad) } } + log.debug(s""":::: remapped dd: $dd""") dd } directDependencies(md0) flatMap { dep => expandInternalDeps(dep, Map()) } diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/multi.sbt index eac83b079..fceefc6de 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/multi.sbt +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/multi.sbt @@ -16,8 +16,16 @@ lazy val classifierTest = project. ) ) +lazy val transitiveTest = project. + settings(commonSettings: _*). + settings( + libraryDependencies := Seq( + "junit" % "junit" % "4.11" % "test" + ) + ) + lazy val a = project. - dependsOn(classifierTest). + dependsOn(classifierTest, transitiveTest % "test->test"). settings(commonSettings: _*). settings( updateOptions := updateOptions.value.withCachedResolution(true), @@ -26,21 +34,19 @@ lazy val a = project. "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). + dependsOn(classifierTest, transitiveTest % "test->test"). settings(commonSettings: _*). settings( libraryDependencies := Seq( "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" ) ) @@ -59,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) @@ -67,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) } ) From cef4479685d32cc95452f8de03b8c6fcaeb640c8 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Tue, 11 Nov 2014 21:15:11 -0500 Subject: [PATCH 10/16] Fixes #1723. classifier-related fixes - minigraph sha now contains extra attributes from artifacts - artifacts are merged from different mini graphs (in some cases, this should result to better resolution than stock ivy) --- .../CachedResolutionResolveEngine.scala | 70 +++++++++++-------- .../cached-resolution-classifier/multi.sbt | 34 ++++++--- 2 files changed, 64 insertions(+), 40 deletions(-) diff --git a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala index 15c3e3b79..32442167a 100644 --- a/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala +++ b/ivy/src/main/scala/sbt/ivyint/CachedResolutionResolveEngine.scala @@ -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, DependencyArtifactDescriptor } +import core.module.descriptor.{ OverrideDependencyDescriptorMediator, DependencyArtifactDescriptor, DefaultDependencyArtifactDescriptor } import core.{ IvyPatternHelper, LogOptions } import org.apache.ivy.util.Message import org.apache.ivy.plugins.latest.{ ArtifactInfo => IvyArtifactInfo } @@ -42,7 +42,11 @@ private[sbt] class CachedResolutionResolveCache() { log.debug(s":: building artificial module descriptors from ${md0.getModuleRevisionId}") val expanded = expandInternalDependencies(md0, data, prOpt, log) val rootModuleConfigs = md0.getConfigurations.toArray.toVector - expanded map { buildArtificialModuleDescriptor(_, rootModuleConfigs, md0, prOpt, log) } + expanded map { dd => + val arts = dd.getAllDependencyArtifacts.toVector map { x => s"""${x.getName}:${x.getType}:${x.getExt}:${x.getExtraAttributes}""" } + log.debug(s"::: expanded dd: $dd (artifacts: ${arts.mkString(",")})") + buildArtificialModuleDescriptor(dd, rootModuleConfigs, md0, prOpt, log) + } } // This expands out all internal dependencies and merge them into a single graph that consists // only of external dependencies. @@ -58,17 +62,16 @@ private[sbt] class CachedResolutionResolveCache() { 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 + val allConfigurations: Vector[String] = confMap.values.flatten.toVector.distinct val next = nextConfMap(dep, confMap) - val directs = directDependencies(internal) filter { dd => + // direct dependencies of an internal dependency + val directs0 = directDependencies(internal) + val directs = directs0 filter { dd => allConfigurations exists { conf => !dd.getDependencyConfigurations(conf).isEmpty } } directs flatMap { dd => expandInternalDeps(dd, next) } case _ => - if (confMap.isEmpty) Vector(dep) - else Vector(remapConfigurations(dep, confMap, log)) + Vector(remapConfigurations(dep, confMap, log)) } def internalDependency(dep: DependencyDescriptor): Option[ModuleDescriptor] = prOpt match { @@ -77,14 +80,7 @@ private[sbt] class CachedResolutionResolveCache() { } // This creates confMap. The key of the map is rootModuleConf for md0, the value is the dependency configs for dd. def nextConfMap(dd: DependencyDescriptor, previous: Map[String, Array[String]]): Map[String, Array[String]] = - if (previous.isEmpty) { - ListMap(dd.getModuleConfigurations.toList map { conf => - conf -> (dd.getDependencyConfigurations(conf) flatMap { confName => - if (confName == "*") Array(confName) - else rootNode.getRealConfs(confName) - }) - }: _*) - } else previous map { + previous map { case (rootModuleConf, vs) => rootModuleConf -> (vs flatMap { conf => dd.getDependencyConfigurations(conf) flatMap { confName => @@ -93,6 +89,10 @@ private[sbt] class CachedResolutionResolveCache() { } }) } + // The key of the confMap is rootModuleConf for md0, and the values are modules configuratons of dd0. + // For example if project Root depends on project B % "test", and project B depends on junit, + // confMap should contain Map("test", Array("compile")). + // This remaps junit dependency as junit % "test". 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) }}""") @@ -113,14 +113,25 @@ private[sbt] class CachedResolutionResolveCache() { dd0.getExcludeRules(moduleConf) foreach { rule => dd.addExcludeRule(rootModuleConf, rule) } - dd0.getDependencyArtifacts(moduleConf) foreach { dad => - dd.addDependencyArtifact(rootModuleConf, dad) + dd0.getAllDependencyArtifacts foreach { dad0 => + (Option(dad0.getConfigurations) map { confs => confs.isEmpty || confs.contains(moduleConf) || confs.contains("*") }) match { + case Some(false) => // do nothing + case _ => + val dad = new DefaultDependencyArtifactDescriptor(dd, dad0.getName, dad0.getType, dad0.getExt, dad0.getUrl, dad0.getExtraAttributes) + dad.addConfiguration(rootModuleConf) + dd.addDependencyArtifact(rootModuleConf, dad) + } } } log.debug(s""":::: remapped dd: $dd""") dd } - directDependencies(md0) flatMap { dep => expandInternalDeps(dep, Map()) } + directDependencies(md0) flatMap { dep => + val initialMap = Map(dep.getModuleConfigurations map { rootModuleConf => + (rootModuleConf -> Array(rootModuleConf)) + }: _*) + expandInternalDeps(dep, initialMap) + } } def buildArtificialModuleDescriptor(dd: DependencyDescriptor, rootModuleConfigs: Vector[IvyConfiguration], parent: ModuleDescriptor, prOpt: Option[ProjectResolver], log: Logger): (DefaultModuleDescriptor, Boolean) = @@ -130,7 +141,7 @@ private[sbt] class CachedResolutionResolveCache() { 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(",")})""" + s"""Artifact(${dad.getName},${dad.getType},${dad.getExt},${dad.getUrl},${dad.getConfigurations.mkString(",")},${dad.getExtraAttributes})""" val mrid = dd.getDependencyRevisionId val confMap = (dd.getModuleConfigurations map { conf => conf + "->(" + dd.getDependencyConfigurations(conf).mkString(",") + ")" @@ -147,19 +158,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 explicitArtifacts = dd.getAllDependencyArtifacts.toVector map { artifactString } val mes = parent.getAllExcludeRules.toVector val mesStr = (mes map excludeRuleString).mkString(",") val os = extractOverrides(parent) val moduleLevel = s"""dependencyOverrides=${os.mkString(",")};moduleExclusions=$mesStr""" val depsString = s"""$mrid;${confMap.mkString(",")};isForce=${dd.isForce};isChanging=${dd.isChanging};isTransitive=${dd.isTransitive};""" + - s"""exclusions=${exclusions.mkString(",")};inclusions=${inclusions.mkString(",")};$moduleLevel;""" + s"""exclusions=${exclusions.mkString(",")};inclusions=${inclusions.mkString(",")};explicitArtifacts=${explicitArtifacts.mkString(",")};$moduleLevel;""" val sha1 = Hash.toHex(Hash(depsString)) val md1 = new DefaultModuleDescriptor(createID(sbtOrgTemp, "temp-resolve-" + sha1, "1.0"), "release", null, false) with ArtificialModuleDescriptor { def targetModuleRevisionId: ModuleRevisionId = mrid @@ -393,10 +398,17 @@ private[sbt] trait CachedResolutionResolveEngine extends ResolveEngine { } def mergeModuleReports(rootModuleConf: String, modules: Vector[ModuleReport], os: Vector[IvyOverride], log: Logger): Vector[ModuleReport] = { + def mergeModuleReports(org: String, name: String, version: String, xs: Vector[ModuleReport]): ModuleReport = { + val completelyEvicted = xs forall { _.evicted } + val allCallers = xs flatMap { _.callers } + val allArtifacts = (xs flatMap { _.artifacts }).distinct + log.debug(s":: merging module report for $org:$name:$version - $allArtifacts") + xs.head.copy(artifacts = allArtifacts, evicted = completelyEvicted, callers = allCallers) + } val merged = (modules groupBy { m => (m.module.organization, m.module.name, m.module.revision) }).toSeq.toVector flatMap { case ((org, name, version), xs) => if (xs.size < 2) xs - else Vector(xs.head.copy(evicted = xs forall { _.evicted }, callers = xs flatMap { _.callers })) + else Vector(mergeModuleReports(org, name, version, xs)) } val conflicts = merged filter { m => !m.evicted && m.problem.isEmpty } if (conflicts.size < 2) merged diff --git a/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/multi.sbt b/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/multi.sbt index fceefc6de..5fd69c6c6 100644 --- a/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/multi.sbt +++ b/sbt/src/sbt-test/dependency-management/cached-resolution-classifier/multi.sbt @@ -12,7 +12,8 @@ lazy val classifierTest = project. settings(commonSettings: _*). settings( libraryDependencies := Seq( - "net.sf.json-lib" % "json-lib" % "2.4" classifier "jdk15" intransitive() + "net.sf.json-lib" % "json-lib" % "2.4" classifier "jdk15" intransitive(), + "commons-io" % "commons-io" % "1.4" ) ) @@ -33,7 +34,7 @@ lazy val a = project. libraryDependencies := Seq( "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", + "commons-io" % "commons-io" % "1.4" classifier "sources", "com.typesafe" % "config" % "0.4.9-SNAPSHOT" ) ) @@ -45,7 +46,7 @@ lazy val b = project. libraryDependencies := Seq( "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", + "commons-io" % "commons-io" % "1.4" classifier "sources", "com.typesafe" % "config" % "0.4.9-SNAPSHOT" ) ) @@ -65,18 +66,29 @@ 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"} + if (!(acp exists { _.data.getName contains "commons-io-1.4-sources.jar" })) { + sys.error("commons-io-1.4-sources not found when it should be included: " + acp.toString) + } + if (!(acp exists { _.data.getName contains "commons-io-1.4.jar" })) { + sys.error("commons-io-1.4 not found when it should be included: " + acp.toString) + } - 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"} + // stock Ivy implementation doesn't contain regular (non-source) jar, which probably is a bug + val acpWithoutSource = acp filterNot { _.data.getName contains "commons-io-1.4"} + val bcpWithoutSource = bcp filterNot { _.data.getName contains "commons-io-1.4"} + val ccpWithoutSource = ccp filterNot { _.data.getName contains "commons-io-1.4"} + if (acpWithoutSource == bcpWithoutSource && acpWithoutSource == ccpWithoutSource) () + else sys.error("Different classpaths are found:" + + "\n - a (cached) " + acpWithoutSource.toString + + "\n - b (plain) " + bcpWithoutSource.toString + + "\n - c (inter-project) " + ccpWithoutSource.toString) + + val atestcp = (externalDependencyClasspath in Test in a).value.sortBy {_.data.getName} filterNot { _.data.getName contains "commons-io-1.4"} + val btestcp = (externalDependencyClasspath in Test in b).value.sortBy {_.data.getName} filterNot { _.data.getName contains "commons-io-1.4"} + val ctestcp = (externalDependencyClasspath in Test in c).value.sortBy {_.data.getName} filterNot { _.data.getName == "demo_2.10.jar"} filterNot { _.data.getName contains "commons-io-1.4"} if (ctestcp exists { _.data.getName contains "junit-4.11.jar" }) { sys.error("junit found when it should be excluded: " + ctestcp.toString) } - if (acp == bcp && acp == ccp) () - else sys.error("Different classpaths are found:" + - "\n - a (cached) " + acp.toString + - "\n - b (plain) " + bcp.toString + - "\n - c (inter-project) " + ccp.toString) if (atestcp == btestcp) () else sys.error("Different classpaths are found:" + From 98e241f5a73e4470449dfd8a50f1dd9583909a22 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 19 Nov 2014 01:25:52 -0500 Subject: [PATCH 11/16] notes --- notes/0.13.7.markdown | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/notes/0.13.7.markdown b/notes/0.13.7.markdown index 87ad134fb..5d1233085 100644 --- a/notes/0.13.7.markdown +++ b/notes/0.13.7.markdown @@ -1,4 +1,5 @@ + [Cached-Resolution]: http://www.scala-sbt.org/0.13/docs/Cached-Resolution.html [@cunei]: https://github.com/cunei [@eed3si9n]: https://github.com/eed3si9n [@gkossakowski]: https://github.com/gkossakowski @@ -104,7 +105,7 @@ By default circular dependencies are warned, but they do not halt the dependency ### Cached resolution (minigraph caching) -sbt 0.13.7 adds a new update option called *cached resolution*, which replaces consolidated resolution: +sbt 0.13.7 adds a new **experimental** update option called *cached resolution*, which replaces consolidated resolution: updateOptions := updateOptions.value.withCachedResolution(true) @@ -112,6 +113,6 @@ Unlike consolidated resolution, which only consolidated subprojects with identic Once the minigraphs are resolved and saved as files, dependency resolution turns into a matter of loading json file from the second run onwards, which should complete in a matter of seconds even for large projects. Also, because the files are saved under a global `~/.sbt/0.13/dependency` (or what's specified by `sbt.dependency.base` flag), the resolution result is shared across all builds. -Breaking graphs into minigraphs allows partial resolution results to be shared, which scales better for subprojects with similar but slightly different dependencies, and also for making small changes to the dependencies graph over time. +Breaking graphs into minigraphs allows partial resolution results to be shared, which scales better for subprojects with similar but slightly different dependencies, and also for making small changes to the dependencies graph over time. See [documentation on cached resolution][Cached-Resolution] for more details. [#1631][1631] by [@eed3si9n][@eed3si9n] From 48b8b9492778da0e724fb83b62c42c79ec0932e4 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 19 Nov 2014 01:40:59 -0500 Subject: [PATCH 12/16] launchconfig --- src/main/conscript/sbt/launchconfig | 2 +- src/main/conscript/scalas/launchconfig | 2 +- src/main/conscript/screpl/launchconfig | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/conscript/sbt/launchconfig b/src/main/conscript/sbt/launchconfig index b011c5f16..42fd0f075 100644 --- a/src/main/conscript/sbt/launchconfig +++ b/src/main/conscript/sbt/launchconfig @@ -4,7 +4,7 @@ [app] org: ${sbt.organization-org.scala-sbt} name: sbt - version: ${sbt.version-read(sbt.version)[0.13.5]} + version: ${sbt.version-read(sbt.version)[0.13.7]} class: sbt.xMain components: xsbti,extra cross-versioned: ${sbt.cross.versioned-false} diff --git a/src/main/conscript/scalas/launchconfig b/src/main/conscript/scalas/launchconfig index 52ea0cccc..49bfed9d2 100644 --- a/src/main/conscript/scalas/launchconfig +++ b/src/main/conscript/scalas/launchconfig @@ -4,7 +4,7 @@ [app] org: ${sbt.organization-org.scala-sbt} name: sbt - version: ${sbt.version-read(sbt.version)[0.13.5]} + version: ${sbt.version-read(sbt.version)[0.13.7]} class: sbt.ScriptMain components: xsbti,extra cross-versioned: ${sbt.cross.versioned-false} diff --git a/src/main/conscript/screpl/launchconfig b/src/main/conscript/screpl/launchconfig index 70d26c10f..704c5aa51 100644 --- a/src/main/conscript/screpl/launchconfig +++ b/src/main/conscript/screpl/launchconfig @@ -4,7 +4,7 @@ [app] org: ${sbt.organization-org.scala-sbt} name: sbt - version: ${sbt.version-read(sbt.version)[0.13.5]} + version: ${sbt.version-read(sbt.version)[0.13.7]} class: sbt.ConsoleMain components: xsbti,extra cross-versioned: ${sbt.cross.versioned-false} From 46f7e2d2587a1fbbbda9634ff2f9f9007f842246 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 19 Nov 2014 01:53:59 -0500 Subject: [PATCH 13/16] notes --- notes/0.13.7.markdown | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/notes/0.13.7.markdown b/notes/0.13.7.markdown index 5d1233085..9ffab81c0 100644 --- a/notes/0.13.7.markdown +++ b/notes/0.13.7.markdown @@ -5,7 +5,7 @@ [@gkossakowski]: https://github.com/gkossakowski [@jsuereth]: https://github.com/jsuereth [@ajozwik]: https://github.com/ajozwik - [@cocumpkin]: https://github.com/copumpkin + [@copumpkin]: https://github.com/copumpkin [@Duhemm]: https://github.com/Duhemm [@jedesah]: https://github.com/jedesah [@rkrzewski]: https://github.com/rkrzewski @@ -16,12 +16,14 @@ [1237]: https://github.com/sbt/sbt/issues/1237 [1430]: https://github.com/sbt/sbt/issues/1430 [1544]: https://github.com/sbt/sbt/issues/1544 + [1563]: https://github.com/sbt/sbt/pull/1563 [1572]: https://github.com/sbt/sbt/pull/1572 [1573]: https://github.com/sbt/sbt/pull/1573 [1579]: https://github.com/sbt/sbt/pull/1579 [1584]: https://github.com/sbt/sbt/pull/1584 [1586]: https://github.com/sbt/sbt/pull/1586 [1589]: https://github.com/sbt/sbt/issues/1589 + [1590]: https://github.com/sbt/sbt/pull/1590 [1591]: https://github.com/sbt/sbt/pull/1591 [1598]: https://github.com/sbt/sbt/issues/1598 [1600]: https://github.com/sbt/sbt/pull/1600 @@ -42,11 +44,9 @@ - Maven artifact dependencies will limit their transitive dependencies to `Compile` rather than *every configuration* if no `master` configuration is found. [#1586][1586] by [@jsuereth][@jsuereth] - The new natural whitspace handling parser is unable to cope with certain classes of Scala syntax. In particular, top-level pattern matches, or multi-value defintions are no longer supported: -```scala val x, y = project // BAD - val x = project // - val y = project // GOOD -``` + val x = project // + val y = project // GOOD ### Improvements From 37bf97dfce30dc71314f3dfa5dd7c810893c870a Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 19 Nov 2014 01:55:07 -0500 Subject: [PATCH 14/16] notes --- notes/0.13.7.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/notes/0.13.7.markdown b/notes/0.13.7.markdown index 9ffab81c0..ceb104882 100644 --- a/notes/0.13.7.markdown +++ b/notes/0.13.7.markdown @@ -73,7 +73,7 @@ [#1611][1611]/[#1618][1618] by [@jsuereth][@jsuereth] - Fixes NullPointerException when using `ChainResolver` and Maven repositories. [#1611][1611]/[#1618][1618] by [@jsuereth][@jsuereth] - Fixes `Resolver`'s `url` method dropping `descriptorOptional` and `skipConsistencyCheck`. [#1621][1621] by [@tmandke][@tmandke] -- Revert `useLatestSnapshot` on `updateOptions` to default to `false`. Reverts chain resolver to previous behavior. [#1683]/[1683] by [@jsuereth][@jsuereth] +- Revert `useLatestSnapshot` on `updateOptions` to default to `false`. Reverts chain resolver to previous behavior. [#1683][1683] by [@jsuereth][@jsuereth] ### Natural whitespace handling From 80b9cd0cc0f3ba4675f4052a1a8fbf5058f6209b Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 19 Nov 2014 01:55:56 -0500 Subject: [PATCH 15/16] notes --- notes/0.13.7.markdown | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/notes/0.13.7.markdown b/notes/0.13.7.markdown index ceb104882..2dfd771fc 100644 --- a/notes/0.13.7.markdown +++ b/notes/0.13.7.markdown @@ -44,9 +44,9 @@ - Maven artifact dependencies will limit their transitive dependencies to `Compile` rather than *every configuration* if no `master` configuration is found. [#1586][1586] by [@jsuereth][@jsuereth] - The new natural whitspace handling parser is unable to cope with certain classes of Scala syntax. In particular, top-level pattern matches, or multi-value defintions are no longer supported: - val x, y = project // BAD - val x = project // - val y = project // GOOD + val x, y = project // BAD + val x = project // + val y = project // GOOD ### Improvements From b6a201173418e827c0a9641ad5569d900a33f0b1 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 19 Nov 2014 01:57:28 -0500 Subject: [PATCH 16/16] notes --- notes/0.13.7.markdown | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/notes/0.13.7.markdown b/notes/0.13.7.markdown index 2dfd771fc..986136ce7 100644 --- a/notes/0.13.7.markdown +++ b/notes/0.13.7.markdown @@ -42,11 +42,13 @@ ### Fixes with compatibility implications - Maven artifact dependencies will limit their transitive dependencies to `Compile` rather than *every configuration* if no `master` configuration is found. [#1586][1586] by [@jsuereth][@jsuereth] -- The new natural whitspace handling parser is unable to cope with certain classes of Scala syntax. In particular, top-level pattern matches, or multi-value defintions are no longer supported: +- The new natural whitspace handling parser is unable to cope with certain classes of Scala syntax. In particular, top-level pattern matches, or multi-value defintions are no longer supported. - val x, y = project // BAD - val x = project // - val y = project // GOOD +Here are examples: + + val x, y = project // BAD + val x = project // + val y = project // GOOD ### Improvements