11package scala .build .options
2-
32import coursier .cache .{ArchiveCache , FileCache }
4- import coursier .core .Version
3+ import coursier .core .{ Version , Versions => CoreVersions }
54import coursier .jvm .{JavaHome , JvmCache , JvmIndex }
65import coursier .util .{Artifact , Task }
6+ import coursier .{Module , Versions }
77import dependency ._
88
99import java .math .BigInteger
@@ -13,15 +13,10 @@ import java.security.MessageDigest
1313
1414import scala .build .EitherCps .{either , value }
1515import scala .build .blooprifle .VersionUtil .parseJavaVersion
16- import scala .build .errors .{
17- BuildException ,
18- Diagnostic ,
19- InvalidBinaryScalaVersionError ,
20- NoValidScalaVersionFoundError ,
21- UnsupportedScalaVersionError
22- }
16+ import scala .build .errors ._
2317import scala .build .internal .Constants ._
2418import scala .build .internal .CsLoggerUtil ._
19+ import scala .build .internal .ScalaParse .scala2NightlyRegex
2520import scala .build .internal .{OsLibc , StableScalaVersion , Util }
2621import scala .build .options .validation .BuildOptionsRule
2722import scala .build .{Artifacts , Logger , Os , Position , Positioned }
@@ -230,7 +225,7 @@ final case class BuildOptions(
230225 private def defaultStableScalaVersions =
231226 Seq (defaultScala212Version, defaultScala213Version, defaultScalaVersion)
232227
233- private def latestSupportedScalaVersion (): Seq [Version ] = {
228+ private def latestSupportedStableScalaVersion (): Seq [Version ] = {
234229
235230 val cache = finalCache.withMessage(" Getting list of Scala CLI-supported Scala versions" )
236231 val supportedScalaVersionsUrl = scalaOptions.scalaVersionsUrl
@@ -298,30 +293,42 @@ final case class BuildOptions(
298293 def finalRepositories : Seq [String ] =
299294 classPathOptions.extraRepositories ++ internal.localRepository.toSeq
300295
301- private def computeScalaVersions (
302- scalaVersion : Option [String ],
303- scalaBinaryVersion : Option [String ]
296+ private lazy val maxSupportedStableScalaVersions = latestSupportedStableScalaVersion()
297+
298+ private lazy val latestSupportedStableVersions = maxSupportedStableScalaVersions.map(_.repr)
299+
300+ /** @param scalaVersionArg
301+ * the command line, using directive, or default argument passed as scala version
302+ * @param scalaBinaryVersionArg
303+ * the command line, using directive, or default argument passed as scala Binary version
304+ * @return
305+ * Either a BuildException or the calculated (ScalaVersion, ScalaBinaryVersion) tuple
306+ */
307+ private def turnScalaVersionArgToScalaVersions (
308+ scalaVersionArg : Option [String ],
309+ scalaBinaryVersionArg : Option [String ]
304310 ): Either [BuildException , (String , String )] = either {
305- lazy val allVersions = {
311+ def isSupportedVersion (version : String ): Boolean =
312+ version.startsWith(" 2.12." ) || version.startsWith(" 2.13." ) || version.startsWith(" 3." )
313+ lazy val allStableVersions = {
306314 import coursier ._
307- import scala .concurrent .ExecutionContext .{global => ec }
308315 val modules = {
309316 def scala2 = mod " org.scala-lang:scala-library "
310317 // No unstable, that *ought* not to be a problem down-the-line…?
311318 def scala3 = mod " org.scala-lang:scala3-library_3 "
312- if (scalaVersion.contains(" 2" ) || scalaVersion.exists(_.startsWith(" 2." ))) Seq (scala2)
313- else if (scalaVersion.contains(" 3" ) || scalaVersion.exists(_.startsWith(" 3." ))) Seq (scala3)
319+ if (scalaVersionArg.contains(" 2" ) || scalaVersionArg.exists(_.startsWith(" 2." ))) Seq (scala2)
320+ else if (scalaVersionArg.contains(" 3" ) || scalaVersionArg.exists(_.startsWith(" 3." )))
321+ Seq (scala3)
314322 else Seq (scala2, scala3)
315323 }
316324 def isStable (v : String ): Boolean =
317325 ! v.endsWith(" -NIGHTLY" ) && ! v.contains(" -RC" )
318326 def moduleVersions (mod : Module ): Seq [String ] = {
319- val cache = finalCache.withMessage(" Getting Scala version list" )
320- val res = cache.logger.use {
321- try Versions (cache)
327+ val res = finalCache.logger.use {
328+ try Versions (finalCache)
322329 .withModule(mod)
323330 .result()
324- .unsafeRun()(ec)
331+ .unsafeRun()(finalCache. ec)
325332 catch {
326333 case NonFatal (e) => throw new Exception (e)
327334 }
@@ -330,53 +337,187 @@ final case class BuildOptions(
330337 }
331338 modules.flatMap(moduleVersions).distinct
332339 }
333- def matchNewestScalaVersion (sv : Option [String ]) = {
334- lazy val maxSupportedScalaVersions = latestSupportedScalaVersion()
335340
336- sv match {
337- case Some (sv0) =>
338- val prefix = if (sv0.endsWith(" ." )) sv0 else sv0 + " ."
339- val matchingVersions = allVersions.filter(_.startsWith(prefix)).map(Version (_))
341+ def matchNewestStableScalaVersion (maybeScalaVersionStringArg : Option [String ])
342+ : Either [ScalaVersionError , String ] =
343+ maybeScalaVersionStringArg match {
344+ case Some (scalaVersionStringArg) =>
345+ val prefix =
346+ if (Util .isFullScalaVersion(scalaVersionStringArg)) scalaVersionStringArg
347+ else if (scalaVersionStringArg.endsWith(" ." )) scalaVersionStringArg
348+ else scalaVersionStringArg + " ."
349+ val matchingVersions = allStableVersions.filter(_.startsWith(prefix)).map(Version (_))
340350 if (matchingVersions.isEmpty)
341- Left (new InvalidBinaryScalaVersionError (sv0))
351+ Left (new InvalidBinaryScalaVersionError (
352+ scalaVersionStringArg,
353+ latestSupportedStableVersions
354+ ))
342355 else {
343- val validMaxVersions = maxSupportedScalaVersions
356+ val validMaxVersions = maxSupportedStableScalaVersions
344357 .filter(_.repr.startsWith(prefix))
345358 val validMatchingVersions = {
346359 val filtered = matchingVersions.filter(v => validMaxVersions.exists(v <= _))
347360 if (filtered.isEmpty) matchingVersions
348361 else filtered
349- }
362+ }.filter(v => isSupportedVersion(v.repr))
350363 if (validMatchingVersions.isEmpty)
351- Left (new UnsupportedScalaVersionError (sv0))
364+ Left (new UnsupportedScalaVersionError (
365+ scalaVersionStringArg,
366+ latestSupportedStableVersions
367+ ))
352368 else
353369 Right (validMatchingVersions.max.repr)
354370 }
355371 case None =>
356- val validVersions = allVersions
372+ val validVersions = allStableVersions
357373 .map(Version (_))
358- .filter(v => maxSupportedScalaVersions .exists(v <= _))
374+ .filter(v => maxSupportedStableScalaVersions .exists(v <= _))
359375 if (validVersions.isEmpty)
360- Left (new NoValidScalaVersionFoundError (allVersions))
376+ Left (new NoValidScalaVersionFoundError (
377+ allStableVersions,
378+ latestSupportedStableVersions
379+ ))
361380 else
362381 Right (validVersions.max.repr)
363382 }
383+
384+ val scalaVersion = value(matchNewestStableScalaVersion(scalaVersionArg))
385+ val scalaBinaryVersion = scalaBinaryVersionArg.getOrElse(ScalaVersion .binary(scalaVersion))
386+ (scalaVersion, scalaBinaryVersion)
387+ }
388+
389+ private def latestScalaVersionFrom (
390+ versions : CoreVersions ,
391+ desc : String
392+ ): Either [scala.build.errors.ScalaVersionError , String ] =
393+ versions.latest(coursier.core.Latest .Release ) match {
394+ case Some (versionString) => Right (versionString)
395+ case None =>
396+ val msg =
397+ s " Unable to find matching version for $desc in available version: ${versions.available.mkString(" , " )}. " +
398+ " This error may indicate a network or other problem accessing repository."
399+ Left (new ScalaVersionError (msg))
364400 }
365- val maybeSv = scalaVersion match {
366- case None => matchNewestScalaVersion(None )
367- case Some (sv0) =>
368- if (Util .isFullScalaVersion(sv0)) Right (sv0)
369- else matchNewestScalaVersion(Some (sv0))
401+
402+ /** @return
403+ * Either a BuildException or the calculated (ScalaVersion, ScalaBinaryVersion) tuple
404+ */
405+ private def computeLatestScalaThreeNightlyVersions (): Either [BuildException , (String , String )] =
406+ either {
407+ import coursier .Versions
408+ import coursier ._
409+
410+ val moduleVersion : Either [ScalaVersionError , String ] = {
411+ def scala3 = mod " org.scala-lang:scala3-library_3 "
412+ val res = finalCache.logger.use {
413+ Versions (finalCache)
414+ .withModule(scala3)
415+ .result()
416+ .unsafeRun()(finalCache.ec)
417+ }
418+ latestScalaVersionFrom(res.versions, " latest Scala 3 nightly build" )
419+ }
420+
421+ val scalaVersion = value(moduleVersion)
422+ val scalaBinaryVersion = ScalaVersion .binary(scalaVersion)
423+ (scalaVersion, scalaBinaryVersion)
370424 }
371425
372- val sv = value(maybeSv)
373- val sbv = scalaBinaryVersion.getOrElse(ScalaVersion .binary(sv))
374- (sv, sbv)
426+ /** @return
427+ * Either a BuildException or the calculated (ScalaVersion, ScalaBinaryVersion) tuple
428+ */
429+ private def computeLatestScalaTwoNightlyVersions (): Either [BuildException , (String , String )] =
430+ either {
431+ import coursier .Versions
432+ import coursier ._
433+
434+ val moduleVersion : Either [ScalaVersionError , String ] = {
435+ def scalaNightly2Module : Module = mod " org.scala-lang:scala-library "
436+ val res = finalCache.logger.use {
437+ Versions (finalCache)
438+ .withModule(scalaNightly2Module)
439+ .withRepositories(Seq (coursier.Repositories .scalaIntegration))
440+ .result()
441+ .unsafeRun()(finalCache.ec)
442+ }
443+ latestScalaVersionFrom(res.versions, " latest Scala 2 nightly build" )
444+ }
445+
446+ val scalaVersion = value(moduleVersion)
447+ val scalaBinaryVersion = ScalaVersion .binary(scalaVersion)
448+ (scalaVersion, scalaBinaryVersion)
449+ }
450+
451+ private def turnScala2NightlyVersionArgToVersions (versionString : String )
452+ : Either [BuildException , (String , String )] = either {
453+
454+ val moduleVersion : Either [ScalaVersionError , String ] = {
455+ import coursier ._
456+ def scalaNightly2Module : Module = mod " org.scala-lang:scala-library "
457+ val res = finalCache.logger.use {
458+ Versions (finalCache)
459+ .withModule(scalaNightly2Module)
460+ .withRepositories(Seq (coursier.Repositories .scalaIntegration))
461+ .result()
462+ .unsafeRun()(finalCache.ec)
463+ }
464+ if (res.versions.available.contains(versionString)) Right (versionString)
465+ else
466+ Left (
467+ new NoValidScalaVersionFoundError (res.versions.available, latestSupportedStableVersions)
468+ )
469+ }
470+
471+ val scalaVersion = value(moduleVersion)
472+ val scalaBinaryVersion = ScalaVersion .binary(scalaVersion)
473+ (scalaVersion, scalaBinaryVersion)
474+ }
475+
476+ private def turnScala3NightlyVersionArgIntoVersion (versionString : String )
477+ : Either [BuildException , (String , String )] = either {
478+ val moduleVersion : Either [ScalaVersionError , String ] = {
479+ import coursier ._
480+ def scala3 = mod " org.scala-lang:scala3-library_3 "
481+ val res = finalCache.logger.use {
482+ Versions (finalCache)
483+ .withModule(scala3)
484+ .result()
485+ .unsafeRun()(finalCache.ec)
486+ }
487+ if (res.versions.available.contains(versionString)) Right (versionString)
488+ else
489+ Left (
490+ new NoValidScalaVersionFoundError (res.versions.available, latestSupportedStableVersions)
491+ )
492+ }
493+
494+ val scalaVersion = value(moduleVersion)
495+ val scalaBinaryVersion = ScalaVersion .binary(scalaVersion)
496+ (scalaVersion, scalaBinaryVersion)
375497 }
376498
377499 lazy val scalaParams : Either [BuildException , ScalaParameters ] = either {
500+ def isScala2Nightly (version : String ): Boolean =
501+ scala2NightlyRegex.unapplySeq(version).isDefined
502+ def isScala3Nightly (version : String ): Boolean =
503+ version.startsWith(" 3" ) && version.endsWith(" -NIGHTLY" )
504+
378505 val (scalaVersion, scalaBinaryVersion) =
379- value(computeScalaVersions(scalaOptions.scalaVersion, scalaOptions.scalaBinaryVersion))
506+ value {
507+ scalaOptions.scalaVersion match {
508+ case Some (" 3.nightly" ) => computeLatestScalaThreeNightlyVersions()
509+ case Some (" 2.nightly" ) => computeLatestScalaTwoNightlyVersions()
510+ case Some (versionString) if isScala3Nightly(versionString) =>
511+ turnScala3NightlyVersionArgIntoVersion(versionString)
512+ case Some (versionString) if isScala2Nightly(versionString) =>
513+ turnScala2NightlyVersionArgToVersions(versionString)
514+ case _ => turnScalaVersionArgToScalaVersions(
515+ scalaOptions.scalaVersion,
516+ scalaOptions.scalaBinaryVersion
517+ )
518+ }
519+ }
520+
380521 val maybePlatformSuffix = platform.value match {
381522 case Platform .JVM => None
382523 case Platform .JS => Some (scalaJsOptions.platformSuffix)
0 commit comments