diff --git a/cli/src/main/scala/coursier/cli/Helper.scala b/cli/src/main/scala/coursier/cli/Helper.scala index db82754d4..3f1a98d4d 100644 --- a/cli/src/main/scala/coursier/cli/Helper.scala +++ b/cli/src/main/scala/coursier/cli/Helper.scala @@ -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 = { diff --git a/core/shared/src/main/scala/coursier/core/Definitions.scala b/core/shared/src/main/scala/coursier/core/Definitions.scala index 130b48092..44550acf0 100644 --- a/core/shared/src/main/scala/coursier/core/Definitions.scala +++ b/core/shared/src/main/scala/coursier/core/Definitions.scala @@ -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] ) diff --git a/core/shared/src/main/scala/coursier/core/Orders.scala b/core/shared/src/main/scala/coursier/core/Orders.scala index dd5c7a28a..7df4a87eb 100644 --- a/core/shared/src/main/scala/coursier/core/Orders.scala +++ b/core/shared/src/main/scala/coursier/core/Orders.scala @@ -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)(_ ++ _) } diff --git a/core/shared/src/main/scala/coursier/core/Parse.scala b/core/shared/src/main/scala/coursier/core/Parse.scala index 4d89c35ea..3259d1ef1 100644 --- a/core/shared/src/main/scala/coursier/core/Parse.scala +++ b/core/shared/src/main/scala/coursier/core/Parse.scala @@ -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)) diff --git a/core/shared/src/main/scala/coursier/core/Resolution.scala b/core/shared/src/main/scala/coursier/core/Resolution.scala index fa4203bdd..891afee00 100644 --- a/core/shared/src/main/scala/coursier/core/Resolution.scala +++ b/core/shared/src/main/scala/coursier/core/Resolution.scala @@ -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 { diff --git a/core/shared/src/main/scala/coursier/maven/MavenRepository.scala b/core/shared/src/main/scala/coursier/maven/MavenRepository.scala index 3ea96fc81..b2b13f3fe 100644 --- a/core/shared/src/main/scala/coursier/maven/MavenRepository.scala +++ b/core/shared/src/main/scala/coursier/maven/MavenRepository.scala @@ -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) } } } diff --git a/core/shared/src/main/scala/coursier/maven/Pom.scala b/core/shared/src/main/scala/coursier/maven/Pom.scala index 8033498fa..4cd412d2a 100644 --- a/core/shared/src/main/scala/coursier/maven/Pom.scala +++ b/core/shared/src/main/scala/coursier/maven/Pom.scala @@ -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, diff --git a/core/shared/src/main/scala/coursier/package.scala b/core/shared/src/main/scala/coursier/package.scala index 6803f43ed..c815d5dcd 100644 --- a/core/shared/src/main/scala/coursier/package.scala +++ b/core/shared/src/main/scala/coursier/package.scala @@ -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 diff --git a/tests/shared/src/test/scala/coursier/test/CentralTests.scala b/tests/shared/src/test/scala/coursier/test/CentralTests.scala index 751397d13..7ea36ea21 100644 --- a/tests/shared/src/test/scala/coursier/test/CentralTests.scala +++ b/tests/shared/src/test/scala/coursier/test/CentralTests.scala @@ -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/")) ) } diff --git a/tests/shared/src/test/scala/coursier/test/PomParsingTests.scala b/tests/shared/src/test/scala/coursier/test/PomParsingTests.scala index 6bea7b696..833cabbbb 100644 --- a/tests/shared/src/test/scala/coursier/test/PomParsingTests.scala +++ b/tests/shared/src/test/scala/coursier/test/PomParsingTests.scala @@ -21,7 +21,7 @@ object PomParsingTests extends TestSuite { """ - 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 )) diff --git a/tests/shared/src/test/scala/coursier/test/ResolutionTests.scala b/tests/shared/src/test/scala/coursier/test/ResolutionTests.scala index fd9d0ea4d..627ed288b 100644 --- a/tests/shared/src/test/scala/coursier/test/ResolutionTests.scala +++ b/tests/shared/src/test/scala/coursier/test/ResolutionTests.scala @@ -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) } diff --git a/tests/shared/src/test/scala/coursier/test/package.scala b/tests/shared/src/test/scala/coursier/test/package.scala index ebcf0b6ab..dc8d31315 100644 --- a/tests/shared/src/test/scala/coursier/test/package.scala +++ b/tests/shared/src/test/scala/coursier/test/package.scala @@ -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) } } diff --git a/web/src/main/scala/coursier/web/Backend.scala b/web/src/main/scala/coursier/web/Backend.scala index 56117eb1c..8ae7d05a9 100644 --- a/web/src/main/scala/coursier/web/Backend.scala +++ b/web/src/main/scala/coursier/web/Backend.scala @@ -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" - ) - ) ) ) }