Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import mill.*
import mill.api.BuildCtx
import mill.scalajslib.*
import mill.scalalib.*
import mill.scalanativelib.*
import mill.scalalib.publish.*
import mill.util.VcsVersion

Expand All @@ -17,6 +18,7 @@ object DepVersions {
def scala213 = "2.13.18"
def scala3 = "3.3.8"
def scalaJs = "1.22.0"
def scalaNative = "0.5.12"

def scala = Seq(scala213, "2.12.21", scala3)
}
Expand Down Expand Up @@ -226,6 +228,38 @@ trait VersionsJs extends Versions with ScalaJSModule {
}
}

trait VersionsNative extends Versions with ScalaNativeModule {
def scalaNativeVersion = DepVersions.scalaNative

def mimaPreviousVersions = Task {
import _root_.coursier.version.Version
val cutOff = Version("0.6.0")
super.mimaPreviousVersions()
.map(Version(_))
.filter(_ >= cutOff)
.map(_.repr)
}

// no Scala Native artifacts published before 0.6.0, so unlike on the JVM and on Scala.js,
// there are no Scala 2.13 ones for the Scala 3 module to fall back on
def mimaPreviousArtifacts = Task {
mimaPreviousVersions().distinct.map(version =>
mvn"${pomSettings().organization}:${artifactId()}:$version"
)
}

object test extends ScalaNativeTests {
def mvnDeps = super.mvnDeps() ++ Seq(
Deps.pprint,
Deps.utest
)
def testFramework = "utest.runner.Framework"
def sources = Task {
super.sources() ++ Seq(versions.shared.testSources())
}
}
}

object versions extends Module {
object shared extends Module {
def sources = Task.Source("src")
Expand All @@ -235,6 +269,7 @@ object versions extends Module {
}
object jvm extends Cross[VersionsJvm](DepVersions.scala)
object js extends Cross[VersionsJs](DepVersions.scala)
object native extends Cross[VersionsNative](DepVersions.scala)
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ object Compatibility {
def letterOrDigit: Boolean = between(c, '0', '9') || letter
}

// unused, kept for binary compatibility
def regexLookbehind: String = ":"

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ object Compatibility {
def letterOrDigit = c.isLetterOrDigit
}

// unused, kept for binary compatibility
def regexLookbehind: String = "<="

}
10 changes: 10 additions & 0 deletions versions/native/src/coursier/version/internal/Compatibility.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package coursier.version.internal

object Compatibility {

implicit class RichChar(private val c: Char) extends AnyVal {
def letter = c.isLetter
def letterOrDigit = c.isLetterOrDigit
}

}
25 changes: 19 additions & 6 deletions versions/shared/src/coursier/version/VersionParse.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package coursier.version

import java.util.regex.Pattern.quote

import coursier.version.internal.Compatibility._

object VersionParse {
Expand Down Expand Up @@ -59,17 +57,32 @@ object VersionParse {
} yield itv
}

private val multiVersionIntervalSplit = ("(?" + regexLookbehind + "[" + quote("])") + "]),(?=[" + quote("([") + "])").r
private def isIntervalStart(c: Char): Boolean = c == '[' || c == '('
private def isIntervalEnd(c: Char): Boolean = c == ']' || c == ')'

/** Index of the first char of the last interval of `s`, that is the index right after the last
* ',' preceded by ']' or ')' and followed by '[' or '(' - `0` if there's no such ','.
*/
private def lastIntervalStart(s: String): Int = {
var idx = s.length - 2
var res = 0
while (idx >= 1 && res == 0) {
if (s.charAt(idx) == ',' && isIntervalEnd(s.charAt(idx - 1)) && isIntervalStart(s.charAt(idx + 1)))
res = idx + 1
idx -= 1
}
res
}

def multiVersionInterval(s: String): Option[VersionInterval] = {

// TODO Use a full-fledged (fastparsed-based) parser for this and versionInterval above

val openCount = s.count(c => c == '[' || c == '(')
val closeCount = s.count(c => c == ']' || c == ')')
val openCount = s.count(isIntervalStart)
val closeCount = s.count(isIntervalEnd)

if (openCount == closeCount && openCount >= 1)
versionInterval(multiVersionIntervalSplit.split(s).last)
versionInterval(s.substring(lastIntervalStart(s)))
else
None
}
Expand Down
53 changes: 53 additions & 0 deletions versions/shared/test/src/coursier/version/ModuleMatcherTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package coursier.version

import utest._

object ModuleMatcherTests extends TestSuite {

def check(matcher: ModuleMatcher, organization: String, name: String): Unit =
assert(matcher.matches(organization, name))
def checkNot(matcher: ModuleMatcher, organization: String, name: String): Unit =
assert(!matcher.matches(organization, name))

val tests = Tests {
test("all") {
check(ModuleMatcher.all, "org.scala-lang", "scala-library")
check(ModuleMatcher.all, "io.get-coursier", "versions_2.13")
}

test("exact") {
val matcher = ModuleMatcher("io.get-coursier", "versions_2.13")
check(matcher, "io.get-coursier", "versions_2.13")
checkNot(matcher, "io.get-coursier", "versions_2.12")
checkNot(matcher, "io.get-coursier.thing", "versions_2.13")
}

test("specialChars") {
// '.' and '-' are quoted, not interpreted as regex constructs
val matcher = ModuleMatcher("io.get-coursier", "versions_2.13")
checkNot(matcher, "ioaget-coursier", "versions_2.13")
checkNot(matcher, "io.get-coursier", "versions_2a13")
}

test("blob") {
val matcher = ModuleMatcher("io.get-coursier*", "versions_*")
check(matcher, "io.get-coursier", "versions_2.13")
check(matcher, "io.get-coursier.scala-cli", "versions_native0.5_2.13")
checkNot(matcher, "org.get-coursier", "versions_2.13")
checkNot(matcher, "io.get-coursier", "coursier_2.13")
}

test("leadingBlob") {
val matcher = ModuleMatcher("*", "*_2.13")
check(matcher, "io.get-coursier", "versions_2.13")
checkNot(matcher, "io.get-coursier", "versions_2.12")
}

test("attributes") {
val matcher = ModuleMatcher("io.get-coursier", "versions_2.13", Map("scope" -> "*"))
assert(matcher.matches("io.get-coursier", "versions_2.13", Map("scope" -> "test")))
assert(!matcher.matches("io.get-coursier", "versions_2.13", Map.empty))
assert(!matcher.matches("io.get-coursier", "versions_2.13", Map("other" -> "test")))
}
}
}