mirror of https://github.com/sbt/sbt.git
Implement assumedVersionScheme
Ref https://github.com/sbt/sbt/issues/6302 Ref https://github.com/sbt/sbt/issues/6301 Apparently some users are interested in keeping the eviction warning feature, so here's an option to bring guessing back.
This commit is contained in:
parent
70e7718353
commit
dc0b682d7e
|
|
@ -10,7 +10,7 @@ package internal
|
|||
package librarymanagement
|
||||
|
||||
import sbt.internal.librarymanagement.mavenint.SbtPomExtraProperties
|
||||
import sbt.librarymanagement.ModuleID
|
||||
import sbt.librarymanagement.{ EvictionWarningOptions, ModuleID, ScalaModuleInfo }
|
||||
|
||||
// See APIMappings.scala
|
||||
private[sbt] object VersionSchemes {
|
||||
|
|
@ -42,4 +42,15 @@ private[sbt] object VersionSchemes {
|
|||
|
||||
def extractFromExtraAttributes(extraAttributes: Map[String, String]): Option[String] =
|
||||
extraAttributes.get(SbtPomExtraProperties.VERSION_SCHEME_KEY)
|
||||
|
||||
def evalFunc(
|
||||
scheme: String
|
||||
): Function1[(ModuleID, Option[ModuleID], Option[ScalaModuleInfo]), Boolean] =
|
||||
scheme match {
|
||||
case EarlySemVer => EvictionWarningOptions.guessEarlySemVer
|
||||
case SemVerSpec => EvictionWarningOptions.guessSemVer
|
||||
case PackVer => EvictionWarningOptions.evalPvp
|
||||
case Strict => EvictionWarningOptions.guessStrict
|
||||
case Always => EvictionWarningOptions.guessTrue
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,23 +3,47 @@ package librarymanagement
|
|||
|
||||
import scala.collection.mutable
|
||||
import sbt.internal.librarymanagement.VersionSchemes
|
||||
import sbt.util.ShowLines
|
||||
import sbt.util.{ Level, ShowLines }
|
||||
import EvictionWarningOptions.isNameScalaSuffixed
|
||||
|
||||
object EvictionError {
|
||||
def apply(
|
||||
report: UpdateReport,
|
||||
module: ModuleDescriptor,
|
||||
schemes: Seq[ModuleID],
|
||||
): EvictionError = {
|
||||
apply(report, module, schemes, "always", "always", Level.Debug)
|
||||
}
|
||||
|
||||
def apply(
|
||||
report: UpdateReport,
|
||||
module: ModuleDescriptor,
|
||||
schemes: Seq[ModuleID],
|
||||
assumedVersionScheme: String,
|
||||
assumedVersionSchemeJava: String,
|
||||
assumedEvictionErrorLevel: Level.Value,
|
||||
): EvictionError = {
|
||||
val options = EvictionWarningOptions.full
|
||||
val evictions = EvictionWarning.buildEvictions(options, report)
|
||||
processEvictions(module, options, evictions, schemes)
|
||||
processEvictions(
|
||||
module,
|
||||
options,
|
||||
evictions,
|
||||
schemes,
|
||||
assumedVersionScheme,
|
||||
assumedVersionSchemeJava,
|
||||
assumedEvictionErrorLevel,
|
||||
)
|
||||
}
|
||||
|
||||
private[sbt] def processEvictions(
|
||||
module: ModuleDescriptor,
|
||||
options: EvictionWarningOptions,
|
||||
reports: Seq[OrganizationArtifactReport],
|
||||
schemes: Seq[ModuleID],
|
||||
assumedVersionScheme: String,
|
||||
assumedVersionSchemeJava: String,
|
||||
assumedEvictionErrorLevel: Level.Value,
|
||||
): EvictionError = {
|
||||
val directDependencies = module.directDependencies
|
||||
val pairs = reports map { detail =>
|
||||
|
|
@ -35,6 +59,7 @@ object EvictionError {
|
|||
)
|
||||
}
|
||||
val incompatibleEvictions: mutable.ListBuffer[(EvictionPair, String)] = mutable.ListBuffer()
|
||||
val assumedIncompatEvictions: mutable.ListBuffer[(EvictionPair, String)] = mutable.ListBuffer()
|
||||
val sbvOpt = module.scalaModuleInfo.map(_.scalaBinaryVersion)
|
||||
val userDefinedSchemes: Map[(String, String), String] = Map(schemes flatMap { s =>
|
||||
val organization = s.organization
|
||||
|
|
@ -57,7 +82,8 @@ object EvictionError {
|
|||
List((s.organization, s.name) -> versionScheme)
|
||||
}
|
||||
}: _*)
|
||||
def calculateCompatible(p: EvictionPair): (Boolean, String) = {
|
||||
|
||||
def calculateCompatible(p: EvictionPair): (Boolean, String, Boolean, String) = {
|
||||
val winnerOpt = p.winner map { _.module }
|
||||
val extraAttributes = ((p.winner match {
|
||||
case Some(r) => r.extraAttributes.toMap
|
||||
|
|
@ -73,36 +99,34 @@ object EvictionError {
|
|||
.orElse(VersionSchemes.extractFromExtraAttributes(extraAttributes))
|
||||
.orElse(userDefinedSchemes.get(("*", "*")))
|
||||
val f = (winnerOpt, schemeOpt) match {
|
||||
case (Some(_), Some(VersionSchemes.Always)) =>
|
||||
EvictionWarningOptions.guessTrue
|
||||
case (Some(_), Some(VersionSchemes.Strict)) =>
|
||||
EvictionWarningOptions.guessStrict
|
||||
case (Some(_), Some(VersionSchemes.EarlySemVer)) =>
|
||||
EvictionWarningOptions.guessEarlySemVer
|
||||
case (Some(_), Some(VersionSchemes.SemVerSpec)) =>
|
||||
EvictionWarningOptions.guessSemVer
|
||||
case (Some(_), Some(VersionSchemes.PackVer)) =>
|
||||
EvictionWarningOptions.evalPvp
|
||||
case _ => EvictionWarningOptions.guessTrue
|
||||
case (Some(_), Some(scheme)) => VersionSchemes.evalFunc(scheme)
|
||||
case _ => EvictionWarningOptions.guessTrue
|
||||
}
|
||||
val scheme =
|
||||
if (isNameScalaSuffixed(p.name)) assumedVersionScheme
|
||||
else assumedVersionSchemeJava
|
||||
val guess = VersionSchemes.evalFunc(scheme)
|
||||
(p.evicteds forall { r =>
|
||||
f((r.module, winnerOpt, module.scalaModuleInfo))
|
||||
}, schemeOpt.getOrElse("?"))
|
||||
}, schemeOpt.getOrElse("?"), p.evicteds forall { r =>
|
||||
guess((r.module, winnerOpt, module.scalaModuleInfo))
|
||||
}, scheme)
|
||||
}
|
||||
pairs foreach {
|
||||
// don't report on a transitive eviction that does not have a winner
|
||||
// https://github.com/sbt/sbt/issues/4946
|
||||
case p if p.winner.isDefined =>
|
||||
// don't report on a transitive eviction that does not have a winner
|
||||
// https://github.com/sbt/sbt/issues/4946
|
||||
if (p.winner.isDefined) {
|
||||
val r = calculateCompatible(p)
|
||||
if (!r._1) {
|
||||
incompatibleEvictions += (p -> r._2)
|
||||
}
|
||||
val r = calculateCompatible(p)
|
||||
if (!r._1) {
|
||||
incompatibleEvictions += (p -> r._2)
|
||||
} else if (!r._3) {
|
||||
assumedIncompatEvictions += (p -> r._4)
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
new EvictionError(
|
||||
incompatibleEvictions.toList,
|
||||
assumedIncompatEvictions.toList,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -113,17 +137,22 @@ object EvictionError {
|
|||
|
||||
final class EvictionError private[sbt] (
|
||||
val incompatibleEvictions: Seq[(EvictionPair, String)],
|
||||
val assumedIncompatibleEvictions: Seq[(EvictionPair, String)],
|
||||
) {
|
||||
def run(): Unit =
|
||||
if (incompatibleEvictions.nonEmpty) {
|
||||
sys.error(toLines.mkString("\n"))
|
||||
}
|
||||
|
||||
def toLines: List[String] = {
|
||||
def toLines: List[String] = toLines(incompatibleEvictions, false)
|
||||
|
||||
def toAssumedLines: List[String] = toLines(assumedIncompatibleEvictions, true)
|
||||
|
||||
def toLines(evictions: Seq[(EvictionPair, String)], assumed: Boolean): List[String] = {
|
||||
val out: mutable.ListBuffer[String] = mutable.ListBuffer()
|
||||
out += "found version conflict(s) in library dependencies; some are suspected to be binary incompatible:"
|
||||
out += ""
|
||||
incompatibleEvictions.foreach({
|
||||
evictions.foreach({
|
||||
case (a, scheme) =>
|
||||
val revs = a.evicteds map { _.module.revision }
|
||||
val revsStr = if (revs.size <= 1) revs.mkString else "{" + revs.mkString(", ") + "}"
|
||||
|
|
@ -138,8 +167,9 @@ final class EvictionError private[sbt] (
|
|||
}
|
||||
}
|
||||
}
|
||||
val que = if (assumed) "?" else ""
|
||||
val winnerRev = a.winner match {
|
||||
case Some(r) => s":${r.module.revision} ($scheme) is selected over ${revsStr}"
|
||||
case Some(r) => s":${r.module.revision} ($scheme$que) is selected over ${revsStr}"
|
||||
case _ => " is evicted for all versions"
|
||||
}
|
||||
val title = s"\t* ${a.organization}:${a.name}$winnerRev"
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ object EvictionWarningOptions {
|
|||
lazy val defaultGuess: Function1[(ModuleID, Option[ModuleID], Option[ScalaModuleInfo]), Boolean] =
|
||||
guessSbtOne orElse guessSecondSegment orElse guessSemVer orElse guessFalse
|
||||
|
||||
private def isNameScalaSuffixed(name: String): Boolean =
|
||||
private[sbt] def isNameScalaSuffixed(name: String): Boolean =
|
||||
name.contains("_2.") || name.contains("_3") || name.contains("_4")
|
||||
|
||||
/** A partial function that checks if given m2 is suffixed, and use pvp to evaluate. */
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package sbt.internal.librarymanagement
|
|||
import sbt.librarymanagement._
|
||||
import sbt.internal.librarymanagement.cross.CrossVersionUtil
|
||||
import sbt.librarymanagement.syntax._
|
||||
import sbt.util.Level
|
||||
|
||||
object EvictionErrorSpec extends BaseIvySpecification {
|
||||
// This is a specification to check the eviction errors
|
||||
|
|
@ -50,6 +51,24 @@ object EvictionErrorSpec extends BaseIvySpecification {
|
|||
)
|
||||
}
|
||||
|
||||
test("it should be able to emulate eviction warnings") {
|
||||
val deps = Vector(`scala2.10.4`, `bananaSesame0.4`, `akkaRemote2.3.4`)
|
||||
val m = module(defaultModuleId, deps, Some("2.10.4"))
|
||||
val report = ivyUpdate(m)
|
||||
assert(
|
||||
EvictionError(report, m, Nil, "pvp", "early-semver", Level.Warn).toAssumedLines ==
|
||||
List(
|
||||
"found version conflict(s) in library dependencies; some are suspected to be binary incompatible:",
|
||||
"",
|
||||
"\t* com.typesafe.akka:akka-actor_2.10:2.3.4 (pvp?) is selected over 2.1.4",
|
||||
"\t +- com.typesafe.akka:akka-remote_2.10:2.3.4 (depends on 2.3.4)",
|
||||
"\t +- org.w3:banana-rdf_2.10:0.4 (depends on 2.1.4)",
|
||||
"\t +- org.w3:banana-sesame_2.10:0.4 (depends on 2.1.4)",
|
||||
""
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
test("it should detect Semantic Versioning violations") {
|
||||
val deps = Vector(`scala2.13.3`, `http4s0.21.11`, `cats-effect3.0.0-M4`)
|
||||
val m = module(defaultModuleId, deps, Some("2.13.3"))
|
||||
|
|
|
|||
Loading…
Reference in New Issue