diff --git a/ivy/src/main/scala/org/apache/maven/repository/internal/SbtExtraProperties.java b/ivy/src/main/java/sbt/SbtExtraProperties.java similarity index 94% rename from ivy/src/main/scala/org/apache/maven/repository/internal/SbtExtraProperties.java rename to ivy/src/main/java/sbt/SbtExtraProperties.java index 5570d4fac..bcc9b8189 100644 --- a/ivy/src/main/scala/org/apache/maven/repository/internal/SbtExtraProperties.java +++ b/ivy/src/main/java/sbt/SbtExtraProperties.java @@ -1,6 +1,4 @@ -package org.apache.maven.repository.internal; - - +package sbt; /** * Extra properties we dump from Aether into the properties list. diff --git a/ivy/src/main/scala/org/apache/ivy/plugins/resolver/MavenRepositoryResolver.scala b/ivy/src/main/scala/org/apache/ivy/plugins/resolver/MavenRepositoryResolver.scala deleted file mode 100644 index 6ff019c6a..000000000 --- a/ivy/src/main/scala/org/apache/ivy/plugins/resolver/MavenRepositoryResolver.scala +++ /dev/null @@ -1,674 +0,0 @@ -package org.apache.ivy.plugins.resolver - -import java.io.{ File, IOException } -import java.text.ParseException -import java.util.Date -import org.apache.ivy.core.IvyContext -import org.apache.ivy.core.module.descriptor._ -import org.apache.ivy.core.module.id.{ ModuleId, ArtifactId, ModuleRevisionId } -import org.apache.ivy.core.report.{ ArtifactDownloadReport, DownloadStatus, MetadataArtifactDownloadReport, DownloadReport } -import org.apache.ivy.core.resolve.{ ResolvedModuleRevision, ResolveData, DownloadOptions } -import org.apache.ivy.core.settings.IvySettings -import org.apache.ivy.plugins.matcher.ExactPatternMatcher -import org.apache.ivy.plugins.parser.m2.{ PomModuleDescriptorBuilder, ReplaceMavenConfigurationMappings } -import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter -import org.apache.ivy.plugins.resolver.MavenRepositoryResolver.JarPackaging -import org.apache.ivy.plugins.resolver.util.ResolvedResource -import org.apache.ivy.util.Message -import org.apache.maven.repository.internal.{ PomExtraDependencyAttributes, SbtRepositoryLayout, SbtExtraProperties } -import org.eclipse.aether.{ RepositorySystemSession, RepositorySystem } -import org.eclipse.aether.artifact.{ DefaultArtifact => AetherArtifact } -import org.eclipse.aether.metadata.{ Metadata, DefaultMetadata } -import org.eclipse.aether.resolution.{ - ArtifactDescriptorRequest => AetherDescriptorRequest, - ArtifactDescriptorResult => AetherDescriptorResult, - MetadataRequest => AetherMetadataRequest, - ArtifactRequest => AetherArtifactRequest, - ArtifactResolutionException -} -import org.eclipse.aether.deployment.{ DeployRequest => AetherDeployRequest } -import org.eclipse.aether.installation.{ InstallRequest => AetherInstallRequest } -import org.apache.ivy.core.cache.{ ModuleDescriptorWriter, ArtifactOrigin } -import sbt.{ MavenCache, MavenRepository } -import scala.collection.JavaConverters._ - -object MavenRepositoryResolver { - val MAVEN_METADATA_XML = "maven-metadata.xml" - val CLASSIFIER_ATTRIBUTE = "e:classifier" - - val JarPackagings = Set("eclipse-plugin", "hk2-jar", "orbit", "scala-jar", "jar", "bundle") - - object JarPackaging { - def unapply(in: String): Boolean = JarPackagings.contains(in) - } - - // Example: 2014 12 18 09 33 56 - val LAST_UPDATE_FORMAT = new java.text.SimpleDateFormat("yyyyMMddhhmmss") - def parseTimeString(in: String): Option[Long] = - try Some(LAST_UPDATE_FORMAT.parse(in).getTime) - catch { - case _: java.text.ParseException => None - } - - val DEFAULT_ARTIFACT_CONFIGURATION = "master" - -} - -/** - * A resolver instance which can resolve from a REMOTE maven repository. - * - * Note: This creates its *own* local cache directory for cache metadata. using its name. - * - */ -class MavenRemoteRepositoryResolver(val repo: MavenRepository, settings: IvySettings) extends AbstractMavenRepositoryResolver(settings) { - setName(repo.name) - override def toString = s"${repo.name}: ${repo.root}" - protected val system = MavenRepositorySystemFactory.newRepositorySystemImpl - // Note: All maven repository resolvers will use the SAME maven cache. - // We're not sure if we care whether or not this means that the wrong resolver may report finding an artifact. - // The key is not to duplicate files repeatedly across many caches. - private val localRepo = new java.io.File(settings.getDefaultIvyUserDir, s"maven-cache") - sbt.IO.createDirectory(localRepo) - protected val session = MavenRepositorySystemFactory.newSessionImpl(system, localRepo) - private val aetherRepository = { - new org.eclipse.aether.repository.RemoteRepository.Builder(repo.name, SbtRepositoryLayout.LAYOUT_NAME, repo.root).build() - } - // TODO - Check if isUseCacheOnly is used correctly. - private def isUseCacheOnly: Boolean = - Option(IvyContext.getContext).flatMap(x => Option(x.getResolveData)).flatMap(x => Option(x.getOptions)).map(_.isUseCacheOnly).getOrElse(false) - protected def addRepositories(request: AetherDescriptorRequest): AetherDescriptorRequest = - if (isUseCacheOnly) request else request.addRepository(aetherRepository) - protected def addRepositories(request: AetherArtifactRequest): AetherArtifactRequest = - if (isUseCacheOnly) request else request.addRepository(aetherRepository) - /** Actually publishes aether artifacts. */ - protected def publishArtifacts(artifacts: Seq[AetherArtifact]): Unit = { - val request = new AetherDeployRequest() - request.setRepository(aetherRepository) - artifacts foreach request.addArtifact - system.deploy(session, request) - } - protected def getPublicationTime(mrid: ModuleRevisionId): Option[Long] = { - val metadataRequest = new AetherMetadataRequest() - metadataRequest.setMetadata( - new DefaultMetadata( - mrid.getOrganisation, - mrid.getName, - mrid.getRevision, - MavenRepositoryResolver.MAVEN_METADATA_XML, - Metadata.Nature.RELEASE_OR_SNAPSHOT)) - if (!isUseCacheOnly) metadataRequest.setRepository(aetherRepository) - val metadataResultOpt = - try system.resolveMetadata(session, java.util.Arrays.asList(metadataRequest)).asScala.headOption - catch { - case e: org.eclipse.aether.resolution.ArtifactResolutionException => None - } - try metadataResultOpt match { - case Some(md) if md.isResolved => - import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader - import org.codehaus.plexus.util.ReaderFactory - val readMetadata = { - val reader = ReaderFactory.newXmlReader(md.getMetadata.getFile) - try new MetadataXpp3Reader().read(reader, false) - finally reader.close() - } - val timestampOpt = - for { - v <- Option(readMetadata.getVersioning) - sp <- Option(v.getSnapshot) - ts <- Option(sp.getTimestamp) - t <- MavenRepositoryResolver.parseTimeString(ts) - } yield t - val lastUpdatedOpt = - for { - v <- Option(readMetadata.getVersioning) - lu <- Option(v.getLastUpdated) - d <- MavenRepositoryResolver.parseTimeString(lu) - } yield d - // TODO - Only look at timestamp *IF* the version is for a snapshot. - timestampOpt orElse lastUpdatedOpt - case _ => None - } - } -} - -/** - * A resolver instance which can resolve from a maven CACHE. - * - * Note: This should never hit somethign remote, as it just looks in the maven cache for things already resolved. - */ -class MavenCacheRepositoryResolver(val repo: MavenCache, settings: IvySettings) extends AbstractMavenRepositoryResolver(settings) { - setName(repo.name) - protected val system = MavenRepositorySystemFactory.newRepositorySystemImpl - sbt.IO.createDirectory(repo.rootFile) - protected val session = MavenRepositorySystemFactory.newSessionImpl(system, repo.rootFile) - protected def setRepository(request: AetherMetadataRequest): AetherMetadataRequest = request - protected def addRepositories(request: AetherDescriptorRequest): AetherDescriptorRequest = request - protected def addRepositories(request: AetherArtifactRequest): AetherArtifactRequest = request - protected def publishArtifacts(artifacts: Seq[AetherArtifact]): Unit = { - val request = new AetherInstallRequest() - artifacts foreach request.addArtifact - system.install(session, request) - } - // TODO - Share this with non-local repository code, since it's MOSTLY the same. - protected def getPublicationTime(mrid: ModuleRevisionId): Option[Long] = { - val metadataRequest = new AetherMetadataRequest() - metadataRequest.setMetadata( - new DefaultMetadata( - mrid.getOrganisation, - mrid.getName, - mrid.getRevision, - MavenRepositoryResolver.MAVEN_METADATA_XML, - Metadata.Nature.RELEASE_OR_SNAPSHOT)) - val metadataResultOpt = - try system.resolveMetadata(session, java.util.Arrays.asList(metadataRequest)).asScala.headOption - catch { - case e: org.eclipse.aether.resolution.ArtifactResolutionException => None - } - try metadataResultOpt match { - case Some(md) if md.isResolved => - import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader - import org.codehaus.plexus.util.ReaderFactory - val readMetadata = { - val reader = ReaderFactory.newXmlReader(md.getMetadata.getFile) - try new MetadataXpp3Reader().read(reader, false) - finally reader.close() - } - val timestampOpt = - for { - v <- Option(readMetadata.getVersioning) - sp <- Option(v.getSnapshot) - ts <- Option(sp.getTimestamp) - t <- MavenRepositoryResolver.parseTimeString(ts) - } yield t - val lastUpdatedOpt = - for { - v <- Option(readMetadata.getVersioning) - lu <- Option(v.getLastUpdated) - d <- MavenRepositoryResolver.parseTimeString(lu) - } yield d - // TODO - Only look at timestamp *IF* the version is for a snapshot. - timestampOpt orElse lastUpdatedOpt - case _ => None - } - } - override def toString = s"${repo.name}: ${repo.root}" -} - -/** An exception we can throw if we encounter issues. */ -class MavenResolutionException(msg: String) extends RuntimeException(msg) {} - -abstract class AbstractMavenRepositoryResolver(settings: IvySettings) extends AbstractResolver { - - /** Our instance of the aether repository system. */ - protected val system: RepositorySystem - /** - * Our instance of the aether repository system session. - * - * TODO - We may want to tie this into an IvyContext. - */ - protected val session: RepositorySystemSession - - /** Determine the publication time of a module. The mechanism may differ if the repository is remote vs. local. */ - protected def getPublicationTime(mrid: ModuleRevisionId): Option[Long] - /** Inject necessary repositories into a descriptor request. */ - protected def addRepositories(request: AetherDescriptorRequest): AetherDescriptorRequest - protected def addRepositories(request: AetherArtifactRequest): AetherArtifactRequest - - /** Actually publishes aether artifacts. */ - protected def publishArtifacts(artifacts: Seq[AetherArtifact]): Unit - - // TOOD - deal with packaging here. - private def aetherArtifactIdFromMrid(mrid: ModuleRevisionId): String = - getSbtVersion(mrid) match { - case Some(sbt) => s"${mrid.getName}_sbt_$sbt" - case None => mrid.getName - } - private def aetherCoordsFromMrid(mrid: ModuleRevisionId): String = - s"${mrid.getOrganisation}:${aetherArtifactIdFromMrid(mrid)}:${mrid.getRevision}" - - private def aetherCoordsFromMrid(mrid: ModuleRevisionId, packaging: String): String = - s"${mrid.getOrganisation}:${aetherArtifactIdFromMrid(mrid)}:$packaging:${mrid.getRevision}" - - private def aetherCoordsFromMrid(mrid: ModuleRevisionId, packaging: String, extension: String): String = - s"${mrid.getOrganisation}:${aetherArtifactIdFromMrid(mrid)}:$extension:$packaging:${mrid.getRevision}" - - // Handles appending licenses to the module descriptor fromthe pom. - private def addLicenseInfo(md: DefaultModuleDescriptor, map: java.util.Map[String, AnyRef]) = { - val count = map.get(SbtExtraProperties.LICENSE_COUNT_KEY) match { - case null => 0 - case x: java.lang.Integer => x.intValue - case x: String => x.toInt - case _ => 0 - } - for { - i <- 0 until count - name <- Option(map.get(SbtExtraProperties.makeLicenseName(i))).map(_.toString) - url <- Option(map.get(SbtExtraProperties.makeLicenseUrl(i))).map(_.toString) - } md.addLicense(new License(name, url)) - } - - // This grabs the dependency for Ivy. - override def getDependency(dd: DependencyDescriptor, rd: ResolveData): ResolvedModuleRevision = { - val context = IvyContext.pushNewCopyContext - try { - // 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}") - val request = new AetherDescriptorRequest() - val coords = aetherCoordsFromMrid(dd.getDependencyRevisionId) - Message.debug(s"Aether about to resolve [$coords]...") - request.setArtifact(new AetherArtifact(coords, getArtifactProperties(dd.getDependencyRevisionId))) - 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 - - // 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" - else "release" - val md = - new DefaultModuleDescriptor(dd.getDependencyRevisionId, status, null /* pubDate */ , false) - //DefaultModuleDescriptor.newDefaultInstance(dd.getDependencyRevisionId) - // Here we add the standard configurations - for (config <- PomModuleDescriptorBuilder.MAVEN2_CONFIGURATIONS) { - md.addConfiguration(config) - } - - // 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) - // Here we add dependencies. - addDependenciesFromAether(result, md) - // Here we use pom.xml Dependency management section to create Ivy dependency mediators. - addManagedDependenciesFromAether(result, md) - // TODO - Add excludes? - - // Here we rip out license info. - addLicenseInfo(md, result.getProperties) - md.addExtraInfo(SbtExtraProperties.MAVEN_PACKAGING_KEY, packaging) - Message.debug(s"Setting publication date to ${new Date(lastModifiedTime)}") - // TODO - Figure out the differences between these items. - md.setPublicationDate(new Date(lastModifiedTime)) - md.setLastModified(lastModifiedTime) - md.setResolvedPublicationDate(new Date(lastModifiedTime)) - md.check() - // TODO - do we need the toSystem? - toSystem(md) - } - - // Here we need to pretend we downloaded the pom.xml file - val pom = DefaultArtifact.newPomArtifact(dd.getDependencyRevisionId, 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. - val rmr = new ResolvedModuleRevision(this, this, desc, madr, false /* Force */ ) - - // TODO - Here we cache the transformed pom.xml into an ivy.xml in the cache because ChainResolver will be looking at it. - // This doesn't appear to really work correctly. - // However, I think the chain resolver doesn't use this instance anyway. Ideally we don't put anything - // in the ivy cache, but this should be "ok". - getRepositoryCacheManager.originalToCachedModuleDescriptor(this, - null /* ivyRef. Just passed back to us. */ , - pom, - rmr, - new ModuleDescriptorWriter() { - def write(originalMdResource: ResolvedResource, md: ModuleDescriptor, src: File, dest: File): Unit = { - // a basic ivy file is written containing default data - XmlModuleDescriptorWriter.write(md, dest); - } - } - ) - rmr - } catch { - case e: org.eclipse.aether.resolution.ArtifactDescriptorException => - Message.info(s"Failed to read descriptor ${dd} from ${getName}, ${e.getMessage}") - rd.getCurrentResolvedModuleRevision - case e: MavenResolutionException => - Message.debug(s"Resolution Exception from ${getName}, ${e.getMessage}, returning: ${rd.getCurrentResolvedModuleRevision}") - rd.getCurrentResolvedModuleRevision - } finally IvyContext.popContext() - } - - def getSbtVersion(dd: ModuleRevisionId): Option[String] = - Option(dd.getExtraAttribute(PomExtraDependencyAttributes.SbtVersionKey)) - - def getArtifactProperties(dd: ModuleRevisionId): java.util.Map[String, String] = { - val m = new java.util.HashMap[String, String] - Option(dd.getExtraAttribute(PomExtraDependencyAttributes.ScalaVersionKey)) foreach { sv => - m.put(SbtExtraProperties.POM_SCALA_VERSION, sv) - } - getSbtVersion(dd) foreach { sv => - m.put(SbtExtraProperties.POM_SBT_VERSION, sv) - } - m - } - - final def checkJarArtifactExists(dd: DependencyDescriptor): Boolean = { - // TODO - We really want this to be as fast/efficient as possible! - val request = new AetherArtifactRequest() - val art = new AetherArtifact( - aetherCoordsFromMrid(dd.getDependencyRevisionId, "jar"), - getArtifactProperties(dd.getDependencyRevisionId)) - request.setArtifact(art) - addRepositories(request) - try { - val result = system.resolveArtifact(session, request) - result.isResolved && !result.isMissing - } catch { - case e: ArtifactResolutionException => - // Ignore, as we're just working around issues with pom.xml's with no jars or POM packaging - Message.debug(s"Could not find $art in ${getName}") - false - } - } - - /** 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 = { - 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}" - // Add the artifacts we know about the module - packaging match { - case "pom" => - // THere we have to attempt to download the JAR and see if it comes, if not, we can punt. - // This is because sometimes pom-packaging attaches a JAR. - if (checkJarArtifactExists(dd)) { - val defaultArt = - new DefaultArtifact(md.getModuleRevisionId, new Date(lastModifiedTime), artifactId, packaging, "jar") - md.addArtifact(MavenRepositoryResolver.DEFAULT_ARTIFACT_CONFIGURATION, defaultArt) - } - case JarPackaging() => - // Here we fail the resolution. This is an issue when pom.xml files exist with no JAR, which happens - // on maven central for some reason on old artifacts. - if (!checkJarArtifactExists(dd)) - throw new MavenResolutionException(s"Failed to find JAR file associated with $dd") - // Assume for now everything else is a jar. - val defaultArt = - new DefaultArtifact(md.getModuleRevisionId, new Date(lastModifiedTime), artifactId, packaging, "jar") - // TODO - Unfortunately we have to try to download the JAR file HERE and then fail resolution if we cannot find it. - // This is because sometime a pom.xml exists with no JARs. - md.addArtifact(MavenRepositoryResolver.DEFAULT_ARTIFACT_CONFIGURATION, defaultArt) - case _ => // Ignore, we have no idea what this artifact is. - Message.warn(s"Not adding artifacts for resolution because we don't understand packaging: $packaging") - } - - } else { - // NOTE: this means that someone is requested specific artifacts from us. What we need to do is *only* download the - // requested artifacts rather than the default "jar". What's odd, is that pretty much this almost ALWAYS happens. - // but in some circumstances, the above logic is checked. - // Additionally, we may want to somehow merge the "defined" artifacts from maven with the requested ones here, rather - // than having completely separate logic. For now, this appears to work the same way it was before. - // Since we aren't accurately guessing what maven files are meant to be included as artifacts ANYWAY, this - // is probably the right way to go. - for (requestedArt <- dd.getAllDependencyArtifacts) { - getClassifier(requestedArt) match { - case None => - // This is the default artifact. We do need to add this, and to the default configuration. - val defaultArt = - new DefaultArtifact(md.getModuleRevisionId, new Date(lastModifiedTime), requestedArt.getName, requestedArt.getType, requestedArt.getExt) - md.addArtifact(MavenRepositoryResolver.DEFAULT_ARTIFACT_CONFIGURATION, defaultArt) - case Some(scope) => - Message.debug(s"Adding additional artifact in $scope, $requestedArt") - // TODO - more Extra attributes? - val mda = - new MDArtifact( - md, - requestedArt.getName, - requestedArt.getType, - requestedArt.getExt, - requestedArt.getUrl, - requestedArt.getExtraAttributes) - md.addArtifact(getConfiguration(scope), mda) - } - } - } - } - - /** Adds the dependency mediators required based on the managed dependency instances from this pom. */ - def addManagedDependenciesFromAether(result: AetherDescriptorResult, md: DefaultModuleDescriptor) { - for (d <- result.getManagedDependencies.asScala) { - - if (d.getArtifact.getArtifactId == "stringtemplate") { - Message.warn(s"Found managed stringtemplate in $md !") - } - - md.addDependencyDescriptorMediator( - ModuleId.newInstance(d.getArtifact.getGroupId, d.getArtifact.getArtifactId), - ExactPatternMatcher.INSTANCE, - new OverrideDependencyDescriptorMediator(null, d.getArtifact.getVersion) { - override def mediate(dd: DependencyDescriptor): DependencyDescriptor = { - super.mediate(dd) - } - }) - - } - } - - /** Adds the list of dependencies this artifact has on other artifacts. */ - def addDependenciesFromAether(result: AetherDescriptorResult, md: DefaultModuleDescriptor) { - // First we construct a map of any extra attributes we must append to dependencies. - // This is necessary for transitive maven-based sbt plugin dependencies, where we need to - // attach the sbtVersion/scalaVersion to the dependency id otherwise we'll fail to resolve the - // dependency correctly. - val extraAttributes = PomExtraDependencyAttributes.readFromAether(result.getProperties) - for (d <- result.getDependencies.asScala) { - // TODO - Is this correct for changing detection. We should use the Ivy mechanism configured... - val isChanging = d.getArtifact.getVersion.endsWith("-SNAPSHOT") - val drid = { - val tmp = ModuleRevisionId.newInstance(d.getArtifact.getGroupId, d.getArtifact.getArtifactId, d.getArtifact.getVersion) - extraAttributes get tmp match { - case Some(props) => - Message.debug(s"Found $tmp w/ extra attributes ${props.mkString(",")}") - ModuleRevisionId.newInstance( - d.getArtifact.getGroupId, - d.getArtifact.getArtifactId, - d.getArtifact.getVersion, - props.asJava - ) - case _ => tmp - } - } - - // Note: The previous maven integration ALWAYS set force to true for dependnecies. If we do not do this, for some - // reason, Ivy will create dummy nodes when doing dependnecy mediation (e.g. dependencyManagement of one pom overrides version of a dependency) - // which was leading to "data not found" exceptions as Ivy would pick the correct IvyNode in the dependency tree but never load it with data.... - val dd = new DefaultDependencyDescriptor(md, drid, /* force */ true, isChanging, true) {} - - // TODO - Configuration mappings (are we grabbing scope correctly, or should the default not always be compile?) - val scope = Option(d.getScope).filterNot(_.isEmpty).getOrElse("compile") - val mapping = ReplaceMavenConfigurationMappings.addMappings(dd, scope, d.isOptional) - // TODO - include rules and exclude rules. - Message.debug(s"Adding maven transitive dependency ${md.getModuleRevisionId} -> ${dd}") - // TODO - Unify this borrowed Java code into something a bit friendlier. - // Now we add the artifact.... - if ((d.getArtifact.getClassifier != null) || ((d.getArtifact.getExtension != null) && !("jar" == d.getArtifact.getExtension))) { - val tpe: String = - if (d.getArtifact.getExtension != null) d.getArtifact.getExtension - else "jar" - val ext: String = tpe match { - case "test-jar" => "jar" - case JarPackaging() => "jar" - case other => other - } - // Here we add the classifier, hopefully correctly... - val extraAtt = new java.util.HashMap[String, AnyRef]() - if (d.getArtifact.getClassifier != null) { - extraAtt.put("m:classifier", d.getArtifact.getClassifier) - } - val depArtifact: DefaultDependencyArtifactDescriptor = - new DefaultDependencyArtifactDescriptor(dd, dd.getDependencyId.getName, tpe, ext, null, extraAtt) - val optionalizedScope: String = if (d.isOptional) "optional" else scope - // TOOD - We may need to fix the configuration mappings here. - dd.addDependencyArtifact(optionalizedScope, depArtifact) - } - // TODO - is toSystem call correct? - md.addDependency(dd) - - if (d.getArtifact.getArtifactId == "stringtemplate") { - Message.warn(s"Found stringtemplate dependency! $dd") - } - } - } - - // This method appears to be deprecated/unused in all of Ivy so we do not implement it. - override def findIvyFileRef(dd: DependencyDescriptor, rd: ResolveData): ResolvedResource = { - Message.error(s"Looking for ivy file ref, method not implemented! MavenRepositoryResolver($getName) will always return null.") - null - } - - private def getPackagingFromPomProperties(props: java.util.Map[String, AnyRef]): String = - if (props.containsKey(SbtExtraProperties.MAVEN_PACKAGING_KEY)) - props.get(SbtExtraProperties.MAVEN_PACKAGING_KEY).toString - else "jar" - - override def download(artifacts: Array[Artifact], dopts: DownloadOptions): DownloadReport = { - // TODO - Status reports on download and possibly parallel downloads - val report = new DownloadReport - val requests = - for (a <- artifacts) yield { - val request = new AetherArtifactRequest - val aetherArt = - getClassifier(a) match { - case None | Some("") => - new AetherArtifact( - aetherCoordsFromMrid(a.getModuleRevisionId), - getArtifactProperties(a.getModuleRevisionId)) - case Some(other) => new AetherArtifact( - aetherCoordsFromMrid(a.getModuleRevisionId, other, a.getExt), - getArtifactProperties(a.getModuleRevisionId)) - } - Message.debug(s"Requesting download of [$aetherArt]") - request.setArtifact(aetherArt) - addRepositories(request) - request - } - val (aetherResults, failed) = - try { - (system.resolveArtifacts(session, requests.toList.asJava).asScala, false) - } catch { - case e: org.eclipse.aether.resolution.ArtifactResolutionException => - Message.error(s"Failed to resolve artifacts from ${getName}, ${e.getMessage}") - (e.getResults.asScala, true) - } - for ((result, art) <- aetherResults zip artifacts) { - Message.debug(s"Aether resolved artifact result: $result") - val adr = new ArtifactDownloadReport(art) - adr.setDownloadDetails(result.toString) - // TODO - Fill this out with a real estimate on time... - adr.setDownloadTimeMillis(0L) - // TODO - what is artifact origin actuallyused for? - adr.setArtifactOrigin(new ArtifactOrigin( - art, - true, - getName)) - if (result.isMissing) { - adr.setDownloadStatus(DownloadStatus.FAILED) - adr.setDownloadDetails(ArtifactDownloadReport.MISSING_ARTIFACT) - } else if (!result.isResolved) { - adr.setDownloadStatus(DownloadStatus.FAILED) - adr.setDownloadDetails(result.toString) - // TODO - we should set download status to NO in the event we don't care about an artifact... - } else { - val file = result.getArtifact.getFile - Message.debug(s"Succesffully downloaded: $file") - adr.setLocalFile(file) - adr.setSize(file.length) - adr.setDownloadStatus(DownloadStatus.SUCCESSFUL) - } - report.addArtifactReport(adr) - } - report - } - - case class PublishTransaction(module: ModuleRevisionId, artifacts: Seq[(Artifact, File)]) - private var currentTransaction: Option[PublishTransaction] = None - - override def beginPublishTransaction(module: ModuleRevisionId, overwrite: Boolean): Unit = { - currentTransaction match { - case Some(t) => throw new IllegalStateException(s"Publish Transaction already open for [$getName]") - case None => currentTransaction = Some(PublishTransaction(module, Nil)) - } - } - override def abortPublishTransaction(): Unit = { - currentTransaction = None - } - - def getClassifier(art: Artifact): Option[String] = - // TODO - Do we need to look anywere else? - Option(art.getExtraAttribute("classifier")) - - def getClassifier(art: org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor): Option[String] = - art.getType match { - case "doc" | "javadoc" => Some("javadoc") - case "src" | "source" => Some("sources") - case "test-jar" | "test" => Some("tests") - case _ => - // Look for extra attributes - art.getExtraAttribute(MavenRepositoryResolver.CLASSIFIER_ATTRIBUTE) match { - case null => None - case c => Some(c) - } - } - - def getConfiguration(classifier: String): String = - classifier match { - // TODO - choice of configuration actually depends on whether or not the artifact is - // REQUESTED by the user, in which case it should be on master. - // Currently, we don't actually look for sources/javadoc/test artifacts at all, - // which means any artifact is in the master configuration, but we should - // fix this for better integration into the maven ecosystem from ivy. - //case "sources" => "sources" - //case "javadoc" => "javadoc" - case other => MavenRepositoryResolver.DEFAULT_ARTIFACT_CONFIGURATION - } - - override def commitPublishTransaction(): Unit = { - // TODO - actually send all artifacts to aether - currentTransaction match { - case Some(t) => - Message.debug(s"Publishing module ${t.module}, with artifact count = ${t.artifacts.size}") - val artifacts = - for ((art, file) <- t.artifacts) yield { - Message.debug(s" - Publishing $art (${art.getType})(${art.getExtraAttribute("classifier")}) in [${art.getConfigurations.mkString(",")}] from $file") - new AetherArtifact( - t.module.getOrganisation, - aetherArtifactIdFromMrid(t.module), - getClassifier(art).orNull, - art.getExt, - t.module.getRevision, - getArtifactProperties(t.module), - file - ) - } - publishArtifacts(artifacts) - // TODO - Any kind of validity checking? - currentTransaction = None - case None => throw new IllegalStateException(s"Publish Transaction already open for [$getName]") - } - } - - override def publish(art: Artifact, file: File, overwrite: Boolean): Unit = { - currentTransaction match { - case Some(t) => - val allArts = t.artifacts ++ List(art -> file) - currentTransaction = Some(t.copy(artifacts = allArts)) - case None => - throw new IllegalStateException(("MavenRepositories require transactional publish")) - } - } - - override def equals(a: Any): Boolean = - a match { - case x: AbstractMavenRepositoryResolver => x.getName == getName - case _ => false - } -} diff --git a/ivy/src/main/scala/org/apache/ivy/plugins/resolver/MavenRepositorySystemFactory.scala b/ivy/src/main/scala/org/apache/ivy/plugins/resolver/MavenRepositorySystemFactory.scala deleted file mode 100644 index 1946dc0da..000000000 --- a/ivy/src/main/scala/org/apache/ivy/plugins/resolver/MavenRepositorySystemFactory.scala +++ /dev/null @@ -1,160 +0,0 @@ -package org.apache.ivy.plugins.resolver - -import java.net.URI - -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.eclipse.aether.artifact.Artifact -import org.eclipse.aether.impl.{ MetadataGeneratorFactory, ArtifactDescriptorReader, RepositoryConnectorProvider, DefaultServiceLocator } -import org.eclipse.aether.metadata.Metadata -import org.eclipse.aether.spi.connector.RepositoryConnectorFactory -import org.eclipse.aether.spi.connector.layout.{ RepositoryLayoutFactory, RepositoryLayoutProvider, RepositoryLayout } -import org.eclipse.aether.spi.connector.layout.RepositoryLayout.Checksum -import org.eclipse.aether.{ - RepositorySystem, - RepositorySystemSession -} - -import org.eclipse.aether.repository.{ RemoteRepository, LocalRepository } -import org.eclipse.aether.RepositorySystemSession -import org.apache.maven.repository.internal._ -import org.eclipse.aether.spi.connector.transport._ -import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory -import java.io.File - -/** Helper methods for dealing with starting up Aether. */ -object MavenRepositorySystemFactory { - def newRepositorySystemImpl: RepositorySystem = { - // For now we just log Aether instantiation issues. These should probably cause fatal errors. - val locator = MavenRepositorySystemUtils.newServiceLocator() - locator.setErrorHandler(new DefaultServiceLocator.ErrorHandler { - override def serviceCreationFailed(tpe: Class[_], impl: Class[_], exception: Throwable): Unit = { - Message.error(s"Failed to create $tpe, of class $impl") - } - }) - // Here we register the Ivy <-> Aether transport bridge - locator.addService(classOf[TransporterFactory], classOf[MyTransportFactory]) - // This connects the download mechanism to our transports. Why is it needed? no clue. - locator.addService(classOf[RepositoryConnectorFactory], classOf[BasicRepositoryConnectorFactory]) - - // Plugins cause issues here, as their layout is super odd. Here we inject a new plugin layout - locator.addService(classOf[RepositoryLayoutFactory], classOf[SbtPluginLayoutFactory]) - - // Here we add the metadata services so aether will automatically add maven-metadata.xml files. - locator.addService(classOf[MetadataGeneratorFactory], classOf[SnapshotMetadataGeneratorFactory]) - locator.addService(classOf[MetadataGeneratorFactory], classOf[VersionsMetadataGeneratorFactory]) - - // Add our hook for parsing pom.xml files. - locator.setService(classOf[ArtifactDescriptorReader], classOf[SbtArtifactDescriptorReader]) - // Finally, use the DI to create our repository system. - locator.getService(classOf[RepositorySystem]) - } - def newSessionImpl(system: RepositorySystem, localRepoDir: File): RepositorySystemSession = { - val session = MavenRepositorySystemUtils.newSession() - val localRepo = new LocalRepository(localRepoDir) - session setLocalRepositoryManager (system.newLocalRepositoryManager(session, localRepo)) - // Here we set a descriptor policy that FORCES the pom.xml to exist, otherwise Ivy's resolution - // algorithm freaks out. What we could do is also do the ivy lame-thing of checking for a JAR - // instead of a pom.xml, but let's see if this is actually a problem in practice. - val descriptorPolicy = new org.eclipse.aether.util.repository.SimpleArtifactDescriptorPolicy( - /* ignoreMissing */ false, /* ignoreInvalid. */ true) - session.setArtifactDescriptorPolicy(descriptorPolicy) - session - } - - def defaultLocalRepo: java.io.File = { - new java.io.File(s"${sys.props("user.home")}/.m2/repository") - } -} -/** Override aether's default transport with Ivy-ones. */ -class MyTransportFactory extends TransporterFactory { - override def newInstance(session: RepositorySystemSession, repository: RemoteRepository): Transporter = - repository.getProtocol match { - case "http" | "https" => new HttpTransport(repository) - case "file" => new FileTransport(repository) - case other => throw new IllegalArgumentException(s"Unsupported transport protocol: $other") - } - override def getPriority: Float = 1.0f -} - -/** Aether Http <-> Ivy Http adapter. Aether's is better, but Ivy's has configuration hooks in sbt. */ -class HttpTransport(repository: RemoteRepository) extends AbstractTransporter { - class NotFoundException(msg: String) extends Exception(msg) - private def toURL(task: TransportTask): java.net.URL = - try new java.net.URL(s"${repository.getUrl}/${task.getLocation.toASCIIString}") - catch { - case e: IllegalArgumentException => throw new IllegalArgumentException(s" URL (${task.getLocation}) is not absolute.") - } - private def toResource(task: TransportTask): Resource = new URLResource(toURL(task)) - override def implPeek(peek: PeekTask): Unit = { - if (!toResource(peek).exists()) throw new NotFoundException(s"Could not find ${peek.getLocation}") - } - override def implClose(): Unit = () - override def implGet(out: GetTask): Unit = { - if (!toResource(out).exists()) throw new NotFoundException(s"Could not find ${out.getLocation}") - URLHandlerRegistry.getDefault.download(toURL(out), out.getDataFile, null) - } - override def implPut(put: PutTask): Unit = { - val to = toURL(put) - Option(put.getDataFile) match { - case Some(file) => URLHandlerRegistry.getDefault.upload(file, to, null) - case None => - // TODO - Ivy does not support uploading not from a file. This isn't very efficient in ANY way, - // so if we rewrite the URL handler for Ivy we should fix this as well. - sbt.IO.withTemporaryFile("tmp", "upload") { file => - val in = put.newInputStream() - try sbt.IO.transfer(in, file) - finally in.close() - URLHandlerRegistry.getDefault.upload(file, to, null) - } - } - - } - override def classify(err: Throwable): Int = - err match { - // TODO - Implement - case _: NotFoundException => Transporter.ERROR_NOT_FOUND - case _ => Transporter.ERROR_OTHER - } -} - -class FileTransport(repository: RemoteRepository) extends AbstractTransporter { - class NotFoundException(msg: String) extends Exception(msg) - private def toURL(task: TransportTask): java.net.URL = - try new java.net.URL(s"${repository.getUrl}/${task.getLocation.toASCIIString}") - catch { - case e: IllegalArgumentException => throw new IllegalArgumentException(s" URL (${task.getLocation}) is not absolute.") - } - private def toResource(task: TransportTask): Resource = new URLResource(toURL(task)) - private def toFile(task: TransportTask): java.io.File = - new java.io.File(toURL(task).toURI) - override def implPeek(peek: PeekTask): Unit = { - if (!toFile(peek).exists()) throw new NotFoundException(s"Could not find ${peek.getLocation}") - } - override def implClose(): Unit = () - override def implGet(out: GetTask): Unit = { - val from = toFile(out) - if (!from.exists()) throw new NotFoundException(s"Could not find ${out.getLocation}") - sbt.IO.copyFile(from, out.getDataFile, true) - } - override def implPut(put: PutTask): Unit = { - val to = toFile(put) - Option(put.getDataFile) match { - case Some(from) => - sbt.IO.copyFile(from, to, true) - case None => - // Here it's most likely a SHA or somethign where we read from memory. - val in = put.newInputStream - try sbt.IO.transfer(in, to) - finally in.close() - } - } - override def classify(err: Throwable): Int = - err match { - // TODO - Implement - case _: NotFoundException => Transporter.ERROR_NOT_FOUND - case _ => Transporter.ERROR_OTHER - } -} \ No newline at end of file diff --git a/ivy/src/main/scala/org/apache/maven/repository/internal/SbtArtifactDescriptorReader.java b/ivy/src/main/scala/org/apache/maven/repository/internal/SbtArtifactDescriptorReader.java deleted file mode 100644 index c816145e9..000000000 --- a/ivy/src/main/scala/org/apache/maven/repository/internal/SbtArtifactDescriptorReader.java +++ /dev/null @@ -1,558 +0,0 @@ -package org.apache.maven.repository.internal; - -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Set; - -import javax.inject.Inject; -import javax.inject.Named; - -import org.apache.maven.model.DependencyManagement; -import org.apache.maven.model.DistributionManagement; -import org.apache.maven.model.License; -import org.apache.maven.model.Model; -import org.apache.maven.model.Prerequisites; -import org.apache.maven.model.Relocation; -import org.apache.maven.model.Repository; -import org.apache.maven.model.building.DefaultModelBuilderFactory; -import org.apache.maven.model.building.DefaultModelBuildingRequest; -import org.apache.maven.model.building.FileModelSource; -import org.apache.maven.model.building.ModelBuilder; -import org.apache.maven.model.building.ModelBuildingException; -import org.apache.maven.model.building.ModelBuildingRequest; -import org.apache.maven.model.building.ModelProblem; -import org.apache.maven.model.resolution.UnresolvableModelException; -import org.codehaus.plexus.component.annotations.Component; -import org.codehaus.plexus.component.annotations.Requirement; -import org.eclipse.aether.RepositoryException; -import org.eclipse.aether.RepositoryEvent.EventType; -import org.eclipse.aether.RepositoryEvent; -import org.eclipse.aether.RepositorySystemSession; -import org.eclipse.aether.RequestTrace; -import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.artifact.ArtifactProperties; -import org.eclipse.aether.artifact.ArtifactType; -import org.eclipse.aether.artifact.ArtifactTypeRegistry; -import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.artifact.DefaultArtifactType; -import org.eclipse.aether.graph.Dependency; -import org.eclipse.aether.graph.Exclusion; -import org.eclipse.aether.impl.*; -import org.eclipse.aether.repository.WorkspaceRepository; -import org.eclipse.aether.resolution.ArtifactDescriptorException; -import org.eclipse.aether.resolution.ArtifactDescriptorPolicy; -import org.eclipse.aether.resolution.ArtifactDescriptorPolicyRequest; -import org.eclipse.aether.resolution.ArtifactDescriptorRequest; -import org.eclipse.aether.resolution.ArtifactDescriptorResult; -import org.eclipse.aether.resolution.ArtifactRequest; -import org.eclipse.aether.resolution.ArtifactResolutionException; -import org.eclipse.aether.resolution.ArtifactResult; -import org.eclipse.aether.resolution.VersionRequest; -import org.eclipse.aether.resolution.VersionResolutionException; -import org.eclipse.aether.resolution.VersionResult; -import org.eclipse.aether.spi.locator.Service; -import org.eclipse.aether.spi.locator.ServiceLocator; -import org.eclipse.aether.spi.log.Logger; -import org.eclipse.aether.spi.log.LoggerFactory; -import org.eclipse.aether.spi.log.NullLoggerFactory; -import org.eclipse.aether.transfer.ArtifactNotFoundException; - -/** - * A hacked version of maven's default artifact descriptor reader which we use in place of the standard aether adapter. - * - * This adds the following to the parsing of maven files: - * - * Additonal properties: - * - * - `sbt.pom.packaging` - The pom.packaging value. - * - * - * @author Benjamin Bentmann - * @author Josh Suereth - Adapted for sbt - */ -@Named -@Component( role = ArtifactDescriptorReader.class ) -public class SbtArtifactDescriptorReader - implements ArtifactDescriptorReader, Service -{ - - @SuppressWarnings( "unused" ) - @Requirement( role = LoggerFactory.class ) - private Logger logger = NullLoggerFactory.LOGGER; - - @Requirement - private RemoteRepositoryManager remoteRepositoryManager; - - @Requirement - private VersionResolver versionResolver; - - @Requirement - private VersionRangeResolver versionRangeResolver; - - @Requirement - private ArtifactResolver artifactResolver; - - @Requirement - private RepositoryEventDispatcher repositoryEventDispatcher; - - @Requirement - private ModelBuilder modelBuilder; - - public SbtArtifactDescriptorReader() - { - // enable no-arg constructor - } - - @Inject - SbtArtifactDescriptorReader( RemoteRepositoryManager remoteRepositoryManager, VersionResolver versionResolver, - ArtifactResolver artifactResolver, ModelBuilder modelBuilder, - RepositoryEventDispatcher repositoryEventDispatcher, LoggerFactory loggerFactory ) - { - setRemoteRepositoryManager( remoteRepositoryManager ); - setVersionResolver( versionResolver ); - setArtifactResolver( artifactResolver ); - setModelBuilder( modelBuilder ); - setLoggerFactory( loggerFactory ); - setRepositoryEventDispatcher( repositoryEventDispatcher ); - } - - public void initService( ServiceLocator locator ) - { - setLoggerFactory(locator.getService(LoggerFactory.class)); - setRemoteRepositoryManager(locator.getService(RemoteRepositoryManager.class)); - setVersionResolver(locator.getService(VersionResolver.class)); - setArtifactResolver(locator.getService(ArtifactResolver.class)); - setRepositoryEventDispatcher(locator.getService(RepositoryEventDispatcher.class)); - setVersionRangeResolver(locator.getService(VersionRangeResolver.class)); - modelBuilder = locator.getService( ModelBuilder.class ); - if ( modelBuilder == null ) - { - setModelBuilder( new DefaultModelBuilderFactory().newInstance() ); - } - } - - public SbtArtifactDescriptorReader setLoggerFactory( LoggerFactory loggerFactory ) - { - this.logger = NullLoggerFactory.getSafeLogger( loggerFactory, getClass() ); - return this; - } - - void setLogger( LoggerFactory loggerFactory ) - { - // plexus support - setLoggerFactory( loggerFactory ); - } - - public SbtArtifactDescriptorReader setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager ) - { - if ( remoteRepositoryManager == null ) - { - throw new IllegalArgumentException( "remote repository manager has not been specified" ); - } - this.remoteRepositoryManager = remoteRepositoryManager; - return this; - } - - public SbtArtifactDescriptorReader setVersionResolver( VersionResolver versionResolver ) - { - if ( versionResolver == null ) - { - throw new IllegalArgumentException( "version resolver has not been specified" ); - } - this.versionResolver = versionResolver; - return this; - } - - public SbtArtifactDescriptorReader setArtifactResolver( ArtifactResolver artifactResolver ) - { - if ( artifactResolver == null ) - { - throw new IllegalArgumentException( "artifact resolver has not been specified" ); - } - this.artifactResolver = artifactResolver; - return this; - } - - public SbtArtifactDescriptorReader setRepositoryEventDispatcher( RepositoryEventDispatcher repositoryEventDispatcher ) - { - if ( repositoryEventDispatcher == null ) - { - throw new IllegalArgumentException( "repository event dispatcher has not been specified" ); - } - this.repositoryEventDispatcher = repositoryEventDispatcher; - return this; - } - - public SbtArtifactDescriptorReader setModelBuilder( ModelBuilder modelBuilder ) - { - if ( modelBuilder == null ) - { - throw new IllegalArgumentException( "model builder has not been specified" ); - } - this.modelBuilder = modelBuilder; - return this; - } - - public ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession session, - ArtifactDescriptorRequest request ) - throws ArtifactDescriptorException - { - ArtifactDescriptorResult result = new ArtifactDescriptorResult( request ); - - Model model = loadPom( session, request, result ); - - if ( model != null ) - { - - ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry(); - - for ( Repository r : model.getRepositories() ) - { - result.addRepository( ArtifactDescriptorUtils.toRemoteRepository( r ) ); - } - - for ( org.apache.maven.model.Dependency dependency : model.getDependencies() ) - { - result.addDependency( convert( dependency, stereotypes ) ); - } - - DependencyManagement mngt = model.getDependencyManagement(); - if ( mngt != null ) - { - for ( org.apache.maven.model.Dependency dependency : mngt.getDependencies() ) - { - result.addManagedDependency( convert( dependency, stereotypes ) ); - } - } - - Map properties = new LinkedHashMap(); - - Prerequisites prerequisites = model.getPrerequisites(); - if ( prerequisites != null ) - { - properties.put( "prerequisites.maven", prerequisites.getMaven() ); - } - - List licenses = model.getLicenses(); - properties.put( SbtExtraProperties.LICENSE_COUNT_KEY, licenses.size() ); - for ( int i = 0; i < licenses.size(); i++ ) - { - License license = licenses.get( i ); - properties.put( SbtExtraProperties.makeLicenseName(i), license.getName() ); - properties.put( SbtExtraProperties.makeLicenseUrl(i), license.getUrl() ); - properties.put( "license." + i + ".comments", license.getComments() ); - properties.put( "license." + i + ".distribution", license.getDistribution() ); - } - - // SBT ADDED - Here we push in the pom packaging type for Ivy expectations. - final String packaging = - (model.getPackaging() == null) ? "jar" : model.getPackaging(); - properties.put(SbtExtraProperties.MAVEN_PACKAGING_KEY, packaging); - // SBT ADDED - Here we inject the sbt/scala version we parse out of the pom. - final Properties mprops = model.getProperties(); - if(mprops.containsKey(SbtExtraProperties.POM_SBT_VERSION)) { - final String sbtVersion = mprops.getProperty(SbtExtraProperties.POM_SBT_VERSION); - properties.put(SbtExtraProperties.SBT_VERSION_KEY, sbtVersion); - } - if(mprops.containsKey(SbtExtraProperties.POM_SCALA_VERSION)) { - properties.put(SbtExtraProperties.SCALA_VERSION_KEY, mprops.getProperty(SbtExtraProperties.POM_SCALA_VERSION)); - } - - // SBT-Added - Here we inject the additional dependency attributes (for transitive plugin resolution). - PomExtraDependencyAttributes.transferDependencyExtraAttributes(model.getProperties(), properties); - - result.setProperties( properties); - - setArtifactProperties( result, model ); - } - - return result; - } - - // SBT FIX - We make sure that artifact properties are copied over here, so we can find sbt-plugin POM files. - public static Artifact toPomArtifact(Artifact artifact) { - Artifact pomArtifact = artifact; - if(artifact.getClassifier().length() > 0 || !"pom".equals(artifact.getExtension())) { - // TODO - only copy over sbt-important properties. - pomArtifact = new DefaultArtifact(artifact.getGroupId(), artifact.getArtifactId(), "pom", artifact.getVersion()).setProperties(artifact.getProperties()); - } - return pomArtifact; - } - - private Model loadPom( RepositorySystemSession session, ArtifactDescriptorRequest request, - ArtifactDescriptorResult result ) - throws ArtifactDescriptorException - { - RequestTrace trace = RequestTrace.newChild( request.getTrace(), request ); - - Set visited = new LinkedHashSet(); - for ( Artifact artifact = request.getArtifact();; ) - { - // SBT FIX - we need to use our own variant here to preserve extra attributes. - // Artifact pomArtifact = ArtifactDescriptorUtils.toPomArtifact( artifact ); - Artifact pomArtifact = toPomArtifact(artifact); - try - { - VersionRequest versionRequest = - new VersionRequest( artifact, request.getRepositories(), request.getRequestContext() ); - versionRequest.setTrace( trace ); - VersionResult versionResult = versionResolver.resolveVersion( session, versionRequest ); - - artifact = artifact.setVersion( versionResult.getVersion() ); - - versionRequest = - new VersionRequest( pomArtifact, request.getRepositories(), request.getRequestContext() ); - versionRequest.setTrace( trace ); - versionResult = versionResolver.resolveVersion( session, versionRequest ); - - pomArtifact = pomArtifact.setVersion( versionResult.getVersion() ); - } - catch ( VersionResolutionException e ) - { - result.addException( e ); - throw new ArtifactDescriptorException( result ); - } - - if ( !visited.add( artifact.getGroupId() + ':' + artifact.getArtifactId() + ':' + artifact.getBaseVersion() ) ) - { - RepositoryException exception = - new RepositoryException( "Artifact relocations form a cycle: " + visited ); - invalidDescriptor( session, trace, artifact, exception ); - if ( ( getPolicy( session, artifact, request ) & ArtifactDescriptorPolicy.IGNORE_INVALID ) != 0 ) - { - return null; - } - result.addException( exception ); - throw new ArtifactDescriptorException( result ); - } - - ArtifactResult resolveResult; - try - { - ArtifactRequest resolveRequest = - new ArtifactRequest( pomArtifact, request.getRepositories(), request.getRequestContext() ); - resolveRequest.setTrace( trace ); - resolveResult = artifactResolver.resolveArtifact( session, resolveRequest ); - pomArtifact = resolveResult.getArtifact(); - result.setRepository( resolveResult.getRepository() ); - } - catch ( ArtifactResolutionException e ) - { - if ( e.getCause() instanceof ArtifactNotFoundException ) - { - missingDescriptor( session, trace, artifact, (Exception) e.getCause() ); - if ( ( getPolicy( session, artifact, request ) & ArtifactDescriptorPolicy.IGNORE_MISSING ) != 0 ) - { - return null; - } - } - result.addException( e ); - throw new ArtifactDescriptorException( result ); - } - - Model model; - try - { - ModelBuildingRequest modelRequest = new DefaultModelBuildingRequest(); - modelRequest.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL ); - modelRequest.setProcessPlugins(false); - modelRequest.setTwoPhaseBuilding(false); - modelRequest.setSystemProperties(toProperties(session.getUserProperties(), - session.getSystemProperties())); - modelRequest.setModelCache(DefaultModelCache.newInstance(session)); - modelRequest.setModelResolver( - new DefaultModelResolver( - session, - trace.newChild(modelRequest), - request.getRequestContext(), - artifactResolver, - versionRangeResolver, - remoteRepositoryManager, - request.getRepositories()) - ); - if ( resolveResult.getRepository() instanceof WorkspaceRepository ) - { - modelRequest.setPomFile( pomArtifact.getFile() ); - } - else - { - modelRequest.setModelSource( new FileModelSource( pomArtifact.getFile() ) ); - } - - model = modelBuilder.build( modelRequest ).getEffectiveModel(); - } - catch ( ModelBuildingException e ) - { - for ( ModelProblem problem : e.getProblems() ) - { - if ( problem.getException() instanceof UnresolvableModelException ) - { - result.addException( problem.getException() ); - throw new ArtifactDescriptorException( result ); - } - } - invalidDescriptor( session, trace, artifact, e ); - if ( ( getPolicy( session, artifact, request ) & ArtifactDescriptorPolicy.IGNORE_INVALID ) != 0 ) - { - return null; - } - result.addException( e ); - throw new ArtifactDescriptorException( result ); - } - - Relocation relocation = getRelocation( model ); - - if ( relocation != null ) - { - result.addRelocation( artifact ); - artifact = - new RelocatedArtifact( artifact, relocation.getGroupId(), relocation.getArtifactId(), - relocation.getVersion() ); - result.setArtifact( artifact ); - } - else - { - return model; - } - } - } - - private Properties toProperties( Map dominant, Map recessive ) - { - Properties props = new Properties(); - if ( recessive != null ) - { - props.putAll( recessive ); - } - if ( dominant != null ) - { - props.putAll( dominant ); - } - return props; - } - - private Relocation getRelocation( Model model ) - { - Relocation relocation = null; - DistributionManagement distMngt = model.getDistributionManagement(); - if ( distMngt != null ) - { - relocation = distMngt.getRelocation(); - } - return relocation; - } - - private void setArtifactProperties( ArtifactDescriptorResult result, Model model ) - { - String downloadUrl = null; - DistributionManagement distMngt = model.getDistributionManagement(); - if ( distMngt != null ) - { - downloadUrl = distMngt.getDownloadUrl(); - } - if ( downloadUrl != null && downloadUrl.length() > 0 ) - { - Artifact artifact = result.getArtifact(); - Map props = new HashMap( artifact.getProperties() ); - props.put( ArtifactProperties.DOWNLOAD_URL, downloadUrl ); - result.setArtifact( artifact.setProperties( props ) ); - } - } - - private Dependency convert( org.apache.maven.model.Dependency dependency, ArtifactTypeRegistry stereotypes ) - { - ArtifactType stereotype = stereotypes.get( dependency.getType() ); - if ( stereotype == null ) - { - stereotype = new DefaultArtifactType( dependency.getType() ); - } - - boolean system = dependency.getSystemPath() != null && dependency.getSystemPath().length() > 0; - - Map props = null; - if ( system ) - { - props = Collections.singletonMap( ArtifactProperties.LOCAL_PATH, dependency.getSystemPath() ); - } - - Artifact artifact = - new DefaultArtifact( dependency.getGroupId(), dependency.getArtifactId(), dependency.getClassifier(), null, - dependency.getVersion(), props, stereotype ); - - List exclusions = new ArrayList( dependency.getExclusions().size() ); - for ( org.apache.maven.model.Exclusion exclusion : dependency.getExclusions() ) - { - exclusions.add( convert( exclusion ) ); - } - - Dependency result = new Dependency( artifact, dependency.getScope(), dependency.isOptional(), exclusions ); - - return result; - } - - private Exclusion convert( org.apache.maven.model.Exclusion exclusion ) - { - return new Exclusion( exclusion.getGroupId(), exclusion.getArtifactId(), "*", "*" ); - } - - private void missingDescriptor( RepositorySystemSession session, RequestTrace trace, Artifact artifact, - Exception exception ) - { - RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DESCRIPTOR_MISSING ); - event.setTrace( trace ); - event.setArtifact( artifact ); - event.setException( exception ); - - repositoryEventDispatcher.dispatch( event.build() ); - } - - private void invalidDescriptor( RepositorySystemSession session, RequestTrace trace, Artifact artifact, - Exception exception ) - { - RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.ARTIFACT_DESCRIPTOR_INVALID ); - event.setTrace( trace ); - event.setArtifact( artifact ); - event.setException( exception ); - - repositoryEventDispatcher.dispatch( event.build() ); - } - - private int getPolicy( RepositorySystemSession session, Artifact artifact, ArtifactDescriptorRequest request ) - { - ArtifactDescriptorPolicy policy = session.getArtifactDescriptorPolicy(); - if ( policy == null ) - { - return ArtifactDescriptorPolicy.STRICT; - } - return policy.getPolicy( session, new ArtifactDescriptorPolicyRequest( artifact, request.getRequestContext() ) ); - } - - public void setVersionRangeResolver(final VersionRangeResolver versionRangeResolver) { - this.versionRangeResolver = versionRangeResolver; - } -} \ No newline at end of file diff --git a/ivy/src/main/scala/org/apache/maven/repository/internal/SbtRepositoryLayout.scala b/ivy/src/main/scala/org/apache/maven/repository/internal/SbtRepositoryLayout.scala deleted file mode 100644 index 23fe7630a..000000000 --- a/ivy/src/main/scala/org/apache/maven/repository/internal/SbtRepositoryLayout.scala +++ /dev/null @@ -1,100 +0,0 @@ -package org.apache.maven.repository.internal - -import org.apache.ivy.util.Message -import org.eclipse.aether.spi.connector.layout.{ RepositoryLayout, RepositoryLayoutFactory } -import org.eclipse.aether.RepositorySystemSession -import org.eclipse.aether.repository.RemoteRepository -import org.eclipse.aether.transfer.NoRepositoryLayoutException -import org.eclipse.aether.metadata.Metadata -import org.eclipse.aether.spi.connector.layout.RepositoryLayout.Checksum -import org.eclipse.aether.artifact.Artifact -import java.net.URI - -import scala.util.matching.Regex - -/** A factory which knows how to create repository layouts which can find sbt plugins. */ -class SbtPluginLayoutFactory extends RepositoryLayoutFactory { - def newInstance(session: RepositorySystemSession, repository: RemoteRepository): RepositoryLayout = { - repository.getContentType match { - case SbtRepositoryLayout.LAYOUT_NAME => - SbtRepositoryLayout - case _ => throw new NoRepositoryLayoutException(repository, "Not an sbt-plugin repository") - } - } - def getPriority: Float = 100.0f -} - -object SbtRepositoryLayout extends RepositoryLayout { - - val LAYOUT_NAME = "sbt-plugin" - - // get location is ALMOST the same for Metadata + artifact... but subtle differences are important. - - def getLocation(artifact: Artifact, upload: Boolean): URI = { - import collection.JavaConverters._ - val sbtVersion = Option(artifact.getProperties.get(SbtExtraProperties.POM_SBT_VERSION)) - val scalaVersion = Option(artifact.getProperties.get(SbtExtraProperties.POM_SCALA_VERSION)) - val path = new StringBuilder(128) - path.append(artifact.getGroupId.replace('.', '/')).append('/') - (sbtVersion zip scalaVersion).headOption match { - case Some((sbt, scala)) => - if (artifact.getArtifactId contains "_sbt_") { - // TODO - Write this handler. - val SbtNameVersionSplit(name, sbt2) = artifact.getArtifactId - path.append(name).append('_').append(scala).append('_').append(sbt).append('/') - } else path.append(artifact.getArtifactId).append('_').append(scala).append('_').append(sbt).append('/') - case None => - // TODO - Should we automatically append the _ here if it's not there? Probably not for now. - path.append(artifact.getArtifactId).append('/') - } - path.append(artifact.getBaseVersion).append('/') - sbtVersion match { - case Some(_) if artifact.getArtifactId contains "_sbt_" => - val SbtNameVersionSplit(name, sbt2) = artifact.getArtifactId - path.append(name).append('-').append(artifact.getVersion) - case None => path.append(artifact.getArtifactId).append('-').append(artifact.getVersion) - } - - if (artifact.getClassifier != null && !artifact.getClassifier.trim.isEmpty) { - path.append("-").append(artifact.getClassifier) - } - if (artifact.getExtension.length > 0) { - path.append('.').append(artifact.getExtension) - } - URI.create(path.toString()) - } - - // Trickery for disambiguating sbt plugins in maven repositories. - val SbtNameVersionSplit = new Regex("(.*)_sbt_(.*)") - - def getLocation(metadata: Metadata, upload: Boolean): URI = { - val sbtVersion = Option(metadata.getProperties.get(SbtExtraProperties.POM_SBT_VERSION)) - val scalaVersion = Option(metadata.getProperties.get(SbtExtraProperties.POM_SCALA_VERSION)) - val path = new StringBuilder(128) - path.append(metadata.getGroupId.replace('.', '/')).append('/') - (sbtVersion zip scalaVersion).headOption match { - case Some((sbt, scala)) => - if (metadata.getArtifactId contains "_sbt_") { - val SbtNameVersionSplit(name, sbt2) = metadata.getArtifactId - path.append(name).append('_').append(scala).append('_').append(sbt).append('/') - // TODO - Write this handler. - } else path.append(metadata.getArtifactId).append('_').append(scala).append('_').append(sbt).append('/') - case None => - // TODO - Should we automatically append the _ here? Proabbly not for now. - path.append(metadata.getArtifactId).append('/') - } - if (metadata.getVersion.length > 0) - path.append(metadata.getVersion).append('/') - path.append(metadata.getType) - URI.create(path.toString) - } - - // TODO - This should be the same as configured from Ivy... - def getChecksums(artifact: Artifact, upload: Boolean, location: URI): java.util.List[Checksum] = - getChecksums(location) - def getChecksums(metadata: Metadata, upload: Boolean, location: URI): java.util.List[Checksum] = - getChecksums(location) - - private def getChecksums(location: URI): java.util.List[Checksum] = - java.util.Arrays.asList(Checksum.forLocation(location, "SHA-1"), Checksum.forLocation(location, "MD5")) -} \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/ConvertResolver.scala b/ivy/src/main/scala/sbt/ConvertResolver.scala index 9c7545eb9..26abec09c 100644 --- a/ivy/src/main/scala/sbt/ConvertResolver.scala +++ b/ivy/src/main/scala/sbt/ConvertResolver.scala @@ -19,6 +19,8 @@ import org.apache.ivy.util.{ FileUtil, ChecksumHelper } import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact } private[sbt] object ConvertResolver { + import UpdateOptions.ResolverConverter + /** * This class contains all the reflective lookups used in the * checksum-friendly URL publishing shim. @@ -94,37 +96,34 @@ private[sbt] object ConvertResolver { } } - private[sbt] val USE_AETHER_PROPERTY = "sbt.use.aether" - private def isUseAetherForResolution(settings: IvySettings): Boolean = - settings.getVariable(USE_AETHER_PROPERTY) == "true" + /** Converts the given sbt resolver into an Ivy resolver. */ + @deprecated("0.13.8", "Use the variant with updateOptions") + def apply(r: Resolver, settings: IvySettings, log: Logger): DependencyResolver = + apply(r, settings, UpdateOptions(), log) - /** Converts the given sbt resolver into an Ivy resolver..*/ - def apply(r: Resolver, settings: IvySettings, log: Logger) = - { + /** Converts the given sbt resolver into an Ivy resolver. */ + def apply(r: Resolver, settings: IvySettings, updateOptions: UpdateOptions, log: Logger): DependencyResolver = + (updateOptions.resolverConverter orElse defaultConvert)((r, settings, log)) + + /** The default implementation of converter. */ + lazy val defaultConvert: ResolverConverter = { + case (r, settings, log) => r match { case repo: MavenRepository => { - if (isUseAetherForResolution(settings)) { - repo match { - case cache: MavenCache => new org.apache.ivy.plugins.resolver.MavenCacheRepositoryResolver(cache, settings) - case _ => new org.apache.ivy.plugins.resolver.MavenRemoteRepositoryResolver(repo, settings) + val pattern = Collections.singletonList(Resolver.resolvePattern(repo.root, Resolver.mavenStyleBasePattern)) + final class PluginCapableResolver extends IBiblioResolver with ChecksumFriendlyURLResolver with DescriptorRequired { + def setPatterns() { + // done this way for access to protected methods. + setArtifactPatterns(pattern) + setIvyPatterns(pattern) } - } else { - val pattern = Collections.singletonList(Resolver.resolvePattern(repo.root, Resolver.mavenStyleBasePattern)) - final class PluginCapableResolver extends IBiblioResolver with ChecksumFriendlyURLResolver with DescriptorRequired { - def setPatterns() { - // done this way for access to protected methods. - setArtifactPatterns(pattern) - setIvyPatterns(pattern) - } - } - val resolver = new PluginCapableResolver - resolver.setRepository(new LocalIfFileRepo) - initializeMavenStyle(resolver, repo.name, repo.root) - resolver.setPatterns() // has to be done after initializeMavenStyle, which calls methods that overwrite the patterns - resolver } - + val resolver = new PluginCapableResolver + resolver.setRepository(new LocalIfFileRepo) + initializeMavenStyle(resolver, repo.name, repo.root) + resolver.setPatterns() // has to be done after initializeMavenStyle, which calls methods that overwrite the patterns + resolver } // TODO: HTTP repository is no longer recommended. #1541 // Remove `JavaNet1Repository` when we bump up the API. @@ -176,7 +175,7 @@ private[sbt] object ConvertResolver { case repo: ChainedResolver => IvySbt.resolverChain(repo.name, repo.resolvers, false, settings, log) case repo: RawRepository => repo.resolver } - } + } private sealed trait DescriptorRequired extends BasicResolver { override def getDependency(dd: DependencyDescriptor, data: ResolveData) = diff --git a/ivy/src/main/scala/sbt/CustomPomParser.scala b/ivy/src/main/scala/sbt/CustomPomParser.scala index d931cefe6..c9e660833 100644 --- a/ivy/src/main/scala/sbt/CustomPomParser.scala +++ b/ivy/src/main/scala/sbt/CustomPomParser.scala @@ -13,7 +13,7 @@ import java.io.{ File, InputStream } import java.net.URL import java.util.regex.Pattern -import org.apache.maven.repository.internal.{ SbtExtraProperties, PomExtraDependencyAttributes } +import org.apache.maven.repository.internal.{ PomExtraDependencyAttributes } @deprecated("0.13.8", "We now use an Aether-based pom parser.") final class CustomPomParser(delegate: ModuleDescriptorParser, transform: (ModuleDescriptorParser, ModuleDescriptor) => ModuleDescriptor) extends ModuleDescriptorParser { diff --git a/ivy/src/main/scala/sbt/Ivy.scala b/ivy/src/main/scala/sbt/Ivy.scala index d2d0388ee..be86d1131 100644 --- a/ivy/src/main/scala/sbt/Ivy.scala +++ b/ivy/src/main/scala/sbt/Ivy.scala @@ -73,7 +73,6 @@ final class IvySbt(val configuration: IvyConfiguration) { is.setBaseDir(baseDirectory) is.setCircularDependencyStrategy(configuration.updateOptions.circularDependencyLevel.ivyStrategy) CustomPomParser.registerDefault - is.setVariable(ConvertResolver.USE_AETHER_PROPERTY, s"${configuration.updateOptions.aetherResolution}") configuration match { case e: ExternalIvyConfiguration => @@ -289,7 +288,7 @@ private[sbt] object IvySbt { def resolverChain(name: String, resolvers: Seq[Resolver], localOnly: Boolean, settings: IvySettings, log: Logger): DependencyResolver = resolverChain(name, resolvers, localOnly, settings, UpdateOptions(), log) def resolverChain(name: String, resolvers: Seq[Resolver], localOnly: Boolean, settings: IvySettings, updateOptions: UpdateOptions, log: Logger): DependencyResolver = { - def mapResolvers(rs: Seq[Resolver]) = rs.map(r => ConvertResolver(r, settings, log)) + def mapResolvers(rs: Seq[Resolver]) = rs.map(r => ConvertResolver(r, settings, updateOptions, log)) val (projectResolvers, rest) = resolvers.partition(_.name == "inter-project") if (projectResolvers.isEmpty) new ivyint.SbtChainResolver(name, mapResolvers(rest), settings, updateOptions, log) else { diff --git a/ivy/src/main/scala/sbt/MakePom.scala b/ivy/src/main/scala/sbt/MakePom.scala index 13ca70f1b..fc15a3cd9 100644 --- a/ivy/src/main/scala/sbt/MakePom.scala +++ b/ivy/src/main/scala/sbt/MakePom.scala @@ -20,6 +20,7 @@ import org.apache.ivy.Ivy 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 class MakePom(val log: Logger) { @deprecated("Use `write(Ivy, ModuleDescriptor, ModuleInfo, Option[Iterable[Configuration]], Set[String], NodeSeq, XNode => XNode, MavenRepository => Boolean, Boolean, File)` instead", "0.11.2") @@ -333,7 +334,7 @@ class MakePom(val log: Logger) { val repositories = if (includeAll) allResolvers(settings) else resolvers(settings.getDefaultResolver) val mavenRepositories = repositories.flatMap { - case m: org.apache.ivy.plugins.resolver.MavenRemoteRepositoryResolver if m.repo.root != DefaultMavenRepository.root => + case m: CustomRemoteMavenResolver if m.repo.root != DefaultMavenRepository.root => MavenRepository(m.repo.name, m.repo.root) :: Nil case m: IBiblioResolver if m.isM2compatible && m.getRoot != DefaultMavenRepository.root => MavenRepository(m.getName, m.getRoot) :: Nil diff --git a/ivy/src/main/scala/sbt/ModuleID.scala b/ivy/src/main/scala/sbt/ModuleID.scala index bf734f5f7..4b244df44 100644 --- a/ivy/src/main/scala/sbt/ModuleID.scala +++ b/ivy/src/main/scala/sbt/ModuleID.scala @@ -5,8 +5,6 @@ package sbt import java.net.URL -import org.apache.maven.repository.internal.SbtExtraProperties - final case class ModuleID(organization: String, name: String, revision: String, configurations: Option[String] = None, isChanging: Boolean = false, isTransitive: Boolean = true, isForce: Boolean = false, explicitArtifacts: Seq[Artifact] = Nil, exclusions: Seq[ExclusionRule] = Nil, extraAttributes: Map[String, String] = Map.empty, crossVersion: CrossVersion = CrossVersion.Disabled) { override def toString: String = organization + ":" + name + ":" + revision + diff --git a/ivy/src/main/scala/sbt/UpdateOptions.scala b/ivy/src/main/scala/sbt/UpdateOptions.scala index e13fed72f..3a710026c 100644 --- a/ivy/src/main/scala/sbt/UpdateOptions.scala +++ b/ivy/src/main/scala/sbt/UpdateOptions.scala @@ -1,6 +1,8 @@ package sbt import java.io.File +import org.apache.ivy.plugins.resolver.DependencyResolver +import org.apache.ivy.core.settings.IvySettings /** * Represents configurable options for update task. @@ -18,13 +20,8 @@ final class UpdateOptions private[sbt] ( val consolidatedResolution: Boolean, /** If set to true, use cached resolution. */ val cachedResolution: Boolean, - /** If set to true, use aether for resolving maven artifacts. */ - val aetherResolution: Boolean) { - - /** Enables Aether for dependency resolution. */ - def withAetherResolution(aetherResolution: Boolean): UpdateOptions = - copy(aetherResolution = aetherResolution) - + /** Extention point for an alternative resolver converter. */ + val resolverConverter: UpdateOptions.ResolverConverter) { def withCircularDependencyLevel(circularDependencyLevel: CircularDependencyLevel): UpdateOptions = copy(circularDependencyLevel = circularDependencyLevel) def withLatestSnapshots(latestSnapshots: Boolean): UpdateOptions = @@ -36,24 +33,28 @@ final class UpdateOptions private[sbt] ( def withCachedResolution(cachedResoluton: Boolean): UpdateOptions = copy(cachedResolution = cachedResoluton, consolidatedResolution = cachedResolution) + /** Extention point for an alternative resolver converter. */ + def withResolverConverter(resolverConverter: UpdateOptions.ResolverConverter): UpdateOptions = + copy(resolverConverter = resolverConverter) private[sbt] def copy( circularDependencyLevel: CircularDependencyLevel = this.circularDependencyLevel, latestSnapshots: Boolean = this.latestSnapshots, consolidatedResolution: Boolean = this.consolidatedResolution, cachedResolution: Boolean = this.cachedResolution, - aetherResolution: Boolean = this.aetherResolution): UpdateOptions = + resolverConverter: UpdateOptions.ResolverConverter = this.resolverConverter): UpdateOptions = new UpdateOptions(circularDependencyLevel, latestSnapshots, consolidatedResolution, cachedResolution, - aetherResolution) + resolverConverter) override def equals(o: Any): Boolean = o match { case o: UpdateOptions => this.circularDependencyLevel == o.circularDependencyLevel && this.latestSnapshots == o.latestSnapshots && - this.cachedResolution == o.cachedResolution + this.cachedResolution == o.cachedResolution && + this.resolverConverter == o.resolverConverter case _ => false } @@ -63,17 +64,19 @@ final class UpdateOptions private[sbt] ( hash = hash * 31 + this.circularDependencyLevel.## hash = hash * 31 + this.latestSnapshots.## hash = hash * 31 + this.cachedResolution.## + hash = hash * 31 + this.resolverConverter.## hash } } object UpdateOptions { + type ResolverConverter = PartialFunction[(Resolver, IvySettings, Logger), DependencyResolver] + def apply(): UpdateOptions = new UpdateOptions( circularDependencyLevel = CircularDependencyLevel.Warn, latestSnapshots = false, consolidatedResolution = false, cachedResolution = false, - // TODO - Disable this before release, but make sure test suite passes with it on. - aetherResolution = true) + resolverConverter = PartialFunction.empty) } diff --git a/ivy/src/main/scala/sbt/ivyint/CustomMavenResolver.scala b/ivy/src/main/scala/sbt/ivyint/CustomMavenResolver.scala new file mode 100644 index 000000000..e3e41148e --- /dev/null +++ b/ivy/src/main/scala/sbt/ivyint/CustomMavenResolver.scala @@ -0,0 +1,11 @@ +package sbt +package ivyint + +import org.apache.ivy.plugins.resolver.DependencyResolver + +// These are placeholder traits for sbt-aether-resolver +trait CustomMavenResolver extends DependencyResolver { +} +trait CustomRemoteMavenResolver extends CustomMavenResolver { + def repo: MavenRepository +} diff --git a/ivy/src/main/scala/sbt/ivyint/SbtChainResolver.scala b/ivy/src/main/scala/sbt/ivyint/SbtChainResolver.scala index 13c2642ec..08c8f00e7 100644 --- a/ivy/src/main/scala/sbt/ivyint/SbtChainResolver.scala +++ b/ivy/src/main/scala/sbt/ivyint/SbtChainResolver.scala @@ -184,7 +184,7 @@ private[sbt] case class SbtChainResolver( val artifactOpt = findFirstArtifactRef(rmr.getDescriptor, dd, data, resolver) artifactOpt match { case None if resolver.getName == "inter-project" => // do nothing - case None if resolver.isInstanceOf[AbstractMavenRepositoryResolver] => + case None if resolver.isInstanceOf[CustomMavenResolver] => // do nothing for now.... // We want to see if the maven caching is sufficient and we do not need to duplicate within the ivy cache... case None => throw new RuntimeException(s"\t${resolver.getName}: no ivy file nor artifact found for $rmr") diff --git a/ivy/src/test/scala/MavenResolutionSpec.scala b/ivy/src/test/scala/MavenResolutionSpec.scala deleted file mode 100644 index 8015e78a8..000000000 --- a/ivy/src/test/scala/MavenResolutionSpec.scala +++ /dev/null @@ -1,292 +0,0 @@ -package sbt - -import java.io.FileInputStream - -import org.apache.maven.repository.internal.PomExtraDependencyAttributes -import org.specs2._ - -class MavenResolutionSpec extends BaseIvySpecification { - def is = args(sequential = true) ^ s2""".stripMargin - - This is a specification to check the maven resolution - - Resolving a maven dependency should - handle sbt plugins $resolveSbtPlugins - use ivy for conflict resolution $resolveMajorConflicts - handle cross configuration deps $resolveCrossConfigurations - publish with maven-metadata $publishMavenMetadata - resolve transitive maven dependencies $resolveTransitiveMavenDependency - resolve intransitive maven dependencies $resolveIntransitiveMavenDependency - handle transitive configuration shifts $resolveTransitiveConfigurationMavenDependency - resolve source and doc $resolveSourceAndJavadoc - resolve nonstandard (jdk5) classifier $resolveNonstandardClassifier - Resolve pom artifact dependencies $resolvePomArtifactAndDependencies - Fail if JAR artifact is not found w/ POM $failIfMainArtifactMissing - Fail if POM.xml is not found $failIfPomMissing - resolve publication date for -SNAPSHOT $resolveSnapshotPubDate - - """ // */ - - // TODO - test latest.integration and .+ - - def akkaActor = ModuleID("com.typesafe.akka", "akka-actor_2.11", "2.3.8", Some("compile")) - def akkaActorTestkit = ModuleID("com.typesafe.akka", "akka-testkit_2.11", "2.3.8", Some("test")) - def testngJdk5 = ModuleID("org.testng", "testng", "5.7", Some("compile")).classifier("jdk15") - def jmxri = ModuleID("com.sun.jmx", "jmxri", "1.2.1", Some("compile")) - def scalaLibraryAll = ModuleID("org.scala-lang", "scala-library-all", "2.11.4", Some("compile")) - def scalaCompiler = ModuleID("org.scala-lang", "scala-compiler", "2.8.1", Some("scala-tool->default(compile)")) - def scalaContinuationPlugin = ModuleID("org.scala-lang.plugins", "continuations", "2.8.1", Some("plugin->default(compile)")) - def sbtPlugin = - ModuleID("com.github.mpeltonen", "sbt-idea", "1.6.0", Some("compile")). - extra(PomExtraDependencyAttributes.SbtVersionKey -> "0.13", PomExtraDependencyAttributes.ScalaVersionKey -> "2.10"). - copy(crossVersion = CrossVersion.Disabled) - def oldSbtPlugin = - ModuleID("com.github.mpeltonen", "sbt-idea", "1.6.0", Some("compile")). - extra(PomExtraDependencyAttributes.SbtVersionKey -> "0.12", PomExtraDependencyAttributes.ScalaVersionKey -> "2.9.2"). - copy(crossVersion = CrossVersion.Disabled) - def majorConflictLib = ModuleID("com.joestelmach", "natty", "0.3", Some("compile")) - // TODO - This snapshot and resolver should be something we own/control so it doesn't disappear on us. - def testSnapshot = ModuleID("com.typesafe", "config", "0.4.9-SNAPSHOT", Some("compile")) - val SnapshotResolver = MavenRepository("some-snapshots", "https://oss.sonatype.org/content/repositories/snapshots/") - - override def resolvers = Seq(DefaultMavenRepository, SnapshotResolver, Resolver.publishMavenLocal) - import Configurations.{ Compile, Test, Runtime, CompilerPlugin, ScalaTool } - override def configurations = Seq(Compile, Test, Runtime, CompilerPlugin, ScalaTool) - - import ShowLines._ - - def defaultUpdateOptions = UpdateOptions().withAetherResolution(true) - - def resolveMajorConflicts = { - val m = module(ModuleID("com.example", "foo", "0.1.0", Some("compile")), - Seq(majorConflictLib), None, defaultUpdateOptions) - val report = ivyUpdate(m) // must not(throwAn[IllegalStateException]) - val jars = - for { - conf <- report.configurations - if conf.configuration == Compile.name - m <- conf.modules - if (m.module.name contains "stringtemplate") - (a, f) <- m.artifacts - if a.extension == "jar" - } yield f - jars must haveSize(1) - } - - def resolveCrossConfigurations = { - val m = module(ModuleID("com.example", "foo", "0.1.0", Some("compile")), - Seq(scalaCompiler, scalaContinuationPlugin), None, defaultUpdateOptions) - val report = ivyUpdate(m) - val jars = - for { - conf <- report.configurations - if conf.configuration == ScalaTool.name - m <- conf.modules - if (m.module.name contains "scala-compiler") - (a, f) <- m.artifacts - if a.extension == "jar" - } yield f - jars must haveSize(1) - } - - def resolveSbtPlugins = { - - def sha(f: java.io.File): String = sbt.Hash.toHex(sbt.Hash(f)) - def findSbtIdeaJars(dep: ModuleID, name: String) = { - val m = module(ModuleID("com.example", name, "0.1.0", Some("compile")), Seq(dep), None, defaultUpdateOptions) - val report = ivyUpdate(m) - for { - conf <- report.configurations - if conf.configuration == "compile" - m <- conf.modules - if (m.module.name contains "sbt-idea") - (a, f) <- m.artifacts - if a.extension == "jar" - } yield (f, sha(f)) - } - - val oldJars = findSbtIdeaJars(oldSbtPlugin, "old") - System.err.println(s"${oldJars.mkString("\n")}") - val newJars = findSbtIdeaJars(sbtPlugin, "new") - System.err.println(s"${newJars.mkString("\n")}") - (newJars must haveSize(1)) and (oldJars must haveSize(1)) and (oldJars.map(_._2) must not(containTheSameElementsAs(newJars.map(_._2)))) - } - - def resolveSnapshotPubDate = { - val m = module(ModuleID("com.example", "foo", "0.1.0", Some("compile")), Seq(testSnapshot), Some("2.10.2"), defaultUpdateOptions.withLatestSnapshots(true)) - val report = ivyUpdate(m) - val pubTime = - for { - conf <- report.configurations - if conf.configuration == "compile" - m <- conf.modules - if m.module.revision endsWith "-SNAPSHOT" - date <- m.publicationDate - } yield date - (pubTime must haveSize(1)) - } - - def resolvePomArtifactAndDependencies = { - val m = module(ModuleID("com.example", "foo", "0.1.0", Some("compile")), Seq(scalaLibraryAll), Some("2.10.2"), defaultUpdateOptions) - val report = ivyUpdate(m) - val jars = - for { - conf <- report.configurations - if conf.configuration == "compile" - m <- conf.modules - if (m.module.name == "scala-library") || (m.module.name contains "parser") - (a, f) <- m.artifacts - if a.extension == "jar" - } yield f - jars must haveSize(2) - } - - def failIfPomMissing = { - // TODO - we need the jar to not exist too. - val m = module(ModuleID("com.example", "foo", "0.1.0", Some("compile")), Seq(ModuleID("org.scala-sbt", "does-not-exist", "1.0", Some("compile"))), Some("2.10.2"), defaultUpdateOptions) - ivyUpdate(m) must throwAn[Exception] - } - - def failIfMainArtifactMissing = { - val m = module(ModuleID("com.example", "foo", "0.1.0", Some("compile")), Seq(jmxri), Some("2.10.2"), defaultUpdateOptions) - ivyUpdate(m) must throwAn[Exception] - } - - def resolveNonstandardClassifier = { - val m = module(ModuleID("com.example", "foo", "0.1.0", Some("compile")), Seq(testngJdk5), Some("2.10.2"), defaultUpdateOptions) - val report = ivyUpdate(m) - val jars = - for { - conf <- report.configurations - if conf.configuration == "compile" - m <- conf.modules - if m.module.name == "testng" - (a, f) <- m.artifacts - if a.extension == "jar" - } yield f - (report.configurations must haveSize(configurations.size)) and - (jars must haveSize(1)) - (jars.forall(_.exists) must beTrue) - - } - - def resolveTransitiveMavenDependency = { - val m = module(ModuleID("com.example", "foo", "0.1.0", Some("compile")), Seq(akkaActor), Some("2.10.2"), defaultUpdateOptions) - val report = ivyUpdate(m) - val jars = - for { - conf <- report.configurations - if conf.configuration == "compile" - m <- conf.modules - if m.module.name == "scala-library" - (a, f) <- m.artifacts - if a.extension == "jar" - } yield f - (report.configurations must haveSize(configurations.size)) and - (jars must not(beEmpty)) and - (jars.forall(_.exists) must beTrue) - - } - - def resolveIntransitiveMavenDependency = { - val m = module(ModuleID("com.example", "foo", "0.1.0", Some("compile")), Seq(akkaActorTestkit.intransitive()), Some("2.10.2"), defaultUpdateOptions) - val report = ivyUpdate(m) - val transitiveJars = - for { - conf <- report.configurations - if conf.configuration == "compile" - m <- conf.modules - if (m.module.name contains "akka-actor") && !(m.module.name contains "testkit") - (a, f) <- m.artifacts - if a.extension == "jar" - } yield f - val directJars = - for { - conf <- report.configurations - if conf.configuration == "compile" - m <- conf.modules - if (m.module.name contains "akka-actor") && (m.module.name contains "testkit") - (a, f) <- m.artifacts - if a.extension == "jar" - } yield f - (report.configurations must haveSize(configurations.size)) and - (transitiveJars must beEmpty) and (directJars.forall(_.exists) must beTrue) - } - - def resolveTransitiveConfigurationMavenDependency = { - val m = module(ModuleID("com.example", "foo", "0.1.0", Some("compile")), Seq(akkaActorTestkit), Some("2.10.2"), defaultUpdateOptions) - val report = ivyUpdate(m) - val jars = - for { - conf <- report.configurations - if conf.configuration == "test" - m <- conf.modules - if m.module.name contains "akka-actor" - (a, f) <- m.artifacts - if a.extension == "jar" - } yield f - (report.configurations must haveSize(configurations.size)) and - (jars must not(beEmpty)) and - (jars.forall(_.exists) must beTrue) - - } - - def resolveSourceAndJavadoc = { - val m = module( - ModuleID("com.example", "foo", "0.1.0", Some("sources")), - Seq(akkaActor.artifacts(Artifact(akkaActor.name, "javadoc"), Artifact(akkaActor.name, "sources"))), - Some("2.10.2"), - defaultUpdateOptions - ) - val report = ivyUpdate(m) - val jars = - for { - conf <- report.configurations - // We actually injected javadoc/sources into the compile scope, due to how we did the request. - // SO, we report that here. - if conf.configuration == "compile" - m <- conf.modules - (a, f) <- m.artifacts - if (f.getName contains "sources") || (f.getName contains "javadoc") - } yield f - (report.configurations must haveSize(configurations.size)) and - (jars must haveSize(2)) - } - - def publishMavenMetadata = { - val m = module( - ModuleID("com.example", "test-it", "1.0-SNAPSHOT", Some("compile")), - Seq(), - None, - defaultUpdateOptions.withLatestSnapshots(true) - ) - sbt.IO.withTemporaryDirectory { dir => - val pomFile = new java.io.File(dir, "pom.xml") - sbt.IO.write(pomFile, - """ - | - | com.example - | test-it - | 1.0-SNAPSHOT - | - """.stripMargin) - val jarFile = new java.io.File(dir, "test-it-1.0-SNAPSHOT.jar") - sbt.IO.touch(jarFile) - System.err.println(s"DEBUGME - Publishing $m to ${Resolver.publishMavenLocal}") - ivyPublish(m, mkPublishConfiguration( - Resolver.publishMavenLocal, - Map( - Artifact("test-it-1.0-SNAPSHOT.jar") -> pomFile, - Artifact("test-it-1.0-SNAPSHOT.pom", "pom", "pom") -> jarFile - ))) - } - val baseLocalMavenDir: java.io.File = Resolver.publishMavenLocal.rootFile - val allFiles: Seq[java.io.File] = sbt.PathFinder(new java.io.File(baseLocalMavenDir, "com/example/test-it")).***.get - val metadataFiles = allFiles.filter(_.getName contains "maven-metadata-local") - // TODO - maybe we check INSIDE the metadata, or make sure we can get a publication date on resolve... - // We end up with 4 files, two mavne-metadata files, and 2 maven-metadata-local files. - metadataFiles must haveSize(2) - } - -} -