mirror of https://github.com/sbt/sbt.git
commit
a84306c942
|
|
@ -12,7 +12,8 @@ import scalaz.{-\/, \/-}
|
|||
|
||||
case class Coursier(scope: List[String],
|
||||
keepOptional: Boolean,
|
||||
fetch: Boolean) extends App {
|
||||
fetch: Boolean,
|
||||
@ExtraName("N") maxIterations: Int) extends App {
|
||||
|
||||
val scopes0 =
|
||||
if (scope.isEmpty) List(Scope.Compile, Scope.Runtime)
|
||||
|
|
@ -85,9 +86,15 @@ case class Coursier(scope: List[String],
|
|||
val res = resolve(
|
||||
deps.toSet,
|
||||
fetchFrom(repositories),
|
||||
maxIterations = Some(maxIterations).filter(_ > 0),
|
||||
filter = Some(dep => (keepOptional || !dep.optional) && scopes(dep.scope))
|
||||
).run
|
||||
|
||||
if (!res.isDone) {
|
||||
println(s"Maximum number of iteration reached!")
|
||||
sys exit 1
|
||||
}
|
||||
|
||||
def repr(dep: Dependency) =
|
||||
s"${dep.module.organization}:${dep.module.name}:${dep.`type`}:${Some(dep.classifier).filter(_.nonEmpty).map(_+":").mkString}${dep.version}"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
package coursier.core
|
||||
|
||||
/**
|
||||
* Identify a "module".
|
||||
* Identifies a "module".
|
||||
*
|
||||
* During resolution, all dependencies having the same module
|
||||
* will be given the same version, if there are no version conflicts
|
||||
* between them.
|
||||
*
|
||||
* Ivy attributes would land here, if support for Ivy is added.
|
||||
* Using the same terminology as Ivy.
|
||||
*
|
||||
* Ivy attributes would land here, if support for it is added.
|
||||
*/
|
||||
case class Module(organization: String,
|
||||
name: String) {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package coursier.core
|
|||
import java.util.regex.Pattern.quote
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.collection.mutable
|
||||
import scalaz.concurrent.Task
|
||||
import scalaz.{EitherT, \/-, \/, -\/}
|
||||
|
||||
|
|
@ -16,8 +17,8 @@ object Resolver {
|
|||
* Look at `repositories` from the left, one-by-one, and stop at first success.
|
||||
* Else, return all errors, in the same order.
|
||||
*
|
||||
* The `module` field of the returned `Project` in case of success may not be
|
||||
* equal to `module`, in case the version of the latter is not a specific
|
||||
* The `version` field of the returned `Project` in case of success may not be
|
||||
* equal to the provided one, in case the latter is not a specific
|
||||
* version (e.g. version interval). Which version get chosen depends on
|
||||
* the repository implementation.
|
||||
*/
|
||||
|
|
@ -296,7 +297,7 @@ object Resolver {
|
|||
else dep
|
||||
|
||||
/**
|
||||
* Filters `deps` with `exclusions`.
|
||||
* Filters `dependencies` with `exclusions`.
|
||||
*/
|
||||
def withExclusions(dependencies: Seq[Dependency],
|
||||
exclusions: Set[(String, String)]): Seq[Dependency] = {
|
||||
|
|
@ -378,6 +379,16 @@ object Resolver {
|
|||
filter: Option[Dependency => Boolean],
|
||||
profileActivation: Option[(String, Activation, Map[String, String]) => Boolean]) {
|
||||
|
||||
private val finalDependenciesCache = new mutable.HashMap[Dependency, Seq[Dependency]]()
|
||||
private def finalDependencies0(dep: Dependency) = finalDependenciesCache.synchronized {
|
||||
finalDependenciesCache.getOrElseUpdate(dep,
|
||||
projectsCache.get(dep.moduleVersion) match {
|
||||
case Some((_, proj)) => finalDependencies(dep, proj).filter(filter getOrElse defaultFilter)
|
||||
case None => Nil
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Transitive dependencies of the current dependencies, according to what there currently is in cache.
|
||||
* No attempt is made to solve version conflicts here.
|
||||
|
|
@ -385,14 +396,16 @@ object Resolver {
|
|||
def transitiveDependencies =
|
||||
for {
|
||||
dep <- (dependencies -- conflicts).toList
|
||||
(_, proj) <- projectsCache.get((dep.moduleVersion)).toSeq
|
||||
trDep <- finalDependencies(dep, proj).filter(filter getOrElse defaultFilter)
|
||||
trDep <- finalDependencies0(dep)
|
||||
} yield trDep
|
||||
|
||||
/**
|
||||
* The "next" dependency set, made of the current dependencies and their transitive dependencies,
|
||||
* trying to solve version conflicts. Transitive dependencies are calculated with the current cache.
|
||||
*
|
||||
* May contain dependencies added in previous iterations, but no more required. These are filtered below, see
|
||||
* @newDependencies.
|
||||
*
|
||||
* Returns a tuple made of the conflicting dependencies, and all the dependencies.
|
||||
*/
|
||||
def nextDependenciesAndConflicts = {
|
||||
|
|
@ -403,8 +416,8 @@ object Resolver {
|
|||
* The modules we miss some info about.
|
||||
*/
|
||||
def missingFromCache: Set[ModuleVersion] = {
|
||||
val modules = dependencies.map(dep => (dep.moduleVersion))
|
||||
val nextModules = nextDependenciesAndConflicts._2.map(dep => (dep.moduleVersion))
|
||||
val modules = dependencies.map(_.moduleVersion)
|
||||
val nextModules = nextDependenciesAndConflicts._2.map(_.moduleVersion)
|
||||
|
||||
(modules ++ nextModules)
|
||||
.filterNot(mod => projectsCache.contains(mod) || errors.contains(mod))
|
||||
|
|
@ -416,8 +429,8 @@ object Resolver {
|
|||
*/
|
||||
def isDone: Boolean = {
|
||||
def isFixPoint = {
|
||||
val (nextConflicts, nextDependencies) = nextDependenciesAndConflicts
|
||||
dependencies == (nextDependencies ++ nextConflicts).toSet && conflicts == nextConflicts.toSet
|
||||
val (nextConflicts, _) = nextDependenciesAndConflicts
|
||||
dependencies == (newDependencies ++ nextConflicts) && conflicts == nextConflicts.toSet
|
||||
}
|
||||
|
||||
missingFromCache.isEmpty && isFixPoint
|
||||
|
|
@ -436,8 +449,7 @@ object Resolver {
|
|||
val trDepsSeq =
|
||||
for {
|
||||
dep <- updatedDeps
|
||||
(_, proj) <- projectsCache.get((dep.moduleVersion)).toList
|
||||
trDep <- finalDependencies(dep, proj).filter(filter getOrElse defaultFilter)
|
||||
trDep <- finalDependencies0(dep)
|
||||
} yield key(trDep) -> (key(dep), trDep.exclusions)
|
||||
|
||||
val knownDeps = (updatedDeps ++ updatedConflicts).map(key).toSet
|
||||
|
|
@ -531,7 +543,7 @@ object Resolver {
|
|||
|
||||
val modules =
|
||||
(project.dependencies ++ profileDependencies)
|
||||
.collect{ case dep if dep.scope == Scope.Import => (dep.moduleVersion) } ++
|
||||
.collect{ case dep if dep.scope == Scope.Import => dep.moduleVersion } ++
|
||||
project.parent
|
||||
|
||||
modules.toSet
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ package web
|
|||
import coursier.core.{Resolver, Logger, Remote}
|
||||
import japgolly.scalajs.react.vdom.{TagMod, Attr}
|
||||
import japgolly.scalajs.react.vdom.Attrs.dangerouslySetInnerHtml
|
||||
import japgolly.scalajs.react.{ReactKeyboardEventI, ReactEventI, ReactComponentB, BackendScope}
|
||||
import japgolly.scalajs.react.{ReactEventI, ReactComponentB, BackendScope}
|
||||
import japgolly.scalajs.react.vdom.prefix_<^._
|
||||
import scala.scalajs.concurrent.JSExecutionContext.Implicits.queue
|
||||
import org.scalajs.jquery.jQuery
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
import scala.scalajs.js
|
||||
import js.Dynamic.{global => g}
|
||||
|
||||
|
|
@ -117,13 +119,15 @@ class Backend($: BackendScope[Unit, State]) {
|
|||
}
|
||||
|
||||
val s = $.state
|
||||
val task = coursier.resolve(
|
||||
def task = coursier.resolve(
|
||||
s.modules.toSet,
|
||||
fetchFrom(s.repositories.map(_.copy(logger = Some(logger)))),
|
||||
filter = Some(dep => (s.options.followOptional || !dep.optional) && (s.options.keepTest || dep.scope != Scope.Test))
|
||||
)
|
||||
|
||||
task.runF.foreach { res: Resolution =>
|
||||
// For reasons that are unclear to me, not delaying this when using the runNow execution context
|
||||
// somehow discards the $.modState above. (Not a major problem as queue is used by default.)
|
||||
Future(task)(scala.scalajs.concurrent.JSExecutionContext.Implicits.queue).flatMap(_.runF).foreach { res: Resolution =>
|
||||
$.modState{ s => updateDepGraph(res); updateTree(res, "#deptree", reverse = s.reverseTree); s.copy(resolutionOpt = Some(res), resolving = false)}
|
||||
g.$("#resResTab a:last").tab("show")
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue