From 2318269dc5fd14f31ccfbb8fa8564deabc91d613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Gonc=CC=A7alves?= Date: Sat, 25 Jun 2022 21:41:10 +0100 Subject: [PATCH 1/2] Add CrossVersionExtra.isBinaryCompatible utility --- .gitignore | 5 ++ .../cross/CrossVersionUtil.scala | 17 +++++++ .../librarymanagement/CrossVersionExtra.scala | 8 ++- .../librarymanagement/CrossVersionTest.scala | 49 +++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0cbd43574..99e785f4e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,9 @@ __pycache__ scripted-test/src/sbt-test/*/*/project/build.properties scripted-test/src/sbt-test/*/*/project/plugins.sbt + +.idea +.bloop +.metals +.bsp/ metals.sbt diff --git a/core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala b/core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala index fe6b107cf..b09f42ca7 100644 --- a/core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala +++ b/core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala @@ -84,6 +84,23 @@ object CrossVersionUtil { case _ => full } + // Uses the following rules: + // + // - Forwards and backwards compatibility is guaranteed for Scala 2.N.x (https://docs.scala-lang.org/overviews/core/binary-compatibility-of-scala-releases.html) + // + // - A Scala compiler in version 3.x1.y1 is able to read TASTy files produced by another compiler in version 3.x2.y2 if x1 >= x2 (https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html) + // + // - For non-stable Scala 3 versions, compiler versions can read TASTy in an older stable format but their TASTY versions are not compatible between each other even if the compilers have the same minor version (https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html) + // + private[sbt] def isBinaryCompatibleWith(newVersion: String, origVersion: String): Boolean = { + (newVersion, origVersion) match { + case (NonReleaseV_n("2", nMin, _, _), NonReleaseV_n("2", oMin, _, _)) => nMin == oMin + case (ReleaseV("3", nMin, _, _), ReleaseV("3", oMin, _, _)) => nMin.toInt >= oMin.toInt + case (NonReleaseV_1("3", nMin, _, _), ReleaseV("3", oMin, _, _)) => nMin.toInt > oMin.toInt + case _ => newVersion == origVersion + } + } + def binaryScalaVersion(full: String): String = { if (ScalaArtifacts.isScala3(full)) binaryScala3Version(full) else diff --git a/core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala b/core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala index 9de8605ea..a60f30325 100644 --- a/core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala +++ b/core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala @@ -9,7 +9,7 @@ private[librarymanagement] abstract class CrossVersionFunctions { /** Compatibility with 0.13 */ @deprecated( - "use CrossVersion.disabled instead. prior to sbt 1.3.0, Diabled did not work without apply(). sbt/sbt#4977", + "use CrossVersion.disabled instead. prior to sbt 1.3.0, Disabled did not work without apply(). sbt/sbt#4977", "1.3.0" ) final val Disabled = sbt.librarymanagement.Disabled @@ -235,4 +235,10 @@ private[librarymanagement] abstract class CrossVersionFunctions { * Full sbt versions earlier than [[sbt.librarymanagement.CrossVersion.TransitionSbtVersion]] are returned as is. */ def binarySbtVersion(full: String): String = CrossVersionUtil.binarySbtVersion(full) + + /** + * Returns `true` if a project targeting version `origVersion` can run with version `newVersion`. + */ + def isBinaryCompatibleWith(newVersion: String, origVersion: String): Boolean = + CrossVersionUtil.isBinaryCompatibleWith(newVersion, origVersion) } diff --git a/core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala b/core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala index d47b17c14..b4e570083 100644 --- a/core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala +++ b/core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala @@ -284,6 +284,55 @@ class CrossVersionTest extends UnitSpec { patchVersion("2.11.8-X1.5-bin-extra") shouldBe Some("artefact_2.11.8-X1.5") } + "isBinaryCompatibleWith" should "for (2.10.4, 2.10.5) return true" in { + isBinaryCompatibleWith("2.10.4", "2.10.5") shouldBe true + } + it should "for (2.10.6, 2.10.5) return true" in { + isBinaryCompatibleWith("2.10.6", "2.10.5") shouldBe true + } + it should "for (2.11.0, 2.10.5) return false" in { + isBinaryCompatibleWith("2.11.0", "2.10.5") shouldBe false + } + it should "for (3.0.0, 2.10.5) return false" in { + isBinaryCompatibleWith("3.0.0", "2.10.5") shouldBe false + } + it should "for (3.0.0, 3.1.0) return false" in { + isBinaryCompatibleWith("3.0.0", "3.1.0") shouldBe false + } + it should "for (3.1.0, 3.0.0) return true" in { + isBinaryCompatibleWith("3.1.0", "3.0.0") shouldBe true + } + it should "for (3.1.0, 3.1.1) return true" in { + isBinaryCompatibleWith("3.1.0", "3.1.1") shouldBe true + } + it should "for (3.1.1, 3.1.0) return true" in { + isBinaryCompatibleWith("3.1.1", "3.1.0") shouldBe true + } + it should "for (2.10.0-M1, 2.10.5) return true" in { + isBinaryCompatibleWith("2.10.0-M1", "2.10.5") shouldBe true + } + it should "for (2.10.5, 2.10.0-M1) return true" in { + isBinaryCompatibleWith("2.10.5", "2.10.0-M1") shouldBe true + } + it should "for (2.10.0-M1, 2.10.0-M2) return true" in { + isBinaryCompatibleWith("2.10.0-M1", "2.10.0-M2") shouldBe true + } + it should "for (2.10.0-M1, 2.11.0-M1) return false" in { + isBinaryCompatibleWith("2.10.0-M1", "2.11.0-M1") shouldBe false + } + it should "for (3.1.0-M1, 3.0.0) return true" in { + isBinaryCompatibleWith("3.1.0-M1", "3.0.0") shouldBe true + } + it should "for (3.1.0-M1, 3.1.0) return false" in { + isBinaryCompatibleWith("3.1.0-M1", "3.1.0") shouldBe false + } + it should "for (3.1.0-M1, 3.1.0-M2) return false" in { + isBinaryCompatibleWith("3.1.0-M1", "3.1.0-M2") shouldBe false + } + it should "for (3.1.0-M2, 3.1.0-M1) return false" in { + isBinaryCompatibleWith("3.1.0-M2", "3.1.0-M1") shouldBe false + } + private def constantVersion(value: String) = CrossVersion(CrossVersion.constant(value), "dummy1", "dummy2") map (fn => fn("artefact")) From daec6265c441ba8dedfd893250b4152bd9bcbc9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Gonc=CC=A7alves?= Date: Sat, 25 Jun 2022 22:54:08 +0100 Subject: [PATCH 2/2] Rename function and extend support to Scala 4+ --- .../cross/CrossVersionUtil.scala | 16 ++++++--- .../librarymanagement/CrossVersionExtra.scala | 4 +-- .../librarymanagement/CrossVersionTest.scala | 34 +++++++++---------- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala b/core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala index b09f42ca7..1f58cfe7d 100644 --- a/core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala +++ b/core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala @@ -92,12 +92,18 @@ object CrossVersionUtil { // // - For non-stable Scala 3 versions, compiler versions can read TASTy in an older stable format but their TASTY versions are not compatible between each other even if the compilers have the same minor version (https://docs.scala-lang.org/scala3/reference/language-versions/binary-compatibility.html) // - private[sbt] def isBinaryCompatibleWith(newVersion: String, origVersion: String): Boolean = { + private[sbt] def isScalaBinaryCompatibleWith(newVersion: String, origVersion: String): Boolean = { (newVersion, origVersion) match { - case (NonReleaseV_n("2", nMin, _, _), NonReleaseV_n("2", oMin, _, _)) => nMin == oMin - case (ReleaseV("3", nMin, _, _), ReleaseV("3", oMin, _, _)) => nMin.toInt >= oMin.toInt - case (NonReleaseV_1("3", nMin, _, _), ReleaseV("3", oMin, _, _)) => nMin.toInt > oMin.toInt - case _ => newVersion == origVersion + case (NonReleaseV_n("2", nMin, _, _), NonReleaseV_n("2", oMin, _, _)) => + nMin == oMin + case (ReleaseV(nMaj, nMin, _, _), ReleaseV(oMaj, oMin, _, _)) + if nMaj == oMaj && nMaj.toLong >= 3 => + nMin.toInt >= oMin.toInt + case (NonReleaseV_1(nMaj, nMin, _, _), ReleaseV(oMaj, oMin, _, _)) + if nMaj == oMaj && nMaj.toLong >= 3 => + nMin.toInt > oMin.toInt + case _ => + newVersion == origVersion } } diff --git a/core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala b/core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala index a60f30325..03cd97e88 100644 --- a/core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala +++ b/core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala @@ -239,6 +239,6 @@ private[librarymanagement] abstract class CrossVersionFunctions { /** * Returns `true` if a project targeting version `origVersion` can run with version `newVersion`. */ - def isBinaryCompatibleWith(newVersion: String, origVersion: String): Boolean = - CrossVersionUtil.isBinaryCompatibleWith(newVersion, origVersion) + def isScalaBinaryCompatibleWith(newVersion: String, origVersion: String): Boolean = + CrossVersionUtil.isScalaBinaryCompatibleWith(newVersion, origVersion) } diff --git a/core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala b/core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala index b4e570083..aae585b16 100644 --- a/core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala +++ b/core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala @@ -284,53 +284,53 @@ class CrossVersionTest extends UnitSpec { patchVersion("2.11.8-X1.5-bin-extra") shouldBe Some("artefact_2.11.8-X1.5") } - "isBinaryCompatibleWith" should "for (2.10.4, 2.10.5) return true" in { - isBinaryCompatibleWith("2.10.4", "2.10.5") shouldBe true + "isScalaBinaryCompatibleWith" should "for (2.10.4, 2.10.5) return true" in { + isScalaBinaryCompatibleWith("2.10.4", "2.10.5") shouldBe true } it should "for (2.10.6, 2.10.5) return true" in { - isBinaryCompatibleWith("2.10.6", "2.10.5") shouldBe true + isScalaBinaryCompatibleWith("2.10.6", "2.10.5") shouldBe true } it should "for (2.11.0, 2.10.5) return false" in { - isBinaryCompatibleWith("2.11.0", "2.10.5") shouldBe false + isScalaBinaryCompatibleWith("2.11.0", "2.10.5") shouldBe false } it should "for (3.0.0, 2.10.5) return false" in { - isBinaryCompatibleWith("3.0.0", "2.10.5") shouldBe false + isScalaBinaryCompatibleWith("3.0.0", "2.10.5") shouldBe false } it should "for (3.0.0, 3.1.0) return false" in { - isBinaryCompatibleWith("3.0.0", "3.1.0") shouldBe false + isScalaBinaryCompatibleWith("3.0.0", "3.1.0") shouldBe false } it should "for (3.1.0, 3.0.0) return true" in { - isBinaryCompatibleWith("3.1.0", "3.0.0") shouldBe true + isScalaBinaryCompatibleWith("3.1.0", "3.0.0") shouldBe true } it should "for (3.1.0, 3.1.1) return true" in { - isBinaryCompatibleWith("3.1.0", "3.1.1") shouldBe true + isScalaBinaryCompatibleWith("3.1.0", "3.1.1") shouldBe true } it should "for (3.1.1, 3.1.0) return true" in { - isBinaryCompatibleWith("3.1.1", "3.1.0") shouldBe true + isScalaBinaryCompatibleWith("3.1.1", "3.1.0") shouldBe true } it should "for (2.10.0-M1, 2.10.5) return true" in { - isBinaryCompatibleWith("2.10.0-M1", "2.10.5") shouldBe true + isScalaBinaryCompatibleWith("2.10.0-M1", "2.10.5") shouldBe true } it should "for (2.10.5, 2.10.0-M1) return true" in { - isBinaryCompatibleWith("2.10.5", "2.10.0-M1") shouldBe true + isScalaBinaryCompatibleWith("2.10.5", "2.10.0-M1") shouldBe true } it should "for (2.10.0-M1, 2.10.0-M2) return true" in { - isBinaryCompatibleWith("2.10.0-M1", "2.10.0-M2") shouldBe true + isScalaBinaryCompatibleWith("2.10.0-M1", "2.10.0-M2") shouldBe true } it should "for (2.10.0-M1, 2.11.0-M1) return false" in { - isBinaryCompatibleWith("2.10.0-M1", "2.11.0-M1") shouldBe false + isScalaBinaryCompatibleWith("2.10.0-M1", "2.11.0-M1") shouldBe false } it should "for (3.1.0-M1, 3.0.0) return true" in { - isBinaryCompatibleWith("3.1.0-M1", "3.0.0") shouldBe true + isScalaBinaryCompatibleWith("3.1.0-M1", "3.0.0") shouldBe true } it should "for (3.1.0-M1, 3.1.0) return false" in { - isBinaryCompatibleWith("3.1.0-M1", "3.1.0") shouldBe false + isScalaBinaryCompatibleWith("3.1.0-M1", "3.1.0") shouldBe false } it should "for (3.1.0-M1, 3.1.0-M2) return false" in { - isBinaryCompatibleWith("3.1.0-M1", "3.1.0-M2") shouldBe false + isScalaBinaryCompatibleWith("3.1.0-M1", "3.1.0-M2") shouldBe false } it should "for (3.1.0-M2, 3.1.0-M1) return false" in { - isBinaryCompatibleWith("3.1.0-M2", "3.1.0-M1") shouldBe false + isScalaBinaryCompatibleWith("3.1.0-M2", "3.1.0-M1") shouldBe false } private def constantVersion(value: String) =