2009-08-16 20:29:08 +02:00
|
|
|
/* sbt -- Simple Build Tool
|
2010-01-16 01:05:23 +01:00
|
|
|
* Copyright 2008, 2009, 2010 Mark Harrah
|
2009-08-16 20:29:08 +02:00
|
|
|
*/
|
2010-01-16 01:05:23 +01:00
|
|
|
package sbt
|
2009-08-16 20:29:08 +02:00
|
|
|
|
|
|
|
|
import Artifact.{defaultExtension, defaultType}
|
|
|
|
|
|
|
|
|
|
import java.io.File
|
2010-01-29 01:31:04 +01:00
|
|
|
import java.util.concurrent.Callable
|
2011-03-17 01:10:41 +01:00
|
|
|
import java.util.{Collection, Collections}
|
2009-08-16 20:29:08 +02:00
|
|
|
|
|
|
|
|
import org.apache.ivy.{core, plugins, util, Ivy}
|
2009-09-27 20:39:26 +02:00
|
|
|
import core.IvyPatternHelper
|
2011-02-17 22:20:30 +01:00
|
|
|
import core.cache.{CacheMetadataOptions, DefaultRepositoryCacheManager}
|
2011-04-14 13:32:42 +02:00
|
|
|
import core.module.descriptor.{Artifact => IArtifact, DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact}
|
2011-02-17 22:20:30 +01:00
|
|
|
import core.module.descriptor.{DefaultDependencyDescriptor, DefaultModuleDescriptor, DependencyDescriptor, ModuleDescriptor}
|
2009-08-16 20:29:08 +02:00
|
|
|
import core.module.id.{ArtifactId,ModuleId, ModuleRevisionId}
|
2011-03-17 01:10:41 +01:00
|
|
|
import core.resolve.IvyNode
|
2009-08-16 20:29:08 +02:00
|
|
|
import core.settings.IvySettings
|
2011-03-17 01:10:41 +01:00
|
|
|
import plugins.conflict.{ConflictManager, LatestConflictManager}
|
|
|
|
|
import plugins.latest.LatestRevisionStrategy
|
2009-08-16 20:29:08 +02:00
|
|
|
import plugins.matcher.PatternMatcher
|
|
|
|
|
import plugins.parser.m2.PomModuleDescriptorParser
|
2011-04-16 00:31:16 +02:00
|
|
|
import plugins.resolver.{ChainResolver, DependencyResolver}
|
2011-03-22 23:19:48 +01:00
|
|
|
import util.{Message, MessageLogger}
|
2009-08-16 20:29:08 +02:00
|
|
|
|
2010-04-02 02:14:05 +02:00
|
|
|
import scala.xml.NodeSeq
|
|
|
|
|
|
2010-10-26 23:59:24 +02:00
|
|
|
final class IvySbt(val configuration: IvyConfiguration)
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2011-03-22 23:19:48 +01:00
|
|
|
import configuration.baseDirectory
|
|
|
|
|
|
2009-08-16 20:29:08 +02:00
|
|
|
/** ========== Configuration/Setup ============
|
|
|
|
|
* This part configures the Ivy instance by first creating the logger interface to ivy, then IvySettings, and then the Ivy instance.
|
|
|
|
|
* These are lazy so that they are loaded within the right context. This is important so that no Ivy XML configuration needs to be loaded,
|
|
|
|
|
* saving some time. This is necessary because Ivy has global state (IvyContext, Message, DocumentBuilder, ...).
|
|
|
|
|
*/
|
2011-03-22 23:19:48 +01:00
|
|
|
private def withDefaultLogger[T](logger: MessageLogger)(f: => T): T =
|
2010-01-29 01:31:04 +01:00
|
|
|
{
|
|
|
|
|
def action() =
|
2011-02-06 03:34:17 +01:00
|
|
|
IvySbt.synchronized
|
2010-01-29 01:31:04 +01:00
|
|
|
{
|
|
|
|
|
val originalLogger = Message.getDefaultLogger
|
|
|
|
|
Message.setDefaultLogger(logger)
|
|
|
|
|
try { f }
|
|
|
|
|
finally { Message.setDefaultLogger(originalLogger) }
|
|
|
|
|
}
|
|
|
|
|
// Ivy is not thread-safe nor can the cache be used concurrently.
|
|
|
|
|
// If provided a GlobalLock, we can use that to ensure safe access to the cache.
|
|
|
|
|
// Otherwise, we can at least synchronize within the JVM.
|
2011-03-17 01:10:41 +01:00
|
|
|
// For thread-safety in particular, Ivy uses a static DocumentBuilder, which is not thread-safe.
|
2010-01-29 01:31:04 +01:00
|
|
|
configuration.lock match
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2010-01-29 01:31:04 +01:00
|
|
|
case Some(lock) => lock(ivyLockFile, new Callable[T] { def call = action() })
|
|
|
|
|
case None => action()
|
2009-08-16 20:29:08 +02:00
|
|
|
}
|
2010-01-29 01:31:04 +01:00
|
|
|
}
|
2011-03-22 23:19:48 +01:00
|
|
|
private lazy val settings: IvySettings =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
|
|
|
|
val is = new IvySettings
|
2010-01-16 01:05:23 +01:00
|
|
|
is.setBaseDir(baseDirectory)
|
2011-03-17 01:10:41 +01:00
|
|
|
is.setDefaultConflictManager(IvySbt.latestNoForce(is))
|
2010-01-16 01:05:23 +01:00
|
|
|
configuration match
|
|
|
|
|
{
|
|
|
|
|
case e: ExternalIvyConfiguration => is.load(e.file)
|
2011-04-14 01:01:22 +02:00
|
|
|
case i: InlineIvyConfiguration =>
|
2011-04-16 00:25:54 +02:00
|
|
|
is.setVariable("ivy.checksums", i.checksums mkString ",")
|
2011-04-16 02:08:35 +02:00
|
|
|
i.paths.ivyHome foreach is.setDefaultIvyUserDir
|
2011-04-14 01:01:22 +02:00
|
|
|
IvySbt.configureCache(is, i.localOnly)
|
2011-03-22 23:19:48 +01:00
|
|
|
IvySbt.setResolvers(is, i.resolvers, i.otherResolvers, i.localOnly, configuration.log)
|
2010-01-16 01:05:23 +01:00
|
|
|
IvySbt.setModuleConfigurations(is, i.moduleConfigurations)
|
|
|
|
|
}
|
2009-08-16 20:29:08 +02:00
|
|
|
is
|
|
|
|
|
}
|
2011-03-22 23:19:48 +01:00
|
|
|
private lazy val ivy: Ivy =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2010-02-06 20:14:49 +01:00
|
|
|
val i = new Ivy() { private val loggerEngine = new SbtMessageLoggerEngine; override def getLoggerEngine = loggerEngine }
|
|
|
|
|
i.setSettings(settings)
|
|
|
|
|
i.bind()
|
2011-03-22 23:19:48 +01:00
|
|
|
i.getLoggerEngine.pushLogger(new IvyLoggerInterface(configuration.log))
|
2009-08-16 20:29:08 +02:00
|
|
|
i
|
|
|
|
|
}
|
2010-01-29 01:31:04 +01:00
|
|
|
// Must be the same file as is used in Update in the launcher
|
|
|
|
|
private lazy val ivyLockFile = new File(settings.getDefaultIvyUserDir, ".sbt.ivy.lock")
|
2009-08-16 20:29:08 +02:00
|
|
|
/** ========== End Configuration/Setup ============*/
|
|
|
|
|
|
|
|
|
|
/** Uses the configured Ivy instance within a safe context.*/
|
2011-03-22 23:19:48 +01:00
|
|
|
def withIvy[T](log: Logger)(f: Ivy => T): T =
|
|
|
|
|
withIvy(new IvyLoggerInterface(log))(f)
|
|
|
|
|
|
|
|
|
|
def withIvy[T](log: MessageLogger)(f: Ivy => T): T =
|
|
|
|
|
withDefaultLogger(log)
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
|
|
|
|
ivy.pushContext()
|
2011-03-22 23:19:48 +01:00
|
|
|
ivy.getLoggerEngine.pushLogger(log)
|
2009-08-16 20:29:08 +02:00
|
|
|
try { f(ivy) }
|
2011-03-22 23:19:48 +01:00
|
|
|
finally {
|
|
|
|
|
ivy.getLoggerEngine.popLogger()
|
|
|
|
|
ivy.popContext()
|
|
|
|
|
}
|
2009-08-16 20:29:08 +02:00
|
|
|
}
|
2009-08-20 06:02:06 +02:00
|
|
|
|
2011-02-15 00:57:10 +01:00
|
|
|
final class Module(rawModuleSettings: ModuleSettings)
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2011-02-15 00:57:10 +01:00
|
|
|
val moduleSettings: ModuleSettings = IvySbt.substituteCross(rawModuleSettings)
|
2010-10-26 23:59:24 +02:00
|
|
|
def owner = IvySbt.this
|
2011-03-22 23:19:48 +01:00
|
|
|
def withModule[T](log: Logger)(f: (Ivy,DefaultModuleDescriptor,String) => T): T =
|
|
|
|
|
withIvy[T](log) { ivy => f(ivy, moduleDescriptor0, defaultConfig0) }
|
2009-08-20 06:02:06 +02:00
|
|
|
|
2011-03-22 23:19:48 +01:00
|
|
|
def moduleDescriptor(log: Logger): DefaultModuleDescriptor = withModule(log)((_,md,_) => md)
|
|
|
|
|
def defaultConfig(log: Logger): String = withModule(log)( (_,_,dc) => dc)
|
2011-02-12 02:09:42 +01:00
|
|
|
// these should only be referenced by withModule because lazy vals synchronize on this object
|
|
|
|
|
// withIvy explicitly locks the IvySbt object, so they have to be done in the right order to avoid deadlock
|
2011-02-06 03:34:17 +01:00
|
|
|
private[this] lazy val (moduleDescriptor0: DefaultModuleDescriptor, defaultConfig0: String) =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
|
|
|
|
val (baseModule, baseConfiguration) =
|
2009-09-27 20:39:26 +02:00
|
|
|
moduleSettings match
|
2009-09-09 05:13:30 +02:00
|
|
|
{
|
2011-03-22 23:19:48 +01:00
|
|
|
case ic: InlineConfiguration => configureInline(ic, configuration.log)
|
2009-09-09 05:13:30 +02:00
|
|
|
case ec: EmptyConfiguration => configureEmpty(ec.module)
|
|
|
|
|
case pc: PomConfiguration => readPom(pc.file, pc.validate)
|
|
|
|
|
case ifc: IvyFileConfiguration => readIvyFile(ifc.file, ifc.validate)
|
|
|
|
|
}
|
2009-09-27 20:39:26 +02:00
|
|
|
moduleSettings.ivyScala.foreach(IvyScala.checkModule(baseModule, baseConfiguration))
|
2009-09-09 05:13:30 +02:00
|
|
|
baseModule.getExtraAttributesNamespaces.asInstanceOf[java.util.Map[String,String]].put("e", "http://ant.apache.org/ivy/extra")
|
2009-08-16 20:29:08 +02:00
|
|
|
(baseModule, baseConfiguration)
|
|
|
|
|
}
|
2011-03-22 23:19:48 +01:00
|
|
|
private def configureInline(ic: InlineConfiguration, log: Logger) =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2009-09-09 05:13:30 +02:00
|
|
|
import ic._
|
|
|
|
|
val moduleID = newConfiguredModuleID(module, configurations)
|
2009-08-16 20:29:08 +02:00
|
|
|
val defaultConf = defaultConfiguration getOrElse Configurations.config(ModuleDescriptor.DEFAULT_CONFIGURATION)
|
|
|
|
|
log.debug("Using inline dependencies specified in Scala" + (if(ivyXML.isEmpty) "." else " and XML."))
|
2009-08-20 06:02:06 +02:00
|
|
|
|
2009-08-16 20:29:08 +02:00
|
|
|
val parser = IvySbt.parseIvyXML(ivy.getSettings, IvySbt.wrapped(module, ivyXML), moduleID, defaultConf.name, validate)
|
|
|
|
|
|
|
|
|
|
IvySbt.addDependencies(moduleID, dependencies, parser)
|
|
|
|
|
IvySbt.addMainArtifact(moduleID)
|
|
|
|
|
(moduleID, parser.getDefaultConf)
|
|
|
|
|
}
|
2009-09-09 05:13:30 +02:00
|
|
|
private def newConfiguredModuleID(module: ModuleID, configurations: Iterable[Configuration]) =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
|
|
|
|
val mod = new DefaultModuleDescriptor(IvySbt.toID(module), "release", null, false)
|
|
|
|
|
mod.setLastModified(System.currentTimeMillis)
|
|
|
|
|
configurations.foreach(config => mod.addConfiguration(IvySbt.toIvyConfiguration(config)))
|
2010-01-16 01:05:23 +01:00
|
|
|
IvySbt.addArtifacts(mod, module.explicitArtifacts)
|
2009-08-16 20:29:08 +02:00
|
|
|
mod
|
|
|
|
|
}
|
2009-08-20 06:02:06 +02:00
|
|
|
|
2009-08-16 20:29:08 +02:00
|
|
|
/** Parses the given Maven pom 'pomFile'.*/
|
2009-09-09 05:13:30 +02:00
|
|
|
private def readPom(pomFile: File, validate: Boolean) =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
|
|
|
|
val md = PomModuleDescriptorParser.getInstance.parseDescriptor(settings, toURL(pomFile), validate)
|
|
|
|
|
(IvySbt.toDefaultModuleDescriptor(md), "compile")
|
|
|
|
|
}
|
|
|
|
|
/** Parses the given Ivy file 'ivyFile'.*/
|
2009-09-09 05:13:30 +02:00
|
|
|
private def readIvyFile(ivyFile: File, validate: Boolean) =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
|
|
|
|
val url = toURL(ivyFile)
|
2009-09-27 20:39:26 +02:00
|
|
|
val parser = new CustomXmlParser.CustomParser(settings, None)
|
2009-08-16 20:29:08 +02:00
|
|
|
parser.setValidate(validate)
|
|
|
|
|
parser.setSource(url)
|
|
|
|
|
parser.parse()
|
|
|
|
|
val md = parser.getModuleDescriptor()
|
|
|
|
|
(IvySbt.toDefaultModuleDescriptor(md), parser.getDefaultConf)
|
|
|
|
|
}
|
|
|
|
|
private def toURL(file: File) = file.toURI.toURL
|
2009-09-09 05:13:30 +02:00
|
|
|
private def configureEmpty(module: ModuleID) =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2009-09-09 05:13:30 +02:00
|
|
|
val defaultConf = ModuleDescriptor.DEFAULT_CONFIGURATION
|
|
|
|
|
val moduleID = new DefaultModuleDescriptor(IvySbt.toID(module), "release", null, false)
|
|
|
|
|
moduleID.setLastModified(System.currentTimeMillis)
|
|
|
|
|
moduleID.addConfiguration(IvySbt.toIvyConfiguration(Configurations.Default))
|
2010-02-06 20:20:51 +01:00
|
|
|
IvySbt.addArtifacts(moduleID, module.explicitArtifacts)
|
2009-09-09 05:13:30 +02:00
|
|
|
IvySbt.addMainArtifact(moduleID)
|
|
|
|
|
(moduleID, defaultConf)
|
2009-08-16 20:29:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private object IvySbt
|
|
|
|
|
{
|
|
|
|
|
val DefaultIvyConfigFilename = "ivysettings.xml"
|
|
|
|
|
val DefaultIvyFilename = "ivy.xml"
|
|
|
|
|
val DefaultMavenFilename = "pom.xml"
|
2011-04-16 00:25:54 +02:00
|
|
|
val DefaultChecksums = Seq("sha1", "md5")
|
2009-08-20 06:02:06 +02:00
|
|
|
|
2009-09-09 05:13:30 +02:00
|
|
|
def defaultIvyFile(project: File) = new File(project, DefaultIvyFilename)
|
|
|
|
|
def defaultIvyConfiguration(project: File) = new File(project, DefaultIvyConfigFilename)
|
|
|
|
|
def defaultPOM(project: File) = new File(project, DefaultMavenFilename)
|
2009-08-20 06:02:06 +02:00
|
|
|
|
2010-03-04 06:07:12 +01:00
|
|
|
/** Sets the resolvers for 'settings' to 'resolvers'. This is done by creating a new chain and making it the default.
|
|
|
|
|
* 'other' is for resolvers that should be in a different chain. These are typically used for publishing or other actions. */
|
2010-09-04 14:19:58 +02:00
|
|
|
private def setResolvers(settings: IvySettings, resolvers: Seq[Resolver], other: Seq[Resolver], localOnly: Boolean, log: Logger)
|
2010-03-04 06:07:12 +01:00
|
|
|
{
|
2010-05-03 01:15:01 +02:00
|
|
|
def makeChain(label: String, name: String, rs: Seq[Resolver]) = {
|
|
|
|
|
log.debug(label + " repositories:")
|
2011-03-12 16:28:53 +01:00
|
|
|
val chain = resolverChain(name, rs, localOnly, settings, log)
|
2010-05-03 01:15:01 +02:00
|
|
|
settings.addResolver(chain)
|
|
|
|
|
chain
|
|
|
|
|
}
|
|
|
|
|
val otherChain = makeChain("Other", "sbt-other", other)
|
|
|
|
|
val mainChain = makeChain("Default", "sbt-chain", resolvers)
|
|
|
|
|
settings.setDefaultResolver(mainChain.getName)
|
2010-03-04 06:07:12 +01:00
|
|
|
}
|
2011-04-16 00:31:16 +02:00
|
|
|
private def resolverChain(name: String, resolvers: Seq[Resolver], localOnly: Boolean, settings: IvySettings, log: Logger): DependencyResolver =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2011-04-16 00:31:16 +02:00
|
|
|
val newDefault = new ChainResolver {
|
|
|
|
|
// Technically, this should be applied to module configurations.
|
|
|
|
|
// That would require custom subclasses of all resolver types in ConvertResolver (a delegation approach does not work).
|
|
|
|
|
// It would be better to get proper support into Ivy.
|
|
|
|
|
override def locate(artifact: IArtifact) =
|
|
|
|
|
if(hasImplicitClassifier(artifact)) null else super.locate(artifact)
|
|
|
|
|
}
|
2010-03-04 06:07:12 +01:00
|
|
|
newDefault.setName(name)
|
2009-08-16 20:29:08 +02:00
|
|
|
newDefault.setReturnFirst(true)
|
2010-09-28 01:06:52 +02:00
|
|
|
newDefault.setCheckmodified(false)
|
2010-05-03 01:15:01 +02:00
|
|
|
for(sbtResolver <- resolvers) {
|
|
|
|
|
log.debug("\t" + sbtResolver)
|
2011-03-12 16:28:53 +01:00
|
|
|
newDefault.add(ConvertResolver(sbtResolver)(settings))
|
2010-05-03 01:15:01 +02:00
|
|
|
}
|
2010-03-04 06:07:12 +01:00
|
|
|
newDefault
|
2009-08-16 20:29:08 +02:00
|
|
|
}
|
2011-04-16 00:31:16 +02:00
|
|
|
/** A hack to detect if the given artifact is an automatically generated request for a classifier,
|
|
|
|
|
* as opposed to a user-initiated declaration. It relies on Ivy prefixing classifier with m:, while sbt uses e:.
|
|
|
|
|
* Clearly, it would be better to have an explicit option in Ivy to control this.*/
|
|
|
|
|
def hasImplicitClassifier(artifact: IArtifact): Boolean =
|
|
|
|
|
{
|
|
|
|
|
import collection.JavaConversions._
|
|
|
|
|
artifact.getQualifiedExtraAttributes.keys.exists(_.asInstanceOf[String] startsWith "m:")
|
|
|
|
|
}
|
2009-09-27 20:39:26 +02:00
|
|
|
private def setModuleConfigurations(settings: IvySettings, moduleConfigurations: Seq[ModuleConfiguration])
|
|
|
|
|
{
|
|
|
|
|
val existing = settings.getResolverNames
|
|
|
|
|
for(moduleConf <- moduleConfigurations)
|
|
|
|
|
{
|
|
|
|
|
import moduleConf._
|
|
|
|
|
import IvyPatternHelper._
|
|
|
|
|
import PatternMatcher._
|
|
|
|
|
if(!existing.contains(resolver.name))
|
2011-03-12 16:28:53 +01:00
|
|
|
settings.addResolver(ConvertResolver(resolver)(settings))
|
2009-09-27 20:39:26 +02:00
|
|
|
val attributes = javaMap(Map(MODULE_KEY -> name, ORGANISATION_KEY -> organization, REVISION_KEY -> revision))
|
|
|
|
|
settings.addModuleConfiguration(attributes, settings.getMatcher(EXACT_OR_REGEXP), resolver.name, null, null, null)
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-04-14 01:01:22 +02:00
|
|
|
private def configureCache(settings: IvySettings, localOnly: Boolean)
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2011-04-14 01:01:22 +02:00
|
|
|
val cacheDir = settings.getDefaultRepositoryCacheBasedir()
|
2011-02-17 22:20:30 +01:00
|
|
|
val manager = new DefaultRepositoryCacheManager("default-cache", settings, cacheDir) {
|
|
|
|
|
override def findModuleInCache(dd: DependencyDescriptor, revId: ModuleRevisionId, options: CacheMetadataOptions, r: String) =
|
|
|
|
|
super.findModuleInCache(dd,revId,options,null)
|
|
|
|
|
}
|
2009-08-16 20:29:08 +02:00
|
|
|
manager.setUseOrigin(true)
|
2010-05-03 01:15:01 +02:00
|
|
|
if(localOnly)
|
|
|
|
|
manager.setDefaultTTL(java.lang.Long.MAX_VALUE);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
manager.setChangingMatcher(PatternMatcher.REGEXP);
|
|
|
|
|
manager.setChangingPattern(".*-SNAPSHOT");
|
|
|
|
|
}
|
2010-01-30 02:29:55 +01:00
|
|
|
settings.addRepositoryCacheManager(manager)
|
2009-08-16 20:29:08 +02:00
|
|
|
settings.setDefaultRepositoryCacheManager(manager)
|
|
|
|
|
}
|
2009-09-09 05:13:30 +02:00
|
|
|
def toIvyConfiguration(configuration: Configuration) =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
|
|
|
|
import org.apache.ivy.core.module.descriptor.{Configuration => IvyConfig}
|
|
|
|
|
import IvyConfig.Visibility._
|
|
|
|
|
import configuration._
|
|
|
|
|
new IvyConfig(name, if(isPublic) PUBLIC else PRIVATE, description, extendsConfigs.map(_.name).toArray, transitive, null)
|
|
|
|
|
}
|
|
|
|
|
/** Adds the ivy.xml main artifact. */
|
|
|
|
|
private def addMainArtifact(moduleID: DefaultModuleDescriptor)
|
|
|
|
|
{
|
|
|
|
|
val artifact = DefaultArtifact.newIvyArtifact(moduleID.getResolvedModuleRevisionId, moduleID.getPublicationDate)
|
|
|
|
|
moduleID.setModuleArtifact(artifact)
|
|
|
|
|
moduleID.check()
|
|
|
|
|
}
|
|
|
|
|
/** Converts the given sbt module id into an Ivy ModuleRevisionId.*/
|
2009-09-09 05:13:30 +02:00
|
|
|
def toID(m: ModuleID) =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
|
|
|
|
import m._
|
2009-09-09 05:13:30 +02:00
|
|
|
ModuleRevisionId.newInstance(organization, name, revision, javaMap(extraAttributes))
|
2009-08-16 20:29:08 +02:00
|
|
|
}
|
2011-02-15 00:57:10 +01:00
|
|
|
|
|
|
|
|
private def substituteCross(m: ModuleSettings): ModuleSettings =
|
|
|
|
|
m.ivyScala match { case None => m; case Some(is) => substituteCross(m, is.scalaVersion) }
|
|
|
|
|
private def substituteCross(m: ModuleSettings, cross: String): ModuleSettings =
|
|
|
|
|
m match {
|
|
|
|
|
case ec: EmptyConfiguration => ec.copy(module = substituteCross(ec.module, cross))
|
|
|
|
|
case ic: InlineConfiguration => ic.copy(module = substituteCross(ic.module, cross), dependencies = substituteCrossM(ic.dependencies, cross))
|
|
|
|
|
case _ => m
|
|
|
|
|
}
|
2011-04-16 01:55:22 +02:00
|
|
|
def crossName(name: String, cross: String): String =
|
2011-02-15 00:57:10 +01:00
|
|
|
name + "_" + cross
|
2011-04-16 01:55:22 +02:00
|
|
|
def substituteCross(a: Artifact, cross: String): Artifact =
|
2011-02-15 00:57:10 +01:00
|
|
|
a.copy(name = crossName(a.name, cross))
|
2011-04-16 01:55:22 +02:00
|
|
|
def substituteCrossA(as: Seq[Artifact], cross: String): Seq[Artifact] =
|
2011-02-15 00:57:10 +01:00
|
|
|
as.map(art => substituteCross(art, cross))
|
2011-04-16 01:55:22 +02:00
|
|
|
def substituteCrossM(ms: Seq[ModuleID], cross: String): Seq[ModuleID] =
|
2011-02-15 00:57:10 +01:00
|
|
|
ms.map(m => substituteCross(m, cross))
|
2011-04-16 01:55:22 +02:00
|
|
|
def substituteCross(m: ModuleID, cross: String): ModuleID =
|
2011-02-15 00:57:10 +01:00
|
|
|
if(m.crossVersion)
|
2011-04-16 01:55:22 +02:00
|
|
|
m.copy(name = crossName(m.name, cross), explicitArtifacts = substituteCrossA(m.explicitArtifacts, cross))
|
2011-02-15 00:57:10 +01:00
|
|
|
else
|
|
|
|
|
m
|
|
|
|
|
|
2009-08-16 20:29:08 +02:00
|
|
|
private def toIvyArtifact(moduleID: ModuleDescriptor, a: Artifact, configurations: Iterable[String]): MDArtifact =
|
|
|
|
|
{
|
2011-04-14 13:32:42 +02:00
|
|
|
val artifact = new MDArtifact(moduleID, a.name, a.`type`, a.extension, null, extra(a, false))
|
2009-08-16 20:29:08 +02:00
|
|
|
configurations.foreach(artifact.addConfiguration)
|
|
|
|
|
artifact
|
|
|
|
|
}
|
2011-03-16 01:35:43 +01:00
|
|
|
private[sbt] def extra(artifact: Artifact, unqualify: Boolean = false): java.util.Map[String, String] =
|
2009-09-09 05:13:30 +02:00
|
|
|
{
|
|
|
|
|
val ea = artifact.classifier match { case Some(c) => artifact.extra("e:classifier" -> c); case None => artifact }
|
2011-03-16 01:35:43 +01:00
|
|
|
javaMap(ea.extraAttributes, unqualify)
|
2009-09-09 05:13:30 +02:00
|
|
|
}
|
2011-03-16 01:35:43 +01:00
|
|
|
private[sbt] def javaMap(m: Map[String,String], unqualify: Boolean = false) =
|
|
|
|
|
{
|
2011-04-14 13:32:42 +02:00
|
|
|
val map = if(unqualify) m map { case (k, v) => (k.stripPrefix("e:"), v) } else m
|
2010-09-22 04:05:57 +02:00
|
|
|
if(map.isEmpty) null else scala.collection.JavaConversions.asJavaMap(map)
|
2011-03-16 01:35:43 +01:00
|
|
|
}
|
2009-08-16 20:29:08 +02:00
|
|
|
|
|
|
|
|
private object javaMap
|
|
|
|
|
{
|
|
|
|
|
import java.util.{HashMap, Map}
|
|
|
|
|
def apply[K,V](pairs: (K,V)*): Map[K,V] =
|
|
|
|
|
{
|
|
|
|
|
val map = new HashMap[K,V]
|
|
|
|
|
pairs.foreach { case (key, value) => map.put(key, value) }
|
|
|
|
|
map
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-11 21:43:42 +02:00
|
|
|
/** Creates a full ivy file for 'module' using the 'dependencies' XML as the part after the <info>...</info> section. */
|
2010-04-02 02:14:05 +02:00
|
|
|
private def wrapped(module: ModuleID, dependencies: NodeSeq) =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
|
|
|
|
import module._
|
|
|
|
|
<ivy-module version="2.0">
|
2009-10-11 21:43:42 +02:00
|
|
|
{ if(hasInfo(dependencies))
|
2010-04-02 02:14:05 +02:00
|
|
|
NodeSeq.Empty
|
2009-10-11 21:43:42 +02:00
|
|
|
else
|
|
|
|
|
<info organisation={organization} module={name} revision={revision}/>
|
|
|
|
|
}
|
|
|
|
|
{dependencies}
|
2010-04-02 02:14:05 +02:00
|
|
|
{
|
|
|
|
|
// this is because Ivy adds a default artifact if none are specified.
|
|
|
|
|
if(dependencies \\ "publications" isEmpty) <publications/> else NodeSeq.Empty
|
|
|
|
|
}
|
2009-08-16 20:29:08 +02:00
|
|
|
</ivy-module>
|
|
|
|
|
}
|
2009-10-11 21:43:42 +02:00
|
|
|
private def hasInfo(x: scala.xml.NodeSeq) = !(<g>{x}</g> \ "info").isEmpty
|
2009-08-16 20:29:08 +02:00
|
|
|
/** Parses the given in-memory Ivy file 'xml', using the existing 'moduleID' and specifying the given 'defaultConfiguration'. */
|
|
|
|
|
private def parseIvyXML(settings: IvySettings, xml: scala.xml.NodeSeq, moduleID: DefaultModuleDescriptor, defaultConfiguration: String, validate: Boolean): CustomXmlParser.CustomParser =
|
|
|
|
|
parseIvyXML(settings, xml.toString, moduleID, defaultConfiguration, validate)
|
|
|
|
|
/** Parses the given in-memory Ivy file 'xml', using the existing 'moduleID' and specifying the given 'defaultConfiguration'. */
|
|
|
|
|
private def parseIvyXML(settings: IvySettings, xml: String, moduleID: DefaultModuleDescriptor, defaultConfiguration: String, validate: Boolean): CustomXmlParser.CustomParser =
|
|
|
|
|
{
|
2009-09-27 20:39:26 +02:00
|
|
|
val parser = new CustomXmlParser.CustomParser(settings, Some(defaultConfiguration))
|
2009-08-16 20:29:08 +02:00
|
|
|
parser.setMd(moduleID)
|
|
|
|
|
parser.setValidate(validate)
|
|
|
|
|
parser.setInput(xml.getBytes)
|
|
|
|
|
parser.parse()
|
|
|
|
|
parser
|
|
|
|
|
}
|
2009-08-20 06:02:06 +02:00
|
|
|
|
2009-08-16 20:29:08 +02:00
|
|
|
/** This method is used to add inline dependencies to the provided module. */
|
|
|
|
|
def addDependencies(moduleID: DefaultModuleDescriptor, dependencies: Iterable[ModuleID], parser: CustomXmlParser.CustomParser)
|
|
|
|
|
{
|
|
|
|
|
for(dependency <- dependencies)
|
|
|
|
|
{
|
|
|
|
|
val dependencyDescriptor = new DefaultDependencyDescriptor(moduleID, toID(dependency), false, dependency.isChanging, dependency.isTransitive)
|
|
|
|
|
dependency.configurations match
|
|
|
|
|
{
|
|
|
|
|
case None => // The configuration for this dependency was not explicitly specified, so use the default
|
|
|
|
|
parser.parseDepsConfs(parser.getDefaultConf, dependencyDescriptor)
|
|
|
|
|
case Some(confs) => // The configuration mapping (looks like: test->default) was specified for this dependency
|
|
|
|
|
parser.parseDepsConfs(confs, dependencyDescriptor)
|
|
|
|
|
}
|
|
|
|
|
for(artifact <- dependency.explicitArtifacts)
|
|
|
|
|
{
|
|
|
|
|
import artifact.{name, classifier, `type`, extension, url}
|
|
|
|
|
val extraMap = extra(artifact)
|
|
|
|
|
val ivyArtifact = new DefaultDependencyArtifactDescriptor(dependencyDescriptor, name, `type`, extension, url.getOrElse(null), extraMap)
|
|
|
|
|
for(conf <- dependencyDescriptor.getModuleConfigurations)
|
|
|
|
|
dependencyDescriptor.addDependencyArtifact(conf, ivyArtifact)
|
|
|
|
|
}
|
|
|
|
|
moduleID.addDependency(dependencyDescriptor)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/** This method is used to add inline artifacts to the provided module. */
|
2011-04-14 13:32:42 +02:00
|
|
|
def addArtifacts(moduleID: DefaultModuleDescriptor, artifacts: Iterable[Artifact]): Unit =
|
|
|
|
|
for(art <- mapArtifacts(moduleID, artifacts.toSeq); c <- art.getConfigurations)
|
|
|
|
|
moduleID.addArtifact(c, art)
|
|
|
|
|
|
|
|
|
|
def mapArtifacts(moduleID: ModuleDescriptor, artifacts: Seq[Artifact]): Seq[IArtifact] =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2010-01-16 01:05:23 +01:00
|
|
|
lazy val allConfigurations = moduleID.getPublicConfigurationsNames
|
2011-04-14 13:32:42 +02:00
|
|
|
for(artifact <- artifacts) yield
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
2010-06-16 02:38:18 +02:00
|
|
|
val configurationStrings: Iterable[String] =
|
2009-08-16 20:29:08 +02:00
|
|
|
{
|
|
|
|
|
val artifactConfigurations = artifact.configurations
|
|
|
|
|
if(artifactConfigurations.isEmpty)
|
|
|
|
|
allConfigurations
|
|
|
|
|
else
|
|
|
|
|
artifactConfigurations.map(_.name)
|
|
|
|
|
}
|
2011-04-14 13:32:42 +02:00
|
|
|
toIvyArtifact(moduleID, artifact, configurationStrings)
|
2009-08-16 20:29:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
2011-04-14 13:32:42 +02:00
|
|
|
|
2009-08-16 20:29:08 +02:00
|
|
|
/** This code converts the given ModuleDescriptor to a DefaultModuleDescriptor by casting or generating an error.
|
|
|
|
|
* Ivy 2.0.0 always produces a DefaultModuleDescriptor. */
|
|
|
|
|
private def toDefaultModuleDescriptor(md: ModuleDescriptor) =
|
|
|
|
|
md match
|
|
|
|
|
{
|
|
|
|
|
case dmd: DefaultModuleDescriptor => dmd
|
|
|
|
|
case _ => error("Unknown ModuleDescriptor type.")
|
|
|
|
|
}
|
2010-03-30 15:19:36 +02:00
|
|
|
def getConfigurations(module: ModuleDescriptor, configurations: Option[Iterable[Configuration]]) =
|
|
|
|
|
configurations match
|
|
|
|
|
{
|
|
|
|
|
case Some(confs) => confs.map(_.name).toList.toArray
|
|
|
|
|
case None => module.getPublicConfigurationsNames
|
|
|
|
|
}
|
2011-03-17 01:10:41 +01:00
|
|
|
|
|
|
|
|
// same as Ivy's builtin latest-revision manager except that it ignores the force setting,
|
|
|
|
|
// which seems to be added to dependencies read from poms (perhaps only in certain circumstances)
|
|
|
|
|
// causing revisions of indirect dependencies other than latest to be selected
|
|
|
|
|
def latestNoForce(settings: IvySettings): ConflictManager =
|
|
|
|
|
{
|
|
|
|
|
import collection.JavaConversions._
|
|
|
|
|
|
|
|
|
|
new LatestConflictManager("latest-revision-no-force", new LatestRevisionStrategy)
|
|
|
|
|
{
|
|
|
|
|
setSettings(settings)
|
|
|
|
|
|
|
|
|
|
override def resolveConflicts(parent: IvyNode, conflicts: Collection[_]): Collection[_] =
|
|
|
|
|
if(conflicts.size < 2)
|
|
|
|
|
conflicts
|
|
|
|
|
else
|
|
|
|
|
resolveMultiple(parent, conflicts.asInstanceOf[Collection[IvyNode]]).asInstanceOf[Collection[_]]
|
|
|
|
|
|
|
|
|
|
def resolveMultiple(parent: IvyNode, conflicts: Collection[IvyNode]): Collection[IvyNode] =
|
|
|
|
|
{
|
|
|
|
|
val matcher = settings.getVersionMatcher
|
|
|
|
|
val dynamic = conflicts.exists { node => matcher.isDynamic(node.getResolvedId) }
|
|
|
|
|
if(dynamic) null else {
|
|
|
|
|
try {
|
|
|
|
|
val l = getStrategy.findLatest(toArtifactInfo(conflicts), null).asInstanceOf[{def getNode(): IvyNode}]
|
|
|
|
|
if(l eq null) conflicts else Collections.singleton(l.getNode)
|
|
|
|
|
}
|
|
|
|
|
catch { case e: LatestConflictManager.NoConflictResolvedYetException => null }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-08-16 20:29:08 +02:00
|
|
|
}
|