Allow to force some module versions during resolution

What SBT calls "dependency overrides"
This commit is contained in:
Alexandre Archambault 2015-11-29 20:54:19 +01:00
parent f1cf78b543
commit 98acae19bc
3 changed files with 130 additions and 19 deletions

View File

@ -163,25 +163,34 @@ object Resolution {
* Returns the conflicted dependencies, and the merged others.
*/
def merge(
dependencies: TraversableOnce[Dependency]
dependencies: TraversableOnce[Dependency],
forceVersions: Map[Module, String]
): (Seq[Dependency], Seq[Dependency]) = {
val mergedByModVer = dependencies
.toList
.groupBy(dep => dep.module)
.mapValues { deps =>
if (deps.lengthCompare(1) == 0) \/-(deps)
else {
val versions = deps
.map(_.version)
.distinct
val versionOpt = mergeVersions(versions)
versionOpt match {
case Some(version) =>
\/-(deps.map(dep => dep.copy(version = version)))
.map { case (module, deps) =>
module -> {
forceVersions.get(module) match {
case None =>
-\/(deps)
if (deps.lengthCompare(1) == 0) \/-(deps)
else {
val versions = deps
.map(_.version)
.distinct
val versionOpt = mergeVersions(versions)
versionOpt match {
case Some(version) =>
\/-(deps.map(dep => dep.copy(version = version)))
case None =>
-\/(deps)
}
}
case Some(forcedVersion) =>
\/-(deps.map(dep => dep.copy(version = forcedVersion)))
}
}
}
@ -380,6 +389,7 @@ object Resolution {
case class Resolution(
rootDependencies: Set[Dependency],
dependencies: Set[Dependency],
forceVersions: Map[Module, String],
conflicts: Set[Dependency],
projectCache: Map[Resolution.ModuleVersion, (Artifact.Source, Project)],
errorCache: Map[Resolution.ModuleVersion, Seq[String]],
@ -426,9 +436,10 @@ case class Resolution(
* the dependencies.
*/
def nextDependenciesAndConflicts: (Seq[Dependency], Seq[Dependency]) =
// TODO Provide the modules whose version was forced by dependency overrides too
merge(
rootDependencies.map(withDefaultScope) ++ dependencies ++
transitiveDependencies
rootDependencies.map(withDefaultScope) ++ dependencies ++ transitiveDependencies,
forceVersions
)
/**

View File

@ -63,6 +63,7 @@ package object coursier {
def apply(
rootDependencies: Set[Dependency] = Set.empty,
dependencies: Set[Dependency] = Set.empty,
forceVersions: Map[Module, String] = Map.empty,
conflicts: Set[Dependency] = Set.empty,
projectCache: Map[ModuleVersion, (Artifact.Source, Project)] = Map.empty,
errorCache: Map[ModuleVersion, Seq[String]] = Map.empty,
@ -72,6 +73,7 @@ package object coursier {
core.Resolution(
rootDependencies,
dependencies,
forceVersions,
conflicts,
projectCache,
errorCache,

View File

@ -9,12 +9,15 @@ import coursier.test.compatibility._
object ResolutionTests extends TestSuite {
def resolve0(deps: Set[Dependency], filter: Option[Dependency => Boolean] = None) = {
Resolution(deps, filter = filter)
def resolve0(
deps: Set[Dependency],
filter: Option[Dependency => Boolean] = None,
forceVersions: Map[Module, String] = Map.empty
) =
Resolution(deps, filter = filter, forceVersions = forceVersions)
.process
.run(Fetch.default(repositories))
.runF
}
implicit class ProjectOps(val p: Project) extends AnyVal {
def kv: (ModuleVersion, (Artifact.Source, Project)) = p.moduleVersion -> (testRepository.source, p)
@ -134,9 +137,16 @@ object ResolutionTests extends TestSuite {
Project(Module("an-org", "a-name"), "1.0"),
Project(Module("an-org", "a-name"), "1.2"),
Project(Module("an-org", "a-lib"), "1.0",
Seq(Dependency(Module("an-org", "a-name"), "1.0"))),
Project(Module("an-org", "a-lib"), "1.1"),
Project(Module("an-org", "a-lib"), "1.2",
Seq(Dependency(Module("an-org", "a-name"), "1.2"))),
Project(Module("an-org", "another-lib"), "1.0",
Seq(Dependency(Module("an-org", "a-name"), "1.0"))),
@ -144,7 +154,15 @@ object ResolutionTests extends TestSuite {
Project(Module("an-org", "an-app"), "1.0",
Seq(
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", "another-lib"), "1.0", optional = true))),
Project(Module("an-org", "an-app"), "1.1",
Seq(
Dependency(Module("an-org", "a-lib"), "1.1"))),
Project(Module("an-org", "an-app"), "1.2",
Seq(
Dependency(Module("an-org", "a-lib"), "1.2")))
)
val projectsMap = projects.map(p => p.moduleVersion -> p).toMap
@ -483,6 +501,86 @@ object ResolutionTests extends TestSuite {
}
}
'dependencyOverrides - {
* - {
async {
val deps = Set(
Dependency(Module("an-org", "a-name"), "1.1"))
val depOverrides = Map(
Module("an-org", "a-name") -> "1.0")
val res = await(resolve0(
deps,
forceVersions = depOverrides,
filter = Some(_.scope == Scope.Compile)
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
rootDependencies = deps,
dependencies = Set(
Dependency(Module("an-org", "a-name"), "1.0")
).map(_.withCompileScope),
forceVersions = depOverrides
)
assert(res == expected)
}
}
* - {
async {
val deps = Set(
Dependency(Module("an-org", "an-app"), "1.1"))
val depOverrides = Map(
Module("an-org", "a-lib") -> "1.0")
val res = await(resolve0(
deps,
forceVersions = depOverrides,
filter = Some(_.scope == Scope.Compile)
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
rootDependencies = deps,
dependencies = Set(
Dependency(Module("an-org", "an-app"), "1.1"),
Dependency(Module("an-org", "a-lib"), "1.0"),
Dependency(Module("an-org", "a-name"), "1.0")
).map(_.withCompileScope),
forceVersions = depOverrides
)
assert(res == expected)
}
}
* - {
async {
val deps = Set(
Dependency(Module("an-org", "an-app"), "1.2"))
val depOverrides = Map(
Module("an-org", "a-lib") -> "1.1")
val res = await(resolve0(
deps,
forceVersions = depOverrides,
filter = Some(_.scope == Scope.Compile)
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
rootDependencies = deps,
dependencies = Set(
Dependency(Module("an-org", "an-app"), "1.2"),
Dependency(Module("an-org", "a-lib"), "1.1")
).map(_.withCompileScope),
forceVersions = depOverrides
)
assert(res == expected)
}
}
}
'parts{
'propertySubstitution{
val res =