Skip to content

Commit

Permalink
added support for 2.12.nightly and 2.13.nightly and some non stable v…
Browse files Browse the repository at this point in the history
…ersions (#736)

* add support for 2.12.nightly, 2.13.nightly, and some nonstable versions.

* Update website/docs/commands/compile.md

Co-authored-by: Alexandre Archambault <alexarchambault@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: Alexandre Archambault <alexarchambault@users.noreply.github.com>

* more refactoring

* support for not adding repository for 2.night with repl

* Minimize diff

* Fix regression

Don't force add runner value when we don't have to

* fixup

Co-authored-by: Alexandre Archambault <alexandre.archambault@gmail.com>
  • Loading branch information
zmerr and alexarchambault authored Mar 11, 2022
1 parent f08a524 commit af33e0f
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 91 deletions.
13 changes: 2 additions & 11 deletions modules/build/src/main/scala/scala/build/Artifacts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import scala.build.errors.{
import scala.build.internal.Constants
import scala.build.internal.Constants._
import scala.build.internal.CsLoggerUtil._
import scala.build.internal.ScalaParse.scala2NightlyRegex
import scala.build.internal.Util.ScalaDependencyOps

final case class Artifacts(
Expand Down Expand Up @@ -134,22 +133,14 @@ object Artifacts {
Nil
}

val scala2NightlyRepo = Seq(coursier.Repositories.scalaIntegration.root)

val scalaNativeCliDependency =
scalaNativeCliVersion.map { version =>
import coursier.moduleString
Seq(coursier.Dependency(mod"org.scala-native:scala-native-cli_2.12", version))
}

val isScala2NightlyRequested = scala2NightlyRegex.unapplySeq(params.scalaVersion).isDefined

val allExtraRepositories = {
val baseRepos =
maybeSnapshotRepo ++ extraRepositories
if (isScala2NightlyRequested) baseRepos ++ scala2NightlyRepo
else baseRepos
}
val allExtraRepositories =
maybeSnapshotRepo ++ extraRepositories

val internalDependencies =
jvmRunnerDependencies.map(Positioned.none(_)) ++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ object ScalaVersionError {
def getTheGeneralErrorInfo(latestSupportedStableVersions: Seq[String]): String =
s"""You can only choose one of the 3.x, 2.13.x, and 2.12.x. versions.
|The latest supported stable versions are ${latestSupportedStableVersions.mkString(", ")}.
|In addition, you can request the latest Scala 2 and Scala 3 nightly versions by passing 2.nightly, and 3.nightly arguments respectively.
|In addition, you can request compilation with the last nightly versions of Scala,
|by passing the 2.nightly, 2.12.nightly, 2.13.nightly, or 3.nightly arguments.
|Specific Scala 2 or Scala 3 nightly versions are also accepted.
|""".stripMargin
}
252 changes: 174 additions & 78 deletions modules/build/src/main/scala/scala/build/options/BuildOptions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,16 @@ final case class BuildOptions(
Seq(s"Scala ${value(scalaParams).scalaVersion}", platform0)
}

lazy val scalaVersionIsExotic = scalaParams.exists { scalaParameters =>
scalaParameters.scalaVersion.startsWith("2") && scalaParameters.scalaVersion.exists(_.isLetter)
}

def addRunnerDependency: Option[Boolean] =
internalDependencies.addRunnerDependencyOpt
.orElse(if (platform.value == Platform.JVM) None else Some(false))
.orElse {
if (platform.value == Platform.JVM && !scalaVersionIsExotic) None
else Some(false)
}

private def scalaLibraryDependencies: Either[BuildException, Seq[AnyDependency]] = either {
if (platform.value != Platform.Native && scalaOptions.addScalaLibrary.getOrElse(true)) {
Expand Down Expand Up @@ -289,101 +296,158 @@ final case class BuildOptions(
JavaHome().withCache(jvmCache)
}

private val scala2NightlyRepo = Seq(coursier.Repositories.scalaIntegration.root)

def finalRepositories: Seq[String] =
classPathOptions.extraRepositories ++ internal.localRepository.toSeq
scalaParams.map { params =>
if (isScala2Nightly(params.scalaVersion)) scala2NightlyRepo else Seq.empty
}.getOrElse(Seq.empty) ++
classPathOptions.extraRepositories ++ internal.localRepository.toSeq

private lazy val maxSupportedStableScalaVersions = latestSupportedStableScalaVersion()

private lazy val latestSupportedStableVersions = maxSupportedStableScalaVersions.map(_.repr)

private def getAllMatchingStableVersions(scalaVersionArg: Option[String]): Seq[String] = {

def isStable(version: String): Boolean =
!version.exists(_.isLetter)

modules(scalaVersionArg).flatMap(moduleVersions(_).versions.available.filter(isStable)).distinct
}

private def modules(maybeScalaVersionArg: Option[String]) = {
import coursier.moduleString
def scala2 = mod"org.scala-lang:scala-library"
// No unstable, that *ought* not to be a problem down-the-line…?
def scala3 = mod"org.scala-lang:scala3-library_3"
if (maybeScalaVersionArg.contains("2") || maybeScalaVersionArg.exists(_.startsWith("2.")))
Seq(scala2)
else if (maybeScalaVersionArg.contains("3") || maybeScalaVersionArg.exists(_.startsWith("3.")))
Seq(scala3)
else Seq(scala2, scala3)
}

private def moduleVersions(mod: Module): Versions.Result =
finalCache.logger.use {
try Versions(finalCache)
.withModule(mod)
.result()
.unsafeRun()(finalCache.ec)
catch {
case NonFatal(e) => throw new Exception(e)
}
}

private def getAllMatchingVersions(maybeScalaVersionArg: Option[String]): Seq[String] =
modules(maybeScalaVersionArg).flatMap(moduleVersions(_).versions.available).distinct

/** @param scalaVersionArg
* the command line, using directive, or default argument passed as scala version
* @param scalaBinaryVersionArg
* the command line, using directive, or default argument passed as scala Binary version
* @return
* Either a BuildException or the calculated (ScalaVersion, ScalaBinaryVersion) tuple
*/
private def turnScalaVersionArgToScalaVersions(
private def turnScalaVersionArgToStableScalaVersions(
scalaVersionArg: Option[String],
scalaBinaryVersionArg: Option[String]
): Either[BuildException, (String, String)] = either {
def isSupportedVersion(version: String): Boolean =
version.startsWith("2.12.") || version.startsWith("2.13.") || version.startsWith("3.")
lazy val allStableVersions = {
val modules = {
import coursier.moduleString
def scala2 = mod"org.scala-lang:scala-library"
// No unstable, that *ought* not to be a problem down-the-line…?
def scala3 = mod"org.scala-lang:scala3-library_3"
if (scalaVersionArg.contains("2") || scalaVersionArg.exists(_.startsWith("2."))) Seq(scala2)
else if (scalaVersionArg.contains("3") || scalaVersionArg.exists(_.startsWith("3.")))
Seq(scala3)
else Seq(scala2, scala3)
}
def isStable(v: String): Boolean =
!v.endsWith("-NIGHTLY") && !v.contains("-RC")
def moduleVersions(mod: Module): Seq[String] = {
val res = finalCache.logger.use {
try Versions(finalCache)
.withModule(mod)
.result()
.unsafeRun()(finalCache.ec)
catch {
case NonFatal(e) => throw new Exception(e)
}
}
res.versions.available.filter(isStable)
}
modules.flatMap(moduleVersions).distinct
}

def matchNewestStableScalaVersion(maybeScalaVersionStringArg: Option[String])
: Either[ScalaVersionError, String] =
maybeScalaVersionStringArg match {
case Some(scalaVersionStringArg) =>
val prefix =
if (Util.isFullScalaVersion(scalaVersionStringArg)) scalaVersionStringArg
else if (scalaVersionStringArg.endsWith(".")) scalaVersionStringArg
else scalaVersionStringArg + "."
val matchingVersions = allStableVersions.filter(_.startsWith(prefix)).map(Version(_))
if (matchingVersions.isEmpty)
Left(new InvalidBinaryScalaVersionError(
lazy val allStableVersions = getAllMatchingStableVersions(scalaVersionArg)

val scalaVersion = value(matchNewestStableScalaVersion(scalaVersionArg, allStableVersions))
val scalaBinaryVersion = scalaBinaryVersionArg.getOrElse(ScalaVersion.binary(scalaVersion))
(scalaVersion, scalaBinaryVersion)
}

private def turnScalaVersionArgToNonStableScalaVersions(
scalaVersionArg: Option[String],
scalaBinaryVersionArg: Option[String]
): Either[BuildException, (String, String)] = either {

lazy val allStableVersions = getAllMatchingVersions(scalaVersionArg)

val scalaVersion = value(matchNewestNonStableScalaVersion(scalaVersionArg, allStableVersions))
val scalaBinaryVersion = scalaBinaryVersionArg.getOrElse(ScalaVersion.binary(scalaVersion))
(scalaVersion, scalaBinaryVersion)
}

private def isSupportedVersion(version: String): Boolean =
version.startsWith("2.12.") || version.startsWith("2.13.") || version.startsWith("3.")

private def matchNewestStableScalaVersion(
maybeScalaVersionStringArg: Option[String],
versionPool: Seq[String]
): Either[ScalaVersionError, String] =
maybeScalaVersionStringArg match {
case Some(scalaVersionStringArg) =>
val prefix =
if (Util.isFullScalaVersion(scalaVersionStringArg)) scalaVersionStringArg
else if (scalaVersionStringArg.endsWith(".")) scalaVersionStringArg
else scalaVersionStringArg + "."
val matchingStableVersions = versionPool.filter(_.startsWith(prefix)).map(Version(_))
if (matchingStableVersions.isEmpty)
Left(new InvalidBinaryScalaVersionError(
scalaVersionStringArg,
latestSupportedStableVersions
))
else {
val validMaxVersions = maxSupportedStableScalaVersions
.filter(_.repr.startsWith(prefix))
val validMatchingVersions = {
val filtered = matchingStableVersions.filter(v => validMaxVersions.exists(v <= _))
if (filtered.isEmpty) matchingStableVersions
else filtered
}.filter(v => isSupportedVersion(v.repr))
if (validMatchingVersions.isEmpty)
Left(new UnsupportedScalaVersionError(
scalaVersionStringArg,
latestSupportedStableVersions
))
else {
val validMaxVersions = maxSupportedStableScalaVersions
.filter(_.repr.startsWith(prefix))
val validMatchingVersions = {
val filtered = matchingVersions.filter(v => validMaxVersions.exists(v <= _))
if (filtered.isEmpty) matchingVersions
else filtered
}.filter(v => isSupportedVersion(v.repr))
if (validMatchingVersions.isEmpty)
Left(new UnsupportedScalaVersionError(
scalaVersionStringArg,
latestSupportedStableVersions
))
else
Right(validMatchingVersions.max.repr)
}
case None =>
val validVersions = allStableVersions
.map(Version(_))
.filter(v => maxSupportedStableScalaVersions.exists(v <= _))
if (validVersions.isEmpty)
Left(new NoValidScalaVersionFoundError(
allStableVersions,
else
Right(validMatchingVersions.max.repr)
}
case None =>
val validVersions = versionPool
.map(Version(_))
.filter(v => maxSupportedStableScalaVersions.exists(v <= _))
if (validVersions.isEmpty)
Left(new NoValidScalaVersionFoundError(
versionPool,
latestSupportedStableVersions
))
else
Right(validVersions.max.repr)
}

private def matchNewestNonStableScalaVersion(
maybeScalaVersionStringArg: Option[String],
versionPool: Seq[String]
): Either[ScalaVersionError, String] =
maybeScalaVersionStringArg match {
case Some(scalaVersionStringArg) =>
if (versionPool.contains(scalaVersionStringArg))
if (isSupportedVersion(scalaVersionStringArg))
Right(scalaVersionStringArg)
else
Left(new UnsupportedScalaVersionError(
scalaVersionStringArg,
latestSupportedStableVersions
))
else
Right(validVersions.max.repr)
}
else
Left(new InvalidBinaryScalaVersionError(
scalaVersionStringArg,
latestSupportedStableVersions
))

val scalaVersion = value(matchNewestStableScalaVersion(scalaVersionArg))
val scalaBinaryVersion = scalaBinaryVersionArg.getOrElse(ScalaVersion.binary(scalaVersion))
(scalaVersion, scalaBinaryVersion)
}
case None =>
Left(new NoValidScalaVersionFoundError(
versionPool,
latestSupportedStableVersions
))

}

private def latestScalaVersionFrom(
versions: CoreVersions,
Expand Down Expand Up @@ -491,22 +555,54 @@ final case class BuildOptions(
(scalaVersion, scalaBinaryVersion)
}

def computeLatestScalaTwoTwelveNightlyVersions(): Either[BuildException, (String, String)] =
either {
val moduleVersion: Either[ScalaVersionError, String] = {
import coursier.moduleString
def scalaNightly2Module: Module = mod"org.scala-lang:scala-library"
val res = finalCache.logger.use {
Versions(finalCache)
.withModule(scalaNightly2Module)
.withRepositories(Seq(coursier.Repositories.scalaIntegration))
.result()
.unsafeRun()(finalCache.ec)
}.versions.available
val twoTwelveNightlies = res.filter(_.startsWith("2.12.")).map(Version(_))
if (twoTwelveNightlies.nonEmpty) Right(twoTwelveNightlies.max.repr)
else Left(
new NoValidScalaVersionFoundError(res, latestSupportedStableVersions)
)
}

val scalaVersion = value(moduleVersion)
val scalaBinaryVersion = ScalaVersion.binary(scalaVersion)
(scalaVersion, scalaBinaryVersion)
}

private def isScala2Nightly(version: String): Boolean =
scala2NightlyRegex.unapplySeq(version).isDefined

lazy val scalaParams: Either[BuildException, ScalaParameters] = either {
def isScala2Nightly(version: String): Boolean =
scala2NightlyRegex.unapplySeq(version).isDefined
def isScala3Nightly(version: String): Boolean =
version.startsWith("3") && version.endsWith("-NIGHTLY")

val (scalaVersion, scalaBinaryVersion) =
value {
scalaOptions.scalaVersion match {
case Some("3.nightly") => computeLatestScalaThreeNightlyVersions()
case Some("2.nightly") => computeLatestScalaTwoNightlyVersions()
case Some("3.nightly") => computeLatestScalaThreeNightlyVersions()
case Some("2.nightly") => computeLatestScalaTwoNightlyVersions()
case Some("2.13.nightly") => computeLatestScalaTwoNightlyVersions()
case Some("2.12.nightly") => computeLatestScalaTwoTwelveNightlyVersions()
case Some(versionString) if isScala3Nightly(versionString) =>
turnScala3NightlyVersionArgIntoVersion(versionString)
case Some(versionString) if isScala2Nightly(versionString) =>
turnScala2NightlyVersionArgToVersions(versionString)
case _ => turnScalaVersionArgToScalaVersions(
case Some(versionString) if versionString.exists(_.isLetter) =>
turnScalaVersionArgToNonStableScalaVersions(
scalaOptions.scalaVersion,
scalaOptions.scalaBinaryVersion
)
case _ => turnScalaVersionArgToStableScalaVersions(
scalaOptions.scalaVersion,
scalaOptions.scalaBinaryVersion
)
Expand Down
Loading

0 comments on commit af33e0f

Please sign in to comment.