From c12c9c6187baf15bc24796693bb9f949d53406c8 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 11 Nov 2016 02:44:56 -0500 Subject: [PATCH 1/4] Apply scalaVersion enforcement to Compile related configs Fixes #2786. Ref #2634. sbt 0.13.12 added Ivy mediator that enforces scalaOrganization and scalaVersion for Scala toolchain artifacts. This turns out to be a bit too aggressive because Ivy configurations can be used as an independent dependency graph that does not rely on the scalaVersion used by Compile configuration. By enforcing scalaVersion in those graph causes runtime failure. This change checks if the configuration extends Default, Compile, Provided, or Optional before enforcing scalaVersion. --- .../sbt/internal/librarymanagement/Ivy.scala | 16 +++++++++++++++- .../librarymanagement/ConfigurationExtra.scala | 9 +++++++++ .../sbt/librarymanagement/IvyScalaExtra.scala | 16 ++++++++++------ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala index 320f549a0..3198c95d0 100644 --- a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala +++ b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala @@ -168,7 +168,21 @@ final class IvySbt(val configuration: IvyConfiguration, fileToStore: File => Cac case pc: PomConfiguration => configurePom(pc) case ifc: IvyFileConfiguration => configureIvyFile(ifc) } - moduleSettings.ivyScala.foreach(IvyScala.checkModule(baseModule, baseConfiguration, configuration.log)) + + val configs = + moduleSettings match { + case ic: InlineConfiguration => ic.configurations + case ic: InlineConfigurationWithExcludes => ic.configurations + case ec: EmptyConfiguration => Nil + case pc: PomConfiguration => Configurations.default ++ Configurations.defaultInternal + case ifc: IvyFileConfiguration => Configurations.default ++ Configurations.defaultInternal + } + moduleSettings.ivyScala match { + case Some(is) => + val svc = configs.toVector filter Configurations.underScalaVersion map { _.name } + IvyScala.checkModule(baseModule, baseConfiguration, svc, configuration.log)(is) + case _ => // do nothing + } IvySbt.addExtraNamespace(baseModule) (baseModule, baseConfiguration) } diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/ConfigurationExtra.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/ConfigurationExtra.scala index f3b267e24..8e30e5c6c 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/ConfigurationExtra.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/ConfigurationExtra.scala @@ -47,6 +47,15 @@ object Configurations { private[sbt] def DefaultConfiguration(mavenStyle: Boolean) = if (mavenStyle) DefaultMavenConfiguration else DefaultIvyConfiguration private[sbt] def defaultConfiguration(mavenStyle: Boolean) = if (mavenStyle) Configurations.Compile else Configurations.Default private[sbt] def removeDuplicates(configs: Iterable[Configuration]) = Set(scala.collection.mutable.Map(configs.map(config => (config.name, config)).toSeq: _*).values.toList: _*) + + /** Returns true if the configuration should be under the influence of scalaVersion. */ + private[sbt] def underScalaVersion(c: Configuration): Boolean = + c match { + case Default | Compile | IntegrationTest | Provided | Runtime | Test | Optional | + CompilerPlugin | CompileInternal | RuntimeInternal | TestInternal => true + case config => + config.extendsConfigs exists underScalaVersion + } } abstract class ConfigurationExtra { diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScalaExtra.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScalaExtra.scala index 39ecbace4..625c5e00f 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScalaExtra.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScalaExtra.scala @@ -47,24 +47,28 @@ import ScalaArtifacts._ private[sbt] abstract class IvyScalaFunctions { /** Performs checks/adds filters on Scala dependencies (if enabled in IvyScala). */ - def checkModule(module: DefaultModuleDescriptor, conf: String, log: Logger)(check: IvyScala): Unit = { + def checkModule(module: DefaultModuleDescriptor, conf: String, scalaVersionConfigs: Vector[String], log: Logger)(check: IvyScala): Unit = { if (check.checkExplicit) checkDependencies(module, check.scalaOrganization, check.scalaArtifacts, check.scalaBinaryVersion, check.configurations, log) if (check.filterImplicit) excludeScalaJars(module, check.configurations) if (check.overrideScalaVersion) - overrideScalaVersion(module, check.scalaOrganization, check.scalaFullVersion) + overrideScalaVersion(module, check.scalaOrganization, check.scalaFullVersion, scalaVersionConfigs) } - class OverrideScalaMediator(scalaOrganization: String, scalaVersion: String) extends DependencyDescriptorMediator { + class OverrideScalaMediator(scalaOrganization: String, scalaVersion: String, scalaVersionConfigs0: Vector[String]) extends DependencyDescriptorMediator { + private[this] val scalaVersionConfigs = scalaVersionConfigs0.toSet def mediate(dd: DependencyDescriptor): DependencyDescriptor = { + // Mediate only for the dependencies in scalaVersion configurations. https://github.com/sbt/sbt/issues/2786 + def configQualifies: Boolean = + (dd.getModuleConfigurations exists { scalaVersionConfigs }) val transformer = new NamespaceTransformer { def transform(mrid: ModuleRevisionId): ModuleRevisionId = { if (mrid == null) mrid else mrid.getName match { - case name @ (CompilerID | LibraryID | ReflectID | ActorsID | ScalapID) => + case name @ (CompilerID | LibraryID | ReflectID | ActorsID | ScalapID) if configQualifies => ModuleRevisionId.newInstance(scalaOrganization, name, mrid.getBranch, scalaVersion, mrid.getQualifiedExtraAttributes) case _ => mrid } @@ -77,8 +81,8 @@ private[sbt] abstract class IvyScalaFunctions { } } - def overrideScalaVersion(module: DefaultModuleDescriptor, organization: String, version: String): Unit = { - val mediator = new OverrideScalaMediator(organization, version) + def overrideScalaVersion(module: DefaultModuleDescriptor, organization: String, version: String, scalaVersionConfigs: Vector[String]): Unit = { + val mediator = new OverrideScalaMediator(organization, version, scalaVersionConfigs) module.addDependencyDescriptorMediator(new ModuleId(Organization, "*"), ExactPatternMatcher.INSTANCE, mediator) if (organization != Organization) module.addDependencyDescriptorMediator(new ModuleId(organization, "*"), ExactPatternMatcher.INSTANCE, mediator) From a809e8744efa69755139b920d4f9fbfefa99d98f Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 11 Nov 2016 14:42:48 -0500 Subject: [PATCH 2/4] Bump up Ivy Fixes #2015 --- project/Dependencies.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index f93084d11..82c63e485 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -34,7 +34,6 @@ object Dependencies { } def addSbtIO(p: Project): Project = addSbtModule(p, sbtIoPath, "io", sbtIO) - def addSbtUtilCollection(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilCollection", utilCollection) def addSbtUtilLogging(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilLogging", utilLogging) def addSbtUtilTesting(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilTesting", utilTesting, Some(Test)) @@ -42,7 +41,7 @@ object Dependencies { def addSbtUtilCache(p: Project): Project = addSbtModule(p, sbtUtilPath, "utilCache", utilCache) val launcherInterface = "org.scala-sbt" % "launcher-interface" % "1.0.0" - val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-2cf13e211b2cb31f0d3b317289dca70eca3362f6" + val ivy = "org.scala-sbt.ivy" % "ivy" % "2.3.0-sbt-48dd0744422128446aee9ac31aa356ee203cc9f4" val jsch = "com.jcraft" % "jsch" % "0.1.46" intransitive () val scalaReflect = Def.setting { "org.scala-lang" % "scala-reflect" % scalaVersion.value } val scalaXml = scala211Module("scala-xml", "1.0.5") From 95a209b033499c0bf05cb40ee40741acae7f5805 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 11 Nov 2016 04:25:18 -0500 Subject: [PATCH 3/4] Apply binary version conflict check to Compile related configs Fixes #1466 Ref #2786 Even after fixing the mediator issue, we still have spurious binary version conflict warning that does not account for sandbox configurations. This change follows the scalaVersionConfigs work. --- .../main/scala/sbt/librarymanagement/IvyScalaExtra.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScalaExtra.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScalaExtra.scala index 625c5e00f..13cd79154 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScalaExtra.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScalaExtra.scala @@ -49,7 +49,7 @@ private[sbt] abstract class IvyScalaFunctions { /** Performs checks/adds filters on Scala dependencies (if enabled in IvyScala). */ def checkModule(module: DefaultModuleDescriptor, conf: String, scalaVersionConfigs: Vector[String], log: Logger)(check: IvyScala): Unit = { if (check.checkExplicit) - checkDependencies(module, check.scalaOrganization, check.scalaArtifacts, check.scalaBinaryVersion, check.configurations, log) + checkDependencies(module, check.scalaOrganization, check.scalaArtifacts, check.scalaBinaryVersion, scalaVersionConfigs, log) if (check.filterImplicit) excludeScalaJars(module, check.configurations) if (check.overrideScalaVersion) @@ -98,8 +98,8 @@ private[sbt] abstract class IvyScalaFunctions { * Checks the immediate dependencies of module for dependencies on scala jars and verifies that the version on the * dependencies matches scalaVersion. */ - private def checkDependencies(module: ModuleDescriptor, scalaOrganization: String, scalaArtifacts: Seq[String], scalaBinaryVersion: String, configurations: Iterable[Configuration], log: Logger): Unit = { - val configSet = if (configurations.isEmpty) (c: String) => true else configurationSet(configurations) + private def checkDependencies(module: ModuleDescriptor, scalaOrganization: String, scalaArtifacts: Vector[String], scalaBinaryVersion: String, scalaVersionConfigs0: Vector[String], log: Logger): Unit = { + val scalaVersionConfigs: String => Boolean = if (scalaVersionConfigs0.isEmpty) (c: String) => false else scalaVersionConfigs0.toSet def binaryScalaWarning(dep: DependencyDescriptor): Option[String] = { val id = dep.getDependencyRevisionId @@ -107,7 +107,7 @@ private[sbt] abstract class IvyScalaFunctions { def isScalaLangOrg = id.getOrganisation == scalaOrganization def isScalaArtifact = scalaArtifacts.contains(id.getName) def hasBinVerMismatch = depBinaryVersion != scalaBinaryVersion - def matchesOneOfTheConfigs = dep.getModuleConfigurations.exists(configSet) + def matchesOneOfTheConfigs = dep.getModuleConfigurations exists { scalaVersionConfigs } val mismatched = isScalaLangOrg && isScalaArtifact && hasBinVerMismatch && matchesOneOfTheConfigs if (mismatched) Some("Binary version (" + depBinaryVersion + ") for dependency " + id + From 27397de8cfcd824f8ab15901e9a951f16a305647 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sun, 15 Jan 2017 09:27:26 -0500 Subject: [PATCH 4/4] Adjust to upstream --- .../main/scala/sbt/internal/librarymanagement/Ivy.scala | 8 +++----- librarymanagement/src/test/scala/ScalaOverrideTest.scala | 4 +++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala index 3198c95d0..76ba4ce33 100644 --- a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala +++ b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/Ivy.scala @@ -171,11 +171,9 @@ final class IvySbt(val configuration: IvyConfiguration, fileToStore: File => Cac val configs = moduleSettings match { - case ic: InlineConfiguration => ic.configurations - case ic: InlineConfigurationWithExcludes => ic.configurations - case ec: EmptyConfiguration => Nil - case pc: PomConfiguration => Configurations.default ++ Configurations.defaultInternal - case ifc: IvyFileConfiguration => Configurations.default ++ Configurations.defaultInternal + case ic: InlineConfiguration => ic.configurations + case pc: PomConfiguration => Configurations.default ++ Configurations.defaultInternal + case ifc: IvyFileConfiguration => Configurations.default ++ Configurations.defaultInternal } moduleSettings.ivyScala match { case Some(is) => diff --git a/librarymanagement/src/test/scala/ScalaOverrideTest.scala b/librarymanagement/src/test/scala/ScalaOverrideTest.scala index bf0b82e3e..f14a2d6ca 100644 --- a/librarymanagement/src/test/scala/ScalaOverrideTest.scala +++ b/librarymanagement/src/test/scala/ScalaOverrideTest.scala @@ -12,10 +12,12 @@ class ScalaOverrideTest extends UnitSpec { val OtherOrgID = "other.org" def check(org0: String, version0: String)(org1: String, name1: String, version1: String) = { - val osm = new OverrideScalaMediator(org0, version0) + val scalaConfigs = Configurations.default.toVector filter { Configurations.underScalaVersion } map { _.name } + val osm = new OverrideScalaMediator(org0, version0, scalaConfigs) val mrid = ModuleRevisionId.newInstance(org1, name1, version1) val dd = new DefaultDependencyDescriptor(mrid, false) + dd.addDependencyConfiguration("compile", "compile") val res = osm.mediate(dd) res.getDependencyRevisionId shouldBe ModuleRevisionId.newInstance(org0, name1, version0)