From 697a28771e7c327fd0aa6c6a4cb3340037b0ab9e Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sat, 16 Aug 2014 22:38:35 -0400 Subject: [PATCH 1/2] Fixes #1275. Fixes pom dynamic revision conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ivy and pom uses slightly different notation for version range and dynamic revision. This change fixes the dynamic revisions involving “+”. First, when a revision like “1.1+” is found, it will now be treated as “1.+”. Next, when it finds a revision like “1+” is found, it will be treated as “+”. The conversion of “+” is hardcoded to be “[0,)”. --- ivy/src/main/scala/sbt/MakePom.scala | 31 ++++++++------ ivy/src/test/scala/MakePomSpec.scala | 60 ++++++++++++++++++++++++++++ ivy/src/test/scala/MakePomTest.scala | 28 ------------- 3 files changed, 78 insertions(+), 41 deletions(-) create mode 100644 ivy/src/test/scala/MakePomSpec.scala delete mode 100644 ivy/src/test/scala/MakePomTest.scala diff --git a/ivy/src/main/scala/sbt/MakePom.scala b/ivy/src/main/scala/sbt/MakePom.scala index ee238371c..b49711b19 100644 --- a/ivy/src/main/scala/sbt/MakePom.scala +++ b/ivy/src/main/scala/sbt/MakePom.scala @@ -196,6 +196,7 @@ class MakePom(val log: Logger) { } + /** Converts Ivy revision ranges to that of Maven POM */ def makeDependencyVersion(revision: String): String = { def plusRange(s: String, shift: Int = 0) = { def pow(i: Int): Int = if (i > 0) 10 * pow(i - 1) else 1 @@ -209,20 +210,24 @@ class MakePom(val log: Logger) { } val startSym = Set(']', '[', '(') val stopSym = Set(']', '[', ')') + val PlusPattern = """(.+)(\.\d*\+)""".r try { - if (revision endsWith ".+") { - plusRange(revision.substring(0, revision.length - 2)) - } else if (revision endsWith "+") { - val base = revision.take(revision.length - 1) - // This is a heuristic. Maven just doesn't support Ivy's notions of 1+, so - // we assume version ranges never go beyond 5 siginificant digits. - (0 to 5).map(plusRange(base, _)).mkString(",") - } else if (startSym(revision(0)) && stopSym(revision(revision.length - 1))) { - val start = revision(0) - val stop = revision(revision.length - 1) - val mid = revision.substring(1, revision.length - 1) - (if (start == ']') "(" else start) + mid + (if (stop == '[') ")" else stop) - } else revision + revision match { + case PlusPattern(base, tail) => plusRange(base) + // There's no direct translation for a dynamic revision like "1+". + // This is likely the build user misunderstanding the meaning of "+", + // which means pick the latest in the version segment. + // So if someone wanted (1.0 <= x), then it should be "[1.0)" or "1.+". + // Technically speaking, "1+" should convert to "LATEST", + // but that seems to be deprecated now, so picking the minimum version "0". + case rev if rev endsWith "+" => "[0,)" + case rev if startSym(rev(0)) && stopSym(rev(rev.length - 1)) => + val start = rev(0) + val stop = rev(rev.length - 1) + val mid = rev.substring(1, rev.length - 1) + (if (start == ']') "(" else start) + mid + (if (stop == '[') ")" else stop) + case _ => revision + } } catch { case e: NumberFormatException => // TODO - if the version doesn't meet our expectations, maybe we just issue a hard diff --git a/ivy/src/test/scala/MakePomSpec.scala b/ivy/src/test/scala/MakePomSpec.scala new file mode 100644 index 000000000..55fba326f --- /dev/null +++ b/ivy/src/test/scala/MakePomSpec.scala @@ -0,0 +1,60 @@ +package sbt + +import java.io.File +import org.specs2._ + +// http://ant.apache.org/ivy/history/2.3.0/ivyfile/dependency.html +// http://maven.apache.org/enforcer/enforcer-rules/versionRanges.html +class MakePomSpec extends Specification { + def is = s2""" + + This is a specification to check the Ivy revision number conversion to pom. + + 1.0 should + ${convertTo("1.0", "1.0")} + + [1.0,2.0] should + ${convertTo("[1.0,2.0]", "[1.0,2.0]")} + + [1.0,2.0[ should + ${convertTo("[1.0,2.0[", "[1.0,2.0)")} + + ]1.0,2.0] should + ${convertTo("]1.0,2.0]", "(1.0,2.0]")} + + ]1.0,2.0[ should + ${convertTo("]1.0,2.0[", "(1.0,2.0)")} + + [1.0,) should + ${convertTo("[1.0,)", "[1.0,)")} + + ]1.0,) should + ${convertTo("]1.0,)", "(1.0,)")} + + (,2.0] should + ${convertTo("(,2.0]", "(,2.0]")} + + (,2.0[ should + ${convertTo("(,2.0[", "(,2.0)")} + + 1.+ should + ${convertTo("1.+", "[1,2)")} + + 1.2.3.4.+ should + ${convertTo("1.2.3.4.+", "[1.2.3.4,1.2.3.5)")} + + 12.31.42.+ should + ${convertTo("12.31.42.+", "[12.31.42,12.31.43)")} + + 1.1+ should + ${convertTo("1.1+", "[1,2)")} + + 1+ should + ${convertTo("1+", "[0,)")} + """ + + val mp = new MakePom(ConsoleLogger()) + def convertTo(s: String, expected: String) = + mp.makeDependencyVersion(s) must_== expected +} + diff --git a/ivy/src/test/scala/MakePomTest.scala b/ivy/src/test/scala/MakePomTest.scala deleted file mode 100644 index c96d2102e..000000000 --- a/ivy/src/test/scala/MakePomTest.scala +++ /dev/null @@ -1,28 +0,0 @@ -package sbt - -import java.io.File -import org.specs2._ -import mutable.Specification - -object MakePomTest extends Specification { - val mp = new MakePom(ConsoleLogger()) - import mp.{ makeDependencyVersion => v } - "MakePom makeDependencyVersion" should { - "Handle .+ in versions" in { - v("1.+") must_== "[1,2)" - v("1.2.3.4.+") must_== "[1.2.3.4,1.2.3.5)" - v("12.31.42.+") must_== "[12.31.42,12.31.43)" - } - /* TODO - do we care about this case? - * 1+ --> [1,2),[10,20),[100,200),[1000,2000),[10000,20000),[100000,200000) - */ - "Handle ]* bracket in version ranges" in { - v("]1,3]") must_== "(1,3]" - v("]1.1,1.3]") must_== "(1.1,1.3]" - } - "Handle *[ bracket in version ranges" in { - v("[1,3[") must_== "[1,3)" - v("[1.1,1.3[") must_== "[1.1,1.3)" - } - } -} From 92214f739ac617ea471ceffac51c431d480c19d2 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Fri, 22 Aug 2014 03:54:32 -0400 Subject: [PATCH 2/2] Based on Ivy's behavior, bringing back approximation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I assumed 1.1+ should be treated as 1.+, but it seems like Ivy treats it more as “any version that starts with 1.1” including 1.10. Josh’s original implementation approximates this by making ranges for multiple digits, 1.1~1.2, 1.10~1.20, etc. --- ivy/src/main/scala/sbt/MakePom.scala | 20 +++++++++++--------- ivy/src/test/scala/MakePomSpec.scala | 17 +++++++++++++++-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/ivy/src/main/scala/sbt/MakePom.scala b/ivy/src/main/scala/sbt/MakePom.scala index b49711b19..e05221b43 100644 --- a/ivy/src/main/scala/sbt/MakePom.scala +++ b/ivy/src/main/scala/sbt/MakePom.scala @@ -210,17 +210,19 @@ class MakePom(val log: Logger) { } val startSym = Set(']', '[', '(') val stopSym = Set(']', '[', ')') - val PlusPattern = """(.+)(\.\d*\+)""".r + val DotPlusPattern = """(.+)\.\+""".r + val DotNumPlusPattern = """(.+)\.(\d+)\+""".r + val NumPlusPattern = """(\d+)\+""".r + val maxDigit = 5 try { revision match { - case PlusPattern(base, tail) => plusRange(base) - // There's no direct translation for a dynamic revision like "1+". - // This is likely the build user misunderstanding the meaning of "+", - // which means pick the latest in the version segment. - // So if someone wanted (1.0 <= x), then it should be "[1.0)" or "1.+". - // Technically speaking, "1+" should convert to "LATEST", - // but that seems to be deprecated now, so picking the minimum version "0". - case rev if rev endsWith "+" => "[0,)" + case "+" => "[0,)" + case DotPlusPattern(base) => plusRange(base) + // This is a heuristic. Maven just doesn't support Ivy's notions of 1+, so + // we assume version ranges never go beyond 5 siginificant digits. + case NumPlusPattern(tail) => (0 until maxDigit).map(plusRange(tail, _)).mkString(",") + case DotNumPlusPattern(base, tail) => (0 until maxDigit).map(plusRange(base + "." + tail, _)).mkString(",") + case rev if rev endsWith "+" => sys.error(s"dynamic revision '$rev' cannot be translated to POM") case rev if startSym(rev(0)) && stopSym(rev(rev.length - 1)) => val start = rev(0) val stop = rev(rev.length - 1) diff --git a/ivy/src/test/scala/MakePomSpec.scala b/ivy/src/test/scala/MakePomSpec.scala index 55fba326f..3310ed62b 100644 --- a/ivy/src/test/scala/MakePomSpec.scala +++ b/ivy/src/test/scala/MakePomSpec.scala @@ -47,14 +47,27 @@ class MakePomSpec extends Specification { ${convertTo("12.31.42.+", "[12.31.42,12.31.43)")} 1.1+ should - ${convertTo("1.1+", "[1,2)")} + ${convertTo("1.1+", "[1.1,1.2),[1.10,1.20),[1.100,1.200),[1.1000,1.2000),[1.10000,1.20000)")} 1+ should - ${convertTo("1+", "[0,)")} + ${convertTo("1+", "[1,2),[10,20),[100,200),[1000,2000),[10000,20000)")} + + + should + ${convertTo("+", "[0,)")} + + foo+ should + ${beParsedAsError("foo+")} """ val mp = new MakePom(ConsoleLogger()) def convertTo(s: String, expected: String) = mp.makeDependencyVersion(s) must_== expected + def beParsedAsError(s: String) = + try { + mp.makeDependencyVersion(s) + failure + } catch { + case e: Throwable => success + } }