mirror of https://github.com/sbt/sbt.git
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:
commit
a21ef67c8b
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" =>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 _ =>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue