Merge pull request #1944 from sbt/wip/fix-aether-range-query

Wip/fix aether range query
This commit is contained in:
eugene yokota 2015-03-27 13:01:19 -07:00
commit 289162a301
8 changed files with 119 additions and 61 deletions

View File

@ -21,8 +21,61 @@ import org.apache.ivy.core.settings.IvySettings
import org.apache.ivy.core.module.descriptor.{ DependencyArtifactDescriptor, DependencyDescriptor, License, ModuleDescriptor, ExcludeRule }
import org.apache.ivy.plugins.resolver.{ ChainResolver, DependencyResolver, IBiblioResolver }
import ivyint.CustomRemoteMavenResolver
object MakePom {
/** True if the revision is an ivy-range, not a complete revision. */
def isDependencyVersionRange(revision: String): Boolean = {
(revision endsWith "+") ||
(revision contains "[") ||
(revision contains "]") ||
(revision contains "(") ||
(revision contains ")")
}
/** Converts Ivy revision ranges to that of Maven POM */
def makeDependencyVersion(revision: String): String = {
def plusRange(s: String, shift: Int = 0) = {
def pow(i: Int): Int = if (i > 0) 10 * pow(i - 1) else 1
val (prefixVersion, lastVersion) = (s + "0" * shift).reverse.split("\\.", 2) match {
case Array(revLast, revRest) =>
(revRest.reverse + ".", revLast.reverse)
case Array(revLast) => ("", revLast.reverse)
}
val lastVersionInt = lastVersion.toInt
s"[${prefixVersion}${lastVersion},${prefixVersion}${lastVersionInt + pow(shift)})"
}
val startSym = Set(']', '[', '(')
val stopSym = Set(']', '[', ')')
val DotPlusPattern = """(.+)\.\+""".r
val DotNumPlusPattern = """(.+)\.(\d+)\+""".r
val NumPlusPattern = """(\d+)\+""".r
val maxDigit = 5
try {
revision match {
case "+" => "[0,)"
case DotPlusPattern(base) => plusRange(base)
// This is a heuristic. Maven just doesn't support Ivy's notions of 1+, so
// we assume version ranges never go beyond 5 siginificant digits.
case NumPlusPattern(tail) => (0 until maxDigit).map(plusRange(tail, _)).mkString(",")
case DotNumPlusPattern(base, tail) => (0 until maxDigit).map(plusRange(base + "." + tail, _)).mkString(",")
case rev if rev endsWith "+" => sys.error(s"dynamic revision '$rev' cannot be translated to POM")
case rev if startSym(rev(0)) && stopSym(rev(rev.length - 1)) =>
val start = rev(0)
val stop = rev(rev.length - 1)
val mid = rev.substring(1, rev.length - 1)
(if (start == ']') "(" else start) + mid + (if (stop == '[') ")" else stop)
case _ => revision
}
} catch {
case e: NumberFormatException =>
// TODO - if the version doesn't meet our expectations, maybe we just issue a hard
// error instead of softly ignoring the attempt to rewrite.
//sys.error(s"Could not fix version [$revision] into maven style version")
revision
}
}
}
class MakePom(val log: Logger) {
import MakePom._
@deprecated("Use `write(Ivy, ModuleDescriptor, ModuleInfo, Option[Iterable[Configuration]], Set[String], NodeSeq, XNode => XNode, MavenRepository => Boolean, Boolean, File)` instead", "0.11.2")
def write(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], extra: NodeSeq, process: XNode => XNode, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean, output: File): Unit =
write(ivy, module, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], Set(Artifact.DefaultType), extra, process, filterRepositories, allRepositories, output)
@ -218,49 +271,6 @@ class MakePom(val log: Logger) {
</dependency>
}
/** Converts Ivy revision ranges to that of Maven POM */
def makeDependencyVersion(revision: String): String = {
def plusRange(s: String, shift: Int = 0) = {
def pow(i: Int): Int = if (i > 0) 10 * pow(i - 1) else 1
val (prefixVersion, lastVersion) = (s + "0" * shift).reverse.split("\\.", 2) match {
case Array(revLast, revRest) =>
(revRest.reverse + ".", revLast.reverse)
case Array(revLast) => ("", revLast.reverse)
}
val lastVersionInt = lastVersion.toInt
s"[${prefixVersion}${lastVersion},${prefixVersion}${lastVersionInt + pow(shift)})"
}
val startSym = Set(']', '[', '(')
val stopSym = Set(']', '[', ')')
val DotPlusPattern = """(.+)\.\+""".r
val DotNumPlusPattern = """(.+)\.(\d+)\+""".r
val NumPlusPattern = """(\d+)\+""".r
val maxDigit = 5
try {
revision match {
case "+" => "[0,)"
case DotPlusPattern(base) => plusRange(base)
// This is a heuristic. Maven just doesn't support Ivy's notions of 1+, so
// we assume version ranges never go beyond 5 siginificant digits.
case NumPlusPattern(tail) => (0 until maxDigit).map(plusRange(tail, _)).mkString(",")
case DotNumPlusPattern(base, tail) => (0 until maxDigit).map(plusRange(base + "." + tail, _)).mkString(",")
case rev if rev endsWith "+" => sys.error(s"dynamic revision '$rev' cannot be translated to POM")
case rev if startSym(rev(0)) && stopSym(rev(rev.length - 1)) =>
val start = rev(0)
val stop = rev(rev.length - 1)
val mid = rev.substring(1, rev.length - 1)
(if (start == ']') "(" else start) + mid + (if (stop == '[') ")" else stop)
case _ => revision
}
} catch {
case e: NumberFormatException =>
// TODO - if the version doesn't meet our expectations, maybe we just issue a hard
// error instead of softly ignoring the attempt to rewrite.
//sys.error(s"Could not fix version [$revision] into maven style version")
revision
}
}
@deprecated("No longer used and will be removed.", "0.12.1")
def classifier(dependency: DependencyDescriptor, includeTypes: Set[String]): NodeSeq =
{

View File

@ -61,10 +61,10 @@ class MakePomSpec extends Specification {
val mp = new MakePom(ConsoleLogger())
def convertTo(s: String, expected: String) =
mp.makeDependencyVersion(s) must_== expected
MakePom.makeDependencyVersion(s) must_== expected
def beParsedAsError(s: String) =
try {
mp.makeDependencyVersion(s)
MakePom.makeDependencyVersion(s)
failure
} catch {
case e: Throwable => success

View File

@ -9,7 +9,9 @@ import org.eclipse.aether.metadata.{ DefaultMetadata, Metadata }
import org.eclipse.aether.resolution.{
ArtifactDescriptorRequest => AetherDescriptorRequest,
ArtifactRequest => AetherArtifactRequest,
MetadataRequest => AetherMetadataRequest
MetadataRequest => AetherMetadataRequest,
VersionRequest => AetherVersionRequest,
VersionRangeRequest => AetherVersionRangeRequest
}
import sbt.ivyint.CustomMavenResolver
@ -29,6 +31,8 @@ class MavenCacheRepositoryResolver(val repo: MavenCache, settings: IvySettings)
protected def setRepository(request: AetherMetadataRequest): AetherMetadataRequest = request
protected def addRepositories(request: AetherDescriptorRequest): AetherDescriptorRequest = request
protected def addRepositories(request: AetherArtifactRequest): AetherArtifactRequest = request
protected def addRepositories(request: AetherVersionRequest): AetherVersionRequest = request
protected def addRepositories(request: AetherVersionRangeRequest): AetherVersionRangeRequest = request
protected def publishArtifacts(artifacts: Seq[AetherArtifact]): Unit = {
val request = new AetherInstallRequest()
artifacts foreach request.addArtifact

View File

@ -11,7 +11,9 @@ import org.eclipse.aether.resolution.{
ArtifactDescriptorRequest => AetherDescriptorRequest,
ArtifactDescriptorResult => AetherDescriptorResult,
ArtifactRequest => AetherArtifactRequest,
MetadataRequest => AetherMetadataRequest
MetadataRequest => AetherMetadataRequest,
VersionRequest => AetherVersionRequest,
VersionRangeRequest => AetherVersionRangeRequest
}
import sbt.ivyint.CustomRemoteMavenResolver
import scala.collection.JavaConverters._
@ -43,6 +45,11 @@ class MavenRemoteRepositoryResolver(val repo: MavenRepository, settings: IvySett
if (isUseCacheOnly) request else request.addRepository(aetherRepository)
protected def addRepositories(request: AetherArtifactRequest): AetherArtifactRequest =
if (isUseCacheOnly) request else request.addRepository(aetherRepository)
protected def addRepositories(request: AetherVersionRequest): AetherVersionRequest =
if (isUseCacheOnly) request else request.addRepository(aetherRepository)
protected def addRepositories(request: AetherVersionRangeRequest): AetherVersionRangeRequest =
if (isUseCacheOnly) request else request.addRepository(aetherRepository)
/** Actually publishes aether artifacts. */
protected def publishArtifacts(artifacts: Seq[AetherArtifact]): Unit = {
val request = new AetherDeployRequest()

View File

@ -20,7 +20,15 @@ import org.eclipse.aether.artifact.{ DefaultArtifact => AetherArtifact }
import org.eclipse.aether.deployment.{ DeployRequest => AetherDeployRequest }
import org.eclipse.aether.installation.{ InstallRequest => AetherInstallRequest }
import org.eclipse.aether.metadata.{ DefaultMetadata, Metadata }
import org.eclipse.aether.resolution.{ ArtifactDescriptorRequest => AetherDescriptorRequest, ArtifactDescriptorResult => AetherDescriptorResult, ArtifactRequest => AetherArtifactRequest, ArtifactResolutionException, MetadataRequest => AetherMetadataRequest }
import org.eclipse.aether.resolution.{
ArtifactDescriptorRequest => AetherDescriptorRequest,
ArtifactDescriptorResult => AetherDescriptorResult,
ArtifactRequest => AetherArtifactRequest,
ArtifactResolutionException,
MetadataRequest => AetherMetadataRequest,
VersionRequest => AetherVersionRequest,
VersionRangeRequest => AetherVersionRangeRequest
}
import org.eclipse.aether.{ RepositorySystem, RepositorySystemSession }
import sbt.ivyint.{ CustomMavenResolver, CustomRemoteMavenResolver }
import sbt.mavenint.MavenRepositoryResolver.JarPackaging
@ -67,6 +75,8 @@ abstract class MavenRepositoryResolver(settings: IvySettings) extends AbstractRe
/** Inject necessary repositories into a descriptor request. */
protected def addRepositories(request: AetherDescriptorRequest): AetherDescriptorRequest
protected def addRepositories(request: AetherArtifactRequest): AetherArtifactRequest
protected def addRepositories(request: AetherVersionRequest): AetherVersionRequest
protected def addRepositories(request: AetherVersionRangeRequest): AetherVersionRangeRequest
/** Actually publishes aether artifacts. */
protected def publishArtifacts(artifacts: Seq[AetherArtifact]): Unit
@ -105,29 +115,49 @@ abstract class MavenRepositoryResolver(settings: IvySettings) extends AbstractRe
override def getDependency(dd: DependencyDescriptor, rd: ResolveData): ResolvedModuleRevision = {
val context = IvyContext.pushNewCopyContext
try {
val drid: ModuleRevisionId =
if (sbt.MakePom.isDependencyVersionRange(dd.getDependencyRevisionId.getRevision)) {
Message.debug(s"Got a dynamic revision, attempting to convert to real revision: ${dd.getDependencyRevisionId}")
val revision = sbt.MakePom.makeDependencyVersion(dd.getDependencyRevisionId.getRevision)
// TODO - Alter revision id to be maven-friendly first.
val coords =
s"${dd.getDependencyRevisionId.getOrganisation}:${aetherArtifactIdFromMrid(dd.getDependencyRevisionId)}:${revision}"
//val coords = aetherCoordsFromMrid(dd.getDependencyRevisionId)
Message.debug(s"Aether about to resolve version for [$coords]...")
val versionRequest = addRepositories(new AetherVersionRangeRequest().setArtifact(new AetherArtifact(coords, getArtifactProperties(dd.getDependencyRevisionId))))
val result = system.resolveVersionRange(session, versionRequest)
Message.debug(s"Version result = $result, from $getName")
if (result.getVersions.isEmpty) throw new MavenResolutionException(s"Did not find any versions for $dd")
ModuleRevisionId.newInstance(
dd.getDependencyRevisionId.getOrganisation,
dd.getDependencyRevisionId.getName,
result.getHighestVersion.toString,
dd.getExtraAttributes)
} else dd.getDependencyRevisionId
// TODO - Check to see if we're asking for latest.* version, and if so, we should run a latest version query
// first and use that result to return the metadata/final module.
Message.debug(s"Requesting conf [${dd.getModuleConfigurations.mkString(",")}] from Aether module ${dd.getDependencyRevisionId} in resolver ${getName}")
Message.debug(s"Requesting conf [${dd.getModuleConfigurations.mkString(",")}] from Aether module ${drid} in resolver ${getName}")
val request = new AetherDescriptorRequest()
val coords = aetherCoordsFromMrid(dd.getDependencyRevisionId)
val coords = aetherCoordsFromMrid(drid)
Message.debug(s"Aether about to resolve [$coords]...")
request.setArtifact(new AetherArtifact(coords, getArtifactProperties(dd.getDependencyRevisionId)))
request.setArtifact(new AetherArtifact(coords, getArtifactProperties(drid)))
addRepositories(request)
val result = system.readArtifactDescriptor(session, request)
val packaging = getPackagingFromPomProperties(result.getProperties)
Message.debug(s"Aether resolved ${dd.getDependencyId} w/ packaging ${packaging}")
// TODO - better pub date if we have no metadata.
val lastModifiedTime = getPublicationTime(dd.getDependencyRevisionId) getOrElse 0L
val lastModifiedTime = getPublicationTime(drid) getOrElse 0L
// Construct a new Ivy module descriptor
val desc: ModuleDescriptor = {
// TODO - Better detection of snapshot and handling latest.integration/latest.snapshot
val status =
if (dd.getDependencyRevisionId.getRevision.endsWith("-SNAPSHOT")) "integration"
if (drid.getRevision.endsWith("-SNAPSHOT")) "integration"
else "release"
val md =
new DefaultModuleDescriptor(dd.getDependencyRevisionId, status, null /* pubDate */ , false)
new DefaultModuleDescriptor(drid, status, null /* pubDate */ , false)
//DefaultModuleDescriptor.newDefaultInstance(dd.getDependencyRevisionId)
// Here we add the standard configurations
for (config <- PomModuleDescriptorBuilder.MAVEN2_CONFIGURATIONS) {
@ -136,7 +166,8 @@ abstract class MavenRepositoryResolver(settings: IvySettings) extends AbstractRe
// Here we look into the artifacts specified from the dependency descriptor *and* those that are defaulted,
// and append them to the appropriate configurations.
addArtifactsFromPom(dd, packaging, md, lastModifiedTime)
// TODO - Feed correct revision down here
addArtifactsFromPom(drid, dd, packaging, md, lastModifiedTime)
// Here we add dependencies.
addDependenciesFromAether(result, md)
// Here we use pom.xml Dependency management section to create Ivy dependency mediators.
@ -157,7 +188,7 @@ abstract class MavenRepositoryResolver(settings: IvySettings) extends AbstractRe
}
// Here we need to pretend we downloaded the pom.xml file
val pom = DefaultArtifact.newPomArtifact(dd.getDependencyRevisionId, new java.util.Date(lastModifiedTime))
val pom = DefaultArtifact.newPomArtifact(drid, new java.util.Date(lastModifiedTime))
val madr = new MetadataArtifactDownloadReport(pom)
madr.setSearched(true)
madr.setDownloadStatus(DownloadStatus.SUCCESSFUL) // TODO - Figure this things out for this report.
@ -223,13 +254,13 @@ abstract class MavenRepositoryResolver(settings: IvySettings) extends AbstractRe
}
/** Determines which artifacts are associated with this maven module and appends them to the descriptor. */
def addArtifactsFromPom(dd: DependencyDescriptor, packaging: String, md: DefaultModuleDescriptor, lastModifiedTime: Long): Unit = {
def addArtifactsFromPom(drid: ModuleRevisionId, dd: DependencyDescriptor, packaging: String, md: DefaultModuleDescriptor, lastModifiedTime: Long): Unit = {
Message.debug(s"Calculating artifacts for ${dd.getDependencyId} w/ packaging $packaging")
// Here we add in additional artifact requests, which ALLWAYS have to be explicit since
// Maven/Aether doesn't include all known artifacts in a pom.xml
// TODO - This does not appear to be working correctly.
if (dd.getAllDependencyArtifacts.isEmpty) {
val artifactId = s"${dd.getDependencyId.getName}-${dd.getDependencyRevisionId.getRevision}"
if (dd.getAllDependencyArtifacts.isEmpty) {
val artifactId = s"${drid.getName}-${drid.getRevision}"
// Add the artifacts we know about the module
packaging match {
case "pom" =>

View File

@ -6,10 +6,10 @@ import org.apache.ivy.plugins.repository.Resource
import org.apache.ivy.plugins.repository.url.URLResource
import org.apache.ivy.util.Message
import org.apache.ivy.util.url.URLHandlerRegistry
import org.apache.maven.repository.internal.{ MavenRepositorySystemUtils, SbtArtifactDescriptorReader, SnapshotMetadataGeneratorFactory, VersionsMetadataGeneratorFactory }
import org.apache.maven.repository.internal.{ MavenRepositorySystemUtils, SbtArtifactDescriptorReader, SnapshotMetadataGeneratorFactory, VersionsMetadataGeneratorFactory, DefaultVersionResolver }
import org.eclipse.aether.{ RepositorySystem, RepositorySystemSession }
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory
import org.eclipse.aether.impl.{ ArtifactDescriptorReader, DefaultServiceLocator, MetadataGeneratorFactory }
import org.eclipse.aether.impl.{ ArtifactDescriptorReader, DefaultServiceLocator, MetadataGeneratorFactory, VersionResolver }
import org.eclipse.aether.repository.{ LocalRepository, RemoteRepository }
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory
import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory

View File

@ -0,0 +1,5 @@
organization := "com.test"
name := "maven-version-range-bug"
version := "1.0.0-SNAPSHOT"
scalaVersion := "2.11.6"
libraryDependencies += "com.amazonaws" % "aws-java-sdk" % "1.7.8.1"

View File

@ -0,0 +1 @@
> update