Add support for packaging attribute in POMs

This commit is contained in:
Alexandre Archambault 2016-02-28 16:36:58 +01:00
parent cc36407cc2
commit b9895ce801
9 changed files with 139 additions and 33 deletions

View File

@ -145,7 +145,11 @@ lazy val core = crossProject
import com.typesafe.tools.mima.core._
import com.typesafe.tools.mima.core.ProblemFilters._
Seq()
Seq(
// Since 1.0.0-M10
// New singleton object, problem for forward compatibility only
ProblemFilters.exclude[MissingTypesProblem]("coursier.maven.MavenSource$")
)
}
)
.jvmSettings(

View File

@ -45,8 +45,10 @@ object MavenRepository {
"test" -> Seq("runtime")
)
def defaultPublications(moduleName: String) = Seq(
"compile" -> Publication(moduleName, "jar", "jar", ""),
val defaultPackaging = "jar"
def defaultPublications(moduleName: String, packaging: String) = Seq(
"compile" -> Publication(moduleName, packaging, MavenSource.typeExtension(packaging), ""),
"docs" -> Publication(moduleName, "doc", "jar", "javadoc"),
"sources" -> Publication(moduleName, "src", "jar", "sources")
)
@ -240,15 +242,22 @@ case class MavenRepository(
fetch(projectArtifact(module, version, versioningValue)).flatMap { str =>
EitherT {
F.point {
(for {
F.point[String \/ Project] {
for {
xml <- \/.fromEither(compatibility.xmlParse(str))
_ <- if (xml.label == "project") \/-(()) else -\/("Project definition not found")
proj <- Pom.project(xml)
} yield proj.copy(
configurations = defaultConfigurations,
publications = defaultPublications(module.name)
)): (String \/ Project)
} yield {
val packagingOpt = Pom.packagingOpt(xml)
proj.copy(
configurations = defaultConfigurations,
publications = defaultPublications(
module.name,
packagingOpt.getOrElse(defaultPackaging)
)
)
}
}
}
}

View File

@ -85,7 +85,7 @@ case class MavenSource(
)
}
overrideClassifiers match {
val publications0 = overrideClassifiers match {
case Some(classifiers) =>
val classifiersSet = classifiers.toSet
val publications = project.publications.collect {
@ -93,25 +93,68 @@ case class MavenSource(
p
}
val publications0 =
if (publications.isEmpty)
classifiers.map { classifier =>
Publication(dependency.module.name, "jar", "jar", classifier)
}
else
publications
publications0.map(artifactWithExtra)
// Unlike with Ivy metadata, Maven POMs don't list the available publications (~artifacts)
// so we give a chance to any classifier we're given by returning some publications
// no matter what, even if we're unsure they're available.
if (publications.isEmpty)
classifiers.map { classifier =>
Publication(
dependency.module.name,
"jar",
"jar",
classifier
)
}
else
publications
case None =>
Seq(
artifactWithExtra(
dependency.attributes.publication(
val publications =
if (dependency.attributes.classifier.nonEmpty)
// FIXME We're ignoring dependency.attributes.`type` in this case
project.publications.collect {
case (_, p) if p.classifier == dependency.attributes.classifier =>
p
}
else if (dependency.attributes.`type`.nonEmpty)
project.publications.collect {
case (_, p) if p.`type` == dependency.attributes.`type` =>
p
}
else
project.publications.collect {
case (_, p) if p.classifier.isEmpty =>
p
}
// See comment above
if (publications.isEmpty) {
val type0 = if (dependency.attributes.`type`.isEmpty) "jar" else dependency.attributes.`type`
Seq(
Publication(
dependency.module.name,
dependency.attributes.`type`
type0,
MavenSource.typeExtension(type0),
dependency.attributes.classifier
)
)
)
} else
publications
}
publications0.map(artifactWithExtra)
}
}
object MavenSource {
def typeExtension(`type`: String): String =
`type` match {
// see similar things in sbt-maven-resolver/src/main/scala/sbt/mavenint/MavenRepositoryResolver.scala in SBT 0.13.8
case "eclipse-plugin" | "hk2-jar" | "orbit" | "scala-jar" | "jar" | "bundle" | "doc" | "src" => "jar"
case other => other
}
}

View File

@ -115,6 +115,9 @@ object Pom {
} yield Profile(id, activeByDefault, activation, deps, depMgmts, properties.toMap)
}
def packagingOpt(pom: Node): Option[String] =
text(pom, "packaging", "").toOption
def project(pom: Node): String \/ Project = {
import Scalaz._

View File

@ -30,7 +30,7 @@ package object coursier {
type Attributes = core.Attributes
object Attributes {
def apply(
`type`: String = "jar",
`type`: String = "",
classifier: String = ""
): Attributes =
core.Attributes(`type`, classifier)

View File

@ -2,6 +2,8 @@ package coursier
import java.util.GregorianCalendar
import coursier.maven.MavenSource
import sbt._
object ToSbt {
@ -18,8 +20,9 @@ object ToSbt {
def artifact(module: Module, artifact: Artifact): sbt.Artifact =
sbt.Artifact(
module.name,
// FIXME Get these two from publications
artifact.attributes.`type`,
"jar",
MavenSource.typeExtension(artifact.attributes.`type`),
Some(artifact.attributes.classifier).filter(_.nonEmpty),
Nil,
Some(url(artifact.url)),

View File

@ -7,6 +7,8 @@ import scala.async.Async.{ async, await }
import coursier.Platform.fetch
import coursier.test.compatibility._
import scala.concurrent.Future
object CentralTests extends TestSuite {
val repositories = Seq[Repository](
@ -76,6 +78,21 @@ object CentralTests extends TestSuite {
assert(result == expected)
}
def ensureArtifactHasExtension(module: Module, version: String, extension: String): Future[Unit] = async {
val dep = Dependency(module, version, transitive = false)
val res = await(resolve(Set(dep)))
res.artifacts match {
case Seq(artifact) =>
assert(artifact.url.endsWith("." + extension))
case other =>
throw new Exception(
s"Unexpected artifact list size: ${other.size}\n" +
"Artifacts:\n" + other.map(" " + _).mkString("\n")
)
}
}
val tests = TestSuite {
'logback{
async {
@ -86,8 +103,8 @@ object CentralTests extends TestSuite {
rootDependencies = Set(dep),
dependencies = Set(
dep.withCompileScope,
Dependency(Module("ch.qos.logback", "logback-core"), "1.1.3").withCompileScope,
Dependency(Module("org.slf4j", "slf4j-api"), "1.7.7").withCompileScope))
Dependency(Module("ch.qos.logback", "logback-core"), "1.1.3").withCompileScope.withJarAttributeType,
Dependency(Module("org.slf4j", "slf4j-api"), "1.7.7").withCompileScope.withJarAttributeType))
assert(res == expected)
}
@ -101,8 +118,8 @@ object CentralTests extends TestSuite {
rootDependencies = Set(dep),
dependencies = Set(
dep.withCompileScope,
Dependency(Module("org.ow2.asm", "asm-tree"), "5.0.2").withCompileScope,
Dependency(Module("org.ow2.asm", "asm"), "5.0.2").withCompileScope))
Dependency(Module("org.ow2.asm", "asm-tree"), "5.0.2").withCompileScope.withJarAttributeType,
Dependency(Module("org.ow2.asm", "asm"), "5.0.2").withCompileScope.withJarAttributeType))
assert(res == expected)
}
@ -173,6 +190,26 @@ object CentralTests extends TestSuite {
"7.0.+"
)
}
'packaging - {
* - {
// random aar-based module found on Central
ensureArtifactHasExtension(
Module("com.yandex.android", "speechkit"),
"2.5.0",
"aar"
)
}
* - {
// has packaging bundle - ensuring coursier gives its artifact the .jar extension
ensureArtifactHasExtension(
Module("com.google.guava", "guava"),
"17.0",
"jar"
)
}
}
}
}

View File

@ -21,7 +21,13 @@ 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")
).withJarAttributeType
)
val result = Pom.dependency(xmlParse(depNode).right.get)
@ -90,7 +96,7 @@ object PomParsingTests extends TestSuite {
None,
Profile.Activation(Nil),
Seq(
"" -> Dependency(Module("comp", "lib"), "0.2")),
"" -> Dependency(Module("comp", "lib"), "0.2").withJarAttributeType),
Nil,
Map.empty
))
@ -122,7 +128,7 @@ object PomParsingTests extends TestSuite {
Profile.Activation(Nil),
Nil,
Seq(
"test" -> Dependency(Module("comp", "lib"), "0.2")),
"test" -> Dependency(Module("comp", "lib"), "0.2").withJarAttributeType),
Map.empty
))

View File

@ -4,6 +4,7 @@ package object test {
implicit class DependencyOps(val underlying: Dependency) extends AnyVal {
def withCompileScope: Dependency = underlying.copy(configuration = "compile")
def withJarAttributeType: Dependency = underlying.copy(attributes = underlying.attributes.copy(`type` = "jar"))
}
implicit class ResolutionOps(val underlying: Resolution) extends AnyVal {