summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/settings/ScalaVersion.scala
blob: d7901730a40925af72d31efc617d7da618d71c7a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
/* NSC -- new Scala compiler
 * Copyright 2005-2013 LAMP/EPFL
 * @author  James Iry
 */
// $Id$

package scala
package tools.nsc.settings

/**
 * 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
 */
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
 */
case object AnyScalaVersion extends ScalaVersion {
  def unparse = "any"

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

/**
 * Factory methods for producing ScalaVersions
 */
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 apply(versionString : String, errorHandler: String => Unit): ScalaVersion = {
    def errorAndValue() = {
        errorHandler(
          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."
        )
        AnyScalaVersion
    }

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

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

    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 "none" => NoScalaVersion
      case "any" => AnyScalaVersion
      case R(_, majorS, _, minorS, _, revS, _, buildS) =>
        SpecificScalaVersion(toInt(majorS), toInt(minorS), toInt(revS), toBuild(buildS))
      case _ =>
        errorAndValue()
    } catch {
      case e: NumberFormatException => errorAndValue()
    }
  }

  def apply(versionString: String): ScalaVersion =
      apply(versionString, msg => throw new NumberFormatException(msg))

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

  /**
   * The 2.8.0 version.
   */
  val twoDotEight = SpecificScalaVersion(2, 8, 0, Final)
}

/**
 * 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
}
/**
 * A development, test, integration, 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 final
 */
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

  }
}