mirror of https://github.com/sbt/sbt.git
Merge pull request #1491 from sbt/fix/528
Unresolved dependency warning includes source position. Fixes #528
This commit is contained in:
commit
d20bfa50ef
|
|
@ -5,6 +5,7 @@ package sbt
|
|||
|
||||
import java.io.File
|
||||
import scala.xml.{ Node => XNode, NodeSeq }
|
||||
import collection.mutable
|
||||
|
||||
import org.apache.ivy.Ivy
|
||||
import org.apache.ivy.core.{ IvyPatternHelper, LogOptions }
|
||||
|
|
@ -29,6 +30,14 @@ final case class MakePomConfiguration(file: File, moduleInfo: ModuleInfo, config
|
|||
final case class GetClassifiersConfiguration(module: GetClassifiersModule, exclude: Map[ModuleID, Set[String]], configuration: UpdateConfiguration, ivyScala: Option[IvyScala])
|
||||
final case class GetClassifiersModule(id: ModuleID, modules: Seq[ModuleID], configurations: Seq[Configuration], classifiers: Seq[String])
|
||||
|
||||
final class UnresolvedWarningConfiguration private[sbt] (
|
||||
val modulePositions: Map[ModuleID, SourcePosition])
|
||||
object UnresolvedWarningConfiguration {
|
||||
def apply(): UnresolvedWarningConfiguration = apply(Map())
|
||||
def apply(modulePositions: Map[ModuleID, SourcePosition]): UnresolvedWarningConfiguration =
|
||||
new UnresolvedWarningConfiguration(modulePositions)
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures logging during an 'update'. `level` determines the amount of other information logged.
|
||||
* `Full` is the default and logs the most.
|
||||
|
|
@ -130,43 +139,37 @@ object IvyActions {
|
|||
* Resolves and retrieves dependencies. 'ivyConfig' is used to produce an Ivy file and configuration.
|
||||
* 'updateConfig' configures the actual resolution and retrieval process.
|
||||
*/
|
||||
@deprecated("Use updateEither instead.", "0.13.6")
|
||||
def update(module: IvySbt#Module, configuration: UpdateConfiguration, log: Logger): UpdateReport =
|
||||
updateEither(module, configuration, UnresolvedWarningConfiguration(), log) match {
|
||||
case Right(r) => r
|
||||
case Left(w) =>
|
||||
throw w.resolveException
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves and retrieves dependencies. 'ivyConfig' is used to produce an Ivy file and configuration.
|
||||
* 'updateConfig' configures the actual resolution and retrieval process.
|
||||
*/
|
||||
def updateEither(module: IvySbt#Module, configuration: UpdateConfiguration,
|
||||
uwconfig: UnresolvedWarningConfiguration, log: Logger): Either[UnresolvedWarning, UpdateReport] =
|
||||
module.withModule(log) {
|
||||
case (ivy, md, default) =>
|
||||
val (report, err) = resolve(configuration.logging)(ivy, md, default)
|
||||
err match {
|
||||
case Some(x) if !configuration.missingOk =>
|
||||
processUnresolved(x, log)
|
||||
throw x
|
||||
Left(UnresolvedWarning(x, uwconfig))
|
||||
case _ =>
|
||||
val cachedDescriptor = ivy.getSettings.getResolutionCacheManager.getResolvedIvyFileInCache(md.getModuleRevisionId)
|
||||
val uReport = IvyRetrieve.updateReport(report, cachedDescriptor)
|
||||
configuration.retrieve match {
|
||||
case Some(rConf) => retrieve(ivy, uReport, rConf)
|
||||
case None => uReport
|
||||
case Some(rConf) => Right(retrieve(ivy, uReport, rConf))
|
||||
case None => Right(uReport)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def processUnresolved(err: ResolveException, log: Logger) {
|
||||
val withExtra = err.failed.filter(!_.extraDependencyAttributes.isEmpty)
|
||||
if (!withExtra.isEmpty) {
|
||||
log.warn("\n\tNote: Some unresolved dependencies have extra attributes. Check that these dependencies exist with the requested attributes.")
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@deprecated("No longer used.", "0.13.6")
|
||||
def processUnresolved(err: ResolveException, log: Logger): Unit = ()
|
||||
def groupedConflicts[T](moduleFilter: ModuleFilter, grouping: ModuleID => T)(report: UpdateReport): Map[T, Set[String]] =
|
||||
report.configurations.flatMap { confReport =>
|
||||
val evicted = confReport.evicted.filter(moduleFilter)
|
||||
|
|
@ -303,3 +306,59 @@ final class ResolveException(
|
|||
def this(messages: Seq[String], failed: Seq[ModuleID]) =
|
||||
this(messages, failed, Map(failed map { m => m -> Nil }: _*))
|
||||
}
|
||||
/**
|
||||
* Represents unresolved dependency warning, which displays reconstructed dependency tree
|
||||
* along with source position of each node.
|
||||
*/
|
||||
final class UnresolvedWarning private[sbt] (
|
||||
val resolveException: ResolveException,
|
||||
val failedPaths: Seq[Seq[(ModuleID, Option[SourcePosition])]])
|
||||
object UnresolvedWarning {
|
||||
private[sbt] def apply(err: ResolveException, config: UnresolvedWarningConfiguration): UnresolvedWarning = {
|
||||
def modulePosition(m0: ModuleID): Option[SourcePosition] =
|
||||
config.modulePositions.find {
|
||||
case (m, p) =>
|
||||
(m.organization == m0.organization) &&
|
||||
(m0.name startsWith m.name) &&
|
||||
(m.revision == m0.revision)
|
||||
} map {
|
||||
case (m, p) => p
|
||||
}
|
||||
val failedPaths = err.failed map { x: ModuleID =>
|
||||
err.failedPaths(x).toList.reverse map { id =>
|
||||
(id, modulePosition(id))
|
||||
}
|
||||
}
|
||||
apply(err, failedPaths)
|
||||
}
|
||||
private[sbt] def apply(err: ResolveException, failedPaths: Seq[Seq[(ModuleID, Option[SourcePosition])]]): UnresolvedWarning =
|
||||
new UnresolvedWarning(err, failedPaths)
|
||||
private[sbt] def sourcePosStr(posOpt: Option[SourcePosition]): String =
|
||||
posOpt match {
|
||||
case Some(LinePosition(path, start)) => s" ($path#L$start)"
|
||||
case Some(RangePosition(path, LineRange(start, end))) => s" ($path#L$start-$end)"
|
||||
case _ => ""
|
||||
}
|
||||
implicit val unresolvedWarningLines: ShowLines[UnresolvedWarning] = ShowLines { a =>
|
||||
val withExtra = a.resolveException.failed.filter(!_.extraDependencyAttributes.isEmpty)
|
||||
val buffer = mutable.ListBuffer[String]()
|
||||
if (!withExtra.isEmpty) {
|
||||
buffer += "\n\tNote: Some unresolved dependencies have extra attributes. Check that these dependencies exist with the requested attributes."
|
||||
withExtra foreach { id => buffer += "\t\t" + id }
|
||||
}
|
||||
if (!a.failedPaths.isEmpty) {
|
||||
buffer += "\n\tNote: Unresolved dependencies path:"
|
||||
a.failedPaths foreach { path =>
|
||||
if (!path.isEmpty) {
|
||||
val head = path.head
|
||||
buffer += "\t\t" + head._1.toString + sourcePosStr(head._2)
|
||||
path.tail foreach {
|
||||
case (m, pos) =>
|
||||
buffer += "\t\t +- " + m.toString + sourcePosStr(pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer.toList
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue