From f52e2ecca43a0dfb27cf8a966d584afbba90b067 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 30 Dec 2015 01:34:37 +0100 Subject: [PATCH] Add support for attributes --- cli/src/main/scala/coursier/cli/Helper.scala | 18 +++++++++++++++-- .../coursier/core/compatibility/package.scala | 2 +- .../coursier/core/compatibility/package.scala | 12 ++++++++--- .../scala/coursier/core/Definitions.scala | 14 +++++++++---- .../scala/coursier/ivy/IvyRepository.scala | 20 +++++++++---------- .../src/main/scala/coursier/ivy/IvyXml.scala | 8 ++++++-- .../src/main/scala/coursier/maven/Pom.scala | 4 ++-- .../src/main/scala/coursier/package.scala | 4 ++-- .../src/main/scala/coursier/util/Xml.scala | 11 ++++++++-- web/src/main/scala/coursier/web/Backend.scala | 5 ++++- 10 files changed, 69 insertions(+), 29 deletions(-) diff --git a/cli/src/main/scala/coursier/cli/Helper.scala b/cli/src/main/scala/coursier/cli/Helper.scala index 420d7efdc..ce4db9ea2 100644 --- a/cli/src/main/scala/coursier/cli/Helper.scala +++ b/cli/src/main/scala/coursier/cli/Helper.scala @@ -158,8 +158,22 @@ class Helper( } val moduleVersions = splitDependencies.map{ - case Seq(org, name, version) => - (Module(org, name), version) + case Seq(org, namePart, version) => + val p = namePart.split(';') + val name = p.head + val splitAttributes = p.tail.map(_.split("=", 2).toSeq).toSeq + val malformedAttributes = splitAttributes.filter(_.length != 2) + if (malformedAttributes.nonEmpty) { + // FIXME Get these for all dependencies at once + Console.err.println(s"Malformed attributes in ${splitDependencies.mkString(":")}") + // :( + sys.exit(255) + } + val attributes = splitAttributes.collect { + case Seq(k, v) => k -> v + } + println(s"-> $org:$name:$attributes") + (Module(org, name, attributes.toMap), version) } val deps = moduleVersions.map{case (mod, ver) => diff --git a/core/js/src/main/scala/coursier/core/compatibility/package.scala b/core/js/src/main/scala/coursier/core/compatibility/package.scala index f7659c987..b168d85e1 100644 --- a/core/js/src/main/scala/coursier/core/compatibility/package.scala +++ b/core/js/src/main/scala/coursier/core/compatibility/package.scala @@ -51,7 +51,7 @@ package object compatibility { .map(l => List.tabulate(l.length)(l.item).map(fromNode)) .getOrElse(Nil) - def attributes: Seq[(String, String)] = ??? + def attributes = ??? // `exists` instead of `contains`, for scala 2.10 def isText = diff --git a/core/jvm/src/main/scala/coursier/core/compatibility/package.scala b/core/jvm/src/main/scala/coursier/core/compatibility/package.scala index 1b398bcab..3a8f6dc49 100644 --- a/core/jvm/src/main/scala/coursier/core/compatibility/package.scala +++ b/core/jvm/src/main/scala/coursier/core/compatibility/package.scala @@ -2,7 +2,7 @@ package coursier.core import coursier.util.Xml -import scala.xml.{ MetaData, Null } +import scala.xml.{ Attribute, MetaData, Null } package object compatibility { @@ -19,14 +19,20 @@ package object compatibility { def fromNode(node: scala.xml.Node): Xml.Node = new Xml.Node { lazy val attributes = { - def helper(m: MetaData): Stream[(String, String)] = + def helper(m: MetaData): Stream[(String, String, String)] = m match { case Null => Stream.empty case attr => + val pre = attr match { + case a: Attribute => Option(node.getNamespace(a.pre)).getOrElse("") + case _ => "" + } + val value = attr.value.collect { case scala.xml.Text(t) => t }.mkString("") - (attr.key -> value) #:: helper(m.next) + + (pre, attr.key, value) #:: helper(m.next) } helper(node.attributes).toVector diff --git a/core/shared/src/main/scala/coursier/core/Definitions.scala b/core/shared/src/main/scala/coursier/core/Definitions.scala index c741d6a2d..08a247bf7 100644 --- a/core/shared/src/main/scala/coursier/core/Definitions.scala +++ b/core/shared/src/main/scala/coursier/core/Definitions.scala @@ -8,12 +8,11 @@ package coursier.core * between them. * * Using the same terminology as Ivy. - * - * Ivy attributes would land here, if support for it is added. */ case class Module( organization: String, - name: String + name: String, + attributes: Map[String, String] ) { def trim: Module = copy( @@ -21,7 +20,14 @@ case class Module( name = name.trim ) - override def toString = s"$organization:$name" + private def attributesStr = attributes.toSeq + .sortBy { case (k, _) => k } + .map { case (k, v) => s"$k=$v" } + .mkString(";") + + override def toString = + s"$organization:$name" + + (if (attributes.nonEmpty) s";$attributesStr" else "") } /** diff --git a/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala b/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala index b1ce1a788..9a4e0c3fc 100644 --- a/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala +++ b/core/shared/src/main/scala/coursier/ivy/IvyRepository.scala @@ -145,23 +145,22 @@ case class IvyRepository( // list of variables that should be supported. // Some are missing (branch, conf, originalName). private def variables( - org: String, - name: String, + module: Module, version: String, `type`: String, artifact: String, ext: String ) = Map( - "organization" -> org, - "organisation" -> org, - "orgPath" -> org.replace('.', '/'), - "module" -> name, + "organization" -> module.organization, + "organisation" -> module.organization, + "orgPath" -> module.organization.replace('.', '/'), + "module" -> module.name, "revision" -> version, "type" -> `type`, "artifact" -> artifact, "ext" -> ext - ) + ) ++ module.attributes val source: Artifact.Source = new Artifact.Source { @@ -191,8 +190,7 @@ case class IvyRepository( val retainedWithUrl = retained.flatMap { p => substitute(variables( - dependency.module.organization, - dependency.module.name, + dependency.module, dependency.version, p.`type`, p.name, @@ -225,7 +223,9 @@ case class IvyRepository( val eitherArtifact: String \/ Artifact = for { - url <- substitute(variables(module.organization, module.name, version, "ivy", "ivy", "xml")) + url <- substitute( + variables(module, version, "ivy", "ivy", "xml") + ) } yield Artifact( url, diff --git a/core/shared/src/main/scala/coursier/ivy/IvyXml.scala b/core/shared/src/main/scala/coursier/ivy/IvyXml.scala index 62b70cb8d..7725817bc 100644 --- a/core/shared/src/main/scala/coursier/ivy/IvyXml.scala +++ b/core/shared/src/main/scala/coursier/ivy/IvyXml.scala @@ -7,12 +7,15 @@ import scalaz.{ Node => _, _ }, Scalaz._ object IvyXml { + val attributesNamespace = "http://ant.apache.org/ivy/extra" + private def info(node: Node): String \/ (Module, String) = for { org <- node.attribute("organisation") name <- node.attribute("module") version <- node.attribute("revision") - } yield (Module(org, name), version) + attr = node.attributesFromNamespace(attributesNamespace) + } yield (Module(org, name, attr.toMap), version) // FIXME Errors are ignored here private def configurations(node: Node): Seq[(String, Seq[String])] = @@ -53,8 +56,9 @@ object IvyXml { (fromConf, toConf) <- rawConf.split(',').toSeq.map(_.split("->", 2)).collect { case Array(from, to) => from -> to } + attr = node.attributesFromNamespace(attributesNamespace) } yield fromConf -> Dependency( - Module(org, name), + Module(org, name, attr.toMap), version, toConf, allConfsExcludes ++ excludes.getOrElse(fromConf, Set.empty), diff --git a/core/shared/src/main/scala/coursier/maven/Pom.scala b/core/shared/src/main/scala/coursier/maven/Pom.scala index 6ad669204..f6980bc11 100644 --- a/core/shared/src/main/scala/coursier/maven/Pom.scala +++ b/core/shared/src/main/scala/coursier/maven/Pom.scala @@ -22,7 +22,7 @@ object Pom { else e } name <- text(node, "artifactId", "Name") - } yield Module(organization, name).trim + } yield Module(organization, name, Map.empty).trim } private def readVersion(node: Node) = @@ -307,7 +307,7 @@ object Pom { .toList .traverseU(snapshotVersion) } yield SnapshotVersioning( - Module(organization, name), + Module(organization, name, Map.empty), version, latest, release, diff --git a/core/shared/src/main/scala/coursier/package.scala b/core/shared/src/main/scala/coursier/package.scala index 3a7125b49..ec15aabc6 100644 --- a/core/shared/src/main/scala/coursier/package.scala +++ b/core/shared/src/main/scala/coursier/package.scala @@ -42,8 +42,8 @@ package object coursier { type Module = core.Module object Module { - def apply(organization: String, name: String): Module = - core.Module(organization, name) + def apply(organization: String, name: String, attributes: Map[String, String] = Map.empty): Module = + core.Module(organization, name, attributes) } type ModuleVersion = (core.Module, String) diff --git a/core/shared/src/main/scala/coursier/util/Xml.scala b/core/shared/src/main/scala/coursier/util/Xml.scala index a4e9c86df..83fe20f9b 100644 --- a/core/shared/src/main/scala/coursier/util/Xml.scala +++ b/core/shared/src/main/scala/coursier/util/Xml.scala @@ -7,13 +7,20 @@ object Xml { /** A representation of an XML node/document, with different implementations on JVM and JS */ trait Node { def label: String - def attributes: Seq[(String, String)] + /** Namespace / key / value */ + def attributes: Seq[(String, String, String)] def children: Seq[Node] def isText: Boolean def textContent: String def isElement: Boolean - lazy val attributesMap = attributes.toMap + def attributesFromNamespace(namespace: String): Seq[(String, String)] = + attributes.collect { + case (`namespace`, k, v) => + k -> v + } + + lazy val attributesMap = attributes.map { case (_, k, v) => k -> v }.toMap def attribute(name: String): String \/ String = attributesMap.get(name) match { case None => -\/(s"Missing attribute $name") diff --git a/web/src/main/scala/coursier/web/Backend.scala b/web/src/main/scala/coursier/web/Backend.scala index 5a03d9b97..69e6c3d96 100644 --- a/web/src/main/scala/coursier/web/Backend.scala +++ b/web/src/main/scala/coursier/web/Backend.scala @@ -406,7 +406,10 @@ object App { } val sortedDeps = res.minDependencies.toList - .sortBy(dep => coursier.core.Module.unapply(dep.module).get) + .sortBy { dep => + val (org, name, _) = coursier.core.Module.unapply(dep.module).get + (org, name) + } <.table(^.`class` := "table", <.thead(