mirror of https://github.com/sbt/sbt.git
Merge pull request #1793 from sbt/wip/aether-resolver
Use Aether for resolving maven dependencies
This commit is contained in:
commit
8eabd82b32
|
|
@ -0,0 +1,25 @@
|
|||
package sbt.mavenint;
|
||||
|
||||
/**
|
||||
* Extra properties we dump from Aether into the properties list.
|
||||
*/
|
||||
public class SbtPomExtraProperties {
|
||||
|
||||
public static final String MAVEN_PACKAGING_KEY = "sbt.pom.packaging";
|
||||
public static final String SCALA_VERSION_KEY = "sbt.pom.scalaversion";
|
||||
public static final String SBT_VERSION_KEY = "sbt.pom.sbtversion";
|
||||
|
||||
public static final String POM_INFO_KEY_PREFIX = "info.";
|
||||
public static final String POM_SCALA_VERSION = "scalaVersion";
|
||||
public static final String POM_SBT_VERSION = "sbtVersion";
|
||||
public static final String POM_API_KEY = "info.apiURL";
|
||||
|
||||
public static final String LICENSE_COUNT_KEY = "license.count";
|
||||
|
||||
public static String makeLicenseName(int i) {
|
||||
return "license." + i + ".name";
|
||||
}
|
||||
public static String makeLicenseUrl(int i) {
|
||||
return "license." + i + ".url";
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,11 @@ import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
|
|||
*/
|
||||
object ReplaceMavenConfigurationMappings {
|
||||
|
||||
def addMappings(dd: DefaultDependencyDescriptor, scope: String, isOptional: Boolean) = {
|
||||
val mapping = ReplaceMavenConfigurationMappings.REPLACEMENT_MAVEN_MAPPINGS.get(scope)
|
||||
mapping.addMappingConfs(dd, isOptional)
|
||||
}
|
||||
|
||||
val REPLACEMENT_MAVEN_MAPPINGS = {
|
||||
// Here we copy paste from Ivy
|
||||
val REPLACEMENT_MAPPINGS = new java.util.HashMap[String, PomModuleDescriptorBuilder.ConfMapper]
|
||||
|
|
|
|||
|
|
@ -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,15 +96,25 @@ private[sbt] object ConvertResolver {
|
|||
}
|
||||
}
|
||||
|
||||
/** 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. */
|
||||
@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, 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 =>
|
||||
{
|
||||
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.
|
||||
def setPatterns() {
|
||||
// done this way for access to protected methods.
|
||||
setArtifactPatterns(pattern)
|
||||
setIvyPatterns(pattern)
|
||||
}
|
||||
|
|
@ -163,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) =
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ import org.apache.ivy.util.extendable.ExtendableItem
|
|||
import java.io.{ File, InputStream }
|
||||
import java.net.URL
|
||||
import java.util.regex.Pattern
|
||||
import sbt.mavenint.{ PomExtraDependencyAttributes, SbtPomExtraProperties }
|
||||
|
||||
@deprecated("0.13.8", "We now use an Aether-based pom parser.")
|
||||
final class CustomPomParser(delegate: ModuleDescriptorParser, transform: (ModuleDescriptorParser, ModuleDescriptor) => ModuleDescriptor) extends ModuleDescriptorParser {
|
||||
override def parseDescriptor(ivySettings: ParserSettings, descriptorURL: URL, validate: Boolean) =
|
||||
transform(this, delegate.parseDescriptor(ivySettings, descriptorURL, validate))
|
||||
|
|
@ -26,21 +28,23 @@ final class CustomPomParser(delegate: ModuleDescriptorParser, transform: (Module
|
|||
override def getType() = delegate.getType()
|
||||
override def getMetadataArtifact(mrid: ModuleRevisionId, res: Resource) = delegate.getMetadataArtifact(mrid, res)
|
||||
}
|
||||
@deprecated("0.13.8", "We now use an Aether-based pom parser.")
|
||||
object CustomPomParser {
|
||||
|
||||
// Evil hackery to override the default maven pom mappings.
|
||||
ReplaceMavenConfigurationMappings.init()
|
||||
|
||||
/** The key prefix that indicates that this is used only to store extra information and is not intended for dependency resolution.*/
|
||||
val InfoKeyPrefix = "info."
|
||||
val ApiURLKey = "info.apiURL"
|
||||
val InfoKeyPrefix = SbtPomExtraProperties.POM_INFO_KEY_PREFIX
|
||||
val ApiURLKey = SbtPomExtraProperties.POM_API_KEY
|
||||
|
||||
val SbtVersionKey = "sbtVersion"
|
||||
val ScalaVersionKey = "scalaVersion"
|
||||
val ExtraAttributesKey = "extraDependencyAttributes"
|
||||
val SbtVersionKey = PomExtraDependencyAttributes.SbtVersionKey
|
||||
val ScalaVersionKey = PomExtraDependencyAttributes.ScalaVersionKey
|
||||
val ExtraAttributesKey = PomExtraDependencyAttributes.ExtraAttributesKey
|
||||
private[this] val unqualifiedKeys = Set(SbtVersionKey, ScalaVersionKey, ExtraAttributesKey, ApiURLKey)
|
||||
|
||||
// packagings that should be jars, but that Ivy doesn't handle as jars
|
||||
// TODO - move this elsewhere.
|
||||
val JarPackagings = Set("eclipse-plugin", "hk2-jar", "orbit", "scala-jar")
|
||||
val default = new CustomPomParser(PomModuleDescriptorParser.getInstance, defaultTransform)
|
||||
|
||||
|
|
@ -123,46 +127,24 @@ object CustomPomParser {
|
|||
}
|
||||
|
||||
private[this] def getDependencyExtra(m: Map[String, String]): Map[ModuleRevisionId, Map[String, String]] =
|
||||
(m get ExtraAttributesKey) match {
|
||||
case None => Map.empty
|
||||
case Some(str) =>
|
||||
def processDep(m: ModuleRevisionId) = (simplify(m), filterCustomExtra(m, include = true))
|
||||
readDependencyExtra(str).map(processDep).toMap
|
||||
}
|
||||
PomExtraDependencyAttributes.getDependencyExtra(m)
|
||||
|
||||
def qualifiedExtra(item: ExtendableItem): Map[String, String] =
|
||||
{
|
||||
import collection.JavaConverters._
|
||||
item.getQualifiedExtraAttributes.asInstanceOf[java.util.Map[String, String]].asScala.toMap
|
||||
}
|
||||
def qualifiedExtra(item: ExtendableItem): Map[String, String] = PomExtraDependencyAttributes.qualifiedExtra(item)
|
||||
def filterCustomExtra(item: ExtendableItem, include: Boolean): Map[String, String] =
|
||||
(qualifiedExtra(item) filterKeys { k => qualifiedIsExtra(k) == include })
|
||||
|
||||
def writeDependencyExtra(s: Seq[DependencyDescriptor]): Seq[String] =
|
||||
s.flatMap { dd =>
|
||||
val revId = dd.getDependencyRevisionId
|
||||
if (filterCustomExtra(revId, include = true).isEmpty)
|
||||
Nil
|
||||
else
|
||||
revId.encodeToString :: Nil
|
||||
}
|
||||
PomExtraDependencyAttributes.writeDependencyExtra(s)
|
||||
|
||||
// parses the sequence of dependencies with extra attribute information, with one dependency per line
|
||||
def readDependencyExtra(s: String): Seq[ModuleRevisionId] =
|
||||
LinesP.split(s).map(_.trim).filter(!_.isEmpty).map(ModuleRevisionId.decode)
|
||||
def readDependencyExtra(s: String): Seq[ModuleRevisionId] = PomExtraDependencyAttributes.readDependencyExtra(s)
|
||||
|
||||
private[this] val LinesP = Pattern.compile("(?m)^")
|
||||
|
||||
def qualifiedIsExtra(k: String): Boolean = k.endsWith(ScalaVersionKey) || k.endsWith(SbtVersionKey)
|
||||
def qualifiedIsExtra(k: String): Boolean = PomExtraDependencyAttributes.qualifiedIsExtra(k)
|
||||
|
||||
// Reduces the id to exclude custom extra attributes
|
||||
// This makes the id suitable as a key to associate a dependency parsed from a <dependency> element
|
||||
// with the extra attributes from the <properties> section
|
||||
def simplify(id: ModuleRevisionId): ModuleRevisionId =
|
||||
{
|
||||
import collection.JavaConverters._
|
||||
ModuleRevisionId.newInstance(id.getOrganisation, id.getName, id.getBranch, id.getRevision, filterCustomExtra(id, include = false).asJava)
|
||||
}
|
||||
def simplify(id: ModuleRevisionId): ModuleRevisionId = PomExtraDependencyAttributes.simplify(id)
|
||||
|
||||
private[this] def addExtra(dep: DependencyDescriptor, extra: Map[ModuleRevisionId, Map[String, String]]): DependencyDescriptor =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,19 +14,20 @@ import java.util.{ Collection, Collections => CS, Date }
|
|||
import CS.singleton
|
||||
|
||||
import org.apache.ivy.Ivy
|
||||
import org.apache.ivy.core.report.ResolveReport
|
||||
import org.apache.ivy.core.{ IvyPatternHelper, LogOptions, IvyContext }
|
||||
import org.apache.ivy.core.cache.{ CacheMetadataOptions, DefaultRepositoryCacheManager, ModuleDescriptorWriter }
|
||||
import org.apache.ivy.core.cache.{ ResolutionCacheManager, CacheMetadataOptions, DefaultRepositoryCacheManager, ModuleDescriptorWriter }
|
||||
import org.apache.ivy.core.event.EventManager
|
||||
import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact, DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact }
|
||||
import org.apache.ivy.core.module.descriptor.{ DefaultDependencyDescriptor, DefaultModuleDescriptor, DependencyDescriptor, ModuleDescriptor, License }
|
||||
import org.apache.ivy.core.module.descriptor.{ OverrideDependencyDescriptorMediator }
|
||||
import org.apache.ivy.core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId }
|
||||
import org.apache.ivy.core.resolve.{ IvyNode, ResolveData, ResolvedModuleRevision, ResolveEngine }
|
||||
import org.apache.ivy.core.resolve._
|
||||
import org.apache.ivy.core.settings.IvySettings
|
||||
import org.apache.ivy.core.sort.SortEngine
|
||||
import org.apache.ivy.plugins.latest.{ LatestStrategy, LatestRevisionStrategy, ArtifactInfo }
|
||||
import org.apache.ivy.plugins.matcher.PatternMatcher
|
||||
import org.apache.ivy.plugins.parser.m2.PomModuleDescriptorParser
|
||||
import org.apache.ivy.plugins.parser.m2.{ PomModuleDescriptorParser }
|
||||
import org.apache.ivy.plugins.resolver.{ ChainResolver, DependencyResolver, BasicResolver }
|
||||
import org.apache.ivy.plugins.resolver.util.{ HasLatestStrategy, ResolvedResource }
|
||||
import org.apache.ivy.plugins.version.ExactVersionMatcher
|
||||
|
|
@ -68,9 +69,11 @@ final class IvySbt(val configuration: IvyConfiguration) {
|
|||
private lazy val settings: IvySettings =
|
||||
{
|
||||
val is = new IvySettings
|
||||
|
||||
is.setBaseDir(baseDirectory)
|
||||
is.setCircularDependencyStrategy(configuration.updateOptions.circularDependencyLevel.ivyStrategy)
|
||||
CustomPomParser.registerDefault
|
||||
|
||||
configuration match {
|
||||
case e: ExternalIvyConfiguration =>
|
||||
IvySbt.addResolvers(e.extraResolvers, is, configuration.log)
|
||||
|
|
@ -104,6 +107,7 @@ final class IvySbt(val configuration: IvyConfiguration) {
|
|||
super.bind()
|
||||
}
|
||||
}
|
||||
|
||||
i.setSettings(settings)
|
||||
i.bind()
|
||||
i.getLoggerEngine.pushLogger(new IvyLoggerInterface(configuration.log))
|
||||
|
|
@ -284,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 {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@
|
|||
package sbt
|
||||
|
||||
import java.io.File
|
||||
|
||||
import sbt.mavenint.PomExtraDependencyAttributes
|
||||
|
||||
// Node needs to be renamed to XNode because the task subproject contains a Node type that will shadow
|
||||
// scala.xml.Node when generating aggregated API documentation
|
||||
import scala.xml.{ Elem, Node => XNode, NodeSeq, PrettyPrinter, PrefixedAttribute }
|
||||
|
|
@ -17,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")
|
||||
|
|
@ -119,12 +123,12 @@ class MakePom(val log: Logger) {
|
|||
def makeProperties(module: ModuleDescriptor, dependencies: Seq[DependencyDescriptor]): NodeSeq =
|
||||
{
|
||||
val extra = IvySbt.getExtraAttributes(module)
|
||||
val depExtra = CustomPomParser.writeDependencyExtra(dependencies).mkString("\n")
|
||||
val allExtra = if (depExtra.isEmpty) extra else extra.updated(CustomPomParser.ExtraAttributesKey, depExtra)
|
||||
val depExtra = PomExtraDependencyAttributes.writeDependencyExtra(dependencies).mkString("\n")
|
||||
val allExtra = if (depExtra.isEmpty) extra else extra.updated(PomExtraDependencyAttributes.ExtraAttributesKey, depExtra)
|
||||
if (allExtra.isEmpty) NodeSeq.Empty else makeProperties(allExtra)
|
||||
}
|
||||
def makeProperties(extra: Map[String, String]): NodeSeq = {
|
||||
def _extraAttributes(k: String) = if (k == CustomPomParser.ExtraAttributesKey) xmlSpacePreserve else scala.xml.Null
|
||||
def _extraAttributes(k: String) = if (k == PomExtraDependencyAttributes.ExtraAttributesKey) xmlSpacePreserve else scala.xml.Null
|
||||
<properties> {
|
||||
for ((key, value) <- extra) yield (<x>{ value }</x>).copy(label = key, attributes = _extraAttributes(key))
|
||||
} </properties>
|
||||
|
|
@ -330,6 +334,8 @@ class MakePom(val log: Logger) {
|
|||
val repositories = if (includeAll) allResolvers(settings) else resolvers(settings.getDefaultResolver)
|
||||
val mavenRepositories =
|
||||
repositories.flatMap {
|
||||
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
|
||||
case _ => Nil
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ package sbt
|
|||
|
||||
import java.net.URL
|
||||
|
||||
import sbt.mavenint.SbtPomExtraProperties
|
||||
|
||||
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 +
|
||||
|
|
@ -15,7 +17,7 @@ final case class ModuleID(organization: String, name: String, revision: String,
|
|||
def extraString: String = extraDependencyAttributes.map { case (k, v) => k + "=" + v } mkString ("(", ", ", ")")
|
||||
|
||||
/** Returns the extra attributes except for ones marked as information only (ones that typically would not be used for dependency resolution). */
|
||||
def extraDependencyAttributes: Map[String, String] = extraAttributes.filterKeys(!_.startsWith(CustomPomParser.InfoKeyPrefix))
|
||||
def extraDependencyAttributes: Map[String, String] = extraAttributes.filterKeys(!_.startsWith(SbtPomExtraProperties.POM_INFO_KEY_PREFIX))
|
||||
|
||||
@deprecated("Use `cross(CrossVersion)`, the variant accepting a CrossVersion value constructed by a member of the CrossVersion object instead.", "0.12.0")
|
||||
def cross(v: Boolean): ModuleID = cross(if (v) CrossVersion.binary else CrossVersion.Disabled)
|
||||
|
|
|
|||
|
|
@ -30,8 +30,23 @@ final class RawRepository(val resolver: DependencyResolver) extends Resolver {
|
|||
}
|
||||
}
|
||||
sealed case class ChainedResolver(name: String, resolvers: Seq[Resolver]) extends Resolver
|
||||
|
||||
/** An instance of a remote maven repository. Note: This will use Aether/Maven to resolve artifacts. */
|
||||
sealed case class MavenRepository(name: String, root: String) extends Resolver {
|
||||
override def toString = name + ": " + root
|
||||
def isCache: Boolean = false
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance of maven CACHE directory. You cannot treat a cache directory the same as a a remote repository because
|
||||
* the metadata is different (see Aether ML discussion).
|
||||
*/
|
||||
final class MavenCache(name: String, val rootFile: File) extends MavenRepository(name, rootFile.toURI.toURL.toString) {
|
||||
override val toString = "cache:" + name + ": " + rootFile.getAbsolutePath
|
||||
override def isCache: Boolean = true
|
||||
}
|
||||
object MavenCache {
|
||||
def apply(name: String, rootFile: File): MavenCache = new MavenCache(name, rootFile)
|
||||
}
|
||||
|
||||
final class Patterns(val ivyPatterns: Seq[String], val artifactPatterns: Seq[String], val isMavenCompatible: Boolean, val descriptorOptional: Boolean, val skipConsistencyCheck: Boolean) {
|
||||
|
|
@ -334,8 +349,9 @@ object Resolver {
|
|||
loadHomeFromSettings(() => new File(new File(System.getenv("M2_HOME")), "conf/settings.xml")) getOrElse
|
||||
new File(Path.userHome, ".m2/repository")
|
||||
}
|
||||
def publishMavenLocal = Resolver.file("publish-m2-local", mavenLocalDir)
|
||||
def mavenLocal = MavenRepository("Maven2 Local", mavenLocalDir.toURI.toString)
|
||||
// TODO - should this just be the *exact* same as mavenLocal? probably...
|
||||
def publishMavenLocal: MavenCache = new MavenCache("publish-m2-local", mavenLocalDir)
|
||||
def mavenLocal: MavenRepository = new MavenCache("Maven2 Local", mavenLocalDir)
|
||||
def defaultLocal = defaultUserFileRepository("local")
|
||||
def defaultShared = defaultUserFileRepository("shared")
|
||||
def defaultUserFileRepository(id: String) =
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -17,8 +19,9 @@ final class UpdateOptions private[sbt] (
|
|||
/** If set to true, use consolidated resolution. */
|
||||
val consolidatedResolution: Boolean,
|
||||
/** If set to true, use cached resolution. */
|
||||
val cachedResolution: Boolean) {
|
||||
|
||||
val cachedResolution: Boolean,
|
||||
/** Extention point for an alternative resolver converter. */
|
||||
val resolverConverter: UpdateOptions.ResolverConverter) {
|
||||
def withCircularDependencyLevel(circularDependencyLevel: CircularDependencyLevel): UpdateOptions =
|
||||
copy(circularDependencyLevel = circularDependencyLevel)
|
||||
def withLatestSnapshots(latestSnapshots: Boolean): UpdateOptions =
|
||||
|
|
@ -30,22 +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): UpdateOptions =
|
||||
cachedResolution: Boolean = this.cachedResolution,
|
||||
resolverConverter: UpdateOptions.ResolverConverter = this.resolverConverter): UpdateOptions =
|
||||
new UpdateOptions(circularDependencyLevel,
|
||||
latestSnapshots,
|
||||
consolidatedResolution,
|
||||
cachedResolution)
|
||||
cachedResolution,
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -55,15 +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)
|
||||
cachedResolution = false,
|
||||
resolverConverter = PartialFunction.empty)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -12,7 +12,7 @@ import org.apache.ivy.core.resolve.{ ResolvedModuleRevision, ResolveData }
|
|||
import org.apache.ivy.plugins.latest.LatestStrategy
|
||||
import org.apache.ivy.plugins.repository.file.{ FileRepository => IFileRepository, FileResource }
|
||||
import org.apache.ivy.plugins.repository.url.URLResource
|
||||
import org.apache.ivy.plugins.resolver.{ ChainResolver, BasicResolver, DependencyResolver }
|
||||
import org.apache.ivy.plugins.resolver._
|
||||
import org.apache.ivy.plugins.resolver.util.{ HasLatestStrategy, ResolvedResource }
|
||||
import org.apache.ivy.util.{ Message, MessageLogger, StringUtils => IvyStringUtils }
|
||||
|
||||
|
|
@ -155,34 +155,39 @@ private[sbt] case class SbtChainResolver(
|
|||
val sorted =
|
||||
if (useLatest) (foundRevisions.sortBy {
|
||||
case (rmr, resolver) =>
|
||||
Message.warn(s"Sorrting results from $rmr, using ${rmr.getPublicationDate} and ${rmr.getDescriptor.getPublicationDate}")
|
||||
// Just issue warning about issues with publication date, and fake one on it for now.
|
||||
rmr.getDescriptor.getPublicationDate match {
|
||||
case null =>
|
||||
Option(rmr.getPublicationDate) orElse Option(rmr.getDescriptor.getPublicationDate) match {
|
||||
case None =>
|
||||
(resolver.findIvyFileRef(dd, data), rmr.getDescriptor) match {
|
||||
case (null, _) =>
|
||||
// In this instance, the dependency is specified by a direct URL or some other sort of "non-ivy" file
|
||||
if (dd.isChanging)
|
||||
Message.warn(s"Resolving a changing dependency (${rmr.getId}) with no ivy/pom file!, resolution order is undefined!")
|
||||
0L
|
||||
case (ivf, dmd: DefaultModuleDescriptor) =>
|
||||
val lmd = new java.util.Date(ivf.getLastModified)
|
||||
Message.debug(s"Getting no publication date from resolver: ${resolver} for ${rmr.getId}, setting to: ${lmd}")
|
||||
dmd.setPublicationDate(lmd)
|
||||
ivf.getLastModified
|
||||
case _ =>
|
||||
Message.warn(s"Getting null publication date from resolver: ${resolver} for ${rmr.getId}, resolution order is undefined!")
|
||||
0L
|
||||
}
|
||||
case _ => // All other cases ok
|
||||
}
|
||||
rmr.getDescriptor.getPublicationDate match {
|
||||
case null => 0L
|
||||
case d => d.getTime
|
||||
case Some(date) => // All other cases ok
|
||||
date.getTime
|
||||
}
|
||||
}).reverse.headOption map {
|
||||
case (rmr, resolver) =>
|
||||
Message.warn(s"Choosing ${resolver} for ${rmr.getId}")
|
||||
// Now that we know the real latest revision, let's force Ivy to use it
|
||||
val artifactOpt = findFirstArtifactRef(rmr.getDescriptor, dd, data, resolver)
|
||||
artifactOpt match {
|
||||
case None if resolver.getName == "inter-project" => // do nothing
|
||||
case None => throw new RuntimeException(s"\t${resolver.getName}: no ivy file nor artifact found for $rmr")
|
||||
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")
|
||||
case Some(artifactRef) =>
|
||||
val systemMd = toSystem(rmr.getDescriptor)
|
||||
getRepositoryCacheManager.cacheModuleDescriptor(resolver, artifactRef,
|
||||
|
|
@ -210,6 +215,9 @@ private[sbt] case class SbtChainResolver(
|
|||
}
|
||||
// Ivy seem to not want to use the module descriptor found at the latest resolver
|
||||
private[this] def reparseModuleDescriptor(dd: DependencyDescriptor, data: ResolveData, resolver: DependencyResolver, rmr: ResolvedModuleRevision): ResolvedModuleRevision =
|
||||
// TODO - Redownloading/parsing the ivy file is not really the best way to make this correct.
|
||||
// We should figure out a better alternative, or directly attack the resolvers Ivy uses to
|
||||
// give them correct behavior around -SNAPSHOT.
|
||||
Option(resolver.findIvyFileRef(dd, data)) flatMap { ivyFile =>
|
||||
ivyFile.getResource match {
|
||||
case r: FileResource =>
|
||||
|
|
@ -222,7 +230,10 @@ private[sbt] case class SbtChainResolver(
|
|||
}
|
||||
case _ => None
|
||||
}
|
||||
} getOrElse rmr
|
||||
} getOrElse {
|
||||
Message.warn(s"Unable to reparse ${dd.getDependencyRevisionId} from $resolver, using ${rmr.getPublicationDate}")
|
||||
rmr
|
||||
}
|
||||
/** Ported from BasicResolver#findFirstAirfactRef. */
|
||||
private[this] def findFirstArtifactRef(md: ModuleDescriptor, dd: DependencyDescriptor, data: ResolveData, resolver: DependencyResolver): Option[ResolvedResource] =
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,111 @@
|
|||
package sbt.mavenint
|
||||
|
||||
import java.util.Properties
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import org.apache.ivy.core.module.descriptor.DependencyDescriptor
|
||||
import org.apache.ivy.core.module.id.ModuleRevisionId
|
||||
import org.apache.ivy.util.extendable.ExtendableItem
|
||||
|
||||
/**
|
||||
* This class contains all the logic for dealing with the extra attributes in pom files relating to extra attributes
|
||||
* on dependency declarations.
|
||||
*
|
||||
* Specifically, if we have a dependency on an sbt plugin, there are two properties that need to propogate:
|
||||
* - `sbtVersion`
|
||||
* - `scalaVersion`
|
||||
*
|
||||
* These need to exist on the *dependency declaration*. Maven/Aether has no way to inject these into
|
||||
* the <dependency> section of pom files, so we use Ivy's Extra attribute hackery to inject a lookup table
|
||||
* of extra attributes by dependency id into POM files and later we read these back.
|
||||
*/
|
||||
object PomExtraDependencyAttributes {
|
||||
|
||||
val ExtraAttributesKey = "extraDependencyAttributes"
|
||||
val SbtVersionKey = "sbtVersion"
|
||||
val ScalaVersionKey = "scalaVersion"
|
||||
/**
|
||||
* Reads the extra dependency attributes out of a maven property.
|
||||
* @param props The properties from an Aether resolution.
|
||||
* @return
|
||||
* A map of module id to extra dependency attributes associated with dependencies on that module.
|
||||
*/
|
||||
def readFromAether(props: java.util.Map[String, AnyRef]): Map[ModuleRevisionId, Map[String, String]] = {
|
||||
import scala.collection.JavaConverters._
|
||||
(props.asScala get ExtraAttributesKey) match {
|
||||
case None => Map.empty
|
||||
case Some(str) =>
|
||||
def processDep(m: ModuleRevisionId) = (simplify(m), filterCustomExtra(m, include = true))
|
||||
(for {
|
||||
(id, props) <- readDependencyExtra(str.toString).map(processDep)
|
||||
} yield id -> props).toMap
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutates the to collection with the extra depdendency attributes from the incoming pom properties list.
|
||||
*
|
||||
* @param from The properties directly off a maven POM file
|
||||
* @param to The aaether properties where we can write whatever we want.
|
||||
*
|
||||
* TODO - maybe we can just parse this directly here. Note the `readFromAether` method uses
|
||||
* whatever we set here.
|
||||
*/
|
||||
def transferDependencyExtraAttributes(from: Properties, to: java.util.Map[String, AnyRef]): Unit = {
|
||||
Option(from.getProperty(ExtraAttributesKey, null)) match {
|
||||
case Some(str) => to.put(ExtraAttributesKey, str)
|
||||
case None =>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the extra dependency information out of Ivy's notion of POM properties and returns
|
||||
* the map of ID -> Extra Properties.
|
||||
*/
|
||||
def getDependencyExtra(m: Map[String, String]): Map[ModuleRevisionId, Map[String, String]] =
|
||||
(m get ExtraAttributesKey) match {
|
||||
case None => Map.empty
|
||||
case Some(str) =>
|
||||
def processDep(m: ModuleRevisionId) = (simplify(m), filterCustomExtra(m, include = true))
|
||||
readDependencyExtra(str).map(processDep).toMap
|
||||
}
|
||||
|
||||
def qualifiedExtra(item: ExtendableItem): Map[String, String] = {
|
||||
import scala.collection.JavaConverters._
|
||||
item.getQualifiedExtraAttributes.asInstanceOf[java.util.Map[String, String]].asScala.toMap
|
||||
}
|
||||
def filterCustomExtra(item: ExtendableItem, include: Boolean): Map[String, String] =
|
||||
(qualifiedExtra(item) filterKeys { k => qualifiedIsExtra(k) == include })
|
||||
|
||||
def qualifiedIsExtra(k: String): Boolean = k.endsWith(ScalaVersionKey) || k.endsWith(SbtVersionKey)
|
||||
|
||||
// Reduces the id to exclude custom extra attributes
|
||||
// This makes the id suitable as a key to associate a dependency parsed from a <dependency> element
|
||||
// with the extra attributes from the <properties> section
|
||||
def simplify(id: ModuleRevisionId): ModuleRevisionId = {
|
||||
import scala.collection.JavaConverters._
|
||||
ModuleRevisionId.newInstance(id.getOrganisation, id.getName, id.getBranch, id.getRevision, filterCustomExtra(id, include = false).asJava)
|
||||
}
|
||||
|
||||
/** parses the sequence of dependencies with extra attribute information, with one dependency per line */
|
||||
def readDependencyExtra(s: String): Seq[ModuleRevisionId] =
|
||||
LinesP.split(s).map(_.trim).filter(!_.isEmpty).map(ModuleRevisionId.decode)
|
||||
|
||||
private[this] val LinesP = Pattern.compile("(?m)^")
|
||||
|
||||
/**
|
||||
* Creates the "extra" property values for DependencyDescriptors that can be written into a maven pom
|
||||
* so we don't loose the information.
|
||||
* @param s
|
||||
* @return
|
||||
*/
|
||||
def writeDependencyExtra(s: Seq[DependencyDescriptor]): Seq[String] =
|
||||
s.flatMap { dd =>
|
||||
val revId = dd.getDependencyRevisionId
|
||||
if (filterCustomExtra(revId, include = true).isEmpty)
|
||||
Nil
|
||||
else
|
||||
revId.encodeToString :: Nil
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -4,6 +4,8 @@ import Path._, Configurations._
|
|||
import java.io.File
|
||||
import org.specs2._
|
||||
import cross.CrossVersionUtil
|
||||
import sbt.PublishConfiguration
|
||||
import sbt.ivyint.SbtChainResolver
|
||||
|
||||
trait BaseIvySpecification extends Specification {
|
||||
def currentBase: File = new File(".")
|
||||
|
|
@ -12,6 +14,8 @@ trait BaseIvySpecification extends Specification {
|
|||
def currentDependency: File = currentBase / "target" / "dependency"
|
||||
def defaultModuleId: ModuleID = ModuleID("com.example", "foo", "0.1.0", Some("compile"))
|
||||
lazy val log = ConsoleLogger()
|
||||
|
||||
def configurations = Seq(Compile, Test, Runtime)
|
||||
def module(moduleId: ModuleID, deps: Seq[ModuleID], scalaFullVersion: Option[String],
|
||||
uo: UpdateOptions = UpdateOptions()): IvySbt#Module = {
|
||||
val ivyScala = scalaFullVersion map { fv =>
|
||||
|
|
@ -28,21 +32,24 @@ trait BaseIvySpecification extends Specification {
|
|||
module = moduleId,
|
||||
moduleInfo = ModuleInfo("foo"),
|
||||
dependencies = deps,
|
||||
configurations = Seq(Compile, Test, Runtime),
|
||||
configurations = configurations,
|
||||
ivyScala = ivyScala)
|
||||
val ivySbt = new IvySbt(mkIvyConfiguration(uo))
|
||||
new ivySbt.Module(moduleSetting)
|
||||
}
|
||||
|
||||
def resolvers: Seq[Resolver] = Seq(DefaultMavenRepository)
|
||||
|
||||
def chainResolver = ChainedResolver("sbt-chain", resolvers)
|
||||
|
||||
def mkIvyConfiguration(uo: UpdateOptions): IvyConfiguration = {
|
||||
val paths = new IvyPaths(currentBase, Some(currentTarget))
|
||||
val rs = Seq(DefaultMavenRepository)
|
||||
val other = Nil
|
||||
val moduleConfs = Seq(ModuleConfiguration("*", DefaultMavenRepository))
|
||||
val moduleConfs = Seq(ModuleConfiguration("*", chainResolver))
|
||||
val off = false
|
||||
val check = Nil
|
||||
val resCacheDir = currentTarget / "resolution-cache"
|
||||
new InlineIvyConfiguration(paths, rs, other, moduleConfs, off, None, check, Some(resCacheDir), uo, log)
|
||||
new InlineIvyConfiguration(paths, resolvers, other, moduleConfs, off, None, check, Some(resCacheDir), uo, log)
|
||||
}
|
||||
|
||||
def ivyUpdateEither(module: IvySbt#Module): Either[UnresolvedWarning, UpdateReport] = {
|
||||
|
|
@ -58,4 +65,18 @@ trait BaseIvySpecification extends Specification {
|
|||
case Left(w) =>
|
||||
throw w.resolveException
|
||||
}
|
||||
|
||||
def mkPublishConfiguration(resolver: Resolver, artifacts: Map[Artifact, File]): PublishConfiguration = {
|
||||
new PublishConfiguration(
|
||||
ivyFile = None,
|
||||
resolverName = resolver.name,
|
||||
artifacts = artifacts,
|
||||
checksums = Seq(),
|
||||
logging = UpdateLogging.Full,
|
||||
overwrite = true)
|
||||
}
|
||||
|
||||
def ivyPublish(module: IvySbt#Module, config: PublishConfiguration) = {
|
||||
IvyActions.publish(module, config, log)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue