diff --git a/cli/src/main/scala/coursier/cli/Coursier.scala b/cli/src/main/scala/coursier/cli/Coursier.scala index b1cbef11b..da091496d 100644 --- a/cli/src/main/scala/coursier/cli/Coursier.scala +++ b/cli/src/main/scala/coursier/cli/Coursier.scala @@ -12,8 +12,11 @@ import scalaz.concurrent.Task case class Coursier(scope: List[String], keepOptional: Boolean, fetch: Boolean, + verbose: List[Unit], @ExtraName("N") maxIterations: Int = 100) extends App { + val verbose0 = verbose.length + val scopes0 = if (scope.isEmpty) List(Scope.Compile, Scope.Runtime) else scope.map(Parse.scope) @@ -24,44 +27,48 @@ case class Coursier(scope: List[String], def fileRepr(f: File) = f.toString - val logger: MetadataFetchLogger with FilesLogger = new MetadataFetchLogger with FilesLogger { - def println(s: String) = Console.err.println(s) + val logger: Option[MetadataFetchLogger with FilesLogger] = + if (verbose0 <= 1) None + else Some( + new MetadataFetchLogger with FilesLogger { + def println(s: String) = Console.err.println(s) - def downloading(url: String) = - println(s"Downloading $url") - def downloaded(url: String, success: Boolean) = - println( - if (success) s"Downloaded $url" - else s"Failed to download $url" - ) - def readingFromCache(f: File) = { - println(s"Reading ${fileRepr(f)} from cache") - } - def puttingInCache(f: File) = - println(s"Writing ${fileRepr(f)} in cache") + def downloading(url: String) = + println(s"Downloading $url") + def downloaded(url: String, success: Boolean) = + println( + if (success) s"Downloaded $url" + else s"Failed to download $url" + ) + def readingFromCache(f: File) = { + println(s"Reading ${fileRepr(f)} from cache") + } + def puttingInCache(f: File) = + println(s"Writing ${fileRepr(f)} in cache") - def foundLocally(f: File) = - println(s"Found locally ${fileRepr(f)}") - def downloadingArtifact(url: String) = - println(s"Downloading $url") - def downloadedArtifact(url: String, success: Boolean) = - println( - if (success) s"Downloaded $url" - else s"Failed to download $url" - ) - } + def foundLocally(f: File) = + println(s"Found locally ${fileRepr(f)}") + def downloadingArtifact(url: String) = + println(s"Downloading $url") + def downloadedArtifact(url: String, success: Boolean) = + println( + if (success) s"Downloaded $url" + else s"Failed to download $url" + ) + } + ) val cachedMavenCentral = repository.mavenCentral.copy( fetchMetadata = repository.mavenCentral.fetchMetadata.copy( cache = Some(centralCacheDir), - logger = Some(logger) + logger = logger ) ) val repositories = Seq[Repository]( cachedMavenCentral, repository.ivy2Local.copy( fetchMetadata = repository.ivy2Local.fetchMetadata.copy( - logger = Some(logger) + logger = logger ) ) ) @@ -71,7 +78,7 @@ case class Coursier(scope: List[String], .partition(_.length == 3) if (splitDependencies.isEmpty) { - Console.err.println("Usage: coursier dependencies...") + CaseApp.printUsage[Coursier]() sys exit 1 } @@ -94,7 +101,21 @@ case class Coursier(scope: List[String], filter = Some(dep => (keepOptional || !dep.optional) && scopes(dep.scope)) ) - val res = startRes.last(fetchFrom(repositories), maxIterations).run + val fetchQuiet = fetchSeveralFrom(repositories) + val fetch0 = + if (verbose == 0) fetchQuiet + else { + modVers: Seq[(Module, String)] => + val print = Task{ + println(s"Getting ${modVers.length} project definition(s)") + } + + print.flatMap(_ => fetchQuiet(modVers)) + } + + val res = ResolutionProcess(startRes) + .run(fetch0, maxIterations) + .run if (!res.isDone) { Console.err.println(s"Maximum number of iteration reached!") @@ -140,7 +161,7 @@ case class Coursier(scope: List[String], cachedMavenCentral.fetchMetadata.root -> centralFilesCacheDir ), () => ???, - Some(logger) + logger ) val tasks = artifacts.map(files.file(_, cachePolicy).run) diff --git a/core-jvm/src/main/scala/coursier/core/package.scala b/core-jvm/src/main/scala/coursier/core/package.scala index 3d7851006..0f0a5e5b8 100644 --- a/core-jvm/src/main/scala/coursier/core/package.scala +++ b/core-jvm/src/main/scala/coursier/core/package.scala @@ -6,7 +6,7 @@ import scalaz.concurrent.Task package object core { def resolution(dependencies: Set[Dependency], - fetch: ModuleVersion => EitherT[Task, List[String], (Repository, Project)], + fetch: ModuleVersion => EitherT[Task, Seq[String], (Repository, Project)], filter: Option[Dependency => Boolean], profileActivation: Option[(String, Activation, Map[String, String]) => Boolean]): Stream[Resolution] = { diff --git a/core/src/main/scala/coursier/core/Resolution.scala b/core/src/main/scala/coursier/core/Resolution.scala index 1f771a258..1e844746f 100644 --- a/core/src/main/scala/coursier/core/Resolution.scala +++ b/core/src/main/scala/coursier/core/Resolution.scala @@ -24,10 +24,10 @@ object Resolution { */ def find(repositories: Seq[Repository], module: Module, - version: String): EitherT[Task, List[String], (Repository, Project)] = { + version: String): EitherT[Task, Seq[String], (Repository, Project)] = { val lookups = repositories.map(repo => repo -> repo.find(module, version).run) - val task = lookups.foldLeft(Task.now(-\/(Nil)): Task[List[String] \/ (Repository, Project)]) { + val task = lookups.foldLeft(Task.now(-\/(Nil)): Task[Seq[String] \/ (Repository, Project)]) { case (acc, (repo, t)) => acc.flatMap { case -\/(errors) => @@ -36,7 +36,7 @@ object Resolution { if (project.module == module) \/-((repo, project)) else -\/(s"Wrong module returned (expected: $module, got: ${project.module})") ) - .leftMap(error => error :: errors) + .leftMap(error => error +: errors) ) case res @ \/-(_) => @@ -456,16 +456,20 @@ case class Resolution(rootDependencies: Set[Dependency], * If no module info is missing, the next state of the resolution, which can be immediately calculated. * Else, the current resolution itself. */ - def nextIfNoMissing: Resolution = { + @tailrec + final def nextIfNoMissing: Resolution = { val missing = missingFromCache - if (missing.isEmpty) nextNoMissingUnsafe - else this + if (missing.isEmpty) { + val next0 = nextNoMissingUnsafe + if (next0 == this) this + else next0.nextIfNoMissing + } else this } /** * Do a new iteration, fetching the missing modules along the way. */ - def next(fetchModule: ModuleVersion => EitherT[Task, List[String], (Repository, Project)]): Task[Resolution] = { + def next(fetchModule: ModuleVersion => EitherT[Task, Seq[String], (Repository, Project)]): Task[Resolution] = { val missing = missingFromCache if (missing.isEmpty) Task.now(nextNoMissingUnsafe) else fetch(missing.toList, fetchModule).map(_.nextIfNoMissing) @@ -575,7 +579,7 @@ case class Resolution(rootDependencies: Set[Dependency], * Fetch `modules` with `fetchModules`, and add the resulting errors and projects to the cache. */ def fetch(modules: Seq[ModuleVersion], - fetchModule: ModuleVersion => EitherT[Task, List[String], (Repository, Project)]): Task[Resolution] = { + fetchModule: ModuleVersion => EitherT[Task, Seq[String], (Repository, Project)]): Task[Resolution] = { val lookups = modules.map(dep => fetchModule(dep).run.map(dep -> _)) val gatheredLookups = Task.gatherUnordered(lookups, exceptionCancels = true) @@ -598,7 +602,7 @@ case class Resolution(rootDependencies: Set[Dependency], } } - def last(fetchModule: ModuleVersion => EitherT[Task, List[String], (Repository, Project)], maxIterations: Int = -1): Task[Resolution] = { + def last(fetchModule: ModuleVersion => EitherT[Task, Seq[String], (Repository, Project)], maxIterations: Int = -1): Task[Resolution] = { if (maxIterations == 0 || isDone) Task.now(this) else { next(fetchModule) @@ -606,7 +610,7 @@ case class Resolution(rootDependencies: Set[Dependency], } } - def stream(fetchModule: ModuleVersion => EitherT[Task, List[String], (Repository, Project)], run: Task[Resolution] => Resolution): Stream[Resolution] = { + def stream(fetchModule: ModuleVersion => EitherT[Task, Seq[String], (Repository, Project)], run: Task[Resolution] => Resolution): Stream[Resolution] = { this #:: { if (isDone) Stream.empty else run(next(fetchModule)).stream(fetchModule, run) diff --git a/core/src/main/scala/coursier/core/ResolutionProcess.scala b/core/src/main/scala/coursier/core/ResolutionProcess.scala new file mode 100644 index 000000000..bcb49274c --- /dev/null +++ b/core/src/main/scala/coursier/core/ResolutionProcess.scala @@ -0,0 +1,88 @@ +package coursier +package core + +import scalaz._ +import scala.annotation.tailrec + + +sealed trait ResolutionProcess { + def run[F[_]](fetch: ResolutionProcess.Fetch[F], + maxIterations: Int = -1) + (implicit F: Monad[F]): F[Resolution] = { + + if (maxIterations == 0) F.point(current) + else + this match { + case Done(res) => F.point(res) + case missing0 @ Missing(missing, _, _) => + F.bind(fetch(missing))(result => missing0.next(result).run(fetch, if (maxIterations > 0) maxIterations - 1 else maxIterations)) + case cont @ Continue(_, _) => cont.nextNoCont.run(fetch) + } + } + + def current: Resolution +} + +case class Missing(missing: Seq[(Module, String)], + current: Resolution, + cont: Resolution => ResolutionProcess) extends ResolutionProcess { + + def next(results: ResolutionProcess.FetchResult): ResolutionProcess = { + + val errors = results.collect{case (modVer, -\/(errs)) => modVer -> errs } + val successes = results.collect{case (modVer, \/-(repoProj)) => modVer -> repoProj } + + val depMgmtMissing0 = successes + .map{case (_, (_, proj)) => current.dependencyManagementMissing(proj) } + .fold(Set.empty)(_ ++ _) + + val depMgmtMissing = depMgmtMissing0 -- results.map(_._1) + + def cont0(res: Resolution) = { + val res0 = + successes.foldLeft(res){case (acc, (modVer, (repo, proj))) => + acc.copy(projectCache = acc.projectCache + ( + modVer -> (repo, acc.withDependencyManagement(proj)) + )) + } + Continue(res0, cont) + } + + val current0 = current + .copy(errorCache = current.errorCache ++ errors) + + if (depMgmtMissing.isEmpty) cont0(current0) + else Missing(depMgmtMissing.toSeq, current0, cont0) + } + +} + +case class Continue(current: Resolution, + cont: Resolution => ResolutionProcess) extends ResolutionProcess { + + def next: ResolutionProcess = cont(current) + + @tailrec final def nextNoCont: ResolutionProcess = + next match { + case nextCont: Continue => nextCont.nextNoCont + case other => other + } + +} + +case class Done(resolution: Resolution) extends ResolutionProcess { + def current: Resolution = resolution +} + +object ResolutionProcess { + def apply(resolution: Resolution): ResolutionProcess = { + val resolution0 = resolution.nextIfNoMissing + + if (resolution0.isDone) Done(resolution0) + else Missing(resolution0.missingFromCache.toSeq, resolution0, apply) + } + + type FetchResult = Seq[((Module, String), Seq[String] \/ (Repository, Project))] + type Fetch[F[_]] = Seq[(Module, String)] => F[FetchResult] +} + diff --git a/core/src/main/scala/coursier/package.scala b/core/src/main/scala/coursier/package.scala index 2cfd39143..7c3f35215 100644 --- a/core/src/main/scala/coursier/package.scala +++ b/core/src/main/scala/coursier/package.scala @@ -1,4 +1,4 @@ -import scalaz.EitherT +import scalaz.{ EitherT, \/ } import scalaz.concurrent.Task /** @@ -67,9 +67,15 @@ package object coursier { type Repository = core.Repository - def fetchFrom(repositories: Seq[Repository]): ModuleVersion => EitherT[Task, List[String], (Repository, Project)] = + def fetchFrom(repositories: Seq[Repository]): ModuleVersion => EitherT[Task, Seq[String], (Repository, Project)] = modVersion => core.Resolution.find(repositories, modVersion._1, modVersion._2) + def fetchSeveralFrom(repositories: Seq[Repository]): Seq[ModuleVersion] => Task[Seq[(ModuleVersion, Seq[String] \/ (Repository, Project))]] = { + val fetchOne = fetchFrom(repositories) + modVers => + Task.gatherUnordered(modVers.map(modVer => fetchOne(modVer).run.map(modVer -> _))) + } + type Resolution = core.Resolution object Resolution { val empty = apply() @@ -84,7 +90,7 @@ package object coursier { } def resolve(dependencies: Set[Dependency], - fetch: ModuleVersion => EitherT[Task, List[String], (Repository, Project)], + fetch: ModuleVersion => EitherT[Task, Seq[String], (Repository, Project)], maxIterations: Option[Int] = Some(200), filter: Option[Dependency => Boolean] = None, profileActivation: Option[(String, Profile.Activation, Map[String, String]) => Boolean] = None): Task[Resolution] = { @@ -110,4 +116,6 @@ package object coursier { type MavenRepository[G <: core.FetchMetadata] = core.MavenRepository[G] val MavenRepository: core.MavenRepository.type = core.MavenRepository + type ResolutionProcess = core.ResolutionProcess + val ResolutionProcess: core.ResolutionProcess.type = core.ResolutionProcess } diff --git a/core/src/test/scala/coursier/test/CentralTests.scala b/core/src/test/scala/coursier/test/CentralTests.scala index f8348043f..4a1a90a26 100644 --- a/core/src/test/scala/coursier/test/CentralTests.scala +++ b/core/src/test/scala/coursier/test/CentralTests.scala @@ -12,6 +12,12 @@ object CentralTests extends TestSuite { repository.mavenCentral ) + def resolve(deps: Set[Dependency], filter: Option[Dependency => Boolean] = None) = { + ResolutionProcess(Resolution(deps, filter = filter)) + .run(fetchSeveralFrom(repositories)) + .runF + } + def repr(dep: Dependency) = s"${dep.module.organization}:${dep.module.name}:${dep.attributes.`type`}:${Some(dep.attributes.classifier).filter(_.nonEmpty).map(_+":").mkString}${dep.version}" @@ -20,7 +26,7 @@ object CentralTests extends TestSuite { val expected = await(textResource(s"resolutions/${module.organization}:${module.name}:$version")).split('\n').toSeq val dep = Dependency(module, version) - val res = await(resolve(Set(dep), fetchFrom(repositories)).runF) + val res = await(resolve(Set(dep))) val result = res.dependencies.toVector.map(repr).sorted.distinct @@ -34,7 +40,7 @@ object CentralTests extends TestSuite { 'logback{ async { val dep = Dependency(Module("ch.qos.logback", "logback-classic"), "1.1.3") - val res = await(resolve(Set(dep), fetchFrom(repositories)).runF) + val res = await(resolve(Set(dep))) .copy(projectCache = Map.empty, errorCache = Map.empty) // No validating these here val expected = Resolution( @@ -50,7 +56,7 @@ object CentralTests extends TestSuite { 'asm{ async { val dep = Dependency(Module("org.ow2.asm", "asm-commons"), "5.0.2") - val res = await(resolve(Set(dep), fetchFrom(repositories)).runF) + val res = await(resolve(Set(dep))) .copy(projectCache = Map.empty, errorCache = Map.empty) // No validating these here val expected = Resolution( @@ -66,7 +72,7 @@ object CentralTests extends TestSuite { 'jodaVersionInterval{ async { val dep = Dependency(Module("joda-time", "joda-time"), "[2.2,2.8]") - val res0 = await(resolve(Set(dep), fetchFrom(repositories)).runF) + val res0 = await(resolve(Set(dep))) val res = res0.copy(projectCache = Map.empty, errorCache = Map.empty) val expected = Resolution( diff --git a/core/src/test/scala/coursier/test/ResolutionTests.scala b/core/src/test/scala/coursier/test/ResolutionTests.scala index 4734bd0f5..6902b4e86 100644 --- a/core/src/test/scala/coursier/test/ResolutionTests.scala +++ b/core/src/test/scala/coursier/test/ResolutionTests.scala @@ -8,6 +8,12 @@ import coursier.test.compatibility._ object ResolutionTests extends TestSuite { + def resolve0(deps: Set[Dependency], filter: Option[Dependency => Boolean] = None) = { + ResolutionProcess(Resolution(deps, filter = filter)) + .run(fetchSeveralFrom(repositories)) + .runF + } + implicit class ProjectOps(val p: Project) extends AnyVal { def kv: (ModuleVersion, (Repository, Project)) = p.moduleVersion -> (testRepository, p) } @@ -149,10 +155,9 @@ object ResolutionTests extends TestSuite { val tests = TestSuite { 'empty{ async{ - val res = await(resolve( - Set.empty, - fetchFrom(repositories) - ).runF) + val res = await(resolve0( + Set.empty + )) assert(res == Resolution.empty) } @@ -160,10 +165,9 @@ object ResolutionTests extends TestSuite { 'notFound{ async { val dep = Dependency(Module("acme", "playy"), "2.4.0") - val res = await(resolve( - Set(dep), - fetchFrom(repositories) - ).runF) + val res = await(resolve0( + Set(dep) + )) val expected = Resolution( rootDependencies = Set(dep), @@ -177,10 +181,9 @@ object ResolutionTests extends TestSuite { 'single{ async { val dep = Dependency(Module("acme", "config"), "1.3.0") - val res = await(resolve( - Set(dep), - fetchFrom(repositories) - ).runF) + val res = await(resolve0( + Set(dep) + )) val expected = Resolution( rootDependencies = Set(dep), @@ -195,10 +198,9 @@ object ResolutionTests extends TestSuite { async { val dep = Dependency(Module("acme", "play"), "2.4.0") val trDep = Dependency(Module("acme", "play-json"), "2.4.0") - val res = await(resolve( - Set(dep), - fetchFrom(repositories) - ).runF) + val res = await(resolve0( + Set(dep) + )) val expected = Resolution( rootDependencies = Set(dep), @@ -219,10 +221,9 @@ object ResolutionTests extends TestSuite { Dependency(Module("acme", "play-json"), "2.4.0"), Dependency(Module("acme", "config"), "1.3.0") ) - val res = await(resolve( - Set(dep), - fetchFrom(repositories) - ).runF) + val res = await(resolve0( + Set(dep) + )) val expected = Resolution( rootDependencies = Set(dep), @@ -244,10 +245,9 @@ object ResolutionTests extends TestSuite { Dependency(Module("acme", "play-json"), "2.4.0", exclusions = Set(("acme", "config"))) ) - val res = await(resolve( - Set(dep), - fetchFrom(repositories) - ).runF) + val res = await(resolve0( + Set(dep) + )) val expected = Resolution( rootDependencies = Set(dep), @@ -269,10 +269,9 @@ object ResolutionTests extends TestSuite { Dependency(Module("acme", "play-json"), "2.4.0", exclusions = Set(("*", "config"))) ) - val res = await(resolve( - Set(dep), - fetchFrom(repositories) - ).runF) + val res = await(resolve0( + Set(dep) + )) val expected = Resolution( rootDependencies = Set(dep), @@ -288,11 +287,10 @@ object ResolutionTests extends TestSuite { 'filter{ async { val dep = Dependency(Module("hudsucker", "mail"), "10.0") - val res = await(resolve( + val res = await(resolve0( Set(dep), - fetchFrom(repositories), filter = Some(_.scope == Scope.Compile) - ).runF).copy(filter = None) + )).copy(filter = None) val expected = Resolution( rootDependencies = Set(dep), @@ -312,11 +310,10 @@ object ResolutionTests extends TestSuite { Dependency(Module("acme", "play"), "2.4.0", exclusions = Set(("acme", "play-json"))) ) - val res = await(resolve( + val res = await(resolve0( Set(dep), - fetchFrom(repositories), filter = Some(_.scope == Scope.Compile) - ).runF).copy(filter = None, projectCache = Map.empty) + )).copy(filter = None, projectCache = Map.empty) val expected = Resolution( rootDependencies = Set(dep), @@ -332,11 +329,10 @@ object ResolutionTests extends TestSuite { val trDeps = Seq( Dependency(Module("org.gnu", "glib"), "13.4"), Dependency(Module("org.gnome", "desktop"), "7.0")) - val res = await(resolve( + val res = await(resolve0( Set(dep), - fetchFrom(repositories), filter = Some(_.scope == Scope.Compile) - ).runF).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) + )).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) val expected = Resolution( rootDependencies = Set(dep), @@ -351,11 +347,10 @@ object ResolutionTests extends TestSuite { val dep = Dependency(Module("com.mailapp", "mail-client"), "2.1") val trDeps = Seq( Dependency(Module("gov.nsa", "secure-pgp"), "10.0", exclusions = Set(("*", "crypto")))) - val res = await(resolve( + val res = await(resolve0( Set(dep), - fetchFrom(repositories), filter = Some(_.scope == Scope.Compile) - ).runF).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) + )).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) val expected = Resolution( rootDependencies = Set(dep), @@ -368,11 +363,10 @@ object ResolutionTests extends TestSuite { 'depMgmtInParentDeps{ async { val dep = Dependency(Module("com.thoughtworks.paranamer", "paranamer"), "2.6") - val res = await(resolve( + val res = await(resolve0( Set(dep), - fetchFrom(repositories), filter = Some(_.scope == Scope.Compile) - ).runF).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) + )).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) val expected = Resolution( rootDependencies = Set(dep), @@ -387,11 +381,10 @@ object ResolutionTests extends TestSuite { val dep = Dependency(Module("com.github.dummy", "libb"), "0.3.3") val trDeps = Seq( Dependency(Module("org.escalier", "librairie-standard"), "2.11.6")) - val res = await(resolve( + val res = await(resolve0( Set(dep), - fetchFrom(repositories), filter = Some(_.scope == Scope.Compile) - ).runF).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) + )).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) val expected = Resolution( rootDependencies = Set(dep), @@ -408,11 +401,10 @@ object ResolutionTests extends TestSuite { val dep = Dependency(Module("com.github.dummy", "libb"), version) val trDeps = Seq( Dependency(Module("org.escalier", "librairie-standard"), "2.11.6")) - val res = await(resolve( + val res = await(resolve0( Set(dep), - fetchFrom(repositories), filter = Some(_.scope == Scope.Compile) - ).runF).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) + )).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) val expected = Resolution( rootDependencies = Set(dep), @@ -431,11 +423,10 @@ object ResolutionTests extends TestSuite { val dep = Dependency(Module("com.github.dummy", "libb"), "0.4.2") val trDeps = Seq( Dependency(Module("org.escalier", "librairie-standard"), "2.11.6")) - val res = await(resolve( + val res = await(resolve0( Set(dep), - fetchFrom(repositories), filter = Some(_.scope == Scope.Compile) - ).runF).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) + )).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) val expected = Resolution( rootDependencies = Set(dep), @@ -453,11 +444,10 @@ object ResolutionTests extends TestSuite { Dependency(Module("an-org", "a-lib"), "1.0", exclusions = Set(("an-org", "a-name"))), Dependency(Module("an-org", "another-lib"), "1.0", optional = true), Dependency(Module("an-org", "a-name"), "1.0", optional = true)) - val res = await(resolve( + val res = await(resolve0( Set(dep), - fetchFrom(repositories), filter = Some(_.scope == Scope.Compile) - ).runF).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) + )).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) val expected = Resolution( rootDependencies = Set(dep), @@ -477,11 +467,10 @@ object ResolutionTests extends TestSuite { Dependency(Module("an-org", "a-lib"), "1.0", exclusions = Set(("an-org", "a-name"))), Dependency(Module("an-org", "another-lib"), "1.0", optional = true), Dependency(Module("an-org", "a-name"), "1.0", optional = true)) - val res = await(resolve( + val res = await(resolve0( deps, - fetchFrom(repositories), filter = Some(_.scope == Scope.Compile) - ).runF).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) + )).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty) val expected = Resolution( rootDependencies = deps, diff --git a/core/src/test/scala/coursier/test/package.scala b/core/src/test/scala/coursier/test/package.scala index af3c811e8..cf746c90e 100644 --- a/core/src/test/scala/coursier/test/package.scala +++ b/core/src/test/scala/coursier/test/package.scala @@ -10,7 +10,7 @@ package object test { } def resolve(dependencies: Set[Dependency], - fetch: ModuleVersion => EitherT[Task, List[String], (Repository, Project)], + fetch: ModuleVersion => EitherT[Task, Seq[String], (Repository, Project)], maxIterations: Option[Int] = Some(200), filter: Option[Dependency => Boolean] = None, profileActivation: Option[(String, Profile.Activation, Map[String, String]) => Boolean] = None): Task[Resolution] = { diff --git a/web/src/main/scala/coursier/web/Backend.scala b/web/src/main/scala/coursier/web/Backend.scala index 528e4cd04..c6f15dc7c 100644 --- a/web/src/main/scala/coursier/web/Backend.scala +++ b/web/src/main/scala/coursier/web/Backend.scala @@ -127,7 +127,8 @@ class Backend($: BackendScope[Unit, State]) { filter = Some(dep => (s.options.followOptional || !dep.optional) && (s.options.keepTest || dep.scope != Scope.Test)) ) - res.last(fetchFrom(s.repositories.map(r => r.copy(fetchMetadata = r.fetchMetadata.copy(logger = Some(logger))))), 100) + ResolutionProcess(res) + .run(fetchSeveralFrom(s.repositories.map(r => r.copy(fetchMetadata = r.fetchMetadata.copy(logger = Some(logger))))), 100) } // For reasons that are unclear to me, not delaying this when using the runNow execution context