aboutsummaryrefslogblamecommitdiff
path: root/compiler/src/dotty/tools/dotc/config/ScalaVersion.scala
blob: 02ba74af97c043ba4f0b01ed98be9c88d4693e36 (plain) (tree)
1
2
3
4

                     

                   













                                                                  
                                                           



































                                                                                                            
                                                            










                                                     
                               



















































































































                                                                                                                     
/* @author  James Iry
 */
package dotty.tools
package dotc.config

import scala.util.{Try, Success, Failure}

/**
 * Represents a single Scala version in a manner that
 * supports easy comparison and sorting.
 */
sealed abstract class ScalaVersion extends Ordered[ScalaVersion] {
  def unparse: String
}

/**
 * A scala version that sorts higher than all actual versions
 */
@sharable case object NoScalaVersion extends ScalaVersion {
  def unparse = "none"

  def compare(that: ScalaVersion): Int = that match {
    case NoScalaVersion => 0
    case _ => 1
  }
}

/**
 * A specific Scala version, not one of the magic min/max versions. An SpecificScalaVersion
 * may or may not be a released version - i.e. this same class is used to represent
 * final, release candidate, milestone, and development builds. The build argument is used
 * to segregate builds
 */
case class SpecificScalaVersion(major: Int, minor: Int, rev: Int, build: ScalaBuild) extends ScalaVersion {
  def unparse = s"${major}.${minor}.${rev}.${build.unparse}"

  def compare(that: ScalaVersion): Int =  that match {
    case SpecificScalaVersion(thatMajor, thatMinor, thatRev, thatBuild) =>
      // this could be done more cleanly by importing scala.math.Ordering.Implicits, but we have to do these
      // comparisons a lot so I'm using brute force direct style code
      if (major < thatMajor) -1
      else if (major > thatMajor) 1
      else if (minor < thatMinor) -1
      else if (minor > thatMinor) 1
      else if (rev < thatRev) -1
      else if (rev > thatRev) 1
      else build compare thatBuild
    case AnyScalaVersion => 1
    case NoScalaVersion => -1
  }
}

/**
 * A Scala version that sorts lower than all actual versions
 */
@sharable case object AnyScalaVersion extends ScalaVersion {
  def unparse = "any"

  def compare(that: ScalaVersion): Int = that match {
    case AnyScalaVersion => 0
    case _ => -1
  }
}

/**
 * Methods for parsing ScalaVersions
 */
@sharable object ScalaVersion {
  private val dot = "\\."
  private val dash = "\\-"
  private def not(s:String) = s"[^${s}]"
  private val R = s"((${not(dot)}*)(${dot}(${not(dot)}*)(${dot}(${not(dash)}*)(${dash}(.*))?)?)?)".r

  def parse(versionString : String): Try[ScalaVersion] = {
    def failure = Failure(new NumberFormatException(
      s"There was a problem parsing ${versionString}. " +
       "Versions should be in the form major[.minor[.revision]] " +
       "where each part is a positive number, as in 2.10.1. " +
       "The minor and revision parts are optional."
    ))

    def toInt(s: String) = s match {
      case null | "" => 0
      case _ => s.toInt
    }

    def isInt(s: String) = Try(toInt(s)).isSuccess

    import ScalaBuild._

    def toBuild(s: String) = s match {
      case null | "FINAL" => Final
      case s if (s.toUpperCase.startsWith("RC") && isInt(s.substring(2))) => RC(toInt(s.substring(2)))
      case s if (s.toUpperCase.startsWith("M") && isInt(s.substring(1))) => Milestone(toInt(s.substring(1)))
      case _ => Development(s)
    }

    try versionString match {
      case "" | "any" => Success(AnyScalaVersion)
      case "none" => Success(NoScalaVersion)
      case R(_, majorS, _, minorS, _, revS, _, buildS) =>
        Success(SpecificScalaVersion(toInt(majorS), toInt(minorS), toInt(revS), toBuild(buildS)))
      case _ => failure
    } catch {
      case e: NumberFormatException => failure
    }
  }

  /**
   * The version of the compiler running now
   */
  val current = parse(util.Properties.versionNumberString).get
}

/**
 * Represents the data after the dash in major.minor.rev-build
 */
abstract class ScalaBuild extends Ordered[ScalaBuild] {
  /**
   * Return a version of this build information that can be parsed back into the
   * same ScalaBuild
   */
  def unparse: String
}

object ScalaBuild {

  /** A development, test, nightly, snapshot or other "unofficial" build
   */
  case class Development(id: String) extends ScalaBuild {
    def unparse = s"-${id}"

    def compare(that: ScalaBuild) = that match {
      // sorting two development builds based on id is reasonably valid for two versions created with the same schema
      // otherwise it's not correct, but since it's impossible to put a total ordering on development build versions
      // this is a pragmatic compromise
      case Development(thatId) => id compare thatId
      // assume a development build is newer than anything else, that's not really true, but good luck
      // mapping development build versions to other build types
      case _ => 1
    }
  }

  /** A final build
   */
  case object Final extends ScalaBuild {
    def unparse = ""

    def compare(that: ScalaBuild) = that match {
      case Final => 0
      // a final is newer than anything other than a development build or another final
      case Development(_) => -1
      case _ => 1
    }
  }

  /** A candidate for final release
   */
  case class RC(n: Int) extends ScalaBuild {
    def unparse = s"-RC${n}"

    def compare(that: ScalaBuild) = that match {
      // compare two rcs based on their RC numbers
      case RC(thatN) => n - thatN
      // an rc is older than anything other than a milestone or another rc
      case Milestone(_) => 1
      case _ => -1
    }
  }

  /** An intermediate release
   */
  case class Milestone(n: Int) extends ScalaBuild {
    def unparse = s"-M${n}"

    def compare(that: ScalaBuild) = that match {
      // compare two milestones based on their milestone numbers
      case Milestone(thatN) => n - thatN
      // a milestone is older than anything other than another milestone
      case _ => -1

    }
  }
}