Replace scopes with Ivy-like configuration

This commit is contained in:
Alexandre Archambault 2015-12-30 01:34:32 +01:00
parent d3cd484c15
commit e4dfc862b4
13 changed files with 241 additions and 247 deletions

View File

@ -171,7 +171,7 @@ class Helper(
}
val deps = moduleVersions.map{case (mod, ver) =>
Dependency(mod, ver, scope = Scope.Runtime)
Dependency(mod, ver, configuration = "runtime")
}
val forceVersions = {

View File

@ -24,8 +24,6 @@ case class Module(
override def toString = s"$organization:$name"
}
sealed abstract class Scope(val name: String)
/**
* Dependencies with the same @module will typically see their @version-s merged.
*
@ -35,7 +33,7 @@ sealed abstract class Scope(val name: String)
case class Dependency(
module: Module,
version: String,
scope: Scope,
configuration: String,
attributes: Attributes,
exclusions: Set[(String, String)],
optional: Boolean
@ -51,9 +49,10 @@ case class Attributes(
case class Project(
module: Module,
version: String,
dependencies: Seq[Dependency],
dependencies: Seq[(String, Dependency)],
parent: Option[(Module, String)],
dependencyManagement: Seq[Dependency],
dependencyManagement: Seq[(String, Dependency)],
configurations: Map[String, Seq[String]],
properties: Map[String, String],
profiles: Seq[Profile],
versions: Option[Versions],
@ -62,23 +61,14 @@ case class Project(
def moduleVersion = (module, version)
}
object Scope {
case object Compile extends Scope("compile")
case object Runtime extends Scope("runtime")
case object Test extends Scope("test")
case object Provided extends Scope("provided")
case object Import extends Scope("import")
case class Other(override val name: String) extends Scope(name)
}
case class Activation(properties: Seq[(String, Option[String])])
case class Profile(
id: String,
activeByDefault: Option[Boolean],
activation: Activation,
dependencies: Seq[Dependency],
dependencyManagement: Seq[Dependency],
dependencies: Seq[(String, Dependency)],
dependencyManagement: Seq[(String, Dependency)],
properties: Map[String, String]
)

View File

@ -2,39 +2,56 @@ package coursier.core
object Orders {
/** Minimal ad-hoc partial order */
trait PartialOrder[A] {
/**
* x < y: Some(neg. integer)
* x == y: Some(0)
* x > y: Some(pos. integer)
* x, y not related: None
*/
def cmp(x: A, y: A): Option[Int]
trait PartialOrdering[T] extends scala.math.PartialOrdering[T] {
def lteq(x: T, y: T): Boolean =
tryCompare(x, y)
.exists(_ <= 0)
}
/**
* Only relations:
* Compile < Runtime < Test
*/
implicit val mavenScopePartialOrder: PartialOrder[Scope] =
new PartialOrder[Scope] {
val higher = Map[Scope, Set[Scope]](
Scope.Compile -> Set(Scope.Runtime, Scope.Test),
Scope.Runtime -> Set(Scope.Test)
)
def configurationPartialOrder(configurations: Map[String, Seq[String]]): PartialOrdering[String] =
new PartialOrdering[String] {
def allParents(config: String): Set[String] = {
def helper(configs: Set[String], acc: Set[String]): Set[String] =
if (configs.isEmpty)
acc
else if (configs.exists(acc))
helper(configs -- acc, acc)
else if (configs.exists(!configurations.contains(_))) {
val (remaining, notFound) = configs.partition(configurations.contains)
helper(remaining, acc ++ notFound)
} else {
val extraConfigs = configs.flatMap(configurations)
helper(extraConfigs, acc ++ configs)
}
def cmp(x: Scope, y: Scope) =
if (x == y) Some(0)
else if (higher.get(x).exists(_(y))) Some(-1)
else if (higher.get(y).exists(_(x))) Some(1)
else None
helper(Set(config), Set.empty)
}
val allParentsMap = configurations
.keys
.toList
.map(config => config -> (allParents(config) - config))
.toMap
def tryCompare(x: String, y: String) =
if (x == y)
Some(0)
else if (allParentsMap.get(x).exists(_(y)))
Some(-1)
else if (allParentsMap.get(y).exists(_(x)))
Some(1)
else
None
}
/** Non-optional < optional */
implicit val optionalPartialOrder: PartialOrder[Boolean] =
new PartialOrder[Boolean] {
def cmp(x: Boolean, y: Boolean) =
val optionalPartialOrder: PartialOrdering[Boolean] =
new PartialOrdering[Boolean] {
def tryCompare(x: Boolean, y: Boolean) =
Some(
if (x == y) 0
else if (x) 1
@ -51,8 +68,8 @@ object Orders {
*
* In particular, no exclusions <= anything <= Set(("*", "*"))
*/
implicit val exclusionsPartialOrder: PartialOrder[Set[(String, String)]] =
new PartialOrder[Set[(String, String)]] {
val exclusionsPartialOrder: PartialOrdering[Set[(String, String)]] =
new PartialOrdering[Set[(String, String)]] {
def boolCmp(a: Boolean, b: Boolean) = (a, b) match {
case (true, true) => Some(0)
case (true, false) => Some(1)
@ -60,7 +77,7 @@ object Orders {
case (false, false) => None
}
def cmp(x: Set[(String, String)], y: Set[(String, String)]) = {
def tryCompare(x: Set[(String, String)], y: Set[(String, String)]) = {
val (xAll, xExcludeByOrg1, xExcludeByName1, xRemaining0) = Exclusions.partition(x)
val (yAll, yExcludeByOrg1, yExcludeByName1, yRemaining0) = Exclusions.partition(y)
@ -102,19 +119,22 @@ object Orders {
* Assume all dependencies have same `module`, `version`, and `artifact`; see `minDependencies`
* if they don't.
*/
def minDependenciesUnsafe(dependencies: Set[Dependency]): Set[Dependency] = {
def minDependenciesUnsafe(
dependencies: Set[Dependency],
configs: ((Module, String)) => Map[String, Seq[String]]
): Set[Dependency] = {
val groupedDependencies = dependencies
.groupBy(dep => (dep.optional, dep.scope))
.groupBy(dep => (dep.optional, dep.configuration))
.mapValues(deps => deps.head.copy(exclusions = deps.foldLeft(Exclusions.one)((acc, dep) => Exclusions.meet(acc, dep.exclusions))))
.toList
val remove =
for {
List(((xOpt, xScope), xDep), ((yOpt, yScope), yDep)) <- groupedDependencies.combinations(2)
optCmp <- optionalPartialOrder.cmp(xOpt, yOpt).iterator
scopeCmp <- mavenScopePartialOrder.cmp(xScope, yScope).iterator
optCmp <- optionalPartialOrder.tryCompare(xOpt, yOpt).iterator
scopeCmp <- configurationPartialOrder(configs(xDep.moduleVersion)).tryCompare(xScope, yScope).iterator
if optCmp*scopeCmp >= 0
exclCmp <- exclusionsPartialOrder.cmp(xDep.exclusions, yDep.exclusions).iterator
exclCmp <- exclusionsPartialOrder.tryCompare(xDep.exclusions, yDep.exclusions).iterator
if optCmp*exclCmp >= 0
if scopeCmp*exclCmp >= 0
xIsMin = optCmp < 0 || scopeCmp < 0 || exclCmp < 0
@ -130,10 +150,13 @@ object Orders {
*
* The returned set brings exactly the same things as `dependencies`, with no redundancy.
*/
def minDependencies(dependencies: Set[Dependency]): Set[Dependency] = {
def minDependencies(
dependencies: Set[Dependency],
configs: ((Module, String)) => Map[String, Seq[String]]
): Set[Dependency] = {
dependencies
.groupBy(_.copy(scope = Scope.Other(""), exclusions = Set.empty, optional = false))
.mapValues(minDependenciesUnsafe)
.groupBy(_.copy(configuration = "", exclusions = Set.empty, optional = false))
.mapValues(minDependenciesUnsafe(_, configs))
.valuesIterator
.fold(Set.empty)(_ ++ _)
}

View File

@ -4,15 +4,6 @@ import coursier.core.compatibility._
object Parse {
def scope(s: String): Scope = s match {
case "compile" => Scope.Compile
case "runtime" => Scope.Runtime
case "test" => Scope.Test
case "provided" => Scope.Provided
case "import" => Scope.Import
case other => Scope.Other(other)
}
def version(s: String): Option[Version] = {
if (s.isEmpty || s.exists(c => c != '.' && c != '-' && c != '_' && !c.letterOrDigit)) None
else Some(Version(s))

View File

@ -37,22 +37,22 @@ object Resolution {
(dep.module.organization, dep.module.name, dep.attributes.`type`)
def add(
dict: Map[Key, Dependency],
dep: Dependency
): Map[Key, Dependency] = {
dict: Map[Key, (String, Dependency)],
item: (String, Dependency)
): Map[Key, (String, Dependency)] = {
val key0 = key(dep)
val key0 = key(item._2)
if (dict.contains(key0))
dict
else
dict + (key0 -> dep)
dict + (key0 -> item)
}
def addSeq(
dict: Map[Key, Dependency],
deps: Seq[Dependency]
): Map[Key, Dependency] =
dict: Map[Key, (String, Dependency)],
deps: Seq[(String, Dependency)]
): Map[Key, (String, Dependency)] =
(dict /: deps)(add)
}
@ -62,14 +62,14 @@ object Resolution {
): Map[String, String] =
dict ++ other.filterKeys(!dict.contains(_))
def addDependencies(deps: Seq[Seq[Dependency]]): Seq[Dependency] = {
def addDependencies(deps: Seq[Seq[(String, Dependency)]]): Seq[(String, Dependency)] = {
val res =
(deps :\ (Set.empty[DepMgmt.Key], Seq.empty[Dependency])) {
(deps :\ (Set.empty[DepMgmt.Key], Seq.empty[(String, Dependency)])) {
case (deps0, (set, acc)) =>
val deps = deps0
.filter(dep => !set(DepMgmt.key(dep)))
.filter{case (_, dep) => !set(DepMgmt.key(dep))}
(set ++ deps.map(DepMgmt.key), acc ++ deps)
(set ++ deps.map{case (_, dep) => DepMgmt.key(dep)}, acc ++ deps)
}
res._2
@ -83,9 +83,9 @@ object Resolution {
* Substitutes `properties` in `dependencies`.
*/
def withProperties(
dependencies: Seq[Dependency],
dependencies: Seq[(String, Dependency)],
properties: Map[String, String]
): Seq[Dependency] = {
): Seq[(String, Dependency)] = {
def substituteProps(s: String) = {
val matches = propRegex
@ -107,8 +107,8 @@ object Resolution {
}
dependencies
.map{ dep =>
dep.copy(
.map {case (config, dep) =>
substituteProps(config) -> dep.copy(
module = dep.module.copy(
organization = substituteProps(dep.module.organization),
name = substituteProps(dep.module.name)
@ -118,7 +118,7 @@ object Resolution {
`type` = substituteProps(dep.attributes.`type`),
classifier = substituteProps(dep.attributes.classifier)
),
scope = Parse.scope(substituteProps(dep.scope.name)),
configuration = substituteProps(dep.configuration),
exclusions = dep.exclusions
.map{case (org, name) =>
(substituteProps(org), substituteProps(name))
@ -209,25 +209,6 @@ object Resolution {
)
}
/**
* If one of our dependency has scope `base`, and a transitive
* dependency of it has scope `transitive`, return the scope of
* the latter for us, if any. If empty, means the transitive dependency
* should not be considered a dependency for us.
*
* See https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope.
*/
def resolveScope(
base: Scope,
transitive: Scope
): Option[Scope] =
(base, transitive) match {
case (other, Scope.Compile) => Some(other)
case (Scope.Compile, Scope.Runtime) => Some(Scope.Runtime)
case (other, Scope.Runtime) => Some(other)
case _ => None
}
/**
* Applies `dependencyManagement` to `dependencies`.
*
@ -235,36 +216,39 @@ object Resolution {
* `dependencyManagement`.
*/
def depsWithDependencyManagement(
dependencies: Seq[Dependency],
dependencyManagement: Seq[Dependency]
): Seq[Dependency] = {
dependencies: Seq[(String, Dependency)],
dependencyManagement: Seq[(String, Dependency)]
): Seq[(String, Dependency)] = {
// See http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Management
lazy val dict = DepMgmt.addSeq(Map.empty, dependencyManagement)
dependencies
.map { dep0 =>
.map {case (config0, dep0) =>
var config = config0
var dep = dep0
for (mgmtDep <- dict.get(DepMgmt.key(dep0))) {
for ((mgmtConfig, mgmtDep) <- dict.get(DepMgmt.key(dep0))) {
if (dep.version.isEmpty)
dep = dep.copy(version = mgmtDep.version)
if (dep.scope.name.isEmpty)
dep = dep.copy(scope = mgmtDep.scope)
if (config.isEmpty)
config = mgmtConfig
if (dep.exclusions.isEmpty)
dep = dep.copy(exclusions = mgmtDep.exclusions)
}
dep
(config, dep)
}
}
def withDefaultScope(dep: Dependency): Dependency =
if (dep.scope.name.isEmpty)
dep.copy(scope = Scope.Compile)
val defaultConfiguration = "compile"
def withDefaultConfig(dep: Dependency): Dependency =
if (dep.configuration.isEmpty)
dep.copy(configuration = defaultConfiguration)
else
dep
@ -272,19 +256,37 @@ object Resolution {
* Filters `dependencies` with `exclusions`.
*/
def withExclusions(
dependencies: Seq[Dependency],
dependencies: Seq[(String, Dependency)],
exclusions: Set[(String, String)]
): Seq[Dependency] = {
): Seq[(String, Dependency)] = {
val filter = Exclusions(exclusions)
dependencies
.filter(dep => filter(dep.module.organization, dep.module.name))
.map(dep =>
dep.copy(
.filter{case (_, dep) => filter(dep.module.organization, dep.module.name) }
.map{case (config, dep) =>
config -> dep.copy(
exclusions = Exclusions.minimize(dep.exclusions ++ exclusions)
)
)
}
}
def withParentConfigurations(config: String, configurations: Map[String, Seq[String]]): Set[String] = {
@tailrec
def helper(configs: Set[String], acc: Set[String]): Set[String] =
if (configs.isEmpty)
acc
else if (configs.exists(acc))
helper(configs -- acc, acc)
else if (configs.exists(!configurations.contains(_))) {
val (remaining, notFound) = configs.partition(configurations.contains)
helper(remaining, acc ++ notFound)
} else {
val extraConfigs = configs.flatMap(configurations)
helper(extraConfigs, acc ++ configs)
}
helper(Set(config), Set.empty)
}
/**
@ -312,29 +314,33 @@ object Resolution {
)
)
val deps =
withExclusions(
depsWithDependencyManagement(
// Important: properties have to be applied to both,
// so that dep mgmt can be matched properly
// Tested with org.ow2.asm:asm-commons:5.0.2 in CentralTests
withProperties(project.dependencies, properties),
withProperties(project.dependencyManagement, properties)
),
from.exclusions
)
.map(withDefaultScope)
val configurations = withParentConfigurations(from.configuration, project.configurations)
deps
.flatMap { trDep =>
resolveScope(from.scope, trDep.scope)
.map(scope =>
trDep.copy(
scope = scope,
optional = trDep.optional || from.optional
)
)
}
withExclusions(
depsWithDependencyManagement(
// Important: properties have to be applied to both,
// so that dep mgmt can be matched properly
// Tested with org.ow2.asm:asm-commons:5.0.2 in CentralTests
withProperties(project.dependencies, properties),
withProperties(project.dependencyManagement, properties)
),
from.exclusions
)
.map{
case (config, dep) =>
(if (config.isEmpty) defaultConfiguration else config) -> {
if (dep.configuration.isEmpty)
dep.copy(configuration = defaultConfiguration)
else
dep
}
}
.collect{case (config, dep) if configurations(config) =>
if (from.optional)
dep.copy(optional = true)
else
dep
}
}
/**
@ -438,7 +444,7 @@ case class Resolution(
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(withDefaultConfig) ++ dependencies ++ transitiveDependencies,
forceVersions
)
@ -507,7 +513,7 @@ case class Resolution(
*/
def remainingDependencies: Set[Dependency] = {
val rootDependencies0 = rootDependencies
.map(withDefaultScope)
.map(withDefaultConfig)
.map(eraseVersion)
@tailrec
@ -599,7 +605,7 @@ case class Resolution(
val modules =
(project.dependencies ++ profileDependencies)
.collect{
case dep if dep.scope == Scope.Import => dep.moduleVersion
case ("import", dep) => dep.moduleVersion
}
modules.toSet ++ project.parent
@ -679,7 +685,7 @@ case class Resolution(
val deps = (
dependencies0
.collect { case dep if dep.scope == Scope.Import =>
.collect { case ("import", dep) =>
dep.moduleVersion
} ++
project.parent
@ -693,16 +699,16 @@ case class Resolution(
profiles0.map(_.dependencyManagement) ++
projs.map(_.dependencyManagement)
)
).foldLeft(Map.empty[DepMgmt.Key, Dependency])(DepMgmt.addSeq)
).foldLeft(Map.empty[DepMgmt.Key, (String, Dependency)])(DepMgmt.addSeq)
val depsSet = deps.toSet
project.copy(
dependencies =
dependencies0
.filterNot(dep =>
dep.scope == Scope.Import && depsSet(dep.moduleVersion)
) ++
.filterNot{case (config, dep) =>
config == "import" && depsSet(dep.moduleVersion)
} ++
project.parent
.filter(projectCache.contains)
.toSeq
@ -716,7 +722,14 @@ case class Resolution(
}
def minDependencies: Set[Dependency] =
Orders.minDependencies(dependencies)
Orders.minDependencies(
dependencies,
dep =>
projectCache
.get(dep)
.map(_._2.configurations)
.getOrElse(Map.empty)
)
def artifacts: Seq[Artifact] =
for {

View File

@ -35,6 +35,12 @@ object MavenRepository {
.map(_.value)
.filter(_.nonEmpty)
val defaultConfigurations = Map(
"runtime" -> Seq("compile"),
"test" -> Seq("runtime")
)
}
case class MavenRepository(
@ -231,7 +237,7 @@ case class MavenRepository(
xml <- \/.fromEither(compatibility.xmlParse(str))
_ <- if (xml.label == "project") \/-(()) else -\/("Project definition not found")
proj <- Pom.project(xml)
} yield proj): (String \/ Project)
} yield proj.copy(configurations = defaultConfigurations)): (String \/ Project)
}
}
}

View File

@ -43,16 +43,14 @@ object Pom {
private def readVersion(node: Node) =
text(node, "version", "Version").getOrElse("").trim
private val defaultScope = Scope.Other("")
private val defaultType = "jar"
private val defaultClassifier = ""
def dependency(node: Node): String \/ Dependency = {
def dependency(node: Node): String \/ (String, Dependency) = {
for {
mod <- module(node)
version0 = readVersion(node)
scopeOpt = text(node, "scope", "").toOption
.map(Parse.scope)
typeOpt = text(node, "type", "").toOption
classifierOpt = text(node, "classifier", "").toOption
xmlExclusions = node.child
@ -64,10 +62,10 @@ object Pom {
xmlExclusions.toList.traverseU(module(_))
}
optional = text(node, "optional", "").toOption.toSeq.contains("true")
} yield Dependency(
} yield scopeOpt.getOrElse("") -> Dependency(
mod,
version0,
scopeOpt getOrElse defaultScope,
"",
Attributes(typeOpt getOrElse defaultType, classifierOpt getOrElse defaultClassifier),
exclusions.map(mod => (mod.organization, mod.name)).toSet,
optional
@ -191,6 +189,7 @@ object Pom {
deps,
parentModuleOpt.map((_, parentVersionOpt.getOrElse(""))),
depMgmts,
Map.empty,
properties.toMap,
profiles,
None,

View File

@ -9,8 +9,8 @@ package object coursier {
def apply(
module: Module,
version: String,
// Substituted by Resolver with its own default scope (compile)
scope: Scope = Scope.Other(""),
// Substituted by Resolver with its own default configuration (compile)
configuration: String = "",
attributes: Attributes = Attributes(),
exclusions: Set[(String, String)] = Set.empty,
optional: Boolean = false
@ -18,7 +18,7 @@ package object coursier {
core.Dependency(
module,
version,
scope,
configuration,
attributes,
exclusions,
optional
@ -48,8 +48,6 @@ package object coursier {
type ModuleVersion = (core.Module, String)
type Scope = core.Scope
val Scope = core.Scope
type Repository = core.Repository
val Repository = core.Repository

View File

@ -44,7 +44,8 @@ object CentralTests extends TestSuite {
def resolutionCheck(
module: Module,
version: String,
extraRepo: Option[Repository] = None
extraRepo: Option[Repository] = None,
configuration: String = ""
) =
async {
val expected =
@ -54,7 +55,7 @@ object CentralTests extends TestSuite {
.split('\n')
.toSeq
val dep = Dependency(module, version)
val dep = Dependency(module, version, configuration = configuration)
val res = await(resolve(Set(dep), extraRepo = extraRepo))
val result = res
@ -138,6 +139,7 @@ object CentralTests extends TestSuite {
resolutionCheck(
Module("com.github.fommil", "java-logging"),
"1.2-SNAPSHOT",
configuration = "runtime",
extraRepo = Some(MavenRepository("https://oss.sonatype.org/content/repositories/public/"))
)
}

View File

@ -21,7 +21,7 @@ object PomParsingTests extends TestSuite {
</dependency>
"""
val expected = \/-(Dependency(Module("comp", "lib"), "2.1", attributes = Attributes(classifier = "extra")))
val expected = \/-("" -> Dependency(Module("comp", "lib"), "2.1", attributes = Attributes(classifier = "extra")))
val result = Pom.dependency(xmlParse(depNode).right.get)
@ -90,7 +90,7 @@ object PomParsingTests extends TestSuite {
None,
Profile.Activation(Nil),
Seq(
Dependency(Module("comp", "lib"), "0.2")),
"" -> Dependency(Module("comp", "lib"), "0.2")),
Nil,
Map.empty
))
@ -122,7 +122,7 @@ object PomParsingTests extends TestSuite {
Profile.Activation(Nil),
Nil,
Seq(
Dependency(Module("comp", "lib"), "0.2", scope = Scope.Test)),
"test" -> Dependency(Module("comp", "lib"), "0.2")),
Map.empty
))

View File

@ -2,6 +2,7 @@ package coursier
package test
import coursier.core.Repository
import coursier.maven.MavenRepository
import utest._
import scala.async.Async.{ async, await }
@ -27,69 +28,68 @@ object ResolutionTests extends TestSuite {
Project(Module("acme", "config"), "1.3.0"),
Project(Module("acme", "play"), "2.4.0", Seq(
Dependency(Module("acme", "play-json"), "2.4.0"))),
"" -> Dependency(Module("acme", "play-json"), "2.4.0"))),
Project(Module("acme", "play-json"), "2.4.0"),
Project(Module("acme", "play"), "2.4.1",
dependencies = Seq(
Dependency(Module("acme", "play-json"), "${playJsonVersion}"),
Dependency(Module("${project.groupId}", "${configName}"), "1.3.0")),
"" -> Dependency(Module("acme", "play-json"), "${playJsonVersion}"),
"" -> Dependency(Module("${project.groupId}", "${configName}"), "1.3.0")),
properties = Map(
"playJsonVersion" -> "2.4.0",
"configName" -> "config")),
Project(Module("acme", "play-extra-no-config"), "2.4.1",
Seq(
Dependency(Module("acme", "play"), "2.4.1",
"" -> Dependency(Module("acme", "play"), "2.4.1",
exclusions = Set(("acme", "config"))))),
Project(Module("acme", "play-extra-no-config-no"), "2.4.1",
Seq(
Dependency(Module("acme", "play"), "2.4.1",
"" -> Dependency(Module("acme", "play"), "2.4.1",
exclusions = Set(("*", "config"))))),
Project(Module("hudsucker", "mail"), "10.0",
Seq(
Dependency(Module("${project.groupId}", "test-util"), "${project.version}",
scope = Scope.Test))),
"test" -> Dependency(Module("${project.groupId}", "test-util"), "${project.version}"))),
Project(Module("hudsucker", "test-util"), "10.0"),
Project(Module("se.ikea", "parent"), "18.0",
dependencyManagement = Seq(
Dependency(Module("acme", "play"), "2.4.0",
"" -> Dependency(Module("acme", "play"), "2.4.0",
exclusions = Set(("acme", "play-json"))))),
Project(Module("se.ikea", "billy"), "18.0",
dependencies = Seq(
Dependency(Module("acme", "play"), "")),
"" -> Dependency(Module("acme", "play"), "")),
parent = Some(Module("se.ikea", "parent"), "18.0")),
Project(Module("org.gnome", "parent"), "7.0",
Seq(
Dependency(Module("org.gnu", "glib"), "13.4"))),
"" -> Dependency(Module("org.gnu", "glib"), "13.4"))),
Project(Module("org.gnome", "panel-legacy"), "7.0",
dependencies = Seq(
Dependency(Module("org.gnome", "desktop"), "${project.version}")),
"" -> Dependency(Module("org.gnome", "desktop"), "${project.version}")),
parent = Some(Module("org.gnome", "parent"), "7.0")),
Project(Module("gov.nsa", "secure-pgp"), "10.0",
Seq(
Dependency(Module("gov.nsa", "crypto"), "536.89"))),
"" -> Dependency(Module("gov.nsa", "crypto"), "536.89"))),
Project(Module("com.mailapp", "mail-client"), "2.1",
dependencies = Seq(
Dependency(Module("gov.nsa", "secure-pgp"), "10.0",
"" -> Dependency(Module("gov.nsa", "secure-pgp"), "10.0",
exclusions = Set(("*", "${crypto.name}")))),
properties = Map("crypto.name" -> "crypto", "dummy" -> "2")),
Project(Module("com.thoughtworks.paranamer", "paranamer-parent"), "2.6",
dependencies = Seq(
Dependency(Module("junit", "junit"), "")),
"" -> Dependency(Module("junit", "junit"), "")),
dependencyManagement = Seq(
Dependency(Module("junit", "junit"), "4.11", scope = Scope.Test))),
"test" -> Dependency(Module("junit", "junit"), "4.11"))),
Project(Module("com.thoughtworks.paranamer", "paranamer"), "2.6",
parent = Some(Module("com.thoughtworks.paranamer", "paranamer-parent"), "2.6")),
@ -97,33 +97,33 @@ object ResolutionTests extends TestSuite {
Project(Module("com.github.dummy", "libb"), "0.3.3",
profiles = Seq(
Profile("default", activeByDefault = Some(true), dependencies = Seq(
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))))),
"" -> Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))))),
Project(Module("com.github.dummy", "libb"), "0.4.2",
dependencies = Seq(
Dependency(Module("org.scalaverification", "scala-verification"), "1.12.4")),
"" -> Dependency(Module("org.scalaverification", "scala-verification"), "1.12.4")),
profiles = Seq(
Profile("default", activeByDefault = Some(true), dependencies = Seq(
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"),
Dependency(Module("org.scalaverification", "scala-verification"), "1.12.4", scope = Scope.Test))))),
"" -> Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"),
"test" -> Dependency(Module("org.scalaverification", "scala-verification"), "1.12.4"))))),
Project(Module("com.github.dummy", "libb"), "0.5.3",
properties = Map("special" -> "true"),
profiles = Seq(
Profile("default", activation = Profile.Activation(properties = Seq("special" -> None)), dependencies = Seq(
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))))),
"" -> Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))))),
Project(Module("com.github.dummy", "libb"), "0.5.4",
properties = Map("special" -> "true"),
profiles = Seq(
Profile("default", activation = Profile.Activation(properties = Seq("special" -> Some("true"))), dependencies = Seq(
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))))),
"" -> Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))))),
Project(Module("com.github.dummy", "libb"), "0.5.5",
properties = Map("special" -> "true"),
profiles = Seq(
Profile("default", activation = Profile.Activation(properties = Seq("special" -> Some("!false"))), dependencies = Seq(
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))))),
"" -> Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))))),
Project(Module("com.github.dummy", "libb-parent"), "0.5.6",
properties = Map("special" -> "true")),
@ -133,39 +133,39 @@ object ResolutionTests extends TestSuite {
properties = Map("special" -> "true"),
profiles = Seq(
Profile("default", activation = Profile.Activation(properties = Seq("special" -> Some("!false"))), dependencies = Seq(
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))))),
"" -> Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))))),
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"))),
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"))),
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"))),
Seq("" -> Dependency(Module("an-org", "a-name"), "1.0"))),
// Must bring transitively an-org:a-name, as an optional dependency
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", "a-lib"), "1.0", exclusions = Set(("an-org", "a-name"))),
"" -> 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"))),
"" -> 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")))
"" -> Dependency(Module("an-org", "a-lib"), "1.2")))
)
val projectsMap = projects.map(p => p.moduleVersion -> p).toMap
val projectsMap = projects.map(p => p.moduleVersion -> p.copy(configurations = MavenRepository.defaultConfigurations)).toMap
val testRepository = new TestRepository(projectsMap)
val repositories = Seq[Repository](
@ -308,8 +308,7 @@ object ResolutionTests extends TestSuite {
async {
val dep = Dependency(Module("hudsucker", "mail"), "10.0")
val res = await(resolve0(
Set(dep),
filter = Some(_.scope == Scope.Compile)
Set(dep)
)).copy(filter = None)
val expected = Resolution(
@ -331,8 +330,7 @@ object ResolutionTests extends TestSuite {
exclusions = Set(("acme", "play-json")))
)
val res = await(resolve0(
Set(dep),
filter = Some(_.scope == Scope.Compile)
Set(dep)
)).copy(filter = None, projectCache = Map.empty)
val expected = Resolution(
@ -350,8 +348,7 @@ object ResolutionTests extends TestSuite {
Dependency(Module("org.gnu", "glib"), "13.4"),
Dependency(Module("org.gnome", "desktop"), "7.0"))
val res = await(resolve0(
Set(dep),
filter = Some(_.scope == Scope.Compile)
Set(dep)
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
@ -368,8 +365,7 @@ object ResolutionTests extends TestSuite {
val trDeps = Seq(
Dependency(Module("gov.nsa", "secure-pgp"), "10.0", exclusions = Set(("*", "crypto"))))
val res = await(resolve0(
Set(dep),
filter = Some(_.scope == Scope.Compile)
Set(dep)
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
@ -384,8 +380,7 @@ object ResolutionTests extends TestSuite {
async {
val dep = Dependency(Module("com.thoughtworks.paranamer", "paranamer"), "2.6")
val res = await(resolve0(
Set(dep),
filter = Some(_.scope == Scope.Compile)
Set(dep)
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
@ -402,8 +397,7 @@ object ResolutionTests extends TestSuite {
val trDeps = Seq(
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))
val res = await(resolve0(
Set(dep),
filter = Some(_.scope == Scope.Compile)
Set(dep)
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
@ -422,8 +416,7 @@ object ResolutionTests extends TestSuite {
val trDeps = Seq(
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))
val res = await(resolve0(
Set(dep),
filter = Some(_.scope == Scope.Compile)
Set(dep)
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
@ -444,8 +437,7 @@ object ResolutionTests extends TestSuite {
val trDeps = Seq(
Dependency(Module("org.escalier", "librairie-standard"), "2.11.6"))
val res = await(resolve0(
Set(dep),
filter = Some(_.scope == Scope.Compile)
Set(dep)
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
@ -466,7 +458,7 @@ object ResolutionTests extends TestSuite {
Dependency(Module("an-org", "a-name"), "1.0", optional = true))
val res = await(resolve0(
Set(dep),
filter = Some(_.scope == Scope.Compile)
filter = Some(_ => true)
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
@ -489,7 +481,7 @@ object ResolutionTests extends TestSuite {
Dependency(Module("an-org", "a-name"), "1.0", optional = true))
val res = await(resolve0(
deps,
filter = Some(_.scope == Scope.Compile)
filter = Some(_ => true)
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
@ -511,8 +503,7 @@ object ResolutionTests extends TestSuite {
val res = await(resolve0(
deps,
forceVersions = depOverrides,
filter = Some(_.scope == Scope.Compile)
forceVersions = depOverrides
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
@ -536,8 +527,7 @@ object ResolutionTests extends TestSuite {
val res = await(resolve0(
deps,
forceVersions = depOverrides,
filter = Some(_.scope == Scope.Compile)
forceVersions = depOverrides
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
@ -563,8 +553,7 @@ object ResolutionTests extends TestSuite {
val res = await(resolve0(
deps,
forceVersions = depOverrides,
filter = Some(_.scope == Scope.Compile)
forceVersions = depOverrides
)).copy(filter = None, projectCache = Map.empty, errorCache = Map.empty)
val expected = Resolution(
@ -585,9 +574,9 @@ object ResolutionTests extends TestSuite {
'propertySubstitution{
val res =
core.Resolution.withProperties(
Seq(Dependency(Module("a-company", "a-name"), "${a.property}")),
Seq("" -> Dependency(Module("a-company", "a-name"), "${a.property}")),
Map("a.property" -> "a-version"))
val expected = Seq(Dependency(Module("a-company", "a-name"), "a-version"))
val expected = Seq("" -> Dependency(Module("a-company", "a-name"), "a-version"))
assert(res == expected)
}

View File

@ -3,7 +3,7 @@ package coursier
package object test {
implicit class DependencyOps(val underlying: Dependency) extends AnyVal {
def withCompileScope: Dependency = underlying.copy(scope = Scope.Compile)
def withCompileScope: Dependency = underlying.copy(configuration = "compile")
}
object Profile {
@ -16,8 +16,8 @@ package object test {
def apply(id: String,
activeByDefault: Option[Boolean] = None,
activation: Activation = Activation(),
dependencies: Seq[Dependency] = Nil,
dependencyManagement: Seq[Dependency] = Nil,
dependencies: Seq[(String, Dependency)] = Nil,
dependencyManagement: Seq[(String, Dependency)] = Nil,
properties: Map[String, String] = Map.empty) =
core.Profile(id, activeByDefault, activation, dependencies, dependencyManagement, properties)
}
@ -25,13 +25,15 @@ package object test {
object Project {
def apply(module: Module,
version: String,
dependencies: Seq[Dependency] = Seq.empty,
dependencies: Seq[(String, Dependency)] = Seq.empty,
parent: Option[ModuleVersion] = None,
dependencyManagement: Seq[Dependency] = Seq.empty,
dependencyManagement: Seq[(String, Dependency)] = Seq.empty,
configurations: Map[String, Seq[String]] = Map.empty,
properties: Map[String, String] = Map.empty,
profiles: Seq[Profile] = Seq.empty,
versions: Option[core.Versions] = None,
snapshotVersioning: Option[core.SnapshotVersioning] = None): Project =
core.Project(module, version, dependencies, parent, dependencyManagement, properties, profiles, versions, snapshotVersioning)
snapshotVersioning: Option[core.SnapshotVersioning] = None
): Project =
core.Project(module, version, dependencies, parent, dependencyManagement, configurations, properties, profiles, versions, snapshotVersioning)
}
}

View File

@ -17,8 +17,7 @@ import scala.scalajs.js
import js.Dynamic.{ global => g }
case class ResolutionOptions(
followOptional: Boolean = false,
keepTest: Boolean = false
followOptional: Boolean = false
)
case class State(
@ -66,7 +65,7 @@ class Backend($: BackendScope[Unit, State]) {
Seq(
dep.module.organization,
dep.module.name,
dep.scope.name
dep.configuration
).mkString(":")
for {
@ -176,8 +175,7 @@ class Backend($: BackendScope[Unit, State]) {
val res = coursier.Resolution(
s.modules.toSet,
filter = Some(dep =>
(s.options.followOptional || !dep.optional) &&
(s.options.keepTest || dep.scope != Scope.Test)
s.options.followOptional || !dep.optional
)
)
@ -338,14 +336,6 @@ class Backend($: BackendScope[Unit, State]) {
)
)
}
def toggleTest(e: ReactEventI) = {
$.modState(s =>
s.copy(
options = s.options
.copy(keepTest = !s.options.keepTest)
)
)
}
}
}
@ -380,7 +370,7 @@ object App {
<.td(dep.module.name),
<.td(finalVersionOpt.fold(dep.version)(finalVersion => s"$finalVersion (for ${dep.version})")),
<.td(Seq[Seq[TagMod]](
if (dep.scope == Scope.Compile) Seq() else Seq(infoLabel(dep.scope.name)),
if (dep.configuration == "compile") Seq() else Seq(infoLabel(dep.configuration)),
if (dep.attributes.`type`.isEmpty || dep.attributes.`type` == "jar") Seq() else Seq(infoLabel(dep.attributes.`type`)),
if (dep.attributes.classifier.isEmpty) Seq() else Seq(infoLabel(dep.attributes.classifier)),
Some(dep.exclusions).filter(_.nonEmpty).map(excls => infoPopOver("Exclusions", excls.toList.sorted.map{case (org, name) => s"$org:$name"}.mkString("; "))).toSeq,
@ -683,15 +673,6 @@ object App {
"Follow optional dependencies"
)
)
),
<.div(^.`class` := "checkbox",
<.label(
<.input(^.`type` := "checkbox",
^.onChange ==> backend.options.toggleTest,
if (options.keepTest) Seq(^.checked := "checked") else Seq(),
"Keep test dependencies"
)
)
)
)
}