diff --git a/core/src/main/scala/sbt/librarymanagement/ScalaArtifacts.scala b/core/src/main/scala/sbt/librarymanagement/ScalaArtifacts.scala index 2d25f86d1..dc667a4c8 100644 --- a/core/src/main/scala/sbt/librarymanagement/ScalaArtifacts.scala +++ b/core/src/main/scala/sbt/librarymanagement/ScalaArtifacts.scala @@ -1,16 +1,30 @@ package sbt.librarymanagement object ScalaArtifacts { - val Organization = "org.scala-lang" - val LibraryID = "scala-library" - val CompilerID = "scala-compiler" - val ReflectID = "scala-reflect" - val ActorsID = "scala-actors" - val ScalapID = "scalap" - val Artifacts = Vector(LibraryID, CompilerID, ReflectID, ActorsID, ScalapID) + final val Organization = "org.scala-lang" + final val LibraryID = "scala-library" + final val CompilerID = "scala-compiler" + final val ReflectID = "scala-reflect" + final val ActorsID = "scala-actors" + final val ScalapID = "scalap" + final val Artifacts = Vector(LibraryID, CompilerID, ReflectID, ActorsID, ScalapID) - val Scala3LibraryID = "scala3-library" - val Scala3CompilerID = "scala3-compiler" + final val Scala3LibraryID = "scala3-library" + final val Scala3CompilerID = "scala3-compiler" + final val Scala3InterfacesID = "scala3-interfaces" + final val TastyCoreID = "tasty-core" + + private[sbt] final val Scala3LibraryPrefix = Scala3LibraryID + "_" + private[sbt] final val Scala3CompilerPrefix = Scala3CompilerID + "_" + private[sbt] final val TastyCorePrefix = TastyCoreID + "_" + + def isScala2Artifact(name: String): Boolean = { + name == LibraryID || name == CompilerID || name == ReflectID || name == ActorsID || name == ScalapID + } + def isScala3Artifact(name: String): Boolean = { + name.startsWith(Scala3LibraryPrefix) || name.startsWith(Scala3CompilerPrefix) || + name.startsWith(TastyCorePrefix) || name == Scala3InterfacesID + } def isScala3(scalaVersion: String): Boolean = scalaVersion.startsWith("3.") diff --git a/ivy/src/main/scala/sbt/internal/librarymanagement/IvyScalaUtil.scala b/ivy/src/main/scala/sbt/internal/librarymanagement/IvyScalaUtil.scala index e318eb6c2..2a4e4a27f 100644 --- a/ivy/src/main/scala/sbt/internal/librarymanagement/IvyScalaUtil.scala +++ b/ivy/src/main/scala/sbt/internal/librarymanagement/IvyScalaUtil.scala @@ -12,7 +12,7 @@ import org.apache.ivy.plugins.matcher.ExactPatternMatcher import org.apache.ivy.plugins.namespace.NamespaceTransformer import sbt.util.Logger import sbt.librarymanagement.ScalaArtifacts._ -import sbt.librarymanagement.{ ScalaModuleInfo, CrossVersion, Configuration } +import sbt.librarymanagement.{ Configuration, CrossVersion, ScalaModuleInfo } object IvyScalaUtil { @@ -48,38 +48,43 @@ object IvyScalaUtil { scalaVersionConfigs0: Vector[String] ) extends DependencyDescriptorMediator { private[this] val scalaVersionConfigs = scalaVersionConfigs0.toSet + private val binaryVersion = CrossVersion.binaryScalaVersion(scalaVersion) 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 }) + dd.getModuleConfigurations exists { scalaVersionConfigs } // Do not rewrite the dependencies of Scala dependencies themselves, this prevents bootstrapping // a Scala compiler using another Scala compiler. def dependeeQualifies: Boolean = - dd.getParentRevisionId == null || ( - dd.getParentRevisionId.getName match { - case _ @(CompilerID | LibraryID | ReflectID | ActorsID | ScalapID) => - false - case _ => - true - } - ) + dd.getParentRevisionId == null || + !isScala2Artifact(dd.getParentRevisionId.getName) || + !isScala3Artifact(dd.getParentRevisionId.getName) + + def matchBinaryVersion(version: String): Boolean = + CrossVersion.binaryScalaVersion(version) == binaryVersion + val transformer = new NamespaceTransformer { def transform(mrid: ModuleRevisionId): ModuleRevisionId = { if (mrid == null) mrid - else - mrid.getName match { - case name @ (CompilerID | LibraryID | ReflectID | ActorsID | ScalapID) - if configQualifies && dependeeQualifies => - ModuleRevisionId.newInstance( - scalaOrganization, - name, - mrid.getBranch, - scalaVersion, - mrid.getQualifiedExtraAttributes - ) - case _ => mrid - } + else if ((isScala2Artifact(mrid.getName) || isScala3Artifact(mrid.getName)) && + configQualifies && + dependeeQualifies) { + // do not override the binary incompatible Scala version because: + // - the artifacts compiled with Scala 3 depends on the Scala 2.13 scala-library + // - the Scala 2 TASTy reader can consume the Scala 3 artifacts + val newScalaVersion = + if (matchBinaryVersion(mrid.getRevision)) scalaVersion + else mrid.getRevision + + ModuleRevisionId.newInstance( + scalaOrganization, + mrid.getName, + mrid.getBranch, + newScalaVersion, + mrid.getQualifiedExtraAttributes + ) + } else mrid } def isIdentity: Boolean = false diff --git a/ivy/src/test/scala/sbt/internal/librarymanagement/ScalaOverrideTest.scala b/ivy/src/test/scala/sbt/internal/librarymanagement/ScalaOverrideTest.scala index d9ed46cf3..37a82432f 100644 --- a/ivy/src/test/scala/sbt/internal/librarymanagement/ScalaOverrideTest.scala +++ b/ivy/src/test/scala/sbt/internal/librarymanagement/ScalaOverrideTest.scala @@ -11,10 +11,13 @@ import verify.BasicTestSuite object ScalaOverrideTest extends BasicTestSuite { val OtherOrgID = "other.org" - def check(org0: String, version0: String)(org1: String, name1: String, version1: String) = { - val scalaConfigs = Configurations.default.toVector filter { Configurations.underScalaVersion } map { - _.name - } + private val scalaConfigs = + Configurations.default.filter(Configurations.underScalaVersion).map(_.name) + + def checkOrgAndVersion( + org0: String, + version0: String + )(org1: String, name1: String, version1: String): Unit = { val osm = new OverrideScalaMediator(org0, version0, scalaConfigs) val mrid = ModuleRevisionId.newInstance(org1, name1, version1) @@ -25,8 +28,36 @@ object ScalaOverrideTest extends BasicTestSuite { assert(res.getDependencyRevisionId == ModuleRevisionId.newInstance(org0, name1, version0)) } - test("""OverrideScalaMediator should override compiler version""") { - check(Organization, "2.11.8")( + def checkOnlyOrg( + org0: String, + version0: String + )(org1: String, name1: String, version1: String): Unit = { + 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) + assert(res.getDependencyRevisionId == ModuleRevisionId.newInstance(org0, name1, version1)) + } + + def checkNoOverride( + org0: String, + version0: String + )(org1: String, name1: String, version1: String): Unit = { + 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) + assert(res.getDependencyRevisionId == mrid) + } + + test("OverrideScalaMediator should override compiler version") { + checkOrgAndVersion(Organization, "2.11.8")( Organization, CompilerID, "2.11.9" @@ -34,7 +65,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override library version") { - check(Organization, "2.11.8")( + checkOrgAndVersion(Organization, "2.11.8")( Organization, LibraryID, "2.11.8" @@ -42,7 +73,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override reflect version") { - check(Organization, "2.11.8")( + checkOrgAndVersion(Organization, "2.11.8")( Organization, ReflectID, "2.11.7" @@ -50,7 +81,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override actors version") { - check(Organization, "2.11.8")( + checkOrgAndVersion(Organization, "2.11.8")( Organization, ActorsID, "2.11.6" @@ -58,7 +89,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override scalap version") { - check(Organization, "2.11.8")( + checkOrgAndVersion(Organization, "2.11.8")( Organization, ScalapID, "2.11.5" @@ -66,7 +97,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override default compiler organization") { - check(OtherOrgID, "2.11.8")( + checkOrgAndVersion(OtherOrgID, "2.11.8")( Organization, CompilerID, "2.11.9" @@ -74,7 +105,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override default library organization") { - check(OtherOrgID, "2.11.8")( + checkOrgAndVersion(OtherOrgID, "2.11.8")( Organization, LibraryID, "2.11.8" @@ -82,7 +113,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override default reflect organization") { - check(OtherOrgID, "2.11.8")( + checkOrgAndVersion(OtherOrgID, "2.11.8")( Organization, ReflectID, "2.11.7" @@ -90,7 +121,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override default actors organization") { - check(OtherOrgID, "2.11.8")( + checkOrgAndVersion(OtherOrgID, "2.11.8")( Organization, ActorsID, "2.11.6" @@ -98,7 +129,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override default scalap organization") { - check(OtherOrgID, "2.11.8")( + checkOrgAndVersion(OtherOrgID, "2.11.8")( Organization, ScalapID, "2.11.5" @@ -106,7 +137,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override custom compiler organization") { - check(Organization, "2.11.8")( + checkOrgAndVersion(Organization, "2.11.8")( OtherOrgID, CompilerID, "2.11.9" @@ -114,7 +145,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override custom library organization") { - check(Organization, "2.11.8")( + checkOrgAndVersion(Organization, "2.11.8")( OtherOrgID, LibraryID, "2.11.8" @@ -122,7 +153,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override custom reflect organization") { - check(Organization, "2.11.8")( + checkOrgAndVersion(Organization, "2.11.8")( OtherOrgID, ReflectID, "2.11.7" @@ -130,7 +161,7 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override custom actors organization") { - check(Organization, "2.11.8")( + checkOrgAndVersion(Organization, "2.11.8")( OtherOrgID, ActorsID, "2.11.6" @@ -138,10 +169,106 @@ object ScalaOverrideTest extends BasicTestSuite { } test("it should override custom scalap organization") { - check(Organization, "2.11.8")( + checkOrgAndVersion(Organization, "2.11.8")( OtherOrgID, ScalapID, "2.11.5" ) } + + test("it should override Scala 3 compiler version") { + checkOrgAndVersion(Organization, "3.1.0")( + Organization, + Scala3CompilerPrefix + "3", + "3.0.0" + ) + } + + test("it should override Scala 3 library version") { + checkOrgAndVersion(Organization, "3.1.0")( + Organization, + Scala3LibraryPrefix + "3", + "3.0.0" + ) + } + + test("it should override Scala 3 interfaces version") { + checkOrgAndVersion(Organization, "3.1.0")( + Organization, + Scala3InterfacesID, + "3.0.0" + ) + } + + test("it should override TASTy core version") { + checkOrgAndVersion(Organization, "3.1.0")( + Organization, + TastyCorePrefix + "3", + "3.0.0" + ) + } + + test("it should not override Scala 2 library version when using Scala 3") { + checkNoOverride(Organization, "3.1.0")( + Organization, + LibraryID, + "2.13.4" + ) + } + + test("it should not override TASTy core version when using Scala 2") { + checkNoOverride(Organization, "2.13.4")( + Organization, + TastyCorePrefix + "3", + "3.0.0" + ) + } + + test("it should override default Scala 3 compiler organization") { + checkOrgAndVersion(OtherOrgID, "3.1.0")( + Organization, + Scala3CompilerPrefix + "3", + "3.0.0" + ) + } + + test("it should override default Scala 3 library organization") { + checkOrgAndVersion(OtherOrgID, "3.1.0")( + Organization, + Scala3LibraryPrefix + "3", + "3.0.0" + ) + } + + test("it should override default Scala 3 interfaces organization") { + checkOrgAndVersion(OtherOrgID, "3.1.0")( + Organization, + Scala3InterfacesID, + "3.0.0" + ) + } + + test("it should override default Scala 3 TASTy core organization") { + checkOrgAndVersion(OtherOrgID, "3.1.0")( + Organization, + TastyCorePrefix + "3", + "3.0.0" + ) + } + + test("it should override default Scala 2 library organization when in Scala 3") { + checkOnlyOrg(OtherOrgID, "3.1.0")( + Organization, + LibraryID, + "2.13.4" + ) + } + + test("it should override default TASTy core organization when in Scala 2") { + checkOnlyOrg(OtherOrgID, "2.13.4")( + Organization, + TastyCorePrefix + "3", + "3.0.0" + ) + } }