mirror of https://github.com/sbt/sbt.git
Merge pull request #81 from alexarchambault/topic/force-versions
Allow to force module versions...
This commit is contained in:
commit
c3b842db8f
|
|
@ -30,6 +30,10 @@ case class CommonOptions(
|
|||
@HelpMessage("Repositories - for multiple repositories, separate with comma and/or repeat this option (e.g. -r central,ivy2local -r sonatype-snapshots, or equivalently -r central,ivy2local,sonatype-snapshots)")
|
||||
@ExtraName("r")
|
||||
repository: List[String],
|
||||
@HelpMessage("Force module version")
|
||||
@ValueDescription("organization:name:forcedVersion")
|
||||
@ExtraName("V")
|
||||
forceVersion: List[String],
|
||||
@HelpMessage("Maximum number of parallel downloads (default: 6)")
|
||||
@ExtraName("n")
|
||||
parallel: Int = 6,
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ class Helper(
|
|||
notFoundRepositoryIds.mkString(", ")
|
||||
)
|
||||
|
||||
sys.exit(1)
|
||||
sys.exit(255)
|
||||
}
|
||||
|
||||
val files = cache.files().copy(concurrentDownloadCount = parallel)
|
||||
|
|
@ -139,15 +139,30 @@ class Helper(
|
|||
.map(_.split(":", 3).toSeq)
|
||||
.partition(_.length == 3)
|
||||
|
||||
val (splitForceVersions, malformedForceVersions) = forceVersion
|
||||
.map(_.split(":", 3).toSeq)
|
||||
.partition(_.length == 3)
|
||||
|
||||
if (splitDependencies.isEmpty) {
|
||||
Console.err.println(s"Error: no dependencies specified.")
|
||||
// CaseApp.printUsage[Coursier]()
|
||||
sys exit 1
|
||||
}
|
||||
|
||||
if (malformed.nonEmpty) {
|
||||
errPrintln(s"Malformed dependencies:\n${malformed.map(_.mkString(":")).mkString("\n")}")
|
||||
sys exit 1
|
||||
if (malformed.nonEmpty || malformedForceVersions.nonEmpty) {
|
||||
if (malformed.nonEmpty) {
|
||||
errPrintln("Malformed dependency(ies), should be like org:name:version")
|
||||
for (s <- malformed)
|
||||
errPrintln(s" ${s.mkString(":")}")
|
||||
}
|
||||
|
||||
if (malformedForceVersions.nonEmpty) {
|
||||
errPrintln("Malformed force version(s), should be like org:name:forcedVersion")
|
||||
for (s <- malformedForceVersions)
|
||||
errPrintln(s" ${s.mkString(":")}")
|
||||
}
|
||||
|
||||
sys.exit(1)
|
||||
}
|
||||
|
||||
val moduleVersions = splitDependencies.map{
|
||||
|
|
@ -159,8 +174,24 @@ class Helper(
|
|||
Dependency(mod, ver, scope = Scope.Runtime)
|
||||
}
|
||||
|
||||
val forceVersions = {
|
||||
val forceVersions0 = splitForceVersions.map {
|
||||
case Seq(org, name, version) => (Module(org, name), version)
|
||||
}
|
||||
|
||||
val grouped = forceVersions0
|
||||
.groupBy { case (mod, _) => mod }
|
||||
.map { case (mod, l) => mod -> l.map { case (_, version) => version } }
|
||||
|
||||
for ((mod, forcedVersions) <- grouped if forcedVersions.distinct.lengthCompare(1) > 0)
|
||||
errPrintln(s"Warning: version of $mod forced several times, using only the last one (${forcedVersions.last})")
|
||||
|
||||
grouped.map { case (mod, versions) => mod -> versions.last }
|
||||
}
|
||||
|
||||
val startRes = Resolution(
|
||||
deps.toSet,
|
||||
forceVersions = forceVersions,
|
||||
filter = Some(dep => keepOptional || !dep.optional)
|
||||
)
|
||||
|
||||
|
|
@ -182,8 +213,18 @@ class Helper(
|
|||
print.flatMap(_ => fetchQuiet(modVers))
|
||||
}
|
||||
|
||||
if (verbose0 >= 0)
|
||||
errPrintln(s"Resolving\n" + moduleVersions.map{case (mod, ver) => s" $mod:$ver"}.mkString("\n"))
|
||||
if (verbose0 >= 0) {
|
||||
errPrintln("Dependencies:")
|
||||
for ((mod, ver) <- moduleVersions)
|
||||
errPrintln(s" $mod:$ver")
|
||||
|
||||
if (forceVersions.nonEmpty) {
|
||||
errPrintln("Force versions:")
|
||||
for ((mod, ver) <- forceVersions.toVector.sortBy { case (mod, _) => mod.toString })
|
||||
errPrintln(s" $mod:$ver")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val res = startRes
|
||||
.process
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 =
|
||||
|
|
|
|||
Loading…
Reference in New Issue