mirror of https://github.com/sbt/sbt.git
Revert "[2.x] fix: Skip Conflict when dependency relocations form a cycle (#8919)"
This reverts commit b24ecddbd6.
This commit is contained in:
parent
ade9885ce6
commit
f8504f2a4d
|
|
@ -1,70 +0,0 @@
|
|||
package lmcoursier.internal
|
||||
|
||||
import coursier.core.*
|
||||
import coursier.core.Resolution as CoreResolution
|
||||
import coursier.{ Dependency, Resolution }
|
||||
|
||||
import scala.annotation.tailrec
|
||||
|
||||
/**
|
||||
* Detects cyclic Maven / Gradle relocation chains that make
|
||||
* `coursier.graph.DependencyTree` loop forever (see sbt#8917, coursier#3578).
|
||||
*
|
||||
* Mirrors one step of `coursier.graph.DependencyTree.Node.relocation` so we
|
||||
* only skip `Conflict` when Coursier would spin on the same graph.
|
||||
*/
|
||||
private[internal] object RelocationCycleDetector:
|
||||
|
||||
type Mvc = CoreResolution.ModuleVersionConstraint
|
||||
|
||||
private def oneRelocationStep(resolution: Resolution, dep: Dependency): Option[Mvc] =
|
||||
resolution.reconciledVersions
|
||||
.get(dep.module)
|
||||
.flatMap: recon =>
|
||||
val dep0 =
|
||||
if dep.versionConstraint == recon then dep
|
||||
else dep.withVersionConstraint(recon)
|
||||
resolution.projectCache0
|
||||
.get(dep0.moduleVersionConstraint)
|
||||
.flatMap:
|
||||
case (_, proj) =>
|
||||
val mavenRelocatedOpt =
|
||||
if proj.relocated && proj.dependencies0.lengthCompare(1) == 0 then
|
||||
Some(proj.dependencies0.head._2)
|
||||
else None
|
||||
def gradleRelocated: Option[Dependency] =
|
||||
dep0.variantSelector match
|
||||
case attr: VariantSelector.AttributesBased =>
|
||||
if proj.variants.isEmpty then None
|
||||
else
|
||||
proj.variantFor(attr) match
|
||||
case Left(_) => None
|
||||
case Right(variant) => proj.isRelocatedVariant(variant)
|
||||
case _: VariantSelector.ConfigurationBased => None
|
||||
mavenRelocatedOpt
|
||||
.orElse(gradleRelocated)
|
||||
.map: relocatedTo =>
|
||||
val relocatedTo0 =
|
||||
if relocatedTo.variantSelector.isEmpty then
|
||||
relocatedTo.withVariantSelector(dep0.variantSelector)
|
||||
else relocatedTo
|
||||
relocatedTo0.moduleVersionConstraint
|
||||
|
||||
/** When true, `coursier.graph.Conflict(resolution)` can run indefinitely. */
|
||||
def hasRelocationCycle(resolution: Resolution): Boolean =
|
||||
resolution.isDone && resolution.conflicts.isEmpty && resolution.errors0.isEmpty && {
|
||||
val keys = resolution.projectCache0.keySet
|
||||
keys.exists: start =>
|
||||
@tailrec
|
||||
def walk(visited: Set[Mvc], cur: Option[Mvc]): Boolean =
|
||||
cur match
|
||||
case None => false
|
||||
case Some(mvc) =>
|
||||
if visited.contains(mvc) then true
|
||||
else
|
||||
val dep = Dependency(module = mvc._1, version = mvc._2)
|
||||
walk(visited + mvc, oneRelocationStep(resolution, dep))
|
||||
walk(Set.empty, Some(start))
|
||||
}
|
||||
|
||||
end RelocationCycleDetector
|
||||
|
|
@ -361,22 +361,6 @@ private[internal] object SbtUpdateReport {
|
|||
classLoaders: Seq[ClassLoader],
|
||||
): UpdateReport = {
|
||||
|
||||
val skipConflictDetail: Map[Configuration, Boolean] =
|
||||
resolutions.map { case (cfg, subRes) =>
|
||||
cfg -> RelocationCycleDetector.hasRelocationCycle(subRes)
|
||||
}.toMap
|
||||
val configsWithRelocationCycle =
|
||||
skipConflictDetail.collect { case (cfg, true) => cfg.value }.toSeq.sorted
|
||||
if (configsWithRelocationCycle.nonEmpty) {
|
||||
log.warn(
|
||||
"Skipping dependency conflict detail for configuration(s) " +
|
||||
configsWithRelocationCycle.mkString(", ") +
|
||||
": cyclic Maven or Gradle relocations in the resolved graph. " +
|
||||
"Resolution succeeded; eviction detail may be incomplete. " +
|
||||
"See https://github.com/sbt/sbt/issues/8917"
|
||||
)
|
||||
}
|
||||
|
||||
val configReports = resolutions.map { (config, subRes) =>
|
||||
val reports = moduleReports(
|
||||
thisModule,
|
||||
|
|
@ -412,9 +396,7 @@ private[internal] object SbtUpdateReport {
|
|||
}
|
||||
|
||||
def conflicts: Seq[coursier.graph.Conflict] =
|
||||
try
|
||||
if (skipConflictDetail.getOrElse(config, false)) Nil
|
||||
else coursier.graph.Conflict(subRes)
|
||||
try coursier.graph.Conflict(subRes)
|
||||
catch case e: Throwable if missingOk => Nil
|
||||
val evicted = for {
|
||||
c <- conflicts
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
package lmcoursier.internal
|
||||
|
||||
import coursier.Resolution
|
||||
import org.scalatest.funsuite.AnyFunSuite
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
|
||||
class RelocationCycleDetectorSpec extends AnyFunSuite with Matchers:
|
||||
|
||||
test("incomplete resolution is not treated as having a relocation cycle"):
|
||||
RelocationCycleDetector.hasRelocationCycle(Resolution()) shouldBe false
|
||||
Loading…
Reference in New Issue