diff --git a/plugin/src/main/scala-2.10/coursier/ResolutionError.scala b/plugin/src/main/scala-2.10/coursier/ResolutionError.scala new file mode 100644 index 000000000..7ec8f313f --- /dev/null +++ b/plugin/src/main/scala-2.10/coursier/ResolutionError.scala @@ -0,0 +1,111 @@ +package coursier + +import scala.collection.mutable.ArrayBuffer + +sealed abstract class ResolutionError extends Product with Serializable { + def cause: Option[Throwable] = this match { + case ResolutionError.MaximumIterationsReached => None + case ResolutionError.UnknownException(ex) => Some(ex) + case ResolutionError.UnknownDownloadException(ex) => Some(ex) + case _: ResolutionError.Conflicts => None + case _: ResolutionError.MetadataDownloadErrors => None + case _: ResolutionError.DownloadErrors => None + } + + def message: String = this match { + case ResolutionError.MaximumIterationsReached => + "Maximum number of iteration of dependency resolution reached" + case ResolutionError.UnknownException(ex) => + "Exception during resolution" + case ResolutionError.UnknownDownloadException(ex) => + "Error while downloading / verifying artifacts" + case ResolutionError.Conflicts(description) => + description + + case err: ResolutionError.MetadataDownloadErrors => + err.description() + + case err: ResolutionError.DownloadErrors => + err.description(verbose = true) + } + + def exception(): ResolutionException = + new ResolutionException(this) + + def throwException(): Nothing = + throw exception() +} + +object ResolutionError { + + case object MaximumIterationsReached extends ResolutionError + case class UnknownException(ex: Throwable) extends ResolutionError + case class UnknownDownloadException(ex: Throwable) extends ResolutionError + case class Conflicts(description: String) extends ResolutionError + + case class MetadataDownloadErrors(errors: Seq[(Dependency, Seq[String])]) extends ResolutionError { + def description(): String = { + + def grouped(errs: Seq[String]) = + errs + .map { s => + val idx = s.indexOf(": ") + if (idx >= 0) + (s.take(idx), s.drop(idx + ": ".length)) + else + ("", s) + } + .groupBy(_._1) + .mapValues(_.map(_._2)) + .toVector + .sortBy(_._1) + + val lines = new ArrayBuffer[String] + + lines += s"Encountered ${errors.length} error(s) in dependency resolution:" + + for ((dep, errs) <- errors) { + lines += s" ${dep.module}:${dep.version}:" + + for ((type0, errs0) <- grouped(errs)) + if (type0.isEmpty) + for (err <- errs0) + lines += s" $err" + else + errs0 match { + case Seq(err) => + lines += s" $type0: $err" + case _ => + lines += s" $type0:" + for (err <- errs0) + lines += s" $err" + } + } + + lines.mkString("\n") + } + } + + case class DownloadErrors(errors: Seq[FileError]) extends ResolutionError { + + def description(verbose: Boolean): String = { + + val groupedArtifactErrors = errors + .groupBy(_.`type`) + .mapValues(_.map(_.message).sorted) + .toVector + .sortBy(_._1) + + val b = new StringBuilder + + for ((type0, errors) <- groupedArtifactErrors) { + b ++= s"${errors.size} $type0" + if (verbose) + for (err <- errors) + b ++= " " + err + } + + b.result() + } + } +} diff --git a/plugin/src/main/scala-2.10/coursier/ResolutionException.scala b/plugin/src/main/scala-2.10/coursier/ResolutionException.scala new file mode 100644 index 000000000..9f8925a91 --- /dev/null +++ b/plugin/src/main/scala-2.10/coursier/ResolutionException.scala @@ -0,0 +1,10 @@ +package coursier + +final class ResolutionException( + val error: ResolutionError +) extends Exception( + error.message, + error.cause.orNull, + true, + false // don't keep stack trace around (improves readability from the SBT console) +) diff --git a/plugin/src/main/scala-2.10/coursier/Tasks.scala b/plugin/src/main/scala-2.10/coursier/Tasks.scala index 1798afce1..cad818916 100644 --- a/plugin/src/main/scala-2.10/coursier/Tasks.scala +++ b/plugin/src/main/scala-2.10/coursier/Tasks.scala @@ -440,8 +440,10 @@ object Tasks { } } + val internalRepositories = Seq(globalPluginsRepo, interProjectRepo) + val repositories = - Seq(globalPluginsRepo, interProjectRepo) ++ + internalRepositories ++ sourceRepositories0 ++ resolvers.flatMap { resolver => FromSbt.repository( @@ -507,37 +509,42 @@ object Tasks { .process .run(fetch, maxIterations) .attemptRun - .leftMap(ex => throw new Exception("Exception during resolution", ex)) + .leftMap(ex => + ResolutionError.UnknownException(ex) + .throwException() + ) .merge resLogger.stop() if (!res.isDone) - throw new Exception("Maximum number of iteration of dependency resolution reached") + ResolutionError.MaximumIterationsReached + .throwException() if (res.conflicts.nonEmpty) { val projCache = res.projectCache.mapValues { case (_, p) => p } - log.error( - s"${res.conflicts.size} conflict(s):\n" + - " " + Print.dependenciesUnknownConfigs(res.conflicts.toVector, projCache) - ) - throw new Exception("Conflict(s) in dependency resolution") + + ResolutionError.Conflicts( + "Conflict(s) in dependency resolution:\n " + + Print.dependenciesUnknownConfigs(res.conflicts.toVector, projCache) + ).throwException() } if (res.errors.nonEmpty) { - log.error( - s"\n${res.errors.size} error(s):\n" + + val internalRepositoriesLen = internalRepositories.length + val errors = + if (repositories.length > internalRepositoriesLen) + // drop internal repository errors res.errors.map { case (dep, errs) => - s" ${dep.module}:${dep.version}:\n" + - errs - .map(" " + _.replace("\n", " \n")) - .mkString("\n") - }.mkString("\n") - ) + dep -> errs.drop(internalRepositoriesLen) + } + else + res.errors - throw new Exception(s"Encountered ${res.errors.length} error(s) in dependency resolution") + ResolutionError.MetadataDownloadErrors(errors) + .throwException() } if (verbosityLevel >= 0) @@ -709,7 +716,8 @@ object Tasks { val artifactFilesOrErrors = Task.gatherUnordered(artifactFileOrErrorTasks).attemptRun match { case -\/(ex) => - throw new Exception("Error while downloading / verifying artifacts", ex) + ResolutionError.UnknownDownloadException(ex) + .throwException() case \/-(l) => l.toMap } @@ -733,31 +741,12 @@ object Tasks { } if (artifactErrors.nonEmpty) { - val groupedArtifactErrors = artifactErrors - .groupBy(_.`type`) - .mapValues(_.map(_.message).sorted) - .toVector - .sortBy(_._1) + val error = ResolutionError.DownloadErrors(artifactErrors) - for ((type0, errors) <- groupedArtifactErrors) { - def msg = s"${errors.size} $type0" - if (ignoreArtifactErrors) - log.warn(msg) - else - log.error(msg) - - if (!ignoreArtifactErrors || verbosityLevel >= 1) { - if (ignoreArtifactErrors) - for (err <- errors) - log.warn(" " + err) - else - for (err <- errors) - log.error(" " + err) - } - } - - if (!ignoreArtifactErrors) - throw new Exception(s"Encountered ${artifactErrors.length} errors (see above messages)") + if (ignoreArtifactErrors) + log.warn(error.description(verbosityLevel >= 1)) + else + error.throwException() } // can be non empty only if ignoreArtifactErrors is true