From efb68cf6bfb219cb512d9b2f87f60434a5e53f7c Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 10 Jul 2014 17:46:41 -0400 Subject: [PATCH 1/2] whitespace change due to scalariform kicking in --- project/Release.scala | 2 +- project/Sbt.scala | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/project/Release.scala b/project/Release.scala index 12d5d3a55..ee347fb98 100644 --- a/project/Release.scala +++ b/project/Release.scala @@ -17,7 +17,7 @@ object Release extends Build { // Add credentials if they exist. def lameCredentialSettings: Seq[Setting[_]] = - if(CredentialsFile.exists) Seq(credentials in ThisBuild += Credentials(CredentialsFile)) + if (CredentialsFile.exists) Seq(credentials in ThisBuild += Credentials(CredentialsFile)) else Nil def releaseSettings(nonRoots: => Seq[ProjectReference], launcher: TaskKey[File]): Seq[Setting[_]] = Seq( publishTo in ThisBuild <<= publishResolver, diff --git a/project/Sbt.scala b/project/Sbt.scala index 3f7cf689e..9096b52ea 100644 --- a/project/Sbt.scala +++ b/project/Sbt.scala @@ -14,7 +14,7 @@ object Sbt extends Build { // Aggregate task for 2.11 private def lameAgregateTask(task: String): String = - s"all control/$task collections/$task io/$task completion/$task" + s"all control/$task collections/$task io/$task completion/$task" def buildSettings = Seq( organization := "org.scala-sbt", version := "0.13.6-SNAPSHOT", @@ -57,15 +57,15 @@ object Sbt extends Build { }, commands += Command.command("release-sbt-local") { state => "publishLocal" :: - "setupBuildScala211" :: - lameAgregateTask("publishLocal") :: - "reload" :: - state + "setupBuildScala211" :: + lameAgregateTask("publishLocal") :: + "reload" :: + state }, commands += Command.command("release-sbt") { state => // TODO - Any sort of validation "checkCredentials" :: - "publishSigned" :: + "publishSigned" :: "publishLauncher" :: "release-libs-211" :: state From ae13cd8412aeda022300f2923b29c0471936389e Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 10 Jul 2014 17:49:52 -0400 Subject: [PATCH 2/2] Improves unresolved dependencies error by displaying the deps nodes. fixes #1422, #381 --- ivy/src/main/scala/sbt/IvyActions.scala | 30 ++++++++++++++++++--- ivy/src/main/scala/sbt/IvyRetrieve.scala | 34 ++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/ivy/src/main/scala/sbt/IvyActions.scala b/ivy/src/main/scala/sbt/IvyActions.scala index c87666373..bdbf5fe1c 100644 --- a/ivy/src/main/scala/sbt/IvyActions.scala +++ b/ivy/src/main/scala/sbt/IvyActions.scala @@ -146,6 +146,17 @@ object IvyActions { withExtra foreach { id => log.warn("\t\t" + id) } log.warn("") } + err.failed foreach { x => + val failedPaths = err.failedPaths(x) + if (!failedPaths.isEmpty) { + log.warn("\n\tNote: Unresolved dependencies path:") + val reverseFailedPaths = (failedPaths.toList map { _.toString }).reverse + log.warn("\t\t" + reverseFailedPaths.head) + reverseFailedPaths.tail foreach { id => + log.warn("\t\t +- " + id) + } + } + } } def groupedConflicts[T](moduleFilter: ModuleFilter, grouping: ModuleID => T)(report: UpdateReport): Map[T, Set[String]] = report.configurations.flatMap { confReport => @@ -209,8 +220,15 @@ object IvyActions { val err = if (resolveReport.hasError) { val messages = resolveReport.getAllProblemMessages.toArray.map(_.toString).distinct - val failed = resolveReport.getUnresolvedDependencies.map(node => IvyRetrieve.toModuleID(node.getId)) - Some(new ResolveException(messages, failed)) + val failedPaths = Map(resolveReport.getUnresolvedDependencies map { node => + val m = IvyRetrieve.toModuleID(node.getId) + val path = IvyRetrieve.findPath(node, module.getModuleRevisionId) map { x => + IvyRetrieve.toModuleID(x.getId) + } + m -> path + }: _*) + val failed = failedPaths.keys.toSeq + Some(new ResolveException(messages, failed, failedPaths)) } else None (resolveReport, err) } @@ -269,4 +287,10 @@ object IvyActions { error("Missing files for publishing:\n\t" + missing.map(_._2.getAbsolutePath).mkString("\n\t")) } } -final class ResolveException(val messages: Seq[String], val failed: Seq[ModuleID]) extends RuntimeException(messages.mkString("\n")) +final class ResolveException( + val messages: Seq[String], + val failed: Seq[ModuleID], + val failedPaths: Map[ModuleID, Seq[ModuleID]]) extends RuntimeException(messages.mkString("\n")) { + def this(messages: Seq[String], failed: Seq[ModuleID]) = + this(messages, failed, Map(failed map { m => m -> Nil }: _*)) +} diff --git a/ivy/src/main/scala/sbt/IvyRetrieve.scala b/ivy/src/main/scala/sbt/IvyRetrieve.scala index 5d0f3f6ec..e863d6723 100644 --- a/ivy/src/main/scala/sbt/IvyRetrieve.scala +++ b/ivy/src/main/scala/sbt/IvyRetrieve.scala @@ -5,10 +5,10 @@ package sbt import java.io.File import collection.mutable - -import org.apache.ivy.core.{ module, report } +import org.apache.ivy.core.{ module, report, resolve } import module.descriptor.{ Artifact => IvyArtifact } import module.id.ModuleRevisionId +import resolve.IvyNode import report.{ ArtifactDownloadReport, ConfigurationResolveReport, ResolveReport } object IvyRetrieve { @@ -51,4 +51,34 @@ object IvyRetrieve { new UpdateStats(report.getResolveTime, report.getDownloadTime, report.getDownloadSize, false) def configurationReport(confReport: ConfigurationResolveReport): ConfigurationReport = new ConfigurationReport(confReport.getConfiguration, moduleReports(confReport), evicted(confReport)) + + /** + * Tries to find Ivy graph path the from node to target. + */ + def findPath(target: IvyNode, from: ModuleRevisionId): List[IvyNode] = { + def doFindPath(current: IvyNode, path: List[IvyNode]): List[IvyNode] = { + val callers = current.getAllRealCallers.toList + // Ivy actually returns non-direct callers here. + // that's why we have to calculate all possible paths below and pick the longest path. + val directCallers = callers filter { caller => + val md = caller.getModuleDescriptor + val dd = md.getDependencies.toList find { dd => + (dd.getDependencyRevisionId == current.getId) && + (dd.getParentRevisionId == caller.getModuleRevisionId) + } + dd.isDefined + } + val directCallersRevId = (directCallers map { _.getModuleRevisionId }).distinct + val paths: List[List[IvyNode]] = ((directCallersRevId map { revId => + val node = current.findNode(revId) + if (revId == from) node :: path + else if (node == node.getRoot) Nil + else if (path contains node) path + else doFindPath(node, node :: path) + }) sortBy { _.size }).reverse + paths.headOption getOrElse Nil + } + if (target.getId == from) List(target) + else doFindPath(target, List(target)) + } }