diff --git a/cli/src/main/scala/coursier/cli/Coursier.scala b/cli/src/main/scala/coursier/cli/Coursier.scala index 1fc1357cc..b76a62e21 100644 --- a/cli/src/main/scala/coursier/cli/Coursier.scala +++ b/cli/src/main/scala/coursier/cli/Coursier.scala @@ -13,11 +13,15 @@ case class Coursier( scope: List[String], keepOptional: Boolean, fetch: Boolean, + @ExtraName("J") default: Boolean, + @ExtraName("S") sources: Boolean, + @ExtraName("D") javadoc: Boolean, @ExtraName("P") @ExtraName("cp") classpath: Boolean, @ExtraName("c") offline: Boolean, @ExtraName("v") verbose: List[Unit], @ExtraName("N") maxIterations: Int = 100, - @ExtraName("r") repository: List[String] + @ExtraName("r") repository: List[String], + @ExtraName("n") parallel: Option[Int] ) extends App { val verbose0 = verbose.length @@ -165,6 +169,8 @@ case class Coursier( print.flatMap(_ => fetchQuiet(modVers)) } + println(s"Resolving\n" + moduleVersions.map{case (mod, ver) => s" $mod:$ver"}.mkString("\n")) + val res = startRes .process .run(fetch0, maxIterations) @@ -205,11 +211,29 @@ case class Coursier( if (fetch || classpath) { println("") - val artifacts = res.artifacts + val artifacts0 = res.artifacts + val default0 = default || (!sources && !javadoc) + val artifacts = artifacts0 + .flatMap{ artifact => + var l = List.empty[Artifact] + if (sources) + l = artifact.extra.get("sources").toList ::: l + if (javadoc) + l = artifact.extra.get("javadoc").toList ::: l + if (default0) + l = artifact :: l - val files = cache - .files() - .copy(logger = Some(logger)) + l + } + + val files = { + var files0 = cache + .files() + .copy(logger = Some(logger)) + for (n <- parallel) + files0 = files0.copy(concurrentDownloadCount = n) + files0 + } val tasks = artifacts.map(artifact => files.file(artifact, cachePolicy).run.map(artifact.->)) val task = Task.gatherUnordered(tasks) @@ -225,18 +249,11 @@ case class Coursier( } } - if (classpath) - Console.out.println( - files0 - .map(_.toString) - .mkString(File.pathSeparator) - ) - else - println( - files0 - .map(_.toString) - .mkString("\n") - ) + Console.out.println( + files0 + .map(_.toString) + .mkString(if (classpath) File.pathSeparator else "\n") + ) } } diff --git a/files/src/main/scala/coursier/Files.scala b/files/src/main/scala/coursier/Files.scala index 46b337a9b..8a6613c72 100644 --- a/files/src/main/scala/coursier/Files.scala +++ b/files/src/main/scala/coursier/Files.scala @@ -1,22 +1,29 @@ package coursier import java.net.{ URI, URL } +import java.util.concurrent.{ Executors, ExecutorService } import scala.annotation.tailrec import scalaz.{ -\/, \/-, \/, EitherT } -import scalaz.concurrent.Task +import scalaz.concurrent.{ Task, Strategy } import java.io._ case class Files( cache: Seq[(String, File)], tmp: () => File, - logger: Option[Files.Logger] = None + logger: Option[Files.Logger] = None, + concurrentDownloadCount: Int = Files.defaultConcurrentDownloadCount ) { + lazy val defaultPool = + Executors.newFixedThreadPool(concurrentDownloadCount, Strategy.DefaultDaemonThreadFactory) + def file( artifact: Artifact, cachePolicy: CachePolicy + )(implicit + pool: ExecutorService = defaultPool ): EitherT[Task, String, File] = { if (artifact.url.startsWith("file:///")) { @@ -94,6 +101,8 @@ case class Files( } object Files { + + val defaultConcurrentDownloadCount = 6 // FIXME This kind of side-effecting API is lame, we should aim at a more functional one. trait Logger {