diff --git a/cli/src/main/scala/coursier/cli/Coursier.scala b/cli/src/main/scala/coursier/cli/Coursier.scala index dbd8ec866..b1ea1710d 100644 --- a/cli/src/main/scala/coursier/cli/Coursier.scala +++ b/cli/src/main/scala/coursier/cli/Coursier.scala @@ -53,8 +53,6 @@ case class CacheOptions( cache: String = CacheOptions.default ) -@AppName("Coursier") -@ProgName("coursier") sealed trait CoursierCommand extends Command case class Fetch( @@ -278,7 +276,7 @@ case class Bootstrap( downloadDir: String, @ExtraName("f") force: Boolean, - @HelpMessage(s"Internal use - prepend base classpath options to arguments") + @HelpMessage(s"Internal use - prepend base classpath options to arguments (like -B jar1 -B jar2 etc.)") @ExtraName("b") prependClasspath: Boolean, @HelpMessage("Set environment variables in the generated launcher. No escaping is done. Value is simply put between quotes in the launcher preamble.") @@ -394,8 +392,7 @@ case class Bootstrap( } case class BaseCommand( - // FIXME Need a @NoHelp annotation in case-app to hide an option from help message - @HelpMessage("For internal use only - class path used to launch coursier") + @Hidden @ExtraName("B") baseCp: List[String] ) extends Command { @@ -413,5 +410,8 @@ case class BaseCommand( } object Coursier extends CommandAppOfWithBase[BaseCommand, CoursierCommand] { + override def appName = "Coursier" + override def progName = "coursier" + private[coursier] var baseCp = Seq.empty[String] } diff --git a/cli/src/main/scala/coursier/cli/Helper.scala b/cli/src/main/scala/coursier/cli/Helper.scala index ae28c8b83..c46df1c1e 100644 --- a/cli/src/main/scala/coursier/cli/Helper.scala +++ b/cli/src/main/scala/coursier/cli/Helper.scala @@ -1,6 +1,7 @@ package coursier.cli import java.io.File +import java.util.UUID import caseapp.CaseApp import coursier._ @@ -133,22 +134,50 @@ class Helper( } val repoMap = cache.map() + val repoByBase = repoMap.map { case (_, v @ (m, _)) => + m.root -> v + } - if (repositoryIds.exists(!repoMap.contains(_))) { - val notFound = repositoryIds - .filter(!repoMap.contains(_)) + val repositoryIdsOpt0 = repositoryIds.map { id => + repoMap.get(id) match { + case Some(v) => Right(v) + case None => + if (id.contains("://")) { + val root0 = if (id.endsWith("/")) id else id + "/" + Right( + repoByBase.getOrElse(root0, { + val id0 = UUID.randomUUID().toString + if (verbose0 >= 1) + Console.err.println(s"Addding repository $id0 ($root0)") + // FIXME This could be done more cleanly + cache.add(id0, root0, ivyLike = false) + cache.map().getOrElse(id0, + sys.error(s"Adding repository $id0 ($root0)") + ) + }) + ) + } else + Left(id) + } + } + + val notFoundRepositoryIds = repositoryIdsOpt0.collect { + case Left(id) => id + } + + if (notFoundRepositoryIds.nonEmpty) { errPrintln( - (if (notFound.lengthCompare(1) == 1) "Repository" else "Repositories") + + (if (notFoundRepositoryIds.lengthCompare(1) == 0) "Repository" else "Repositories") + " not found: " + - notFound.mkString(", ") + notFoundRepositoryIds.mkString(", ") ) sys.exit(1) } - val (repositories0, fileCaches) = repositoryIds - .map(repoMap) + val (repositories0, fileCaches) = repositoryIdsOpt0 + .collect { case Right(v) => v } .unzip val repositories = repositories0 @@ -192,7 +221,7 @@ class Helper( filter = Some(dep => keepOptional || !dep.optional) ) - val fetchQuiet = coursier.fetch(repositories) + val fetchQuiet = coursier.fetchLocalFirst(repositories) val fetch0 = if (verbose0 == 0) fetchQuiet else { diff --git a/core/shared/src/main/scala/coursier/package.scala b/core/shared/src/main/scala/coursier/package.scala index f3c3a58f6..bb58b4391 100644 --- a/core/shared/src/main/scala/coursier/package.scala +++ b/core/shared/src/main/scala/coursier/package.scala @@ -1,3 +1,4 @@ +import scalaz.{ -\/, \/- } import scalaz.concurrent.Task /** @@ -109,4 +110,27 @@ package object coursier { ) } + def fetchLocalFirst( + repositories: Seq[core.Repository] + )(implicit + cachePolicy: CachePolicy + ): ResolutionProcess.Fetch[Task] = { + + modVers => + Task.gatherUnordered( + modVers + .map {case (module, version) => + def attempt(cachePolicy: CachePolicy) = + Repository.find(repositories, module, version)(cachePolicy) + .run + .map((module, version) -> _) + + attempt(CachePolicy.LocalOnly).flatMap { + case v @ (_, \/-(_)) => Task.now(v) + case (_, -\/(_)) => attempt(cachePolicy) + } + } + ) + } + } diff --git a/files/src/main/scala/coursier/Files.scala b/files/src/main/scala/coursier/Files.scala index e64e0e690..23c74fd85 100644 --- a/files/src/main/scala/coursier/Files.scala +++ b/files/src/main/scala/coursier/Files.scala @@ -38,7 +38,7 @@ case class Files( cacheDir + "/" + url.stripPrefix(base) } - if (artifact.extra.contains("local") || isLocal) + if (artifact.extra.contains("local")) artifact else artifact.copy(extra = artifact.extra + ("local" -> @@ -141,14 +141,19 @@ case class Files( val tasks = - for ((f, url) <- pairs if url != ("file:" + f) && url != ("file://" + f)) yield { - val file = new File(f) - cachePolicy[FileError \/ File]( - _.isLeft )( - locally(file) )( - _ => remote(file, url) - ).map(e => (file, url) -> e.map(_ => ())) - } + for ((f, url) <- pairs) yield + if (url != ("file:" + f) && url != ("file://" + f)) { + assert(!f.startsWith("file:/"), s"Wrong file detection: $f, $url") + val file = new File(f) + cachePolicy[FileError \/ File]( + _.isLeft)( + locally(file))( + _ => remote(file, url) + ).map(e => (file, url) -> e.map(_ => ())) + } else { + val file = new File(f) + Task.now(((file, url), \/-(()))) + } Nondeterminism[Task].gather(tasks) }