Skip to content

Commit

Permalink
Better output in case of conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
alexarchambault committed Aug 16, 2019
1 parent e29c38c commit e933174
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 13 deletions.
19 changes: 13 additions & 6 deletions modules/core/shared/src/main/scala/coursier/util/Tree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ object Tree {

final class Tree[A](val roots: IndexedSeq[A], val children: A => Seq[A]) {

def render(show: A => String): String = {
def render(show: A => String): String =
customRender()(show)

def customRender(assumeTopRoot: Boolean = true, extraPrefix: String = "", extraSeparator: Option[String] = None)(show: A => String): String = {

/**
* Recursively go down the resolution for the elems to construct the tree for print out.
Expand All @@ -21,21 +24,25 @@ final class Tree[A](val roots: IndexedSeq[A], val children: A => Seq[A]) {
* @param prefix prefix for the print out
* @param acc accumulation method on a string
*/
def recursivePrint(elems: Seq[A], ancestors: Set[A], prefix: String, acc: String => Unit): Unit = {
def recursivePrint(elems: Seq[A], ancestors: Set[A], prefix: String, isRoot: Boolean, acc: String => Unit): Unit = {
val unseenElems: Seq[A] = elems.filterNot(ancestors.contains)
val unseenElemsLen = unseenElems.length
for ((elem, idx) <- unseenElems.iterator.zipWithIndex) {
val isLast = idx == unseenElemsLen - 1
val tee = if (isLast) "└─ " else "├─ "
val tee = if (!assumeTopRoot && isRoot) "" else if (isLast) "└─ " else "├─ "
acc(prefix + tee + show(elem))

val extraPrefix = if (isLast) " " else ""
recursivePrint(children(elem), ancestors + elem, prefix + extraPrefix, acc)
val extraPrefix = if (!assumeTopRoot && isRoot) "" else if (isLast) " " else ""
recursivePrint(children(elem), ancestors + elem, prefix + extraPrefix, isRoot = false, acc)

for (sep <- extraSeparator)
if (!assumeTopRoot && isRoot)
acc(sep)
}
}

val b = new ArrayBuffer[String]
recursivePrint(roots, Set(), "", b += _)
recursivePrint(roots, Set(), extraPrefix, isRoot = true, b += _)
b.mkString("\n")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import coursier.cache.{Cache, CacheLogger}
import coursier.core.{Activation, DependencySet, Exclusions, Reconciliation}
import coursier.error.ResolutionError
import coursier.error.conflict.UnsatisfiedRule
import coursier.graph.ReverseModuleTree
import coursier.internal.Typelevel
import coursier.params.{Mirror, MirrorConfFile, ResolutionParams}
import coursier.params.rule.{Rule, RuleResolution}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ package coursier.error

import coursier.core.{Dependency, Module, Resolution}
import coursier.error.conflict.UnsatisfiedRule
import coursier.graph.ReverseModuleTree
import coursier.params.rule.Rule
import coursier.util.Print
import coursier.util.{Print, Tree}
import coursier.util.Print.{Colors, compatibleVersions}

sealed abstract class ResolutionError(
val resolution: Resolution,
Expand Down Expand Up @@ -38,12 +40,40 @@ object ResolutionError {
) extends Simple(
resolution,
"Conflicting dependencies:\n" +
Print.dependenciesUnknownConfigs(
dependencies.toVector,
Map.empty,
printExclusions = false,
useFinalVersions = false
)
{
val roots = resolution.conflicts.map(_.module)
val trees = ReverseModuleTree(resolution, roots = roots.toVector.sortBy(m => (m.organization.value, m.name.value, m.nameWithAttributes)))
val colors0 = Colors.get(coursier.core.compatibility.hasConsole)

val renderedTrees = trees.map { t =>
val rendered = Tree(t.dependees.toVector)(_.dependees)
.customRender(assumeTopRoot = false, extraPrefix = " ", extraSeparator = Some("")) { node =>
if (node.excludedDependsOn)
s"${colors0.yellow}(excluded by)${colors0.reset} ${node.module}:${node.reconciledVersion}"
else if (node.dependsOnModule == t.module) {
val assumeCompatibleVersions = compatibleVersions(node.dependsOnVersion, node.dependsOnReconciledVersion)

s"${node.module}:${node.reconciledVersion} " +
(if (assumeCompatibleVersions) colors0.yellow else colors0.red) +
s"wants ${node.dependsOnVersion}" +
colors0.reset
} else if (node.dependsOnVersion != node.dependsOnReconciledVersion) {
val assumeCompatibleVersions = compatibleVersions(node.dependsOnVersion, node.dependsOnReconciledVersion)

s"${node.module}:${node.reconciledVersion} " +
(if (assumeCompatibleVersions) colors0.yellow else colors0.red) +
s"wants ${node.dependsOnModule}:${node.dependsOnVersion}" +
colors0.reset
} else
s"${node.module}:${node.reconciledVersion}"
}

s"${t.module.repr}:${t.dependees.map(_.dependsOnVersion).distinct.mkString(" or ")} wanted by\n\n" +
rendered + "\n"
}

renderedTrees.mkString("\n")
}
)

sealed abstract class Simple(resolution: Resolution, message: String, cause: Throwable = null)
Expand Down

0 comments on commit e933174

Please sign in to comment.