mirror of https://github.com/sbt/sbt.git
Unresolved dependency warning includes source position. Fixes #528
Unresolved dependency warning is moved to UnresolvedDependencyWarning class including the fail path that was added in #1467. To display the source position, I need to access the State, so I had to move the error processing out of IvyActions and add UnresolvedDependencyWarning, which is aware of State.
This commit is contained in:
parent
4a83fcaeb1
commit
58b7c63f84
|
|
@ -130,43 +130,36 @@ 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, log) match {
|
||||
case Right(r) => r
|
||||
case Left(e) => throw e
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, log: Logger): Either[ResolveException, 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(x)
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -1253,17 +1253,37 @@ object Classpaths {
|
|||
val transform: UpdateReport => UpdateReport = r => substituteScalaFiles(scalaOrganization.value, r)(subScalaJars)
|
||||
|
||||
val show = Reference.display(thisProjectRef.value)
|
||||
cachedUpdate(s.cacheDirectory / updateCacheName.value, show, ivyModule.value, updateConfiguration.value, transform, skip = (skip in update).value, force = isRoot, depsUpdated = depsUpdated, log = s.log)
|
||||
cachedUpdate(s.cacheDirectory / updateCacheName.value, show, ivyModule.value, updateConfiguration.value, transform,
|
||||
skip = (skip in update).value, force = isRoot, depsUpdated = depsUpdated,
|
||||
unresolvedHandler = { r =>
|
||||
import ShowLines._
|
||||
UnresolvedDependencyWarning(r, Some(state.value)).lines foreach { s.log.warn(_) }
|
||||
},
|
||||
log = s.log)
|
||||
}
|
||||
|
||||
def cachedUpdate(cacheFile: File, label: String, module: IvySbt#Module, config: UpdateConfiguration, transform: UpdateReport => UpdateReport, skip: Boolean, force: Boolean, depsUpdated: Boolean, log: Logger): UpdateReport =
|
||||
@deprecated("Use cachedUpdate with the variant that takes unresolvedHandler instead.", "0.13.6")
|
||||
def cachedUpdate(cacheFile: File, label: String, module: IvySbt#Module, config: UpdateConfiguration,
|
||||
transform: UpdateReport => UpdateReport, skip: Boolean, force: Boolean, depsUpdated: Boolean, log: Logger): UpdateReport =
|
||||
cachedUpdate(cacheFile, label, module, config, transform, skip, force, depsUpdated,
|
||||
{ r =>
|
||||
import ShowLines._
|
||||
UnresolvedDependencyWarning(r, None).lines foreach { log.warn(_) }
|
||||
}, log)
|
||||
def cachedUpdate(cacheFile: File, label: String, module: IvySbt#Module, config: UpdateConfiguration,
|
||||
transform: UpdateReport => UpdateReport, skip: Boolean, force: Boolean, depsUpdated: Boolean,
|
||||
unresolvedHandler: ResolveException => Unit, log: Logger): UpdateReport =
|
||||
{
|
||||
implicit val updateCache = updateIC
|
||||
type In = IvyConfiguration :+: ModuleSettings :+: UpdateConfiguration :+: HNil
|
||||
def work = (_: In) match {
|
||||
case conf :+: settings :+: config :+: HNil =>
|
||||
log.info("Updating " + label + "...")
|
||||
val r = IvyActions.update(module, config, log)
|
||||
val r = IvyActions.updateEither(module, config, log) match {
|
||||
case Right(ur) => ur
|
||||
case Left(re) =>
|
||||
unresolvedHandler(re)
|
||||
throw re
|
||||
}
|
||||
log.info("Done updating.")
|
||||
transform(r)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,81 @@
|
|||
package sbt
|
||||
|
||||
import java.io.File
|
||||
import collection.mutable
|
||||
import Def.Setting
|
||||
import Scope.GlobalScope
|
||||
|
||||
private[sbt] final class UnresolvedDependencyWarning(
|
||||
val withExtra: Seq[ModuleID],
|
||||
val failedPaths: Seq[Seq[(ModuleID, Option[String])]])
|
||||
|
||||
private[sbt] object UnresolvedDependencyWarning {
|
||||
// This method used to live under IvyActions.scala, but it's moved here because it's now aware of State.
|
||||
private[sbt] def apply(err: ResolveException, stateOpt: Option[State]): UnresolvedDependencyWarning = {
|
||||
// This is a mapping between modules and original position, in which the module was introduced.
|
||||
lazy val modulePositions: Seq[(ModuleID, SourcePosition)] =
|
||||
try {
|
||||
stateOpt map { state =>
|
||||
val extracted = (Project extract state)
|
||||
val sk = (Keys.libraryDependencies in (GlobalScope in extracted.currentRef)).scopedKey
|
||||
val empty = extracted.structure.data set (sk.scope, sk.key, Nil)
|
||||
val settings = extracted.structure.settings filter { s: Setting[_] =>
|
||||
(s.key.key == Keys.libraryDependencies.key) &&
|
||||
(s.key.scope.project == Select(extracted.currentRef))
|
||||
}
|
||||
settings flatMap {
|
||||
case s: Setting[Seq[ModuleID]] @unchecked =>
|
||||
s.init.evaluate(empty) map { _ -> s.pos }
|
||||
}
|
||||
} getOrElse Seq()
|
||||
} catch {
|
||||
case _: Throwable => Seq()
|
||||
}
|
||||
def modulePosition(m0: ModuleID): Option[String] =
|
||||
modulePositions.find {
|
||||
case (m, p) =>
|
||||
(m.organization == m0.organization) &&
|
||||
(m0.name startsWith m.name) &&
|
||||
(m.revision == m0.revision)
|
||||
} flatMap {
|
||||
case (m, LinePosition(path, start)) =>
|
||||
Some(s" ($path#L$start)")
|
||||
case (m, RangePosition(path, LineRange(start, end))) =>
|
||||
Some(s" ($path#L$start-$end)")
|
||||
case _ => None
|
||||
}
|
||||
val withExtra = err.failed.filter(!_.extraDependencyAttributes.isEmpty)
|
||||
val failedPaths = err.failed map { x: ModuleID =>
|
||||
err.failedPaths(x).toList.reverse map { id =>
|
||||
(id, modulePosition(id))
|
||||
}
|
||||
}
|
||||
UnresolvedDependencyWarning(withExtra, failedPaths)
|
||||
}
|
||||
|
||||
def apply(withExtra: Seq[ModuleID],
|
||||
failedPaths: Seq[Seq[(ModuleID, Option[String])]]): UnresolvedDependencyWarning =
|
||||
new UnresolvedDependencyWarning(withExtra, failedPaths)
|
||||
|
||||
implicit val unresolvedDependencyWarningLines: ShowLines[UnresolvedDependencyWarning] = ShowLines { a =>
|
||||
val buffer = mutable.ListBuffer[String]()
|
||||
if (!a.withExtra.isEmpty) {
|
||||
buffer += "\n\tNote: Some unresolved dependencies have extra attributes. Check that these dependencies exist with the requested attributes."
|
||||
a.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 + head._2.getOrElse("")
|
||||
path.tail foreach {
|
||||
case (m, pos) =>
|
||||
buffer += "\t\t +- " + m.toString + pos.getOrElse("")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer.toList
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
[413]: https://github.com/sbt/sbt/issues/413
|
||||
[528]: https://github.com/sbt/sbt/issues/528
|
||||
[856]: https://github.com/sbt/sbt/issues/856
|
||||
[1036]: https://github.com/sbt/sbt/pull/1036
|
||||
[1059]: https://github.com/sbt/sbt/issues/1059
|
||||
|
|
@ -105,10 +106,10 @@ This is an approximation, but it should help you figure out where the problemati
|
|||
[warn] Note: Unresolved dependencies path:
|
||||
[warn] foundrylogic.vpp:vpp:2.2.1
|
||||
[warn] +- org.apache.cayenne:cayenne-tools:3.0.2
|
||||
[warn] +- org.apache.cayenne.plugins:maven-cayenne-plugin:3.0.2
|
||||
[warn] +- org.apache.cayenne.plugins:maven-cayenne-plugin:3.0.2 (/foo/some-test/build.sbt#L28)
|
||||
[warn] +- d:d_2.10:0.1-SNAPSHOT
|
||||
|
||||
[#1422][1422]/[#1447][1447] by [@eed3si9n][@eed3si9n]
|
||||
[#528][528]/[#1422][1422]/[#1447][1447] by [@eed3si9n][@eed3si9n]
|
||||
|
||||
### Eviction warnings
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue