Merge pull request #217 from alexarchambault/topic/update-changing

Fix in update changing mode, add benchmark option, use SBT logging, ...
This commit is contained in:
Alexandre Archambault 2016-04-06 22:28:49 +02:00
commit a21ef67c8b
16 changed files with 338 additions and 131 deletions

View File

@ -146,6 +146,11 @@ lazy val core = crossProject
import com.typesafe.tools.mima.core.ProblemFilters._
Seq(
// Since 1.0.0-M11
// Extra parameter with default value added, problem for forward compatibility only
ProblemFilters.exclude[MissingMethodProblem]("coursier.core.ResolutionProcess.next"),
// method made final (for - non critical - tail recursion)
ProblemFilters.exclude[FinalMethodProblem]("coursier.core.ResolutionProcess.next"),
// Since 1.0.0-M10
ProblemFilters.exclude[IncompatibleResultTypeProblem]("coursier.core.Resolution.withParentConfigurations"),
// New singleton object, problem for forward compatibility only
@ -221,6 +226,10 @@ lazy val cache = project
import com.typesafe.tools.mima.core.ProblemFilters._
Seq(
// Since 1.0.0-M11
// Add constructor parameter on FileError - shouldn't be built by users anyway
ProblemFilters.exclude[MissingMethodProblem]("coursier.FileError.this"),
ProblemFilters.exclude[MissingMethodProblem]("coursier.FileError#Recoverable.this"),
// Since 1.0.0-M10
// methods that should have been private anyway
ProblemFilters.exclude[MissingMethodProblem]("coursier.TermDisplay.update"),

View File

@ -43,8 +43,8 @@ object Cache {
}
}
private def withLocal(artifact: Artifact, cache: File): Artifact = {
def local(url: String) =
private def localFile(url: String, cache: File): File = {
val path =
if (url.startsWith("file:///"))
url.stripPrefix("file://")
else if (url.startsWith("file:/"))
@ -68,19 +68,7 @@ object Cache {
throw new Exception(s"No protocol found in URL $url")
}
if (artifact.extra.contains("local"))
artifact
else
artifact.copy(extra = artifact.extra + ("local" ->
artifact.copy(
url = local(artifact.url),
checksumUrls = artifact.checksumUrls
.mapValues(local)
.toVector
.toMap,
extra = Map.empty
)
))
new File(path)
}
private def readFullyTo(
@ -296,30 +284,15 @@ object Cache {
implicit val pool0 = pool
val artifact0 = withLocal(artifact, cache)
.extra
.getOrElse("local", artifact)
// Reference file - if it exists, and we get not found errors on some URLs, we assume
// we can keep track of these missing, and not try to get them again later.
val referenceFileOpt = {
val referenceOpt = artifact.extra.get("metadata").map(withLocal(_, cache))
val referenceOpt0 = referenceOpt.map(a => a.extra.getOrElse("local", a))
referenceOpt0.map(a => new File(a.url))
}
val referenceFileOpt = artifact
.extra
.get("metadata")
.map(a => localFile(a.url, cache))
def referenceFileExists: Boolean = referenceFileOpt.exists(_.exists())
val pairs =
Seq(artifact0.url -> artifact.url) ++ {
checksums
.intersect(artifact0.checksumUrls.keySet)
.intersect(artifact.checksumUrls.keySet)
.toSeq
.map(sumType => artifact0.checksumUrls(sumType) -> artifact.checksumUrls(sumType))
}
def urlConn(url0: String) = {
val conn = url(url0).openConnection() // FIXME Should this be closed?
// Dummy user-agent instead of the default "Java/...",
@ -527,7 +500,7 @@ object Cache {
}
cachePolicy match {
case CachePolicy.FetchMissing | CachePolicy.LocalOnly =>
case CachePolicy.FetchMissing | CachePolicy.LocalOnly | CachePolicy.LocalUpdate | CachePolicy.LocalUpdateChanging =>
validErrFileExists.flatMap { exists =>
if (exists)
EitherT(Task.now(FileError.NotFound(url, Some(true)).left[Unit]))
@ -540,7 +513,7 @@ object Cache {
}
}
def checkFileExists(file: File, url: String): EitherT[Task, FileError, Unit] =
def checkFileExists(file: File, url: String, log: Boolean = true): EitherT[Task, FileError, Unit] =
EitherT {
Task {
if (file.exists()) {
@ -551,9 +524,17 @@ object Cache {
}
}
val urls =
artifact.url +: {
checksums
.intersect(artifact.checksumUrls.keySet)
.toSeq
.map(artifact.checksumUrls)
}
val tasks =
for ((f, url) <- pairs) yield {
val file = new File(f)
for (url <- urls) yield {
val file = localFile(url, cache)
val res =
if (url.startsWith("file:/")) {
@ -576,6 +557,8 @@ object Cache {
val cachePolicy0 = cachePolicy match {
case CachePolicy.UpdateChanging if !artifact.changing =>
CachePolicy.FetchMissing
case CachePolicy.LocalUpdateChanging if !artifact.changing =>
CachePolicy.LocalOnly
case other =>
other
}
@ -583,6 +566,10 @@ object Cache {
cachePolicy0 match {
case CachePolicy.LocalOnly =>
checkFileExists(file, url)
case CachePolicy.LocalUpdateChanging | CachePolicy.LocalUpdate =>
checkFileExists(file, url, log = false).flatMap { _ =>
update
}
case CachePolicy.UpdateChanging | CachePolicy.Update =>
update
case CachePolicy.FetchMissing =>
@ -631,27 +618,26 @@ object Cache {
implicit val pool0 = pool
val artifact0 = withLocal(artifact, cache)
.extra
.getOrElse("local", artifact)
val localFile0 = localFile(artifact.url, cache)
EitherT {
artifact0.checksumUrls.get(sumType) match {
case Some(sumFile) =>
artifact.checksumUrls.get(sumType) match {
case Some(sumUrl) =>
val sumFile = localFile(sumUrl, cache)
Task {
val sumOpt = parseChecksum(
new String(NioFiles.readAllBytes(new File(sumFile).toPath), "UTF-8")
new String(NioFiles.readAllBytes(sumFile.toPath), "UTF-8")
)
sumOpt match {
case None =>
FileError.ChecksumFormatError(sumType, sumFile).left
FileError.ChecksumFormatError(sumType, sumFile.getPath).left
case Some(sum) =>
val md = MessageDigest.getInstance(sumType)
val f = new File(artifact0.url)
val is = new FileInputStream(f)
val is = new FileInputStream(localFile0)
try withContent(is, md.update(_, 0, _))
finally is.close()
@ -665,14 +651,14 @@ object Cache {
sumType,
calculatedSum.toString(16),
sum.toString(16),
artifact0.url,
sumFile
localFile0.getPath,
sumFile.getPath
).left
}
}
case None =>
Task.now(FileError.ChecksumNotFound(sumType, artifact0.url).left)
Task.now(FileError.ChecksumNotFound(sumType, localFile0.getPath).left)
}
}
}

View File

@ -44,6 +44,10 @@ object CacheParse {
s.split(',').toVector.traverseU {
case "offline" =>
Seq(CachePolicy.LocalOnly).successNel
case "update-local-changing" =>
Seq(CachePolicy.LocalUpdateChanging).successNel
case "update-local" =>
Seq(CachePolicy.LocalUpdate).successNel
case "update-changing" =>
Seq(CachePolicy.UpdateChanging).successNel
case "update" =>

View File

@ -4,6 +4,8 @@ sealed abstract class CachePolicy extends Product with Serializable
object CachePolicy {
case object LocalOnly extends CachePolicy
case object LocalUpdateChanging extends CachePolicy
case object LocalUpdate extends CachePolicy
case object UpdateChanging extends CachePolicy
case object Update extends CachePolicy
case object FetchMissing extends CachePolicy

View File

@ -2,26 +2,41 @@ package coursier
import java.io.File
sealed abstract class FileError(val message: String) extends Product with Serializable
sealed abstract class FileError(
val `type`: String,
val message: String
) extends Product with Serializable
object FileError {
final case class DownloadError(reason: String) extends FileError(s"Download error: $reason")
final case class DownloadError(reason: String) extends FileError(
"download error",
reason
)
final case class NotFound(
file: String,
permanent: Option[Boolean] = None
) extends FileError(s"Not found: $file")
) extends FileError(
"not found",
file
)
final case class ChecksumNotFound(
sumType: String,
file: String
) extends FileError(s"$sumType checksum not found: $file")
) extends FileError(
"checksum not found",
file
)
final case class ChecksumFormatError(
sumType: String,
file: String
) extends FileError(s"Unrecognized $sumType checksum format in $file")
) extends FileError(
"checksum format error",
file
)
final case class WrongChecksum(
sumType: String,
@ -29,10 +44,22 @@ object FileError {
expected: String,
file: String,
sumFile: String
) extends FileError(s"$sumType checksum validation failed: $file")
) extends FileError(
"wrong checksum",
file
)
sealed abstract class Recoverable(message: String) extends FileError(message)
final case class Locked(file: File) extends Recoverable(s"Locked: $file")
final case class ConcurrentDownload(url: String) extends Recoverable(s"Concurrent download: $url")
sealed abstract class Recoverable(
`type`: String,
message: String
) extends FileError(`type`, message)
final case class Locked(file: File) extends Recoverable(
"locked",
file.toString
)
final case class ConcurrentDownload(url: String) extends Recoverable(
"concurrent download",
url
)
}

View File

@ -56,10 +56,17 @@ object Terminal {
}
object TermDisplay {
private def defaultFallbackMode: Boolean = {
val env = sys.env.get("COURSIER_NO_TERM").nonEmpty
def defaultFallbackMode: Boolean = {
val env0 = sys.env.get("COURSIER_PROGRESS").map(_.toLowerCase).collect {
case "true" | "enable" | "1" => true
case "false" | "disable" | "0" => false
}
def compatibilityEnv = sys.env.get("COURSIER_NO_TERM").nonEmpty
def nonInteractive = System.console() == null
val env = env0.getOrElse(compatibilityEnv)
env || nonInteractive
}
@ -480,7 +487,7 @@ class TermDisplay(
}
}
updateThread.removeEntry(url, !newUpdate, s"Checked $url") {
updateThread.removeEntry(url, !newUpdate, s"Checked $url\n") {
case info: CheckUpdateInfo =>
info.copy(remoteTimeOpt = remoteTimeOpt, isDone = true)
case _ =>

View File

@ -9,6 +9,7 @@ import java.util.concurrent.Executors
import coursier.ivy.IvyRepository
import coursier.util.{Print, Parse}
import scala.annotation.tailrec
import scalaz.{Failure, Success, \/-, -\/}
import scalaz.concurrent.{ Task, Strategy }
@ -201,9 +202,15 @@ class Helper(
filter = Some(dep => keepOptional || !dep.optional)
)
val loggerFallbackMode =
!progress && TermDisplay.defaultFallbackMode
val logger =
if (verbosityLevel >= 0)
Some(new TermDisplay(new OutputStreamWriter(System.err)))
Some(new TermDisplay(
new OutputStreamWriter(System.err),
fallbackMode = loggerFallbackMode
))
else
None
@ -238,10 +245,108 @@ class Helper(
logger.foreach(_.init())
val res = startRes
.process
.run(fetch0, maxIterations)
.run
val res =
if (benchmark > 0) {
class Counter(var value: Int = 0) {
def add(value: Int): Unit = {
this.value += value
}
}
def timed[T](name: String, counter: Counter, f: Task[T]): Task[T] =
Task(System.currentTimeMillis()).flatMap { start =>
f.map { t =>
val end = System.currentTimeMillis()
Console.err.println(s"$name: ${end - start} ms")
counter.add((end - start).toInt)
t
}
}
def helper(proc: ResolutionProcess, counter: Counter, iteration: Int): Task[Resolution] =
if (iteration >= maxIterations)
Task.now(proc.current)
else
proc match {
case _: core.Done =>
Task.now(proc.current)
case _ =>
val iterationType = proc match {
case _: core.Missing => "IO"
case _: core.Continue => "calculations"
case _ => ???
}
timed(
s"Iteration ${iteration + 1} ($iterationType)",
counter,
proc.next(fetch0, fastForward = false)).flatMap(helper(_, counter, iteration + 1)
)
}
def res = {
val iterationCounter = new Counter
val resolutionCounter = new Counter
val res0 = timed(
"Resolution",
resolutionCounter,
helper(
startRes.process,
iterationCounter,
0
)
).run
Console.err.println(s"Overhead: ${resolutionCounter.value - iterationCounter.value} ms")
res0
}
@tailrec
def result(warmUp: Int): Resolution =
if (warmUp >= benchmark) {
Console.err.println("Benchmark resolution")
res
} else {
Console.err.println(s"Warm-up ${warmUp + 1} / $benchmark")
res
result(warmUp + 1)
}
result(0)
} else if (benchmark < 0) {
def res(index: Int) = {
val start = System.currentTimeMillis()
val res0 = startRes
.process
.run(fetch0, maxIterations)
.run
val end = System.currentTimeMillis()
Console.err.println(s"Resolution ${index + 1} / ${-benchmark}: ${end - start} ms")
res0
}
@tailrec
def result(warmUp: Int): Resolution =
if (warmUp >= -benchmark) {
Console.err.println("Benchmark resolution")
res(warmUp)
} else {
Console.err.println(s"Warm-up ${warmUp + 1} / ${-benchmark}")
res(warmUp)
result(warmUp + 1)
}
result(0)
} else
startRes
.process
.run(fetch0, maxIterations)
.run
logger.foreach(_.stop())
@ -299,13 +404,19 @@ class Helper(
): Seq[Artifact] = {
if (subset == null && verbosityLevel >= 1) {
val msg = cachePolicies match {
case Seq(CachePolicy.LocalOnly) =>
" Checking artifacts"
case _ =>
" Fetching artifacts"
def isLocal(p: CachePolicy) = p match {
case CachePolicy.LocalOnly => true
case CachePolicy.LocalUpdate => true
case CachePolicy.LocalUpdateChanging => true
case _ => false
}
val msg =
if (cachePolicies.forall(isLocal))
" Checking artifacts"
else
" Fetching artifacts"
errPrintln(msg)
}
@ -333,7 +444,10 @@ class Helper(
val logger =
if (verbosityLevel >= 0)
Some(new TermDisplay(new OutputStreamWriter(System.err)))
Some(new TermDisplay(
new OutputStreamWriter(System.err),
fallbackMode = loggerFallbackMode
))
else
None

View File

@ -18,6 +18,9 @@ case class CommonOptions(
@Help("Increase verbosity (specify several times to increase more)")
@Short("v")
verbose: Int @@ Counter,
@Help("Force display of progress bars")
@Short("P")
progress: Boolean,
@Help("Maximum number of resolution iterations (specify a negative value for unlimited, default: 100)")
@Short("N")
maxIterations: Int = 100,
@ -54,6 +57,10 @@ case class CommonOptions(
@Help("Checksums")
@Value("checksum1,checksum2,... - end with none to allow for no checksum validation if none are available")
checksum: List[String],
@Help("Print the duration of each iteration of the resolution")
@Short("B")
@Value("Number of warm-up resolutions - if negative, doesn't print per iteration benchmark (less overhead)")
benchmark: Int,
@Recurse
cacheOptions: CacheOptions
) {
@ -163,7 +170,7 @@ case class BootstrapOptions(
mainClass: String,
@Short("o")
output: String = "bootstrap",
@Short("D")
@Short("d")
downloadDir: String,
@Short("f")
force: Boolean,
@ -172,7 +179,7 @@ case class BootstrapOptions(
standalone: Boolean,
@Help("Set Java properties in the generated launcher.")
@Value("key=value")
@Short("P")
@Short("D")
property: List[String],
@Help("Set Java command-line options in the generated launcher.")
@Value("option")

View File

@ -33,8 +33,10 @@ sealed abstract class ResolutionProcess {
}
}
def next[F[_]](
fetch: Fetch.Metadata[F]
@tailrec
final def next[F[_]](
fetch: Fetch.Metadata[F],
fastForward: Boolean = true
)(implicit
F: Monad[F]
): F[ResolutionProcess] = {
@ -45,7 +47,10 @@ sealed abstract class ResolutionProcess {
case missing0 @ Missing(missing, _, _) =>
F.map(fetch(missing))(result => missing0.next(result))
case cont @ Continue(_, _) =>
cont.nextNoCont.next(fetch)
if (fastForward)
cont.nextNoCont.next(fetch, fastForward = fastForward)
else
F.point(cont.next)
}
}

View File

@ -219,7 +219,7 @@ case class MavenRepository(
}
F.bind(findVersioning(module, version, None, fetch).run) { eitherProj =>
if (eitherProj.isLeft)
if (eitherProj.isLeft && version.contains("-SNAPSHOT"))
F.map(withSnapshotVersioning.run)(eitherProj0 =>
if (eitherProj0.isLeft)
eitherProj

View File

@ -49,7 +49,7 @@ object Parse {
case Right(modVer) => values += modVer
}
(errors.toSeq, values.toSeq)
(errors, values)
}
/**
@ -77,7 +77,7 @@ object Parse {
.map((_, version))
case _ =>
Left(s"Malformed coordinates: $s")
Left(s"Malformed dependency: $s")
}
}
@ -107,7 +107,7 @@ object Parse {
.map((_, version, None))
case _ =>
Left(s"Malformed coordinates: $s")
Left(s"Malformed dependency: $s")
}
}

View File

@ -1,7 +1,5 @@
package coursier
import java.io.File
import sbt._
import sbt.Keys._
@ -43,7 +41,7 @@ object CoursierPlugin extends AutoPlugin {
coursierFallbackDependencies <<= Tasks.coursierFallbackDependenciesTask,
coursierCache := Cache.default,
update <<= Tasks.updateTask(withClassifiers = false),
updateClassifiers <<= Tasks.updateTask(withClassifiers = true),
updateClassifiers <<= Tasks.updateTask(withClassifiers = true, ignoreArtifactErrors = true),
updateSbtClassifiers in Defaults.TaskGlobal <<= Tasks.updateTask(withClassifiers = true, sbtClassifiers = true),
coursierProject <<= Tasks.coursierProjectTask,
coursierProjects <<= Tasks.coursierProjectsTask,

View File

@ -133,7 +133,11 @@ object FromSbt {
)
}
def repository(resolver: Resolver, ivyProperties: Map[String, String]): Option[Repository] =
def repository(
resolver: Resolver,
ivyProperties: Map[String, String],
log: sbt.Logger
): Option[Repository] =
resolver match {
case sbt.MavenRepository(_, root) =>
try {
@ -142,10 +146,10 @@ object FromSbt {
Some(MavenRepository(root0, sbtAttrStub = true))
} catch {
case e: MalformedURLException =>
Console.err.println(
"Warning: error parsing Maven repository base " +
log.warn(
"Error parsing Maven repository base " +
root +
Option(e.getMessage).map(" ("+_+")").mkString +
Option(e.getMessage).map(" (" + _ + ")").mkString +
", ignoring it"
)
@ -177,7 +181,7 @@ object FromSbt {
))
case other =>
Console.err.println(s"Warning: unrecognized repository ${other.name}, ignoring it")
log.warn(s"Unrecognized repository ${other.name}, ignoring it")
None
}

View File

@ -4,7 +4,11 @@ import scala.util.{Failure, Success, Try}
object Settings {
private val baseDefaultVerbosityLevel = 0
private lazy val baseDefaultVerbosityLevel =
if (System.console() == null) // non interactive mode
0
else
1
def defaultVerbosityLevel: Int = {

View File

@ -185,10 +185,11 @@ object Tasks {
Module("org.scala-lang", "scalap") -> scalaVersion
)
def updateTask(withClassifiers: Boolean, sbtClassifiers: Boolean = false) = Def.task {
// SBT logging should be better than that most of the time...
def errPrintln(s: String): Unit = scala.Console.err.println(s)
def updateTask(
withClassifiers: Boolean,
sbtClassifiers: Boolean = false,
ignoreArtifactErrors: Boolean = false
) = Def.task {
def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] =
map.groupBy { case (k, _) => k }.map {
@ -252,6 +253,8 @@ object Tasks {
val cachePolicies = coursierCachePolicies.value
val cache = coursierCache.value
val log = streams.value.log
val sv = scalaVersion.value // is this always defined? (e.g. for Java only projects?)
val sbv = scalaBinaryVersion.value
@ -267,7 +270,7 @@ object Tasks {
rule.configurations.nonEmpty ||
rule.crossVersion != sbt.CrossVersion.Disabled
) {
Console.err.println(s"Warning: unsupported exclusion rule $rule")
log.warn(s"Unsupported exclusion rule $rule")
anyNonSupportedExclusionRule = true
Nil
} else
@ -275,7 +278,7 @@ object Tasks {
}.toSet
if (anyNonSupportedExclusionRule)
Console.err.println(s"Only supported exclusion rule fields: organization, name")
log.warn("Only supported exclusion rule fields: organization, name")
val resolvers =
if (sbtClassifiers)
@ -312,9 +315,9 @@ object Tasks {
}
if (verbosityLevel >= 2) {
println("InterProjectRepository")
log.info("InterProjectRepository")
for (p <- projects)
println(s" ${p.module}:${p.version}")
log.info(s" ${p.module}:${p.version}")
}
val globalPluginsRepo = IvyRepository(
@ -335,7 +338,7 @@ object Tasks {
globalPluginsRepo,
interProjectRepo
) ++ resolvers.flatMap(
FromSbt.repository(_, ivyProperties)
FromSbt.repository(_, ivyProperties, log)
) ++ {
if (fallbackDependencies.isEmpty)
Nil
@ -376,7 +379,7 @@ object Tasks {
s"${dep.module}:${dep.version}:${dep.configuration}"
}.sorted.distinct
if (verbosityLevel >= 1) {
if (verbosityLevel >= 2) {
val repoReprs = repositories.map {
case r: IvyRepository =>
s"ivy:${r.pattern}"
@ -389,14 +392,17 @@ object Tasks {
r.toString
}
errPrintln(s"Repositories:\n${repoReprs.map(" "+_).mkString("\n")}")
log.info(
"Repositories:\n" +
repoReprs.map(" " + _).mkString("\n")
)
}
if (verbosityLevel >= 0)
errPrintln(s"Resolving ${currentProject.module.organization}:${currentProject.module.name}:${currentProject.version}")
if (verbosityLevel >= 1)
log.info(s"Resolving ${currentProject.module.organization}:${currentProject.module.name}:${currentProject.version}")
if (verbosityLevel >= 2)
for (depRepr <- depsRepr(currentProject.dependencies))
errPrintln(s" $depRepr")
log.info(s" $depRepr")
resLogger.init()
@ -404,26 +410,36 @@ object Tasks {
.process
.run(fetch, maxIterations)
.attemptRun
.leftMap(ex => throw new Exception(s"Exception during resolution", ex))
.leftMap(ex => throw new Exception("Exception during resolution", ex))
.merge
resLogger.stop()
if (!res.isDone)
throw new Exception(s"Maximum number of iteration of dependency resolution reached")
throw new Exception("Maximum number of iteration of dependency resolution reached")
if (res.conflicts.nonEmpty) {
val projCache = res.projectCache.mapValues { case (_, p) => p }
println(s"${res.conflicts.size} conflict(s):\n ${Print.dependenciesUnknownConfigs(res.conflicts.toVector, projCache)}")
throw new Exception(s"Conflict(s) in dependency resolution")
log.error(
s"${res.conflicts.size} conflict(s):\n" +
" " + Print.dependenciesUnknownConfigs(res.conflicts.toVector, projCache)
)
throw new Exception("Conflict(s) in dependency resolution")
}
if (res.errors.nonEmpty) {
println(s"\n${res.errors.size} error(s):")
for ((dep, errs) <- res.errors) {
println(s" ${dep.module}:${dep.version}:\n${errs.map(" " + _.replace("\n", " \n")).mkString("\n")}")
}
log.error(
s"\n${res.errors.size} error(s):\n" +
res.errors.map {
case (dep, errs) =>
s" ${dep.module}:${dep.version}:\n" +
errs
.map(" " + _.replace("\n", " \n"))
.mkString("\n")
}.mkString("\n")
)
throw new Exception(s"Encountered ${res.errors.length} error(s) in dependency resolution")
}
@ -454,8 +470,8 @@ object Tasks {
}
if (verbosityLevel >= 0)
errPrintln("Resolution done")
if (verbosityLevel >= 1) {
log.info("Resolution done")
if (verbosityLevel >= 2) {
val finalDeps = Config.dependenciesWithConfig(
res,
depsByConfig.map { case (k, l) => k -> l.toSet },
@ -464,7 +480,7 @@ object Tasks {
val projCache = res.projectCache.mapValues { case (_, p) => p }
val repr = Print.dependenciesUnknownConfigs(finalDeps.toVector, projCache)
println(repr.split('\n').map(" "+_).mkString("\n"))
log.info(repr.split('\n').map(" "+_).mkString("\n"))
}
val classifiers =
@ -504,13 +520,13 @@ object Tasks {
}
if (verbosityLevel >= 0)
errPrintln(s"Fetching artifacts")
log.info("Fetching artifacts")
artifactsLogger.init()
val artifactFilesOrErrors = Task.gatherUnordered(artifactFileOrErrorTasks).attemptRun match {
case -\/(ex) =>
throw new Exception(s"Error while downloading / verifying artifacts", ex)
throw new Exception("Error while downloading / verifying artifacts", ex)
case \/-(l) =>
l.toMap
}
@ -518,20 +534,44 @@ object Tasks {
artifactsLogger.stop()
if (verbosityLevel >= 0)
errPrintln(s"Fetching artifacts: done")
log.info("Fetching artifacts: done")
val artifactFiles = artifactFilesOrErrors.collect {
case (artifact, \/-(file)) =>
artifact -> file
}
val artifactErrors = artifactFilesOrErrors.toVector.collect {
case (_, -\/(err)) =>
err
}
if (artifactErrors.nonEmpty) {
val groupedArtifactErrors = artifactErrors
.groupBy(_.`type`)
.mapValues(_.map(_.message).sorted)
.toVector
.sortBy(_._1)
for ((type0, errors) <- groupedArtifactErrors) {
log.error(s"${errors.size} $type0")
if (!ignoreArtifactErrors || verbosityLevel >= 1)
for (err <- errors)
log.error(" " + err)
}
if (!ignoreArtifactErrors)
throw new Exception(s"Encountered ${artifactErrors.length} errors (see above messages)")
}
def artifactFileOpt(artifact: Artifact) = {
val fileOrError = artifactFilesOrErrors.getOrElse(artifact, -\/("Not downloaded"))
val res = artifactFiles.get(artifact)
fileOrError match {
case \/-(file) =>
if (file.toString.contains("file:/"))
throw new Exception(s"Wrong path: $file")
Some(file)
case -\/(err) =>
errPrintln(s"${artifact.url}: $err")
None
}
if (res.isEmpty)
log.error(s"${artifact.url} not downloaded (should not happen)")
res
}
writeIvyFiles()

View File

@ -1,9 +1,9 @@
addSbtPlugin("org.xerial.sbt" % "sbt-pack" % "0.6.8")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.7")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.8")
addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.0.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.1.0")
addSbtPlugin("org.tpolecat" % "tut-plugin" % "0.4.0")
addSbtPlugin("com.github.alexarchambault" % "coursier-sbt-plugin" % "1.0.0-M8")
addSbtPlugin("com.github.alexarchambault" % "coursier-sbt-plugin" % "1.0.0-M10")
addSbtPlugin("com.typesafe.sbt" % "sbt-proguard" % "0.2.2")
addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "0.1.8")
libraryDependencies += "org.scala-sbt" % "scripted-plugin" % sbtVersion.value