diff --git a/lm-core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala b/lm-core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala index 50fcfd632..b5142754f 100644 --- a/lm-core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala +++ b/lm-core/src/main/scala/sbt/internal/librarymanagement/cross/CrossVersionUtil.scala @@ -1,8 +1,6 @@ package sbt.internal.librarymanagement package cross -import sbt.librarymanagement.ScalaArtifacts - object CrossVersionUtil { val trueString = "true" val falseString = "false" @@ -111,11 +109,26 @@ object CrossVersionUtil { } } - def binaryScalaVersion(full: String): String = { - if (ScalaArtifacts.isScala3(full)) binaryScala3Version(full) - else + def binaryScalaVersion(full: String): String = + if full.startsWith("2.") then binaryVersionWithApi(full, TransitionScalaVersion)(scalaApiVersion) // Scala 2 binary version - } + else binaryScala3Version(full) + + /** + * Returns the binary version of the Scala, except for + * prereleases version, returns the binary version of the release equivalent. + * This was called the partial version in Scala 2.x. + * In Scala 3 onwards, it would be the major version. + */ + def earlyScalaVersion(full: String): String = + if full.startsWith("2.") then + partialVersion(full) match + case Some((major, minor)) => s"$major.$minor" + case None => full + else + partialVersion(full) match + case Some((major, minor)) => major.toString + case None => full def binarySbtVersion(full: String): String = sbtApiVersion(full) match { diff --git a/lm-core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala b/lm-core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala index 56c30b67c..6b1035ada 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/CrossVersionExtra.scala @@ -224,6 +224,14 @@ private[librarymanagement] abstract class CrossVersionFunctions { /** Extracts the major and minor components of a version string `s` or returns `None` if the version is improperly formatted. */ def partialVersion(s: String): Option[(Long, Long)] = CrossVersionUtil.partialVersion(s) + /** + * Returns the binary version of the Scala, except for + * prereleases version, returns the binary version of the release equivalent. + * This was called the partial version in Scala 2.x. + * In Scala 3 onwards, it would be the major version. + */ + def earlyScalaVersion(full: String): String = CrossVersionUtil.earlyScalaVersion(full) + /** * Computes the binary Scala version from the `full` version. * Full Scala versions earlier than [[sbt.librarymanagement.CrossVersion.TransitionScalaVersion]] are returned as is. diff --git a/lm-core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala b/lm-core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala index 374982858..57c8e2ddc 100644 --- a/lm-core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala +++ b/lm-core/src/test/scala/sbt/librarymanagement/CrossVersionTest.scala @@ -231,9 +231,6 @@ class CrossVersionTest extends UnitSpec { it should "for 3.0.0-RC1 return 3.0.0-RC1" in { binaryScalaVersion("3.0.0-RC1") shouldBe "3.0.0-RC1" } - - // Not set in stone but 3 is the favorite candidate so far - // (see https://github.com/lampepfl/dotty/issues/10244) it should "for 3.0.0 return 3" in { binaryScalaVersion("3.0.0") shouldBe "3" } @@ -264,6 +261,22 @@ class CrossVersionTest extends UnitSpec { it should "for 3.0.1-SNAPSHOT return 3" in { binaryScalaVersion("3.0.1-SNAPSHOT") shouldBe "3" } + it should "for 4.0.0-M2 return 4.0.0-M2" in { + binaryScalaVersion("4.0.0-M2") shouldBe "4.0.0-M2" + } + + "earlyScalaVersion" should "for 2.9.2 return 2.9" in { + assert(earlyScalaVersion("2.9.2") == "2.9") + } + it should "for 2.13.0-M1 return 2.13" in { + assert(earlyScalaVersion("2.13.0-M1") == "2.13") + } + it should "for 3.0.0-M1 return 3" in { + assert(earlyScalaVersion("3.0.0-M1") == "3") + } + it should "for 4.0.0-M1 return 4" in { + assert(earlyScalaVersion("4.0.0-M1") == "4") + } private def patchVersion(fullVersion: String) = CrossVersion(CrossVersion.patch, fullVersion, "dummy") map (fn => fn("artefact")) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index d6ac7c1aa..cbff21afb 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -566,12 +566,14 @@ object Defaults extends BuildCommon { scalaSource := sourceDirectory.value / "scala", javaSource := sourceDirectory.value / "java", unmanagedSourceDirectories := { - val isDotty = ScalaInstance.isDotty(scalaVersion.value) - val epochVersion = if (isDotty) "3" else "2" + val early = scalaEarlyVersion.value + val epochVersion = + if scalaVersion.value.startsWith("2.") then "2" + else early makeCrossSources( scalaSource.value, javaSource.value, - scalaBinaryVersion.value, + early, epochVersion, crossPaths.value ) ++ @@ -762,7 +764,8 @@ object Defaults extends BuildCommon { scalaVersion := appConfiguration.value.provider.scalaProvider.version, derive(crossScalaVersions := Seq(scalaVersion.value)), derive(compilersSetting), - derive(scalaBinaryVersion := binaryScalaVersion(scalaVersion.value)) + derive(scalaBinaryVersion := binaryScalaVersion(scalaVersion.value)), + derive(scalaEarlyVersion := CrossVersion.earlyScalaVersion(scalaVersion.value)), ) ) @@ -3490,6 +3493,7 @@ object Classpaths { // to fix https://github.com/sbt/sbt/issues/2686 scalaVersion := appConfiguration.value.provider.scalaProvider.version, scalaBinaryVersion := binaryScalaVersion(scalaVersion.value), + scalaEarlyVersion := CrossVersion.earlyScalaVersion(scalaVersion.value), scalaOrganization := ScalaArtifacts.Organization, scalaModuleInfo := { Some( diff --git a/main/src/main/scala/sbt/Keys.scala b/main/src/main/scala/sbt/Keys.scala index ac421f4ee..94678796e 100644 --- a/main/src/main/scala/sbt/Keys.scala +++ b/main/src/main/scala/sbt/Keys.scala @@ -217,6 +217,7 @@ object Keys { val scalaOrganization = settingKey[String]("Organization/group ID of the Scala used in the project. Default value is 'org.scala-lang'. This is an advanced setting used for clones of the Scala Language. It should be disregarded in standard use cases.").withRank(CSetting) val scalaVersion = settingKey[String]("The version of Scala used for building.").withRank(APlusSetting) val scalaBinaryVersion = settingKey[String]("The Scala version substring describing binary compatibility.").withRank(BPlusSetting) + val scalaEarlyVersion = settingKey[String]("The Scala version substring describing the binary compatibility, except for prereleases it returns the binary version of the release.").withRank(DSetting) val crossScalaVersions = settingKey[Seq[String]]("The versions of Scala used when cross-building.").withRank(BPlusSetting) val crossVersion = settingKey[CrossVersion]("Configures handling of the Scala version when cross-building.").withRank(CSetting) val platform = settingKey[String]("Configures the default suffix to be used for %% operator.").withRank(CSetting)