diff --git a/build.sbt b/build.sbt index 954449aa0..ebff0e355 100644 --- a/build.sbt +++ b/build.sbt @@ -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( diff --git a/core/shared/src/main/scala/coursier/maven/MavenRepository.scala b/core/shared/src/main/scala/coursier/maven/MavenRepository.scala index 3f7b9d57f..fbcc5852b 100644 --- a/core/shared/src/main/scala/coursier/maven/MavenRepository.scala +++ b/core/shared/src/main/scala/coursier/maven/MavenRepository.scala @@ -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) + ) + ) + } } } } diff --git a/core/shared/src/main/scala/coursier/maven/MavenSource.scala b/core/shared/src/main/scala/coursier/maven/MavenSource.scala index ac2b71434..0d47c9839 100644 --- a/core/shared/src/main/scala/coursier/maven/MavenSource.scala +++ b/core/shared/src/main/scala/coursier/maven/MavenSource.scala @@ -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 + } + +} \ No newline at end of file diff --git a/core/shared/src/main/scala/coursier/maven/Pom.scala b/core/shared/src/main/scala/coursier/maven/Pom.scala index c0b734898..0a5e88c45 100644 --- a/core/shared/src/main/scala/coursier/maven/Pom.scala +++ b/core/shared/src/main/scala/coursier/maven/Pom.scala @@ -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._ diff --git a/core/shared/src/main/scala/coursier/package.scala b/core/shared/src/main/scala/coursier/package.scala index 15ab6efb4..e663aba10 100644 --- a/core/shared/src/main/scala/coursier/package.scala +++ b/core/shared/src/main/scala/coursier/package.scala @@ -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) diff --git a/plugin/src/main/scala-2.10/coursier/ToSbt.scala b/plugin/src/main/scala-2.10/coursier/ToSbt.scala index 8839f8ffa..df6e4ebc9 100644 --- a/plugin/src/main/scala-2.10/coursier/ToSbt.scala +++ b/plugin/src/main/scala-2.10/coursier/ToSbt.scala @@ -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)), diff --git a/tests/shared/src/test/scala/coursier/test/CentralTests.scala b/tests/shared/src/test/scala/coursier/test/CentralTests.scala index 56794c494..974700b9c 100644 --- a/tests/shared/src/test/scala/coursier/test/CentralTests.scala +++ b/tests/shared/src/test/scala/coursier/test/CentralTests.scala @@ -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" + ) + } + } } } diff --git a/tests/shared/src/test/scala/coursier/test/PomParsingTests.scala b/tests/shared/src/test/scala/coursier/test/PomParsingTests.scala index bb8452b79..290c968cb 100644 --- a/tests/shared/src/test/scala/coursier/test/PomParsingTests.scala +++ b/tests/shared/src/test/scala/coursier/test/PomParsingTests.scala @@ -21,7 +21,13 @@ 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") + ).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 )) diff --git a/tests/shared/src/test/scala/coursier/test/package.scala b/tests/shared/src/test/scala/coursier/test/package.scala index 7fe13dce0..aab39b85e 100644 --- a/tests/shared/src/test/scala/coursier/test/package.scala +++ b/tests/shared/src/test/scala/coursier/test/package.scala @@ -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 {