diff --git a/ivy/src/main/scala/sbt/CrossVersion.scala b/ivy/src/main/scala/sbt/CrossVersion.scala index 59b51c795..917f5318f 100644 --- a/ivy/src/main/scala/sbt/CrossVersion.scala +++ b/ivy/src/main/scala/sbt/CrossVersion.scala @@ -67,9 +67,41 @@ object CrossVersion m } - def isStable(v: String): Boolean = !v.contains("-") + // @deprecated("Use CrossVersion.isScalaApiCompatible or CrossVersion.isSbtApiCompatible", "0.13.0") + def isStable(v: String): Boolean = isScalaApiCompatible(v) + // @deprecated("Use CrossVersion.scalaApiVersion or CrossVersion.sbtApiVersion", "0.13.0") def selectVersion(full: String, binary: String): String = if(isStable(full)) binary else full - + def isSbtApiCompatible(v: String): Boolean = sbtApiVersion(v).isDefined + /** Returns sbt binary interface x.y API compatible with the given version string v. + * RCs for x.y.0 are considered API compatible. + * Compatibile versions include 0.12.0-1 and 0.12.0-RC1 for Some(0, 12). + */ + def sbtApiVersion(v: String): Option[(Int, Int)] = + { + val ReleaseV = """(\d+)\.(\d+)\.(\d+)(-\d+)?""".r + val CandidateV = """(\d+)\.(\d+)\.(\d+)(-RC\d+)""".r + val NonReleaseV = """(\d+)\.(\d+)\.(\d+)(-\w+)""".r + v match { + case ReleaseV(x, y, z, ht) => Some((x.toInt, y.toInt)) + case CandidateV(x, y, z, ht) => Some((x.toInt, y.toInt)) + case NonReleaseV(x, y, z, ht) if z.toInt > 0 => Some((x.toInt, y.toInt)) + case _ => None + } + } + def isScalaApiCompatible(v: String): Boolean = scalaApiVersion(v).isDefined + /** Returns Scala binary interface x.y API compatible with the given version string v. + * Compatibile versions include 2.10.0-1 and 2.10.1-M1 for Some(2, 10), but not 2.10.0-RC1. + */ + def scalaApiVersion(v: String): Option[(Int, Int)] = + { + val ReleaseV = """(\d+)\.(\d+)\.(\d+)(-\d+)?""".r + val NonReleaseV = """(\d+)\.(\d+)\.(\d+)(-\w+)""".r + v match { + case ReleaseV(x, y, z, ht) => Some((x.toInt, y.toInt)) + case NonReleaseV(x, y, z, ht) if z.toInt > 0 => Some((x.toInt, y.toInt)) + case _ => None + } + } val PartialVersion = """(\d+)\.(\d+)(?:\..+)?""".r def partialVersion(s: String): Option[(Int,Int)] = s match { @@ -79,12 +111,13 @@ object CrossVersion private[this] def isNewer(major: Int, minor: Int, minMajor: Int, minMinor: Int): Boolean = major > minMajor || (major == minMajor && minor >= minMinor) - def binaryScalaVersion(full: String): String = binaryVersion(full, TransitionScalaVersion) - def binarySbtVersion(full: String): String = binaryVersion(full, TransitionSbtVersion) - def binaryVersion(full: String, cutoff: String): String = + def binaryScalaVersion(full: String): String = binaryVersionWithApi(full, TransitionScalaVersion)(scalaApiVersion) + def binarySbtVersion(full: String): String = binaryVersionWithApi(full, TransitionSbtVersion)(sbtApiVersion) + def binaryVersion(full: String, cutoff: String): String = binaryVersionWithApi(full, cutoff)(scalaApiVersion) + private[this] def binaryVersionWithApi(full: String, cutoff: String)(apiVersion: String => Option[(Int,Int)]): String = { def sub(major: Int, minor: Int) = major + "." + minor - (partialVersion(full), partialVersion(cutoff)) match { + (apiVersion(full), partialVersion(cutoff)) match { case (Some((major, minor)), None) => sub(major, minor) case (Some((major, minor)), Some((minMajor, minMinor))) if isNewer(major, minor, minMajor, minMinor) => sub(major, minor) case _ => full