Skip to content

Commit 5e0c260

Browse files
authored
Add rc/*.rc Scala version aliases (#4016)
1 parent 34e31bd commit 5e0c260

File tree

4 files changed

+127
-64
lines changed

4 files changed

+127
-64
lines changed

modules/core/src/main/scala/scala/build/internals/Regexes.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@ package scala.build.internal
33
object Regexes {
44
val scala2NightlyRegex = raw"""2\.(\d+)\.(\d+)-bin-[a-f0-9]*""".r
55
val scala3NightlyNicknameRegex = raw"""3\.([0-9]*)\.nightly""".r
6+
val scala3RcRegex = raw"""3\.([0-9]*\.[0-9]*-[rR][cC][0-9]+)""".r
7+
val scala3RcNicknameRegex = raw"""3\.([0-9]*)\.?[rR][cC]""".r
68
val scala3LtsRegex = raw"""3\.3\.[0-9]+""".r
79
}

modules/integration/src/test/scala/scala/cli/integration/RunTests3NextRc.scala

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,63 @@ package scala.cli.integration
33
import com.eed3si9n.expecty.Expecty.expect
44

55
class RunTests3NextRc extends RunTestDefinitions with Test3NextRc {
6+
7+
def getScalaVersion(
8+
scalaVersionIndex: String,
9+
root: os.Path,
10+
check: Boolean = true,
11+
mergeErrIntoOut: Boolean = false
12+
): String =
13+
os.proc(
14+
TestUtil.cli,
15+
"run",
16+
"-e",
17+
"println(dotty.tools.dotc.config.Properties.simpleVersionString)",
18+
"-S",
19+
scalaVersionIndex,
20+
"--with-compiler",
21+
TestUtil.extraOptions
22+
)
23+
.call(cwd = root, check = check, mergeErrIntoOut = mergeErrIntoOut)
24+
.out
25+
.trim()
26+
627
test("Scala 3.nightly & 3.<latest-minor>.nightly point to the same version") {
728
TestInputs.empty.fromRoot { root =>
8-
def getScalaVersion(scalaVersionIndex: String) =
9-
os.proc(
10-
TestUtil.cli,
11-
"run",
12-
"-e",
13-
s"""println($retrieveScalaVersionCode)""",
14-
"-S",
15-
scalaVersionIndex,
16-
TestUtil.extraOptions
17-
)
18-
.call(cwd = root)
19-
.out
20-
.trim()
21-
22-
val version1 = getScalaVersion("3.nightly")
29+
val version1 = getScalaVersion("3.nightly", root)
2330
val nightlyMinor = version1.split('.').take(2).last
24-
val version2 = getScalaVersion(s"3.$nightlyMinor.nightly")
31+
val version2 = getScalaVersion(s"3.$nightlyMinor.nightly", root)
2532
expect(version1 == version2)
2633
}
2734
}
35+
36+
for {
37+
label <- List("rc", "3.rc", "3.lts.rc", "lts.rc", s"${Constants.scala3LtsPrefix}.rc", "3.7.rc")
38+
}
39+
test(s"$label is valid and works as expected") {
40+
TestInputs.empty.fromRoot { root =>
41+
val latestRcVersion = getScalaVersion(label, root)
42+
if latestRcVersion == actualScalaVersion then {
43+
expect(
44+
label == "rc" || label == "3.rc"
45+
) // this should only be the case for *latest* labels
46+
System.err.println(s"RC version $latestRcVersion is the same as the hardcoded latest RC")
47+
}
48+
expect(latestRcVersion.startsWith("3."))
49+
expect(latestRcVersion.contains("-RC"))
50+
expect(!latestRcVersion.contains("SNAPSHOT"))
51+
expect(!latestRcVersion.contains("NIGHTLY"))
52+
}
53+
}
54+
55+
for { label <- List("2.rc", "2.12.rc", "2.13.rc") }
56+
test(s"$label produces a reasonable error") {
57+
TestInputs.empty.fromRoot { root =>
58+
val result = getScalaVersion(label, root, check = false, mergeErrIntoOut = true)
59+
expect(result.contains("Invalid Scala version"))
60+
expect(result.contains(
61+
"In the case of Scala 2, a particular nightly version serves as a release candidate."
62+
))
63+
}
64+
}
2865
}

modules/options/src/main/scala/scala/build/options/BuildOptions.scala

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ import scala.build.errors.*
1717
import scala.build.interactive.Interactive
1818
import scala.build.interactive.Interactive.*
1919
import scala.build.internal.Constants.*
20-
import scala.build.internal.Regexes.scala3NightlyNicknameRegex
20+
import scala.build.internal.Regexes.{
21+
scala3NightlyNicknameRegex,
22+
scala3RcNicknameRegex,
23+
scala3RcRegex
24+
}
2125
import scala.build.internal.{Constants, OsLibc, Util}
2226
import scala.build.internals.EnvVar
2327
import scala.build.options.validation.BuildOptionsRule
@@ -345,15 +349,23 @@ final case class BuildOptions(
345349
val sv = value {
346350
svInput match {
347351
case sv if ScalaVersionUtil.scala3Lts.contains(sv) =>
348-
ScalaVersionUtil.validateStable(
349-
Constants.scala3LtsPrefix,
350-
cache,
351-
repositories
352-
)
352+
ScalaVersionUtil.validateStable(Constants.scala3LtsPrefix, cache, repositories)
353+
case sv if ScalaVersionUtil.scala3LatestRc.contains(sv.toLowerCase) =>
354+
ScalaVersionUtil.validateRc("3", cache, repositories)
355+
case sv if ScalaVersionUtil.scala3LtsLatestRc.contains(sv.toLowerCase) =>
356+
ScalaVersionUtil.validateRc(Constants.scala3LtsPrefix, cache, repositories)
357+
case scala3RcRegex(threeSubBinarySuffix) =>
358+
ScalaVersionUtil.validateRc(s"3.$threeSubBinarySuffix", cache, repositories)
359+
case scala3RcNicknameRegex(threeSubBinarySuffix) =>
360+
ScalaVersionUtil.validateRc(s"3.$threeSubBinarySuffix", cache, repositories)
353361
case sv if ScalaVersionUtil.scala2Lts.contains(sv) =>
354362
Left(new ScalaVersionError(
355363
s"Invalid Scala version: $sv. There is no official LTS version for Scala 2."
356364
))
365+
case sv if ScalaVersionUtil.scala2LatestRc.contains(sv) =>
366+
Left(new ScalaVersionError(
367+
s"Invalid Scala version: $sv. In the case of Scala 2, a particular nightly version serves as a release candidate."
368+
))
357369
case sv if sv == ScalaVersionUtil.scala3Nightly =>
358370
ScalaVersionUtil.GetNightly.scala3(cache)
359371
case scala3NightlyNicknameRegex(threeSubBinaryNum) =>
@@ -376,7 +388,7 @@ final case class BuildOptions(
376388
)
377389
.map(_ => versionString)
378390
case versionString if versionString.exists(_.isLetter) =>
379-
ScalaVersionUtil.validateNonStable(
391+
ScalaVersionUtil.validateExactVersion(
380392
versionString,
381393
cache,
382394
repositories

modules/options/src/main/scala/scala/build/options/ScalaVersionUtil.scala

Lines changed: 53 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,16 @@ import scala.util.control.NonFatal
2727

2828
object ScalaVersionUtil {
2929

30-
private def scala2Library = cmod"org.scala-lang:scala-library"
31-
private def scala3Library = cmod"org.scala-lang:scala3-library_3"
32-
def scala212Nightly = "2.12.nightly"
33-
def scala213Nightly = List("2.13.nightly", "2.nightly")
34-
def scala3Nightly = "3.nightly"
35-
def scala3Lts = List("3.lts", "lts")
30+
private def scala2Library = cmod"org.scala-lang:scala-library"
31+
private def scala3Library = cmod"org.scala-lang:scala3-library_3"
32+
def scala212Nightly = "2.12.nightly"
33+
def scala213Nightly = List("2.13.nightly", "2.nightly")
34+
def scala3Nightly = "3.nightly"
35+
private def rcAlias(prefix: String) = s"$prefix.rc"
36+
def scala2LatestRc = List(rcAlias("2"), rcAlias("2.12"), rcAlias("2.13"))
37+
def scala3LatestRc = List("rc", rcAlias("3"))
38+
def scala3LtsLatestRc = List(rcAlias("lts"), rcAlias("3.lts"), rcAlias(Constants.scala3LtsPrefix))
39+
def scala3Lts = List("3.lts", "lts")
3640
// not valid versions, defined only for informative error messages
3741
def scala2Lts = List("2.13.lts", "2.12.lts", "2.lts")
3842
extension (cache: FileCache[Task]) {
@@ -182,53 +186,55 @@ object ScalaVersionUtil {
182186
cache.versionsWithTtl0(scala3Library, repositories).verify(versionString)
183187
}
184188

185-
def validateNonStable(
189+
def validate(
186190
scalaVersionStringArg: String,
187191
cache: FileCache[Task],
188-
repositories: Seq[Repository]
189-
): Either[ScalaVersionError, String] = {
192+
repositories: Seq[Repository],
193+
isExactVersion: Boolean
194+
)(validationFunction: String => Boolean): Either[ScalaVersionError, String] = {
190195
val versionPool =
191196
ScalaVersionUtil.allMatchingVersions(Some(scalaVersionStringArg), cache, repositories)
197+
.filter(validationFunction)
192198

193-
if (versionPool.contains(scalaVersionStringArg))
194-
if (isSupportedVersion(scalaVersionStringArg))
195-
Right(scalaVersionStringArg)
196-
else
197-
Left(new UnsupportedScalaVersionError(scalaVersionStringArg))
198-
else
199-
Left(new InvalidBinaryScalaVersionError(scalaVersionStringArg))
199+
val prefix =
200+
if Util.isFullScalaVersion(scalaVersionStringArg) then scalaVersionStringArg
201+
else if scalaVersionStringArg.endsWith(".") then scalaVersionStringArg
202+
else scalaVersionStringArg + "."
203+
val matchingVersions = versionPool.filter(_.startsWith(prefix)).map(Version(_))
204+
if matchingVersions.isEmpty ||
205+
(isExactVersion && !matchingVersions.contains(scalaVersionStringArg))
206+
then Left(new InvalidBinaryScalaVersionError(scalaVersionStringArg))
207+
else {
208+
val supportedMatchingVersions = matchingVersions.filter(v => isSupportedVersion(v.repr))
209+
supportedMatchingVersions.find(_.repr == scalaVersionStringArg) match {
210+
case Some(v) => Right(v.repr)
211+
case None if supportedMatchingVersions.nonEmpty && !isExactVersion =>
212+
Right(supportedMatchingVersions.max.repr)
213+
case _ => Left(new UnsupportedScalaVersionError(scalaVersionStringArg))
214+
}
215+
}
200216
}
201217

218+
def validateExactVersion(
219+
scalaVersionStringArg: String,
220+
cache: FileCache[Task],
221+
repositories: Seq[Repository]
222+
): Either[ScalaVersionError, String] =
223+
validate(scalaVersionStringArg, cache, repositories, isExactVersion = true)(_ => true)
224+
202225
def validateStable(
203226
scalaVersionStringArg: String,
204227
cache: FileCache[Task],
205228
repositories: Seq[Repository]
206-
): Either[ScalaVersionError, String] = {
207-
val versionPool =
208-
ScalaVersionUtil.allMatchingVersions(Some(scalaVersionStringArg), cache, repositories)
209-
.filter(ScalaVersionUtil.isStable)
229+
): Either[ScalaVersionError, String] =
230+
validate(scalaVersionStringArg, cache, repositories, isExactVersion = false)(isStable)
210231

211-
val prefix =
212-
if (Util.isFullScalaVersion(scalaVersionStringArg)) scalaVersionStringArg
213-
else if (scalaVersionStringArg.endsWith(".")) scalaVersionStringArg
214-
else scalaVersionStringArg + "."
215-
val matchingStableVersions = versionPool.filter(_.startsWith(prefix)).map(Version(_))
216-
if (matchingStableVersions.isEmpty)
217-
Left(new InvalidBinaryScalaVersionError(scalaVersionStringArg))
218-
else {
219-
val supportedMatchingStableVersions =
220-
matchingStableVersions.filter(v => isSupportedVersion(v.repr))
221-
222-
supportedMatchingStableVersions.find(_.repr == scalaVersionStringArg) match {
223-
case Some(v) => Right(v.repr)
224-
case None if supportedMatchingStableVersions.nonEmpty =>
225-
Right(supportedMatchingStableVersions.max.repr)
226-
case _ => Left(
227-
new UnsupportedScalaVersionError(scalaVersionStringArg)
228-
)
229-
}
230-
}
231-
}
232+
def validateRc(
233+
scalaVersionStringArg: String,
234+
cache: FileCache[Task],
235+
repositories: Seq[Repository]
236+
): Either[ScalaVersionError, String] =
237+
validate(scalaVersionStringArg, cache, repositories, isExactVersion = false)(isRc)
232238

233239
private def isSupportedVersion(version: String): Boolean =
234240
version.startsWith("2.12.") || version.startsWith("2.13.") || version.startsWith("3.")
@@ -241,6 +247,12 @@ object ScalaVersionUtil {
241247
(version.startsWith("3") && version.endsWith("-NIGHTLY")) || version == scala3Nightly
242248
def isStable(version: String): Boolean =
243249
!version.exists(_.isLetter)
250+
def isRc(version: String): Boolean = {
251+
val lowerCasedVersion = version.toLowerCase
252+
lowerCasedVersion.contains("rc") &&
253+
!lowerCasedVersion.contains("-nightly") &&
254+
!lowerCasedVersion.contains("-snapshot")
255+
}
244256

245257
def allMatchingVersions(
246258
maybeScalaVersionArg: Option[String],

0 commit comments

Comments
 (0)