diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScala.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScala.scala index d2df9b4cf..d01027769 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScala.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/IvyScala.scala @@ -9,6 +9,7 @@ import scala.collection.mutable.HashSet import org.apache.ivy.core.module.descriptor._ import org.apache.ivy.core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId } import org.apache.ivy.plugins.matcher.ExactPatternMatcher +import org.apache.ivy.plugins.namespace.NamespaceTransformer import sbt.util.Logger object ScalaArtifacts { @@ -16,6 +17,8 @@ object ScalaArtifacts { val LibraryID = "scala-library" val CompilerID = "scala-compiler" val ReflectID = "scala-reflect" + val ActorsID = "scala-actors" + val ScalapID = "scalap" val DottyIDPrefix = "dotty" def dottyID(binaryVersion: String): String = s"${DottyIDPrefix}_${binaryVersion}" @@ -47,17 +50,41 @@ private[sbt] object IvyScala { /** Performs checks/adds filters on Scala dependencies (if enabled in IvyScala). */ def checkModule(module: DefaultModuleDescriptor, conf: String, log: Logger)(check: IvyScala): Unit = { if (check.checkExplicit) - checkDependencies(module, check.scalaBinaryVersion, check.configurations, log) + checkDependencies(module, check.scalaOrganization, check.scalaBinaryVersion, check.configurations, log) if (check.filterImplicit) excludeScalaJars(module, check.configurations) if (check.overrideScalaVersion) - overrideScalaVersion(module, check.scalaFullVersion) + overrideScalaVersion(module, check.scalaOrganization, check.scalaFullVersion) } - def overrideScalaVersion(module: DefaultModuleDescriptor, version: String): Unit = { - overrideVersion(module, Organization, LibraryID, version) - overrideVersion(module, Organization, CompilerID, version) - overrideVersion(module, Organization, ReflectID, version) + + class OverrideScalaMediator(scalaOrganization: String, scalaVersion: String) extends DependencyDescriptorMediator { + def mediate(dd: DependencyDescriptor): DependencyDescriptor = { + val transformer = + new NamespaceTransformer { + def transform(mrid: ModuleRevisionId): ModuleRevisionId = { + if (mrid == null) mrid + else + mrid.getName match { + case name @ (CompilerID | LibraryID | ReflectID | ActorsID | ScalapID) => + ModuleRevisionId.newInstance(scalaOrganization, name, mrid.getBranch, scalaVersion, mrid.getQualifiedExtraAttributes) + case _ => mrid + } + } + + def isIdentity: Boolean = false + } + + DefaultDependencyDescriptor.transformInstance(dd, transformer, false) + } } + + def overrideScalaVersion(module: DefaultModuleDescriptor, organization: String, version: String): Unit = { + val mediator = new OverrideScalaMediator(organization, version) + module.addDependencyDescriptorMediator(new ModuleId(Organization, "*"), ExactPatternMatcher.INSTANCE, mediator) + if (organization != Organization) + module.addDependencyDescriptorMediator(new ModuleId(organization, "*"), ExactPatternMatcher.INSTANCE, mediator) + } + def overrideVersion(module: DefaultModuleDescriptor, org: String, name: String, version: String): Unit = { val id = new ModuleId(org, name) val over = new OverrideDependencyDescriptorMediator(null, version) @@ -68,13 +95,13 @@ private[sbt] object IvyScala { * 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, scalaBinaryVersion: String, configurations: Iterable[Configuration], log: Logger): Unit = { + private def checkDependencies(module: ModuleDescriptor, scalaOrganization: String, scalaBinaryVersion: String, configurations: Iterable[Configuration], log: Logger): Unit = { val configSet = if (configurations.isEmpty) (c: String) => true else configurationSet(configurations) def binaryScalaWarning(dep: DependencyDescriptor): Option[String] = { val id = dep.getDependencyRevisionId val depBinaryVersion = CrossVersion.binaryScalaVersion(id.getRevision) - def isScalaLangOrg = id.getOrganisation == Organization + def isScalaLangOrg = id.getOrganisation == scalaOrganization def isNotScalaActorsMigration = !(id.getName startsWith "scala-actors-migration") // Exception to the rule: sbt/sbt#1818 def isNotScalaPickling = !(id.getName startsWith "scala-pickling") // Exception to the rule: sbt/sbt#1899 def hasBinVerMismatch = depBinaryVersion != scalaBinaryVersion diff --git a/librarymanagement/src/test/scala/BaseIvySpecification.scala b/librarymanagement/src/test/scala/BaseIvySpecification.scala index aec1ad198..7e561640d 100644 --- a/librarymanagement/src/test/scala/BaseIvySpecification.scala +++ b/librarymanagement/src/test/scala/BaseIvySpecification.scala @@ -20,7 +20,7 @@ trait BaseIvySpecification extends UnitSpec { def configurations = Seq(Compile, Test, Runtime) def module(moduleId: ModuleID, deps: Seq[ModuleID], scalaFullVersion: Option[String], - uo: UpdateOptions = UpdateOptions()): IvySbt#Module = { + uo: UpdateOptions = UpdateOptions(), overrideScalaVersion: Boolean = true): IvySbt#Module = { val ivyScala = scalaFullVersion map { fv => new IvyScala( scalaFullVersion = fv, @@ -28,7 +28,7 @@ trait BaseIvySpecification extends UnitSpec { configurations = Nil, checkExplicit = true, filterImplicit = false, - overrideScalaVersion = false + overrideScalaVersion = overrideScalaVersion ) } diff --git a/librarymanagement/src/test/scala/EvictionWarningSpec.scala b/librarymanagement/src/test/scala/EvictionWarningSpec.scala index 35b3e994b..c1a77bf31 100644 --- a/librarymanagement/src/test/scala/EvictionWarningSpec.scala +++ b/librarymanagement/src/test/scala/EvictionWarningSpec.scala @@ -5,12 +5,16 @@ import sbt.internal.librarymanagement.BaseIvySpecification class EvictionWarningSpec extends BaseIvySpecification { // This is a specification to check the eviction warnings - """Eviction of scala-library whose scalaVersion + """Eviction of non-overridden scala-library whose scalaVersion """ should "be detected" in scalaVersionWarn1() - it should "not be detected if it's diabled" in scalaVersionWarn2() + it should "not be detected if it's disabled" in scalaVersionWarn2() it should "print out message about the eviction" in scalaVersionWarn3() it should "print out message about the eviction with callers" in scalaVersionWarn4() + """Non-eviction of overridden scala-library whose scalaVersion + """ should "not be detected if it's enabled" in scalaVersionWarn5() + it should "not be detected if it's disabled" in scalaVersionWarn6() + """Including two (suspect) binary incompatible Java libraries to direct dependencies """ should "be detected as eviction" in javaLibWarn1() it should "not be detected if it's disabled" in javaLibWarn2() @@ -61,19 +65,19 @@ class EvictionWarningSpec extends BaseIvySpecification { def scalaVersionDeps = Seq(scala2102, akkaActor230) def scalaVersionWarn1() = { - val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2")) + val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2"), overrideScalaVersion = false) val report = ivyUpdate(m) EvictionWarning(m, defaultOptions, report, log).scalaEvictions should have size (1) } def scalaVersionWarn2() = { - val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2")) + val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2"), overrideScalaVersion = false) val report = ivyUpdate(m) EvictionWarning(m, defaultOptions.withWarnScalaVersionEviction(false), report, log).scalaEvictions should have size (0) } def scalaVersionWarn3() = { - val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2")) + val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2"), overrideScalaVersion = false) val report = ivyUpdate(m) EvictionWarning(m, defaultOptions, report, log).lines shouldBe List( @@ -86,7 +90,7 @@ class EvictionWarningSpec extends BaseIvySpecification { } def scalaVersionWarn4() = { - val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2")) + val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2"), overrideScalaVersion = false) val report = ivyUpdate(m) EvictionWarning(m, defaultOptions.withShowCallers(true), report, log).lines shouldBe List( @@ -97,6 +101,18 @@ class EvictionWarningSpec extends BaseIvySpecification { ) } + def scalaVersionWarn5() = { + val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2")) + val report = ivyUpdate(m) + EvictionWarning(m, defaultOptions, report, log).scalaEvictions should have size (0) + } + + def scalaVersionWarn6() = { + val m = module(defaultModuleId, scalaVersionDeps, Some("2.10.2")) + val report = ivyUpdate(m) + EvictionWarning(m, defaultOptions.withWarnScalaVersionEviction(false), report, log).scalaEvictions should have size (0) + } + def javaLibDirectDeps = Seq(commonsIo14, commonsIo24) def javaLibWarn1() = { diff --git a/librarymanagement/src/test/scala/ScalaOverrideTest.scala b/librarymanagement/src/test/scala/ScalaOverrideTest.scala new file mode 100644 index 000000000..6fd2425c4 --- /dev/null +++ b/librarymanagement/src/test/scala/ScalaOverrideTest.scala @@ -0,0 +1,42 @@ +package sbt.librarymanagement + +import org.apache.ivy.core.module.id.{ ModuleId, ModuleRevisionId } +import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor + +import sbt.internal.util.UnitSpec + +import IvyScala.OverrideScalaMediator +import ScalaArtifacts._ + +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 mrid = ModuleRevisionId.newInstance(org1, name1, version1) + val dd = new DefaultDependencyDescriptor(mrid, false) + + val res = osm.mediate(dd) + res.getDependencyRevisionId shouldBe ModuleRevisionId.newInstance(org0, name1, version0) + } + + """OverrideScalaMediator + """ should "Override compiler version" in check(Organization, "2.11.8")(Organization, CompilerID, "2.11.9") + it should "Override library version" in check(Organization, "2.11.8")(Organization, LibraryID, "2.11.8") + it should "Override reflect version" in check(Organization, "2.11.8")(Organization, ReflectID, "2.11.7") + it should "Override actors version" in check(Organization, "2.11.8")(Organization, ActorsID, "2.11.6") + it should "Override scalap version" in check(Organization, "2.11.8")(Organization, ScalapID, "2.11.5") + + it should "Override default compiler organization" in check(OtherOrgID, "2.11.8")(Organization, CompilerID, "2.11.9") + it should "Override default library organization" in check(OtherOrgID, "2.11.8")(Organization, LibraryID, "2.11.8") + it should "Override default reflect organization" in check(OtherOrgID, "2.11.8")(Organization, ReflectID, "2.11.7") + it should "Override default actors organization" in check(OtherOrgID, "2.11.8")(Organization, ActorsID, "2.11.6") + it should "Override default scalap organization" in check(OtherOrgID, "2.11.8")(Organization, ScalapID, "2.11.5") + + it should "Override custom compiler organization" in check(Organization, "2.11.8")(OtherOrgID, CompilerID, "2.11.9") + it should "Override custom library organization" in check(Organization, "2.11.8")(OtherOrgID, LibraryID, "2.11.8") + it should "Override custom reflect organization" in check(Organization, "2.11.8")(OtherOrgID, ReflectID, "2.11.7") + it should "Override custom actors organization" in check(Organization, "2.11.8")(OtherOrgID, ActorsID, "2.11.6") + it should "Override custom scalap organization" in check(Organization, "2.11.8")(OtherOrgID, ScalapID, "2.11.5") +}