added scalariform

This commit is contained in:
Eugene Yokota 2014-05-01 12:50:07 -04:00
parent 60d038170f
commit e64165ce9e
30 changed files with 3180 additions and 3256 deletions

View File

@ -6,71 +6,69 @@ package sbt
import java.io.File
import java.net.URL
final case class Artifact(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Iterable[Configuration], url: Option[URL], extraAttributes: Map[String,String])
{
def extra(attributes: (String,String)*) = Artifact(name, `type`, extension, classifier, configurations, url, extraAttributes ++ ModuleID.checkE(attributes))
final case class Artifact(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Iterable[Configuration], url: Option[URL], extraAttributes: Map[String, String]) {
def extra(attributes: (String, String)*) = Artifact(name, `type`, extension, classifier, configurations, url, extraAttributes ++ ModuleID.checkE(attributes))
}
import Configurations.{config, Docs, Optional, Pom, Sources, Test}
import Configurations.{ config, Docs, Optional, Pom, Sources, Test }
object Artifact
{
def apply(name: String): Artifact = Artifact(name, DefaultType, DefaultExtension, None, Nil, None)
def apply(name: String, extra: Map[String,String]): Artifact = Artifact(name, DefaultType, DefaultExtension, None, Nil, None, extra)
def apply(name: String, classifier: String): Artifact = Artifact(name, DefaultType, DefaultExtension, Some(classifier), Nil, None)
def apply(name: String, `type`: String, extension: String): Artifact = Artifact(name, `type`, extension, None, Nil, None)
def apply(name: String, `type`: String, extension: String, classifier: String): Artifact = Artifact(name, `type`, extension, Some(classifier), Nil, None)
def apply(name: String, url: URL): Artifact =Artifact(name, extract(url, DefaultType), extract(url, DefaultExtension), None, Nil, Some(url))
def apply(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Iterable[Configuration], url: Option[URL]): Artifact =
Artifact(name, `type`, extension, classifier, configurations, url, Map.empty)
object Artifact {
def apply(name: String): Artifact = Artifact(name, DefaultType, DefaultExtension, None, Nil, None)
def apply(name: String, extra: Map[String, String]): Artifact = Artifact(name, DefaultType, DefaultExtension, None, Nil, None, extra)
def apply(name: String, classifier: String): Artifact = Artifact(name, DefaultType, DefaultExtension, Some(classifier), Nil, None)
def apply(name: String, `type`: String, extension: String): Artifact = Artifact(name, `type`, extension, None, Nil, None)
def apply(name: String, `type`: String, extension: String, classifier: String): Artifact = Artifact(name, `type`, extension, Some(classifier), Nil, None)
def apply(name: String, url: URL): Artifact = Artifact(name, extract(url, DefaultType), extract(url, DefaultExtension), None, Nil, Some(url))
def apply(name: String, `type`: String, extension: String, classifier: Option[String], configurations: Iterable[Configuration], url: Option[URL]): Artifact =
Artifact(name, `type`, extension, classifier, configurations, url, Map.empty)
val DefaultExtension = "jar"
val DefaultType = "jar"
val DefaultExtension = "jar"
val DefaultType = "jar"
def sources(name: String) = classified(name, SourceClassifier)
def javadoc(name: String) = classified(name, DocClassifier)
def pom(name: String) = Artifact(name, PomType, PomType, None, Pom :: Nil, None)
def sources(name: String) = classified(name, SourceClassifier)
def javadoc(name: String) = classified(name, DocClassifier)
def pom(name: String) = Artifact(name, PomType, PomType, None, Pom :: Nil, None)
val DocClassifier = "javadoc"
val SourceClassifier = "sources"
val DocType = "doc"
val SourceType = "src"
val PomType = "pom"
val TestsClassifier = "tests"
val DocClassifier = "javadoc"
val SourceClassifier = "sources"
val DocType = "doc"
val SourceType = "src"
val PomType = "pom"
val TestsClassifier = "tests"
def extract(url: URL, default: String): String = extract(url.toString, default)
def extract(name: String, default: String): String =
{
val i = name.lastIndexOf('.')
if(i >= 0)
name.substring(i+1)
else
default
}
def defaultArtifact(file: File) =
{
val name = file.getName
val i = name.lastIndexOf('.')
val base = if(i >= 0) name.substring(0, i) else name
Artifact(base, extract(name, DefaultType), extract(name, DefaultExtension), None, Nil, Some(file.toURI.toURL))
}
def artifactName(scalaVersion: ScalaVersion, module: ModuleID, artifact: Artifact): String =
{
import artifact._
val classifierStr = classifier match { case None => ""; case Some(c) => "-" + c }
val cross = CrossVersion(module.crossVersion, scalaVersion.full, scalaVersion.binary)
val base = CrossVersion.applyCross(artifact.name, cross)
base + "-" + module.revision + classifierStr + "." + artifact.extension
}
def extract(url: URL, default: String): String = extract(url.toString, default)
def extract(name: String, default: String): String =
{
val i = name.lastIndexOf('.')
if (i >= 0)
name.substring(i + 1)
else
default
}
def defaultArtifact(file: File) =
{
val name = file.getName
val i = name.lastIndexOf('.')
val base = if (i >= 0) name.substring(0, i) else name
Artifact(base, extract(name, DefaultType), extract(name, DefaultExtension), None, Nil, Some(file.toURI.toURL))
}
def artifactName(scalaVersion: ScalaVersion, module: ModuleID, artifact: Artifact): String =
{
import artifact._
val classifierStr = classifier match { case None => ""; case Some(c) => "-" + c }
val cross = CrossVersion(module.crossVersion, scalaVersion.full, scalaVersion.binary)
val base = CrossVersion.applyCross(artifact.name, cross)
base + "-" + module.revision + classifierStr + "." + artifact.extension
}
val classifierConfMap = Map(SourceClassifier -> Sources, DocClassifier -> Docs)
val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType)
def classifierConf(classifier: String): Configuration =
if(classifier.startsWith(TestsClassifier))
Test
else
classifierConfMap.getOrElse(classifier, Optional)
def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix(TestsClassifier + "-"), DefaultType)
def classified(name: String, classifier: String): Artifact =
Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), classifierConf(classifier) :: Nil, None)
val classifierConfMap = Map(SourceClassifier -> Sources, DocClassifier -> Docs)
val classifierTypeMap = Map(SourceClassifier -> SourceType, DocClassifier -> DocType)
def classifierConf(classifier: String): Configuration =
if (classifier.startsWith(TestsClassifier))
Test
else
classifierConfMap.getOrElse(classifier, Optional)
def classifierType(classifier: String): String = classifierTypeMap.getOrElse(classifier.stripPrefix(TestsClassifier + "-"), DefaultType)
def classified(name: String, classifier: String): Artifact =
Artifact(name, classifierType(classifier), DefaultExtension, Some(classifier), classifierConf(classifier) :: Nil, None)
}

View File

@ -3,90 +3,87 @@
*/
package sbt
import java.io.{File,FileOutputStream}
import java.io.{ File, FileOutputStream }
import java.util.concurrent.Callable
/** A component manager provides access to the pieces of xsbt that are distributed as components.
* There are two types of components. The first type is compiled subproject jars with their dependencies.
* The second type is a subproject distributed as a source jar so that it can be compiled against a specific
* version of Scala.
*
* The component manager provides services to install and retrieve components to the local repository.
* This is used for compiled source jars so that the compilation need not be repeated for other projects on the same
* machine.
*/
class ComponentManager(globalLock: xsbti.GlobalLock, provider: xsbti.ComponentProvider, ivyHome: Option[File], val log: Logger)
{
private[this] val ivyCache = new IvyCache(ivyHome)
/** Get all of the files for component 'id', throwing an exception if no files exist for the component. */
def files(id: String)(ifMissing: IfMissing): Iterable[File] =
{
def fromGlobal =
lockGlobalCache {
try { update(id); getOrElse(createAndCache) }
catch { case e: NotInCache => createAndCache }
}
def getOrElse(orElse: => Iterable[File]): Iterable[File] =
{
val existing = provider.component(id)
if(existing.isEmpty) orElse else existing
}
def notFound = invalid("Could not find required component '" + id + "'")
def createAndCache =
ifMissing match {
case IfMissing.Fail => notFound
case d: IfMissing.Define =>
d()
if(d.cache) cache(id)
getOrElse(notFound)
}
/**
* A component manager provides access to the pieces of xsbt that are distributed as components.
* There are two types of components. The first type is compiled subproject jars with their dependencies.
* The second type is a subproject distributed as a source jar so that it can be compiled against a specific
* version of Scala.
*
* The component manager provides services to install and retrieve components to the local repository.
* This is used for compiled source jars so that the compilation need not be repeated for other projects on the same
* machine.
*/
class ComponentManager(globalLock: xsbti.GlobalLock, provider: xsbti.ComponentProvider, ivyHome: Option[File], val log: Logger) {
private[this] val ivyCache = new IvyCache(ivyHome)
/** Get all of the files for component 'id', throwing an exception if no files exist for the component. */
def files(id: String)(ifMissing: IfMissing): Iterable[File] =
{
def fromGlobal =
lockGlobalCache {
try { update(id); getOrElse(createAndCache) }
catch { case e: NotInCache => createAndCache }
}
def getOrElse(orElse: => Iterable[File]): Iterable[File] =
{
val existing = provider.component(id)
if (existing.isEmpty) orElse else existing
}
def notFound = invalid("Could not find required component '" + id + "'")
def createAndCache =
ifMissing match {
case IfMissing.Fail => notFound
case d: IfMissing.Define =>
d()
if (d.cache) cache(id)
getOrElse(notFound)
}
lockLocalCache { getOrElse(fromGlobal) }
}
/** This is used to lock the local cache in project/boot/. By checking the local cache first, we can avoid grabbing a global lock. */
private def lockLocalCache[T](action: => T): T = lock(provider.lockFile)( action )
/** This is used to ensure atomic access to components in the global Ivy cache.*/
private def lockGlobalCache[T](action: => T): T = lock(ivyCache.lockFile)( action )
private def lock[T](file: File)(action: => T): T = globalLock(file, new Callable[T] { def call = action })
/** Get the file for component 'id', throwing an exception if no files or multiple files exist for the component. */
def file(id: String)(ifMissing: IfMissing): File =
files(id)(ifMissing).toList match {
case x :: Nil => x
case xs => invalid("Expected single file for component '" + id + "', found: " + xs.mkString(", "))
}
private def invalid(msg: String) = throw new InvalidComponent(msg)
private def invalid(e: NotInCache) = throw new InvalidComponent(e.getMessage, e)
lockLocalCache { getOrElse(fromGlobal) }
}
/** This is used to lock the local cache in project/boot/. By checking the local cache first, we can avoid grabbing a global lock. */
private def lockLocalCache[T](action: => T): T = lock(provider.lockFile)(action)
/** This is used to ensure atomic access to components in the global Ivy cache.*/
private def lockGlobalCache[T](action: => T): T = lock(ivyCache.lockFile)(action)
private def lock[T](file: File)(action: => T): T = globalLock(file, new Callable[T] { def call = action })
/** Get the file for component 'id', throwing an exception if no files or multiple files exist for the component. */
def file(id: String)(ifMissing: IfMissing): File =
files(id)(ifMissing).toList match {
case x :: Nil => x
case xs => invalid("Expected single file for component '" + id + "', found: " + xs.mkString(", "))
}
private def invalid(msg: String) = throw new InvalidComponent(msg)
private def invalid(e: NotInCache) = throw new InvalidComponent(e.getMessage, e)
def define(id: String, files: Iterable[File]) = lockLocalCache { provider.defineComponent(id, files.toSeq.toArray) }
/** Retrieve the file for component 'id' from the local repository. */
private def update(id: String): Unit = ivyCache.withCachedJar(sbtModuleID(id), Some(globalLock), log)(jar => define(id, Seq(jar)) )
def define(id: String, files: Iterable[File]) = lockLocalCache { provider.defineComponent(id, files.toSeq.toArray) }
/** Retrieve the file for component 'id' from the local repository. */
private def update(id: String): Unit = ivyCache.withCachedJar(sbtModuleID(id), Some(globalLock), log)(jar => define(id, Seq(jar)))
private def sbtModuleID(id: String) = ModuleID(SbtArtifacts.Organization, id, ComponentManager.stampedVersion)
/** Install the files for component 'id' to the local repository. This is usually used after writing files to the directory returned by 'location'. */
def cache(id: String): Unit = ivyCache.cacheJar(sbtModuleID(id), file(id)(IfMissing.Fail), Some(globalLock), log)
def clearCache(id: String): Unit = lockGlobalCache { ivyCache.clearCachedJar(sbtModuleID(id), Some(globalLock), log) }
private def sbtModuleID(id: String) = ModuleID(SbtArtifacts.Organization, id, ComponentManager.stampedVersion)
/** Install the files for component 'id' to the local repository. This is usually used after writing files to the directory returned by 'location'. */
def cache(id: String): Unit = ivyCache.cacheJar(sbtModuleID(id), file(id)(IfMissing.Fail), Some(globalLock), log)
def clearCache(id: String): Unit = lockGlobalCache { ivyCache.clearCachedJar(sbtModuleID(id), Some(globalLock), log) }
}
class InvalidComponent(msg: String, cause: Throwable) extends RuntimeException(msg, cause)
{
def this(msg: String) = this(msg, null)
class InvalidComponent(msg: String, cause: Throwable) extends RuntimeException(msg, cause) {
def this(msg: String) = this(msg, null)
}
sealed trait IfMissing extends NotNull
object IfMissing
{
object Fail extends IfMissing
final class Define(val cache: Boolean, define: => Unit) extends IfMissing { def apply() = define }
object IfMissing {
object Fail extends IfMissing
final class Define(val cache: Boolean, define: => Unit) extends IfMissing { def apply() = define }
}
object ComponentManager
{
lazy val (version, timestamp) =
{
val properties = new java.util.Properties
val propertiesStream = versionResource.openStream
try { properties.load(propertiesStream) } finally { propertiesStream.close() }
(properties.getProperty("version"), properties.getProperty("timestamp"))
}
lazy val stampedVersion = version + "_" + timestamp
object ComponentManager {
lazy val (version, timestamp) =
{
val properties = new java.util.Properties
val propertiesStream = versionResource.openStream
try { properties.load(propertiesStream) } finally { propertiesStream.close() }
(properties.getProperty("version"), properties.getProperty("timestamp"))
}
lazy val stampedVersion = version + "_" + timestamp
import java.net.URL
private def versionResource: URL = getClass.getResource("/xsbt.version.properties")
import java.net.URL
private def versionResource: URL = getClass.getResource("/xsbt.version.properties")
}

View File

@ -3,63 +3,61 @@
*/
package sbt
object Configurations
{
def config(name: String) = new Configuration(name)
def default: Seq[Configuration] = defaultMavenConfigurations
def defaultMavenConfigurations: Seq[Configuration] = Seq(Compile, Runtime, Test, Provided, Optional)
def defaultInternal: Seq[Configuration] = Seq(CompileInternal, RuntimeInternal, TestInternal)
def auxiliary: Seq[Configuration] = Seq(Sources, Docs, Pom)
def names(cs: Seq[Configuration]) = cs.map(_.name)
object Configurations {
def config(name: String) = new Configuration(name)
def default: Seq[Configuration] = defaultMavenConfigurations
def defaultMavenConfigurations: Seq[Configuration] = Seq(Compile, Runtime, Test, Provided, Optional)
def defaultInternal: Seq[Configuration] = Seq(CompileInternal, RuntimeInternal, TestInternal)
def auxiliary: Seq[Configuration] = Seq(Sources, Docs, Pom)
def names(cs: Seq[Configuration]) = cs.map(_.name)
lazy val RuntimeInternal = optionalInternal(Runtime)
lazy val TestInternal = fullInternal(Test)
lazy val IntegrationTestInternal = fullInternal(IntegrationTest)
lazy val CompileInternal = fullInternal(Compile)
lazy val RuntimeInternal = optionalInternal(Runtime)
lazy val TestInternal = fullInternal(Test)
lazy val IntegrationTestInternal = fullInternal(IntegrationTest)
lazy val CompileInternal = fullInternal(Compile)
def internalMap(c: Configuration) = c match {
case Compile => CompileInternal
case Test => TestInternal
case Runtime => RuntimeInternal
case IntegrationTest => IntegrationTestInternal
case _ => c
}
def internalMap(c: Configuration) = c match {
case Compile => CompileInternal
case Test => TestInternal
case Runtime => RuntimeInternal
case IntegrationTest => IntegrationTestInternal
case _ => c
}
def internal(base: Configuration, ext: Configuration*) = config(base.name + "-internal") extend(ext : _*) hide;
def fullInternal(base: Configuration): Configuration = internal(base, base, Optional, Provided)
def optionalInternal(base: Configuration): Configuration = internal(base, base, Optional)
def internal(base: Configuration, ext: Configuration*) = config(base.name + "-internal") extend (ext: _*) hide;
def fullInternal(base: Configuration): Configuration = internal(base, base, Optional, Provided)
def optionalInternal(base: Configuration): Configuration = internal(base, base, Optional)
lazy val Default = config("default")
lazy val Compile = config("compile")
lazy val IntegrationTest = config("it") extend(Runtime)
lazy val Provided = config("provided") ;
lazy val Docs = config("docs")
lazy val Runtime = config("runtime") extend(Compile)
lazy val Test = config("test") extend(Runtime)
lazy val Sources = config("sources")
lazy val System = config("system")
lazy val Optional = config("optional")
lazy val Pom = config("pom")
lazy val Default = config("default")
lazy val Compile = config("compile")
lazy val IntegrationTest = config("it") extend (Runtime)
lazy val Provided = config("provided");
lazy val Docs = config("docs")
lazy val Runtime = config("runtime") extend (Compile)
lazy val Test = config("test") extend (Runtime)
lazy val Sources = config("sources")
lazy val System = config("system")
lazy val Optional = config("optional")
lazy val Pom = config("pom")
lazy val ScalaTool = config("scala-tool") hide
lazy val CompilerPlugin = config("plugin") hide
lazy val ScalaTool = config("scala-tool") hide
lazy val CompilerPlugin = config("plugin") hide
private[sbt] val DefaultMavenConfiguration = defaultConfiguration(true)
private[sbt] val DefaultIvyConfiguration = defaultConfiguration(false)
private[sbt] def DefaultConfiguration(mavenStyle: Boolean) = if(mavenStyle) DefaultMavenConfiguration else DefaultIvyConfiguration
private[sbt] def defaultConfiguration(mavenStyle: Boolean) = if(mavenStyle) Configurations.Compile else Configurations.Default
private[sbt] def removeDuplicates(configs: Iterable[Configuration]) = Set(scala.collection.mutable.Map(configs.map(config => (config.name, config)).toSeq: _*).values.toList: _*)
private[sbt] val DefaultMavenConfiguration = defaultConfiguration(true)
private[sbt] val DefaultIvyConfiguration = defaultConfiguration(false)
private[sbt] def DefaultConfiguration(mavenStyle: Boolean) = if (mavenStyle) DefaultMavenConfiguration else DefaultIvyConfiguration
private[sbt] def defaultConfiguration(mavenStyle: Boolean) = if (mavenStyle) Configurations.Compile else Configurations.Default
private[sbt] def removeDuplicates(configs: Iterable[Configuration]) = Set(scala.collection.mutable.Map(configs.map(config => (config.name, config)).toSeq: _*).values.toList: _*)
}
/** Represents an Ivy configuration. */
final case class Configuration(name: String, description: String, isPublic: Boolean, extendsConfigs: List[Configuration], transitive: Boolean)
{
require(name != null && !name.isEmpty)
require(description != null)
def this(name: String) = this(name, "", true, Nil, true)
def describedAs(newDescription: String) = Configuration(name, newDescription, isPublic, extendsConfigs, transitive)
def extend(configs: Configuration*) = Configuration(name, description, isPublic, configs.toList ::: extendsConfigs, transitive)
def notTransitive = intransitive
def intransitive = Configuration(name, description, isPublic, extendsConfigs, false)
def hide = Configuration(name, description, false, extendsConfigs, transitive)
override def toString = name
final case class Configuration(name: String, description: String, isPublic: Boolean, extendsConfigs: List[Configuration], transitive: Boolean) {
require(name != null && !name.isEmpty)
require(description != null)
def this(name: String) = this(name, "", true, Nil, true)
def describedAs(newDescription: String) = Configuration(name, newDescription, isPublic, extendsConfigs, transitive)
def extend(configs: Configuration*) = Configuration(name, description, isPublic, configs.toList ::: extendsConfigs, transitive)
def notTransitive = intransitive
def intransitive = Configuration(name, description, isPublic, extendsConfigs, false)
def hide = Configuration(name, description, false, extendsConfigs, transitive)
override def toString = name
}

View File

@ -1,80 +1,74 @@
package sbt
import DependencyFilter._
import DependencyFilter._
final case class ConflictWarning(label: String, level: Level.Value, failOnConflict: Boolean)
{
@deprecated("`filter` is no longer used", "0.13.0")
val filter: ModuleFilter = (_: ModuleID) => false
@deprecated("`group` is no longer used", "0.13.0")
val group: ModuleID => String = ConflictWarning.org
final case class ConflictWarning(label: String, level: Level.Value, failOnConflict: Boolean) {
@deprecated("`filter` is no longer used", "0.13.0")
val filter: ModuleFilter = (_: ModuleID) => false
@deprecated("`group` is no longer used", "0.13.0")
val group: ModuleID => String = ConflictWarning.org
}
object ConflictWarning
{
@deprecated("`group` and `filter` are no longer used. Use a standard Ivy conflict manager.", "0.13.0")
def apply(label: String, filter: ModuleFilter, group: ModuleID => String, level: Level.Value, failOnConflict: Boolean): ConflictWarning =
ConflictWarning(label, level, failOnConflict)
object ConflictWarning {
@deprecated("`group` and `filter` are no longer used. Use a standard Ivy conflict manager.", "0.13.0")
def apply(label: String, filter: ModuleFilter, group: ModuleID => String, level: Level.Value, failOnConflict: Boolean): ConflictWarning =
ConflictWarning(label, level, failOnConflict)
def disable: ConflictWarning = ConflictWarning("", Level.Debug, false)
def disable: ConflictWarning = ConflictWarning("", Level.Debug, false)
private def org = (_: ModuleID).organization
private[this] def idString(org: String, name: String) = s"$org:$name"
private def org = (_: ModuleID).organization
private[this] def idString(org: String, name: String) = s"$org:$name"
def default(label: String): ConflictWarning = ConflictWarning(label, Level.Error, true)
def default(label: String): ConflictWarning = ConflictWarning(label, Level.Error, true)
@deprecated("Warning on evicted modules is no longer done, so this is the same as `default`. Use a standard Ivy conflict manager.", "0.13.0")
def strict(label: String): ConflictWarning = ConflictWarning(label, Level.Error, true)
@deprecated("Warning on evicted modules is no longer done, so this is the same as `default`. Use a standard Ivy conflict manager.", "0.13.0")
def strict(label: String): ConflictWarning = ConflictWarning(label, Level.Error, true)
def apply(config: ConflictWarning, report: UpdateReport, log: Logger)
{
processCrossVersioned(config, report, log)
}
private[this] def processCrossVersioned(config: ConflictWarning, report: UpdateReport, log: Logger)
{
val crossMismatches = crossVersionMismatches(report)
if(!crossMismatches.isEmpty)
{
val pre = s"Modules were resolved with conflicting cross-version suffixes in ${config.label}:\n "
val conflictMsgs =
for( ((org,rawName), fullNames) <- crossMismatches ) yield
{
val suffixes = fullNames.map(getCrossSuffix).mkString(", ")
s"${idString(org,rawName)} $suffixes"
}
log.log(config.level, conflictMsgs.mkString(pre, "\n ", ""))
if(config.failOnConflict) {
val summary = crossMismatches.map{ case ((org,raw),_) => idString(org,raw)}.mkString(", ")
sys.error("Conflicting cross-version suffixes in: " + summary)
}
}
}
def apply(config: ConflictWarning, report: UpdateReport, log: Logger) {
processCrossVersioned(config, report, log)
}
private[this] def processCrossVersioned(config: ConflictWarning, report: UpdateReport, log: Logger) {
val crossMismatches = crossVersionMismatches(report)
if (!crossMismatches.isEmpty) {
val pre = s"Modules were resolved with conflicting cross-version suffixes in ${config.label}:\n "
val conflictMsgs =
for (((org, rawName), fullNames) <- crossMismatches) yield {
val suffixes = fullNames.map(getCrossSuffix).mkString(", ")
s"${idString(org, rawName)} $suffixes"
}
log.log(config.level, conflictMsgs.mkString(pre, "\n ", ""))
if (config.failOnConflict) {
val summary = crossMismatches.map { case ((org, raw), _) => idString(org, raw) }.mkString(", ")
sys.error("Conflicting cross-version suffixes in: " + summary)
}
}
}
/** Map from (organization, rawName) to set of multiple full names. */
def crossVersionMismatches(report: UpdateReport): Map[(String,String), Set[String]] =
{
val mismatches = report.configurations.flatMap { confReport =>
groupByRawName(confReport.allModules).mapValues { modules =>
val differentFullNames = modules.map(_.name).toSet
if(differentFullNames.size > 1) differentFullNames else Set.empty[String]
}
}
(Map.empty[(String,String),Set[String]] /: mismatches)(merge)
}
private[this] def merge[A,B](m: Map[A, Set[B]], b: (A, Set[B])): Map[A, Set[B]] =
if(b._2.isEmpty) m else
m.updated(b._1, m.getOrElse(b._1, Set.empty) ++ b._2)
/** Map from (organization, rawName) to set of multiple full names. */
def crossVersionMismatches(report: UpdateReport): Map[(String, String), Set[String]] =
{
val mismatches = report.configurations.flatMap { confReport =>
groupByRawName(confReport.allModules).mapValues { modules =>
val differentFullNames = modules.map(_.name).toSet
if (differentFullNames.size > 1) differentFullNames else Set.empty[String]
}
}
(Map.empty[(String, String), Set[String]] /: mismatches)(merge)
}
private[this] def merge[A, B](m: Map[A, Set[B]], b: (A, Set[B])): Map[A, Set[B]] =
if (b._2.isEmpty) m else
m.updated(b._1, m.getOrElse(b._1, Set.empty) ++ b._2)
private[this] def groupByRawName(ms: Seq[ModuleID]): Map[(String,String), Seq[ModuleID]] =
ms.groupBy(m => (m.organization, dropCrossSuffix(m.name)))
private[this] def groupByRawName(ms: Seq[ModuleID]): Map[(String, String), Seq[ModuleID]] =
ms.groupBy(m => (m.organization, dropCrossSuffix(m.name)))
private[this] val CrossSuffixPattern = """(.+)_(\d+\.\d+(?:\.\d+)?(?:-.+)?)""".r
private[this] def dropCrossSuffix(s: String): String = s match {
case CrossSuffixPattern(raw, _) => raw
case _ => s
}
private[this] def getCrossSuffix(s: String): String = s match {
case CrossSuffixPattern(_, v) => "_" + v
case _ => "<none>"
}
private[this] val CrossSuffixPattern = """(.+)_(\d+\.\d+(?:\.\d+)?(?:-.+)?)""".r
private[this] def dropCrossSuffix(s: String): String = s match {
case CrossSuffixPattern(raw, _) => raw
case _ => s
}
private[this] def getCrossSuffix(s: String): String = s match {
case CrossSuffixPattern(_, v) => "_" + v
case _ => "<none>"
}
}

View File

@ -5,239 +5,234 @@ package sbt
import java.net.URL
import java.util.Collections
import org.apache.ivy.{core,plugins}
import org.apache.ivy.{ core, plugins }
import core.module.id.ModuleRevisionId
import core.module.descriptor.DependencyDescriptor
import core.resolve.ResolveData
import core.settings.IvySettings
import plugins.resolver.{BasicResolver, DependencyResolver, IBiblioResolver, RepositoryResolver}
import plugins.resolver.{AbstractPatternsBasedResolver, AbstractSshBasedResolver, FileSystemResolver, SFTPResolver, SshResolver, URLResolver}
import plugins.repository.url.{URLRepository => URLRepo}
import plugins.repository.file.{FileRepository => FileRepo, FileResource}
import plugins.resolver.{ BasicResolver, DependencyResolver, IBiblioResolver, RepositoryResolver }
import plugins.resolver.{ AbstractPatternsBasedResolver, AbstractSshBasedResolver, FileSystemResolver, SFTPResolver, SshResolver, URLResolver }
import plugins.repository.url.{ URLRepository => URLRepo }
import plugins.repository.file.{ FileRepository => FileRepo, FileResource }
import java.io.File
import org.apache.ivy.util.ChecksumHelper
import org.apache.ivy.core.module.descriptor.{Artifact=>IArtifact}
import org.apache.ivy.core.module.descriptor.{ Artifact => IArtifact }
private object ConvertResolver
{
/** This class contains all the reflective lookups used in the
* checksum-friendly URL publishing shim.
*/
private object ChecksumFriendlyURLResolver {
// TODO - When we dump JDK6 support we can remove this hackery
// import java.lang.reflect.AccessibleObject
type AccessibleObject = {
def setAccessible(value: Boolean): Unit
}
private def reflectiveLookup[A <: AccessibleObject](f: Class[_] => A): Option[A] =
try {
val cls = classOf[RepositoryResolver]
val thing = f(cls)
import scala.language.reflectiveCalls
thing.setAccessible(true)
Some(thing)
} catch {
case (_: java.lang.NoSuchFieldException) |
(_: java.lang.SecurityException) |
(_: java.lang.NoSuchMethodException) => None
}
private val signerNameField: Option[java.lang.reflect.Field] =
reflectiveLookup(_.getDeclaredField("signerName"))
private val putChecksumMethod: Option[java.lang.reflect.Method] =
reflectiveLookup(_.getDeclaredMethod("putChecksum",
classOf[IArtifact], classOf[File], classOf[String],
classOf[Boolean], classOf[String]))
private val putSignatureMethod: Option[java.lang.reflect.Method] =
reflectiveLookup(_.getDeclaredMethod("putSignature",
classOf[IArtifact], classOf[File], classOf[String],
classOf[Boolean]))
}
/**
* The default behavior of ivy's overwrite flags ignores the fact that a lot of repositories
* will autogenerate checksums *for* an artifact if it doesn't already exist. Therefore
* if we succeed in publishing an artifact, we need to just blast the checksums in place.
* This acts as a "shim" on RepositoryResolvers so that we can hook our methods into
* both the IBiblioResolver + URLResolver without having to duplicate the code in two
* places. However, this does mean our use of reflection is awesome.
*
* TODO - See about contributing back to ivy.
*/
private trait ChecksumFriendlyURLResolver extends RepositoryResolver {
import ChecksumFriendlyURLResolver._
private def signerName: String = signerNameField match {
case Some(field) => field.get(this).asInstanceOf[String]
case None => null
}
override protected def put(artifact: IArtifact, src: File, dest: String, overwrite: Boolean): Unit = {
// verify the checksum algorithms before uploading artifacts!
val checksums = getChecksumAlgorithms()
val repository = getRepository()
for {
checksum <- checksums
if !ChecksumHelper.isKnownAlgorithm(checksum)
} throw new IllegalArgumentException("Unknown checksum algorithm: " + checksum)
repository.put(artifact, src, dest, overwrite);
// Fix for sbt#1156 - Artifactory will auto-generate MD5/sha1 files, so
// we need to overwrite what it has.
for (checksum <- checksums) {
putChecksumMethod match {
case Some(method) => method.invoke(this, artifact, src, dest, true: java.lang.Boolean, checksum)
case None => // TODO - issue warning?
}
}
if (signerName != null) {
putSignatureMethod match {
case None => ()
case Some(method) => method.invoke(artifact, src, dest, true: java.lang.Boolean)
}
}
private object ConvertResolver {
/**
* This class contains all the reflective lookups used in the
* checksum-friendly URL publishing shim.
*/
private object ChecksumFriendlyURLResolver {
// TODO - When we dump JDK6 support we can remove this hackery
// import java.lang.reflect.AccessibleObject
type AccessibleObject = {
def setAccessible(value: Boolean): Unit
}
}
private def reflectiveLookup[A <: AccessibleObject](f: Class[_] => A): Option[A] =
try {
val cls = classOf[RepositoryResolver]
val thing = f(cls)
import scala.language.reflectiveCalls
thing.setAccessible(true)
Some(thing)
} catch {
case (_: java.lang.NoSuchFieldException) |
(_: java.lang.SecurityException) |
(_: java.lang.NoSuchMethodException) => None
}
private val signerNameField: Option[java.lang.reflect.Field] =
reflectiveLookup(_.getDeclaredField("signerName"))
private val putChecksumMethod: Option[java.lang.reflect.Method] =
reflectiveLookup(_.getDeclaredMethod("putChecksum",
classOf[IArtifact], classOf[File], classOf[String],
classOf[Boolean], classOf[String]))
private val putSignatureMethod: Option[java.lang.reflect.Method] =
reflectiveLookup(_.getDeclaredMethod("putSignature",
classOf[IArtifact], classOf[File], classOf[String],
classOf[Boolean]))
}
/**
* The default behavior of ivy's overwrite flags ignores the fact that a lot of repositories
* will autogenerate checksums *for* an artifact if it doesn't already exist. Therefore
* if we succeed in publishing an artifact, we need to just blast the checksums in place.
* This acts as a "shim" on RepositoryResolvers so that we can hook our methods into
* both the IBiblioResolver + URLResolver without having to duplicate the code in two
* places. However, this does mean our use of reflection is awesome.
*
* TODO - See about contributing back to ivy.
*/
private trait ChecksumFriendlyURLResolver extends RepositoryResolver {
import ChecksumFriendlyURLResolver._
private def signerName: String = signerNameField match {
case Some(field) => field.get(this).asInstanceOf[String]
case None => null
}
override protected def put(artifact: IArtifact, src: File, dest: String, overwrite: Boolean): Unit = {
// verify the checksum algorithms before uploading artifacts!
val checksums = getChecksumAlgorithms()
val repository = getRepository()
for {
checksum <- checksums
if !ChecksumHelper.isKnownAlgorithm(checksum)
} throw new IllegalArgumentException("Unknown checksum algorithm: " + checksum)
repository.put(artifact, src, dest, overwrite);
// Fix for sbt#1156 - Artifactory will auto-generate MD5/sha1 files, so
// we need to overwrite what it has.
for (checksum <- checksums) {
putChecksumMethod match {
case Some(method) => method.invoke(this, artifact, src, dest, true: java.lang.Boolean, checksum)
case None => // TODO - issue warning?
}
}
if (signerName != null) {
putSignatureMethod match {
case None => ()
case Some(method) => method.invoke(artifact, src, dest, true: java.lang.Boolean)
}
}
}
}
/** Converts the given sbt resolver into an Ivy resolver..*/
def apply(r: Resolver, settings: IvySettings, log: Logger) =
{
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.
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
}
case r: JavaNet1Repository =>
{
// Thanks to Matthias Pfau for posting how to use the Maven 1 repository on java.net with Ivy:
// http://www.nabble.com/Using-gradle-Ivy-with-special-maven-repositories-td23775489.html
val resolver = new IBiblioResolver with DescriptorRequired { override def convertM2IdForResourceSearch(mrid: ModuleRevisionId) = mrid }
initializeMavenStyle(resolver, JavaNet1Repository.name, "http://download.java.net/maven/1/")
resolver.setPattern("[organisation]/[ext]s/[module]-[revision](-[classifier]).[ext]")
resolver
}
case repo: SshRepository =>
{
val resolver = new SshResolver with DescriptorRequired
initializeSSHResolver(resolver, repo, settings)
repo.publishPermissions.foreach(perm => resolver.setPublishPermissions(perm))
resolver
}
case repo: SftpRepository =>
{
val resolver = new SFTPResolver
initializeSSHResolver(resolver, repo, settings)
resolver
}
case repo: FileRepository =>
{
val resolver = new FileSystemResolver with DescriptorRequired {
// Workaround for #1156
// Temporarily in sbt 0.13.x we deprecate overwriting
// in local files for non-changing revisions.
// This will be fully enforced in sbt 1.0.
setRepository(new WarnOnOverwriteFileRepo())
}
resolver.setName(repo.name)
initializePatterns(resolver, repo.patterns, settings)
import repo.configuration.{isLocal, isTransactional}
resolver.setLocal(isLocal)
isTransactional.foreach(value => resolver.setTransactional(value.toString))
resolver
}
case repo: URLRepository =>
{
val resolver = new URLResolver with ChecksumFriendlyURLResolver with DescriptorRequired
resolver.setName(repo.name)
initializePatterns(resolver, repo.patterns, settings)
resolver
}
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) =
{
val prev = descriptorString(isAllownomd)
setDescriptor(descriptorString(hasExplicitURL(dd)))
try super.getDependency(dd, data) finally setDescriptor(prev)
}
def descriptorString(optional: Boolean) =
if(optional) BasicResolver.DESCRIPTOR_OPTIONAL else BasicResolver.DESCRIPTOR_REQUIRED
def hasExplicitURL(dd: DependencyDescriptor): Boolean =
dd.getAllDependencyArtifacts.exists(_.getUrl != null)
}
private def initializeMavenStyle(resolver: IBiblioResolver, name: String, root: String)
{
resolver.setName(name)
resolver.setM2compatible(true)
resolver.setRoot(root)
}
private def initializeSSHResolver(resolver: AbstractSshBasedResolver, repo: SshBasedRepository, settings: IvySettings)
{
resolver.setName(repo.name)
resolver.setPassfile(null)
initializePatterns(resolver, repo.patterns, settings)
initializeConnection(resolver, repo.connection)
}
private def initializeConnection(resolver: AbstractSshBasedResolver, connection: RepositoryHelpers.SshConnection)
{
import resolver._
import connection._
hostname.foreach(setHost)
port.foreach(setPort)
authentication foreach
{
case RepositoryHelpers.PasswordAuthentication(user, password) =>
setUser(user)
password.foreach(setUserPassword)
case RepositoryHelpers.KeyFileAuthentication(user, file, password) =>
setKeyFile(file)
password.foreach(setKeyFilePassword)
setUser(user)
}
}
private def initializePatterns(resolver: AbstractPatternsBasedResolver, patterns: Patterns, settings: IvySettings)
{
resolver.setM2compatible(patterns.isMavenCompatible)
resolver.setDescriptor(if (patterns.descriptorOptional) BasicResolver.DESCRIPTOR_OPTIONAL else BasicResolver.DESCRIPTOR_REQUIRED)
resolver.setCheckconsistency(!patterns.skipConsistencyCheck)
patterns.ivyPatterns.foreach(p => resolver.addIvyPattern(settings substitute p))
patterns.artifactPatterns.foreach(p => resolver.addArtifactPattern(settings substitute p))
}
/** A custom Ivy URLRepository that returns FileResources for file URLs.
* This allows using the artifacts from the Maven local repository instead of copying them to the Ivy cache. */
private[this] final class LocalIfFileRepo extends URLRepo {
private[this] val repo = new WarnOnOverwriteFileRepo()
override def getResource(source: String) = {
val url = new URL(source)
if(url.getProtocol == IO.FileScheme)
new FileResource(repo, IO.toFile(url))
else
super.getResource(source)
}
}
/** Converts the given sbt resolver into an Ivy resolver..*/
def apply(r: Resolver, settings: IvySettings, log: Logger) =
{
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.
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
}
case r: JavaNet1Repository =>
{
// Thanks to Matthias Pfau for posting how to use the Maven 1 repository on java.net with Ivy:
// http://www.nabble.com/Using-gradle-Ivy-with-special-maven-repositories-td23775489.html
val resolver = new IBiblioResolver with DescriptorRequired { override def convertM2IdForResourceSearch(mrid: ModuleRevisionId) = mrid }
initializeMavenStyle(resolver, JavaNet1Repository.name, "http://download.java.net/maven/1/")
resolver.setPattern("[organisation]/[ext]s/[module]-[revision](-[classifier]).[ext]")
resolver
}
case repo: SshRepository =>
{
val resolver = new SshResolver with DescriptorRequired
initializeSSHResolver(resolver, repo, settings)
repo.publishPermissions.foreach(perm => resolver.setPublishPermissions(perm))
resolver
}
case repo: SftpRepository =>
{
val resolver = new SFTPResolver
initializeSSHResolver(resolver, repo, settings)
resolver
}
case repo: FileRepository =>
{
val resolver = new FileSystemResolver with DescriptorRequired {
// Workaround for #1156
// Temporarily in sbt 0.13.x we deprecate overwriting
// in local files for non-changing revisions.
// This will be fully enforced in sbt 1.0.
setRepository(new WarnOnOverwriteFileRepo())
}
resolver.setName(repo.name)
initializePatterns(resolver, repo.patterns, settings)
import repo.configuration.{ isLocal, isTransactional }
resolver.setLocal(isLocal)
isTransactional.foreach(value => resolver.setTransactional(value.toString))
resolver
}
case repo: URLRepository =>
{
val resolver = new URLResolver with ChecksumFriendlyURLResolver with DescriptorRequired
resolver.setName(repo.name)
initializePatterns(resolver, repo.patterns, settings)
resolver
}
case repo: ChainedResolver => IvySbt.resolverChain(repo.name, repo.resolvers, false, settings, log)
case repo: RawRepository => repo.resolver
}
}
private[this] final class WarnOnOverwriteFileRepo extends FileRepo() {
override def put(source: java.io.File, destination: String, overwrite: Boolean): Unit = {
try super.put(source, destination, overwrite)
catch {
case e: java.io.IOException if e.getMessage.contains("destination already exists") =>
import org.apache.ivy.util.Message
Message.warn(s"Attempting to overwrite $destination\n\tThis usage is deprecated and will be removed in sbt 1.0.")
super.put(source, destination, true)
}
}
}
private sealed trait DescriptorRequired extends BasicResolver {
override def getDependency(dd: DependencyDescriptor, data: ResolveData) =
{
val prev = descriptorString(isAllownomd)
setDescriptor(descriptorString(hasExplicitURL(dd)))
try super.getDependency(dd, data) finally setDescriptor(prev)
}
def descriptorString(optional: Boolean) =
if (optional) BasicResolver.DESCRIPTOR_OPTIONAL else BasicResolver.DESCRIPTOR_REQUIRED
def hasExplicitURL(dd: DependencyDescriptor): Boolean =
dd.getAllDependencyArtifacts.exists(_.getUrl != null)
}
private def initializeMavenStyle(resolver: IBiblioResolver, name: String, root: String) {
resolver.setName(name)
resolver.setM2compatible(true)
resolver.setRoot(root)
}
private def initializeSSHResolver(resolver: AbstractSshBasedResolver, repo: SshBasedRepository, settings: IvySettings) {
resolver.setName(repo.name)
resolver.setPassfile(null)
initializePatterns(resolver, repo.patterns, settings)
initializeConnection(resolver, repo.connection)
}
private def initializeConnection(resolver: AbstractSshBasedResolver, connection: RepositoryHelpers.SshConnection) {
import resolver._
import connection._
hostname.foreach(setHost)
port.foreach(setPort)
authentication foreach
{
case RepositoryHelpers.PasswordAuthentication(user, password) =>
setUser(user)
password.foreach(setUserPassword)
case RepositoryHelpers.KeyFileAuthentication(user, file, password) =>
setKeyFile(file)
password.foreach(setKeyFilePassword)
setUser(user)
}
}
private def initializePatterns(resolver: AbstractPatternsBasedResolver, patterns: Patterns, settings: IvySettings) {
resolver.setM2compatible(patterns.isMavenCompatible)
resolver.setDescriptor(if (patterns.descriptorOptional) BasicResolver.DESCRIPTOR_OPTIONAL else BasicResolver.DESCRIPTOR_REQUIRED)
resolver.setCheckconsistency(!patterns.skipConsistencyCheck)
patterns.ivyPatterns.foreach(p => resolver.addIvyPattern(settings substitute p))
patterns.artifactPatterns.foreach(p => resolver.addArtifactPattern(settings substitute p))
}
/**
* A custom Ivy URLRepository that returns FileResources for file URLs.
* This allows using the artifacts from the Maven local repository instead of copying them to the Ivy cache.
*/
private[this] final class LocalIfFileRepo extends URLRepo {
private[this] val repo = new WarnOnOverwriteFileRepo()
override def getResource(source: String) = {
val url = new URL(source)
if (url.getProtocol == IO.FileScheme)
new FileResource(repo, IO.toFile(url))
else
super.getResource(source)
}
}
private[this] final class WarnOnOverwriteFileRepo extends FileRepo() {
override def put(source: java.io.File, destination: String, overwrite: Boolean): Unit = {
try super.put(source, destination, overwrite)
catch {
case e: java.io.IOException if e.getMessage.contains("destination already exists") =>
import org.apache.ivy.util.Message
Message.warn(s"Attempting to overwrite $destination\n\tThis usage is deprecated and will be removed in sbt 1.0.")
super.put(source, destination, true)
}
}
}
}

View File

@ -6,71 +6,66 @@ package sbt
import java.io.File
import org.apache.ivy.util.url.CredentialsStore
object Credentials
{
def apply(realm: String, host: String, userName: String, passwd: String): Credentials =
new DirectCredentials(realm, host, userName, passwd)
def apply(file: File): Credentials =
new FileCredentials(file)
object Credentials {
def apply(realm: String, host: String, userName: String, passwd: String): Credentials =
new DirectCredentials(realm, host, userName, passwd)
def apply(file: File): Credentials =
new FileCredentials(file)
/** Add the provided credentials to Ivy's credentials cache.*/
def add(realm: String, host: String, userName: String, passwd: String): Unit =
CredentialsStore.INSTANCE.addCredentials(realm, host, userName, passwd)
/** Load credentials from the given file into Ivy's credentials cache.*/
def add(path: File, log: Logger): Unit =
loadCredentials(path) match
{
case Left(err) => log.warn(err)
case Right(dc) => add(dc.realm, dc.host, dc.userName, dc.passwd)
}
/** Add the provided credentials to Ivy's credentials cache.*/
def add(realm: String, host: String, userName: String, passwd: String): Unit =
CredentialsStore.INSTANCE.addCredentials(realm, host, userName, passwd)
/** Load credentials from the given file into Ivy's credentials cache.*/
def add(path: File, log: Logger): Unit =
loadCredentials(path) match {
case Left(err) => log.warn(err)
case Right(dc) => add(dc.realm, dc.host, dc.userName, dc.passwd)
}
def forHost(sc: Seq[Credentials], host: String) = allDirect(sc) find { _.host == host }
def allDirect(sc: Seq[Credentials]): Seq[DirectCredentials] = sc map toDirect
def toDirect(c: Credentials): DirectCredentials = c match {
case dc: DirectCredentials => dc
case fc: FileCredentials => loadCredentials(fc.path) match {
case Left(err) => error(err)
case Right(dc) => dc
}
}
def forHost(sc: Seq[Credentials], host: String) = allDirect(sc) find { _.host == host }
def allDirect(sc: Seq[Credentials]): Seq[DirectCredentials] = sc map toDirect
def toDirect(c: Credentials): DirectCredentials = c match {
case dc: DirectCredentials => dc
case fc: FileCredentials => loadCredentials(fc.path) match {
case Left(err) => error(err)
case Right(dc) => dc
}
}
def loadCredentials(path: File): Either[String, DirectCredentials] =
if(path.exists)
{
val properties = read(path)
def get(keys: List[String]) = keys.flatMap(properties.get).headOption.toRight(keys.head + " not specified in credentials file: " + path)
def loadCredentials(path: File): Either[String, DirectCredentials] =
if (path.exists) {
val properties = read(path)
def get(keys: List[String]) = keys.flatMap(properties.get).headOption.toRight(keys.head + " not specified in credentials file: " + path)
IvyUtil.separate( List(RealmKeys, HostKeys, UserKeys, PasswordKeys).map(get) ) match
{
case (Nil, List(realm, host, user, pass)) => Right( new DirectCredentials(realm, host, user, pass) )
case (errors, _) => Left(errors.mkString("\n"))
}
}
else
Left("Credentials file " + path + " does not exist")
IvyUtil.separate(List(RealmKeys, HostKeys, UserKeys, PasswordKeys).map(get)) match {
case (Nil, List(realm, host, user, pass)) => Right(new DirectCredentials(realm, host, user, pass))
case (errors, _) => Left(errors.mkString("\n"))
}
} else
Left("Credentials file " + path + " does not exist")
def register(cs: Seq[Credentials], log: Logger): Unit =
cs foreach {
case f: FileCredentials => add(f.path, log)
case d: DirectCredentials => add(d.realm, d.host, d.userName, d.passwd)
}
def register(cs: Seq[Credentials], log: Logger): Unit =
cs foreach {
case f: FileCredentials => add(f.path, log)
case d: DirectCredentials => add(d.realm, d.host, d.userName, d.passwd)
}
private[this] val RealmKeys = List("realm")
private[this] val HostKeys = List("host", "hostname")
private[this] val UserKeys = List("user", "user.name", "username")
private[this] val PasswordKeys = List("password", "pwd", "pass", "passwd")
private[this] val RealmKeys = List("realm")
private[this] val HostKeys = List("host", "hostname")
private[this] val UserKeys = List("user", "user.name", "username")
private[this] val PasswordKeys = List("password", "pwd", "pass", "passwd")
import collection.JavaConversions._
private[this] def read(from: File): Map[String,String] =
{
val properties = new java.util.Properties
IO.load(properties, from)
properties map { case (k,v) => (k.toString, v.toString.trim) } toMap;
}
import collection.JavaConversions._
private[this] def read(from: File): Map[String, String] =
{
val properties = new java.util.Properties
IO.load(properties, from)
properties map { case (k, v) => (k.toString, v.toString.trim) } toMap;
}
}
sealed trait Credentials
final class FileCredentials(val path: File) extends Credentials {
override def toString = "FileCredentials('" + path + "')"
override def toString = "FileCredentials('" + path + "')"
}
final class DirectCredentials(val realm: String, val host: String, val userName: String, val passwd: String) extends Credentials

View File

@ -7,142 +7,159 @@ final case class ScalaVersion(full: String, binary: String)
/** Configures how a module will be cross-versioned. */
sealed trait CrossVersion
object CrossVersion
{
/** The first `major.minor` Scala version that the Scala binary version should be used for cross-versioning instead of the full version. */
val TransitionScalaVersion = CrossVersionUtil.TransitionScalaVersion
object CrossVersion {
/** The first `major.minor` Scala version that the Scala binary version should be used for cross-versioning instead of the full version. */
val TransitionScalaVersion = CrossVersionUtil.TransitionScalaVersion
/** The first `major.minor` sbt version that the sbt binary version should be used for cross-versioning instead of the full version. */
val TransitionSbtVersion = CrossVersionUtil.TransitionSbtVersion
/** The first `major.minor` sbt version that the sbt binary version should be used for cross-versioning instead of the full version. */
val TransitionSbtVersion = CrossVersionUtil.TransitionSbtVersion
/** Disables cross versioning for a module.*/
object Disabled extends CrossVersion { override def toString = "disabled" }
/** Disables cross versioning for a module.*/
object Disabled extends CrossVersion { override def toString = "disabled" }
/** Cross-versions a module using the result of applying `remapVersion` to the binary version.
* For example, if `remapVersion = v => "2.10"` and the binary version is "2.9.2" or "2.10",
* the module is cross-versioned with "2.10". */
final class Binary(val remapVersion: String => String) extends CrossVersion {
override def toString = "Binary"
}
/**
* Cross-versions a module using the result of applying `remapVersion` to the binary version.
* For example, if `remapVersion = v => "2.10"` and the binary version is "2.9.2" or "2.10",
* the module is cross-versioned with "2.10".
*/
final class Binary(val remapVersion: String => String) extends CrossVersion {
override def toString = "Binary"
}
/** Cross-versions a module with the result of applying `remapVersion` to the full version.
* For example, if `remapVersion = v => "2.10"` and the full version is "2.9.2" or "2.10.3",
* the module is cross-versioned with "2.10". */
final class Full(val remapVersion: String => String) extends CrossVersion {
override def toString = "Full"
}
/**
* Cross-versions a module with the result of applying `remapVersion` to the full version.
* For example, if `remapVersion = v => "2.10"` and the full version is "2.9.2" or "2.10.3",
* the module is cross-versioned with "2.10".
*/
final class Full(val remapVersion: String => String) extends CrossVersion {
override def toString = "Full"
}
/** Cross-versions a module with the full version (typically the full Scala version). */
def full: CrossVersion = new Full(idFun)
/** Cross-versions a module with the full version (typically the full Scala version). */
def full: CrossVersion = new Full(idFun)
/** Cross-versions a module with the result of applying `remapVersion` to the full version
* (typically the full Scala version). See also [[sbt.CrossVersion.Full]]. */
def fullMapped(remapVersion: String => String): CrossVersion = new Full(remapVersion)
/**
* Cross-versions a module with the result of applying `remapVersion` to the full version
* (typically the full Scala version). See also [[sbt.CrossVersion.Full]].
*/
def fullMapped(remapVersion: String => String): CrossVersion = new Full(remapVersion)
/** Cross-versions a module with the binary version (typically the binary Scala version). */
def binary: CrossVersion = new Binary(idFun)
/** Cross-versions a module with the binary version (typically the binary Scala version). */
def binary: CrossVersion = new Binary(idFun)
/** Cross-versions a module with the result of applying `remapVersion` to the binary version
* (typically the binary Scala version). See also [[sbt.CrossVersion.Binary]]. */
def binaryMapped(remapVersion: String => String): CrossVersion = new Binary(remapVersion)
/**
* Cross-versions a module with the result of applying `remapVersion` to the binary version
* (typically the binary Scala version). See also [[sbt.CrossVersion.Binary]].
*/
def binaryMapped(remapVersion: String => String): CrossVersion = new Binary(remapVersion)
private[this] def idFun[T]: T => T = x => x
private[this] def idFun[T]: T => T = x => x
@deprecated("Will be made private.", "0.13.1")
def append(s: String): Option[String => String] = Some(x => crossName(x, s))
@deprecated("Will be made private.", "0.13.1")
def append(s: String): Option[String => String] = Some(x => crossName(x, s))
/** Construct a cross-versioning function given cross-versioning configuration `cross`,
* full version `fullVersion` and binary version `binaryVersion`. The behavior of the
* constructed function is as documented for the [[sbt.CrossVersion]] datatypes. */
def apply(cross: CrossVersion, fullVersion: String, binaryVersion: String): Option[String => String] =
cross match
{
case Disabled => None
case b: Binary => append(b.remapVersion(binaryVersion))
case f: Full => append(f.remapVersion(fullVersion))
}
/**
* Construct a cross-versioning function given cross-versioning configuration `cross`,
* full version `fullVersion` and binary version `binaryVersion`. The behavior of the
* constructed function is as documented for the [[sbt.CrossVersion]] datatypes.
*/
def apply(cross: CrossVersion, fullVersion: String, binaryVersion: String): Option[String => String] =
cross match {
case Disabled => None
case b: Binary => append(b.remapVersion(binaryVersion))
case f: Full => append(f.remapVersion(fullVersion))
}
/** Constructs the cross-version function defined by `module` and `is`, if one is configured. */
def apply(module: ModuleID, is: IvyScala): Option[String => String] =
CrossVersion(module.crossVersion, is.scalaFullVersion, is.scalaBinaryVersion)
/** Constructs the cross-version function defined by `module` and `is`, if one is configured. */
def apply(module: ModuleID, is: IvyScala): Option[String => String] =
CrossVersion(module.crossVersion, is.scalaFullVersion, is.scalaBinaryVersion)
/** Constructs the cross-version function defined by `module` and `is`, if one is configured. */
def apply(module: ModuleID, is: Option[IvyScala]): Option[String => String] =
is flatMap { i => apply(module, i) }
/** Constructs the cross-version function defined by `module` and `is`, if one is configured. */
def apply(module: ModuleID, is: Option[IvyScala]): Option[String => String] =
is flatMap { i => apply(module, i) }
/** Cross-version each `Artifact` in `artifacts` according to cross-version function `cross`. */
def substituteCross(artifacts: Seq[Artifact], cross: Option[String => String]): Seq[Artifact] =
cross match {
case None => artifacts
case Some(is) => substituteCrossA(artifacts, cross)
}
/** Cross-version each `Artifact` in `artifacts` according to cross-version function `cross`. */
def substituteCross(artifacts: Seq[Artifact], cross: Option[String => String]): Seq[Artifact] =
cross match {
case None => artifacts
case Some(is) => substituteCrossA(artifacts, cross)
}
@deprecated("Will be made private.", "0.13.1")
def applyCross(s: String, fopt: Option[String => String]): String =
fopt match {
case None => s
case Some(fopt) => fopt(s)
}
@deprecated("Will be made private.", "0.13.1")
def applyCross(s: String, fopt: Option[String => String]): String =
fopt match {
case None => s
case Some(fopt) => fopt(s)
}
@deprecated("Will be made private.", "0.13.1")
def crossName(name: String, cross: String): String =
name + "_" + cross
@deprecated("Will be made private.", "0.13.1")
def crossName(name: String, cross: String): String =
name + "_" + cross
/** Cross-versions `a` according to cross-version function `cross`. */
def substituteCross(a: Artifact, cross: Option[String => String]): Artifact =
a.copy(name = applyCross(a.name, cross))
/** Cross-versions `a` according to cross-version function `cross`. */
def substituteCross(a: Artifact, cross: Option[String => String]): Artifact =
a.copy(name = applyCross(a.name, cross))
@deprecated("Will be made private.", "0.13.1")
def substituteCrossA(as: Seq[Artifact], cross: Option[String => String]): Seq[Artifact] =
as.map(art => substituteCross(art, cross))
@deprecated("Will be made private.", "0.13.1")
def substituteCrossA(as: Seq[Artifact], cross: Option[String => String]): Seq[Artifact] =
as.map(art => substituteCross(art, cross))
/** Constructs a function that will cross-version a ModuleID
* for the given full and binary Scala versions `scalaFullVersion` and `scalaBinaryVersion`
* according to the ModuleID's cross-versioning setting. */
def apply(scalaFullVersion: String, scalaBinaryVersion: String): ModuleID => ModuleID = m =>
{
val cross = apply(m.crossVersion, scalaFullVersion, scalaBinaryVersion)
if(cross.isDefined)
m.copy(name = applyCross(m.name, cross), explicitArtifacts = substituteCrossA(m.explicitArtifacts, cross))
else
m
}
/**
* Constructs a function that will cross-version a ModuleID
* for the given full and binary Scala versions `scalaFullVersion` and `scalaBinaryVersion`
* according to the ModuleID's cross-versioning setting.
*/
def apply(scalaFullVersion: String, scalaBinaryVersion: String): ModuleID => ModuleID = m =>
{
val cross = apply(m.crossVersion, scalaFullVersion, scalaBinaryVersion)
if (cross.isDefined)
m.copy(name = applyCross(m.name, cross), explicitArtifacts = substituteCrossA(m.explicitArtifacts, cross))
else
m
}
@deprecated("Use CrossVersion.isScalaApiCompatible or CrossVersion.isSbtApiCompatible", "0.13.0")
def isStable(v: String): Boolean = isScalaApiCompatible(v)
@deprecated("Use CrossVersion.isScalaApiCompatible or CrossVersion.isSbtApiCompatible", "0.13.0")
def isStable(v: String): Boolean = isScalaApiCompatible(v)
@deprecated("Use CrossVersion.scalaApiVersion or CrossVersion.sbtApiVersion", "0.13.0")
def selectVersion(full: String, binary: String): String = if(isStable(full)) binary else full
@deprecated("Use CrossVersion.scalaApiVersion or CrossVersion.sbtApiVersion", "0.13.0")
def selectVersion(full: String, binary: String): String = if (isStable(full)) binary else full
def isSbtApiCompatible(v: String): Boolean = CrossVersionUtil.isSbtApiCompatible(v)
def isSbtApiCompatible(v: String): Boolean = CrossVersionUtil.isSbtApiCompatible(v)
/** Returns sbt binary interface x.y API compatible with the given version string v.
* RCs for x.y.0 are considered API compatible.
* Compatibile versions include 0.12.0-1 and 0.12.0-RC1 for Some(0, 12).
*/
def sbtApiVersion(v: String): Option[(Int, Int)] = CrossVersionUtil.sbtApiVersion(v)
/**
* Returns sbt binary interface x.y API compatible with the given version string v.
* RCs for x.y.0 are considered API compatible.
* Compatibile versions include 0.12.0-1 and 0.12.0-RC1 for Some(0, 12).
*/
def sbtApiVersion(v: String): Option[(Int, Int)] = CrossVersionUtil.sbtApiVersion(v)
def isScalaApiCompatible(v: String): Boolean = CrossVersionUtil.isScalaApiCompatible(v)
def isScalaApiCompatible(v: String): Boolean = CrossVersionUtil.isScalaApiCompatible(v)
/** Returns Scala binary interface x.y API compatible with the given version string v.
* Compatibile versions include 2.10.0-1 and 2.10.1-M1 for Some(2, 10), but not 2.10.0-RC1. */
def scalaApiVersion(v: String): Option[(Int, Int)] = CrossVersionUtil.scalaApiVersion(v)
/**
* Returns Scala binary interface x.y API compatible with the given version string v.
* Compatibile versions include 2.10.0-1 and 2.10.1-M1 for Some(2, 10), but not 2.10.0-RC1.
*/
def scalaApiVersion(v: String): Option[(Int, Int)] = CrossVersionUtil.scalaApiVersion(v)
/** Regular expression that extracts the major and minor components of a version into matched groups 1 and 2.*/
val PartialVersion = CrossVersionUtil.PartialVersion
/** Regular expression that extracts the major and minor components of a version into matched groups 1 and 2.*/
val PartialVersion = CrossVersionUtil.PartialVersion
/** Extracts the major and minor components of a version string `s` or returns `None` if the version is improperly formatted. */
def partialVersion(s: String): Option[(Int,Int)] = CrossVersionUtil.partialVersion(s)
/** Extracts the major and minor components of a version string `s` or returns `None` if the version is improperly formatted. */
def partialVersion(s: String): Option[(Int, Int)] = CrossVersionUtil.partialVersion(s)
/** Computes the binary Scala version from the `full` version.
* Full Scala versions earlier than [[sbt.CrossVersion.TransitionScalaVersion]] are returned as is. */
def binaryScalaVersion(full: String): String = CrossVersionUtil.binaryScalaVersion(full)
/**
* Computes the binary Scala version from the `full` version.
* Full Scala versions earlier than [[sbt.CrossVersion.TransitionScalaVersion]] are returned as is.
*/
def binaryScalaVersion(full: String): String = CrossVersionUtil.binaryScalaVersion(full)
/** Computes the binary sbt version from the `full` version.
* Full sbt versions earlier than [[sbt.CrossVersion.TransitionSbtVersion]] are returned as is. */
def binarySbtVersion(full: String): String = CrossVersionUtil.binarySbtVersion(full)
/**
* Computes the binary sbt version from the `full` version.
* Full sbt versions earlier than [[sbt.CrossVersion.TransitionSbtVersion]] are returned as is.
*/
def binarySbtVersion(full: String): String = CrossVersionUtil.binarySbtVersion(full)
@deprecated("Use CrossVersion.scalaApiVersion or CrossVersion.sbtApiVersion", "0.13.0")
def binaryVersion(full: String, cutoff: String): String = CrossVersionUtil.binaryVersion(full, cutoff)
@deprecated("Use CrossVersion.scalaApiVersion or CrossVersion.sbtApiVersion", "0.13.0")
def binaryVersion(full: String, cutoff: String): String = CrossVersionUtil.binaryVersion(full, cutoff)
}

View File

@ -1,224 +1,222 @@
package sbt
import org.apache.ivy.{core, plugins, util}
import core.module.id.ModuleRevisionId
import core.module.descriptor.{DefaultArtifact, DefaultExtendsDescriptor, DefaultModuleDescriptor, ModuleDescriptor}
import core.module.descriptor.{DefaultDependencyDescriptor, DependencyDescriptor}
import plugins.parser.{m2, ModuleDescriptorParser, ModuleDescriptorParserRegistry, ParserSettings}
import m2.{PomModuleDescriptorBuilder, PomModuleDescriptorParser}
import plugins.repository.Resource
import plugins.namespace.NamespaceTransformer
import util.extendable.ExtendableItem
import org.apache.ivy.{ core, plugins, util }
import core.module.id.ModuleRevisionId
import core.module.descriptor.{ DefaultArtifact, DefaultExtendsDescriptor, DefaultModuleDescriptor, ModuleDescriptor }
import core.module.descriptor.{ DefaultDependencyDescriptor, DependencyDescriptor }
import plugins.parser.{ m2, ModuleDescriptorParser, ModuleDescriptorParserRegistry, ParserSettings }
import m2.{ PomModuleDescriptorBuilder, PomModuleDescriptorParser }
import plugins.repository.Resource
import plugins.namespace.NamespaceTransformer
import util.extendable.ExtendableItem
import java.io.{File, InputStream}
import java.net.URL
import java.util.regex.Pattern
import java.io.{ File, InputStream }
import java.net.URL
import java.util.regex.Pattern
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))
override def parseDescriptor(ivySettings: ParserSettings, descriptorURL: URL, res: Resource, validate: Boolean) =
transform(this, delegate.parseDescriptor(ivySettings, descriptorURL, res, validate))
override def toIvyFile(is: InputStream, res: Resource, destFile: File, md: ModuleDescriptor) = delegate.toIvyFile(is, res, destFile, md)
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))
override def accept(res: Resource) = delegate.accept(res)
override def getType() = delegate.getType()
override def getMetadataArtifact(mrid: ModuleRevisionId, res: Resource) = delegate.getMetadataArtifact(mrid, res)
override def parseDescriptor(ivySettings: ParserSettings, descriptorURL: URL, res: Resource, validate: Boolean) =
transform(this, delegate.parseDescriptor(ivySettings, descriptorURL, res, validate))
override def toIvyFile(is: InputStream, res: Resource, destFile: File, md: ModuleDescriptor) = delegate.toIvyFile(is, res, destFile, md)
override def accept(res: Resource) = delegate.accept(res)
override def getType() = delegate.getType()
override def getMetadataArtifact(mrid: ModuleRevisionId, res: Resource) = delegate.getMetadataArtifact(mrid, res)
}
object CustomPomParser
{
/** 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"
object CustomPomParser {
/** 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 SbtVersionKey = "sbtVersion"
val ScalaVersionKey = "scalaVersion"
val ExtraAttributesKey = "extraDependencyAttributes"
private[this] val unqualifiedKeys = Set(SbtVersionKey, ScalaVersionKey, ExtraAttributesKey, ApiURLKey)
val SbtVersionKey = "sbtVersion"
val ScalaVersionKey = "scalaVersion"
val ExtraAttributesKey = "extraDependencyAttributes"
private[this] val unqualifiedKeys = Set(SbtVersionKey, ScalaVersionKey, ExtraAttributesKey, ApiURLKey)
// packagings that should be jars, but that Ivy doesn't handle as jars
val JarPackagings = Set("eclipse-plugin", "hk2-jar", "orbit")
val default = new CustomPomParser(PomModuleDescriptorParser.getInstance, defaultTransform)
// packagings that should be jars, but that Ivy doesn't handle as jars
val JarPackagings = Set("eclipse-plugin", "hk2-jar", "orbit")
val default = new CustomPomParser(PomModuleDescriptorParser.getInstance, defaultTransform)
private[this] val TransformedHashKey = "e:sbtTransformHash"
// A hash of the parameters transformation is based on.
// If a descriptor has a different hash, we need to retransform it.
private[this] val TransformHash: String = hash((unqualifiedKeys ++ JarPackagings).toSeq.sorted)
private[this] def hash(ss: Seq[String]): String = Hash.toHex(Hash(ss.flatMap(_ getBytes "UTF-8").toArray))
private[this] val TransformedHashKey = "e:sbtTransformHash"
// A hash of the parameters transformation is based on.
// If a descriptor has a different hash, we need to retransform it.
private[this] val TransformHash: String = hash((unqualifiedKeys ++ JarPackagings).toSeq.sorted)
private[this] def hash(ss: Seq[String]): String = Hash.toHex(Hash(ss.flatMap(_ getBytes "UTF-8").toArray))
// Unfortunately, ModuleDescriptorParserRegistry is add-only and is a singleton instance.
lazy val registerDefault: Unit = ModuleDescriptorParserRegistry.getInstance.addParser(default)
// Unfortunately, ModuleDescriptorParserRegistry is add-only and is a singleton instance.
lazy val registerDefault: Unit = ModuleDescriptorParserRegistry.getInstance.addParser(default)
def defaultTransform(parser: ModuleDescriptorParser, md: ModuleDescriptor): ModuleDescriptor =
if(transformedByThisVersion(md)) md else defaultTransformImpl(parser, md)
def defaultTransform(parser: ModuleDescriptorParser, md: ModuleDescriptor): ModuleDescriptor =
if (transformedByThisVersion(md)) md else defaultTransformImpl(parser, md)
private[this] def transformedByThisVersion(md: ModuleDescriptor): Boolean =
{
val oldTransformedHashKey = "sbtTransformHash"
val extraInfo = md.getExtraInfo
// sbt 0.13.1 used "sbtTransformHash" instead of "e:sbtTransformHash" until #1192 so read both
Option(extraInfo).isDefined &&
((Option(extraInfo get TransformedHashKey) orElse Option(extraInfo get oldTransformedHashKey)) match {
case Some(TransformHash) => true
case _ => false
})
}
private[this] def transformedByThisVersion(md: ModuleDescriptor): Boolean =
{
val oldTransformedHashKey = "sbtTransformHash"
val extraInfo = md.getExtraInfo
// sbt 0.13.1 used "sbtTransformHash" instead of "e:sbtTransformHash" until #1192 so read both
Option(extraInfo).isDefined &&
((Option(extraInfo get TransformedHashKey) orElse Option(extraInfo get oldTransformedHashKey)) match {
case Some(TransformHash) => true
case _ => false
})
}
private[this] def defaultTransformImpl(parser: ModuleDescriptorParser, md: ModuleDescriptor): ModuleDescriptor =
{
val properties = getPomProperties(md)
private[this] def defaultTransformImpl(parser: ModuleDescriptorParser, md: ModuleDescriptor): ModuleDescriptor =
{
val properties = getPomProperties(md)
// Extracts extra attributes (currently, sbt and Scala versions) stored in the <properties> element of the pom.
// These are attached to the module itself.
val filtered = shouldBeUnqualified(properties)
// Extracts extra attributes (currently, sbt and Scala versions) stored in the <properties> element of the pom.
// These are attached to the module itself.
val filtered = shouldBeUnqualified(properties)
// Extracts extra attributes for the dependencies.
// Because the <dependency> tag in pom.xml cannot include additional metadata,
// sbt includes extra attributes in a 'extraDependencyAttributes' property.
// This is read/written from/to a pure string (no element structure) because Ivy only
// parses the immediate text nodes of the property.
val extraDepAttributes = getDependencyExtra(filtered)
// Extracts extra attributes for the dependencies.
// Because the <dependency> tag in pom.xml cannot include additional metadata,
// sbt includes extra attributes in a 'extraDependencyAttributes' property.
// This is read/written from/to a pure string (no element structure) because Ivy only
// parses the immediate text nodes of the property.
val extraDepAttributes = getDependencyExtra(filtered)
// Fixes up the detected extension in some cases missed by Ivy.
val convertArtifacts = artifactExtIncorrect(md)
// Fixes up the detected extension in some cases missed by Ivy.
val convertArtifacts = artifactExtIncorrect(md)
// Merges artifact sections for duplicate dependency definitions
val mergeDuplicates = IvySbt.hasDuplicateDependencies(md.getDependencies)
// Merges artifact sections for duplicate dependency definitions
val mergeDuplicates = IvySbt.hasDuplicateDependencies(md.getDependencies)
val unqualify = toUnqualify(filtered)
if(unqualify.isEmpty && extraDepAttributes.isEmpty && !convertArtifacts && !mergeDuplicates)
md
else
addExtra(unqualify, extraDepAttributes, parser, md)
}
// The <properties> element of the pom is used to store additional metadata, such as for sbt plugins or for the base URL for API docs.
// This is done because the pom XSD does not appear to allow extra metadata anywhere else.
// The extra sbt plugin metadata in pom.xml does not need to be readable by maven, but the other information may be.
// However, the pom.xml needs to be valid in all cases because other tools like repository managers may read the pom.xml.
private[sbt] def getPomProperties(md: ModuleDescriptor): Map[String,String] =
{
import collection.JavaConverters._
PomModuleDescriptorBuilder.extractPomProperties(md.getExtraInfo).asInstanceOf[java.util.Map[String,String]].asScala.toMap
}
private[sbt] def toUnqualify(propertyAttributes: Map[String, String]): Map[String, String] =
(propertyAttributes - ExtraAttributesKey) map { case (k,v) => ("e:" + k, v) }
val unqualify = toUnqualify(filtered)
if (unqualify.isEmpty && extraDepAttributes.isEmpty && !convertArtifacts && !mergeDuplicates)
md
else
addExtra(unqualify, extraDepAttributes, parser, md)
}
// The <properties> element of the pom is used to store additional metadata, such as for sbt plugins or for the base URL for API docs.
// This is done because the pom XSD does not appear to allow extra metadata anywhere else.
// The extra sbt plugin metadata in pom.xml does not need to be readable by maven, but the other information may be.
// However, the pom.xml needs to be valid in all cases because other tools like repository managers may read the pom.xml.
private[sbt] def getPomProperties(md: ModuleDescriptor): Map[String, String] =
{
import collection.JavaConverters._
PomModuleDescriptorBuilder.extractPomProperties(md.getExtraInfo).asInstanceOf[java.util.Map[String, String]].asScala.toMap
}
private[sbt] def toUnqualify(propertyAttributes: Map[String, String]): Map[String, String] =
(propertyAttributes - ExtraAttributesKey) map { case (k, v) => ("e:" + k, v) }
private[this] def artifactExtIncorrect(md: ModuleDescriptor): Boolean =
md.getConfigurations.exists(conf => md.getArtifacts(conf.getName).exists(art => JarPackagings(art.getExt)))
private[this] def shouldBeUnqualified(m: Map[String, String]): Map[String, String] = m.filterKeys(unqualifiedKeys)
private[this] def condAddExtra(properties: Map[String, String], id: ModuleRevisionId): ModuleRevisionId =
if(properties.isEmpty) id else addExtra(properties, id)
private[this] def addExtra(properties: Map[String, String], id: ModuleRevisionId): ModuleRevisionId =
{
import collection.JavaConverters._
val oldExtra = qualifiedExtra(id)
val newExtra = (oldExtra ++ properties).asJava
ModuleRevisionId.newInstance(id.getOrganisation, id.getName, id.getBranch, id.getRevision, newExtra)
}
private[this] def artifactExtIncorrect(md: ModuleDescriptor): Boolean =
md.getConfigurations.exists(conf => md.getArtifacts(conf.getName).exists(art => JarPackagings(art.getExt)))
private[this] def shouldBeUnqualified(m: Map[String, String]): Map[String, String] = m.filterKeys(unqualifiedKeys)
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
}
private[this] def condAddExtra(properties: Map[String, String], id: ModuleRevisionId): ModuleRevisionId =
if (properties.isEmpty) id else addExtra(properties, id)
private[this] def addExtra(properties: Map[String, String], id: ModuleRevisionId): ModuleRevisionId =
{
import collection.JavaConverters._
val oldExtra = qualifiedExtra(id)
val newExtra = (oldExtra ++ properties).asJava
ModuleRevisionId.newInstance(id.getOrganisation, id.getName, id.getBranch, id.getRevision, newExtra)
}
def qualifiedExtra(item: ExtendableItem): Map[String,String] =
{
import 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 })
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
}
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
}
def qualifiedExtra(item: ExtendableItem): Map[String, String] =
{
import 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 })
// 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 writeDependencyExtra(s: Seq[DependencyDescriptor]): Seq[String] =
s.flatMap { dd =>
val revId = dd.getDependencyRevisionId
if (filterCustomExtra(revId, include = true).isEmpty)
Nil
else
revId.encodeToString :: Nil
}
private[this] val LinesP = Pattern.compile("(?m)^")
// 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 qualifiedIsExtra(k: String): Boolean = k.endsWith(ScalaVersionKey) || k.endsWith(SbtVersionKey)
private[this] val LinesP = Pattern.compile("(?m)^")
// 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 qualifiedIsExtra(k: String): Boolean = k.endsWith(ScalaVersionKey) || k.endsWith(SbtVersionKey)
private[this] def addExtra(dep: DependencyDescriptor, extra: Map[ModuleRevisionId, Map[String, String]]): DependencyDescriptor =
{
val extras = if(extra.isEmpty) None else extra get simplify(dep.getDependencyRevisionId)
extras match {
case None => dep
case Some(extraAttrs) => transform(dep, revId => addExtra(extraAttrs, revId))
}
}
private[this] def transform(dep: DependencyDescriptor, f: ModuleRevisionId => ModuleRevisionId): DependencyDescriptor =
DefaultDependencyDescriptor.transformInstance(dep, namespaceTransformer(dep.getDependencyRevisionId, f), false)
private[this] def extraTransformer(txId: ModuleRevisionId, extra: Map[String, String]): NamespaceTransformer =
namespaceTransformer(txId, revId => addExtra(extra, revId) )
// 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)
}
private[this] def namespaceTransformer(txId: ModuleRevisionId, f: ModuleRevisionId => ModuleRevisionId): NamespaceTransformer =
new NamespaceTransformer {
def transform(revId: ModuleRevisionId): ModuleRevisionId = if(revId == txId) f(revId) else revId
def isIdentity = false
}
private[this] def addExtra(dep: DependencyDescriptor, extra: Map[ModuleRevisionId, Map[String, String]]): DependencyDescriptor =
{
val extras = if (extra.isEmpty) None else extra get simplify(dep.getDependencyRevisionId)
extras match {
case None => dep
case Some(extraAttrs) => transform(dep, revId => addExtra(extraAttrs, revId))
}
}
private[this] def transform(dep: DependencyDescriptor, f: ModuleRevisionId => ModuleRevisionId): DependencyDescriptor =
DefaultDependencyDescriptor.transformInstance(dep, namespaceTransformer(dep.getDependencyRevisionId, f), false)
private[this] def extraTransformer(txId: ModuleRevisionId, extra: Map[String, String]): NamespaceTransformer =
namespaceTransformer(txId, revId => addExtra(extra, revId))
import collection.JavaConverters._
def addExtra(properties: Map[String, String], dependencyExtra: Map[ModuleRevisionId, Map[String,String]], parser: ModuleDescriptorParser, md: ModuleDescriptor): ModuleDescriptor =
{
val dmd = new DefaultModuleDescriptor(parser, md.getResource)
private[this] def namespaceTransformer(txId: ModuleRevisionId, f: ModuleRevisionId => ModuleRevisionId): NamespaceTransformer =
new NamespaceTransformer {
def transform(revId: ModuleRevisionId): ModuleRevisionId = if (revId == txId) f(revId) else revId
def isIdentity = false
}
val mrid = addExtra(properties, md.getModuleRevisionId)
val resolvedMrid = addExtra(properties, md.getResolvedModuleRevisionId)
dmd.setModuleRevisionId(mrid)
dmd.setResolvedModuleRevisionId(resolvedMrid)
import collection.JavaConverters._
def addExtra(properties: Map[String, String], dependencyExtra: Map[ModuleRevisionId, Map[String, String]], parser: ModuleDescriptorParser, md: ModuleDescriptor): ModuleDescriptor =
{
val dmd = new DefaultModuleDescriptor(parser, md.getResource)
dmd.setDefault(md.isDefault)
dmd.setHomePage(md.getHomePage)
dmd.setDescription(md.getDescription)
dmd.setLastModified(md.getLastModified)
dmd.setStatus(md.getStatus())
dmd.setPublicationDate(md.getPublicationDate())
dmd.setResolvedPublicationDate(md.getResolvedPublicationDate())
val mrid = addExtra(properties, md.getModuleRevisionId)
val resolvedMrid = addExtra(properties, md.getResolvedModuleRevisionId)
dmd.setModuleRevisionId(mrid)
dmd.setResolvedModuleRevisionId(resolvedMrid)
for(l <- md.getLicenses) dmd.addLicense(l)
for( (key,value) <- md.getExtraInfo.asInstanceOf[java.util.Map[String,String]].asScala ) dmd.addExtraInfo(key, value)
dmd.addExtraInfo(TransformedHashKey, TransformHash) // mark as transformed by this version, so we don't need to do it again
for( (key, value) <- md.getExtraAttributesNamespaces.asInstanceOf[java.util.Map[String,String]].asScala ) dmd.addExtraAttributeNamespace(key, value)
IvySbt.addExtraNamespace(dmd)
dmd.setDefault(md.isDefault)
dmd.setHomePage(md.getHomePage)
dmd.setDescription(md.getDescription)
dmd.setLastModified(md.getLastModified)
dmd.setStatus(md.getStatus())
dmd.setPublicationDate(md.getPublicationDate())
dmd.setResolvedPublicationDate(md.getResolvedPublicationDate())
val withExtra = md.getDependencies map { dd => addExtra(dd, dependencyExtra) }
val unique = IvySbt.mergeDuplicateDefinitions(withExtra)
unique foreach dmd.addDependency
for (l <- md.getLicenses) dmd.addLicense(l)
for ((key, value) <- md.getExtraInfo.asInstanceOf[java.util.Map[String, String]].asScala) dmd.addExtraInfo(key, value)
dmd.addExtraInfo(TransformedHashKey, TransformHash) // mark as transformed by this version, so we don't need to do it again
for ((key, value) <- md.getExtraAttributesNamespaces.asInstanceOf[java.util.Map[String, String]].asScala) dmd.addExtraAttributeNamespace(key, value)
IvySbt.addExtraNamespace(dmd)
for( ed <- md.getInheritedDescriptors) dmd.addInheritedDescriptor( new DefaultExtendsDescriptor( md, ed.getLocation, ed.getExtendsTypes) )
for( conf <- md.getConfigurations) {
dmd.addConfiguration(conf)
for(art <- md.getArtifacts(conf.getName)) {
val ext = art.getExt
val newExt = if( JarPackagings(ext) ) "jar" else ext
val nart = new DefaultArtifact(mrid, art.getPublicationDate, art.getName, art.getType, newExt, art.getUrl, art.getQualifiedExtraAttributes)
dmd.addArtifact(conf.getName, nart)
}
}
dmd
}
val withExtra = md.getDependencies map { dd => addExtra(dd, dependencyExtra) }
val unique = IvySbt.mergeDuplicateDefinitions(withExtra)
unique foreach dmd.addDependency
for (ed <- md.getInheritedDescriptors) dmd.addInheritedDescriptor(new DefaultExtendsDescriptor(md, ed.getLocation, ed.getExtendsTypes))
for (conf <- md.getConfigurations) {
dmd.addConfiguration(conf)
for (art <- md.getArtifacts(conf.getName)) {
val ext = art.getExt
val newExt = if (JarPackagings(ext)) "jar" else ext
val nart = new DefaultArtifact(mrid, art.getPublicationDate, art.getName, art.getType, newExt, art.getUrl, art.getQualifiedExtraAttributes)
dmd.addArtifact(conf.getName, nart)
}
}
dmd
}
}

View File

@ -6,33 +6,31 @@ package sbt
import java.io.ByteArrayInputStream
import java.net.URL
import org.apache.ivy.{core, plugins}
import core.module.descriptor.{DefaultDependencyDescriptor, DefaultModuleDescriptor}
import org.apache.ivy.{ core, plugins }
import core.module.descriptor.{ DefaultDependencyDescriptor, DefaultModuleDescriptor }
import core.settings.IvySettings
import plugins.parser.xml.XmlModuleDescriptorParser
import plugins.repository.Resource
import plugins.repository.url.URLResource
/** Subclasses the default Ivy file parser in order to provide access to protected methods.*/
private[sbt] object CustomXmlParser extends XmlModuleDescriptorParser
{
import XmlModuleDescriptorParser.Parser
class CustomParser(settings: IvySettings, defaultConfig: Option[String]) extends Parser(CustomXmlParser, settings)
{
def setSource(url: URL) =
{
super.setResource(new URLResource(url))
super.setInput(url)
}
def setInput(bytes: Array[Byte]) { setInput(new ByteArrayInputStream(bytes)) }
/** Overridden because the super implementation overwrites the module descriptor.*/
override def setResource(res: Resource) {}
override def setMd(md: DefaultModuleDescriptor) =
{
super.setMd(md)
if(defaultConfig.isDefined) setDefaultConfMapping("*->default(compile)")
}
override def parseDepsConfs(confs: String, dd: DefaultDependencyDescriptor) = super.parseDepsConfs(confs, dd)
override def getDefaultConf = defaultConfig.getOrElse(super.getDefaultConf)
}
private[sbt] object CustomXmlParser extends XmlModuleDescriptorParser {
import XmlModuleDescriptorParser.Parser
class CustomParser(settings: IvySettings, defaultConfig: Option[String]) extends Parser(CustomXmlParser, settings) {
def setSource(url: URL) =
{
super.setResource(new URLResource(url))
super.setInput(url)
}
def setInput(bytes: Array[Byte]) { setInput(new ByteArrayInputStream(bytes)) }
/** Overridden because the super implementation overwrites the module descriptor.*/
override def setResource(res: Resource) {}
override def setMd(md: DefaultModuleDescriptor) =
{
super.setMd(md)
if (defaultConfig.isDefined) setDefaultConfMapping("*->default(compile)")
}
override def parseDepsConfs(confs: String, dd: DefaultDependencyDescriptor) = super.parseDepsConfs(confs, dd)
override def getDefaultConf = defaultConfig.getOrElse(super.getDefaultConf)
}
}

View File

@ -3,65 +3,58 @@
*/
package sbt
trait DependencyFilterExtra
{
def moduleFilter(organization: NameFilter = AllPassFilter, name: NameFilter = AllPassFilter, revision: NameFilter = AllPassFilter): ModuleFilter =
new ModuleFilter {
def apply(m: ModuleID): Boolean = organization.accept(m.organization) && name.accept(m.name) && revision.accept(m.revision)
}
def artifactFilter(name: NameFilter = AllPassFilter, `type`: NameFilter = AllPassFilter, extension: NameFilter = AllPassFilter, classifier: NameFilter = AllPassFilter): ArtifactFilter =
new ArtifactFilter {
def apply(a: Artifact): Boolean = name.accept(a.name) && `type`.accept(a.`type`) && extension.accept(a.extension) && classifier.accept(a.classifier getOrElse "")
}
def configurationFilter(name: NameFilter = AllPassFilter): ConfigurationFilter =
new ConfigurationFilter {
def apply(c: String): Boolean = name.accept(c)
}
trait DependencyFilterExtra {
def moduleFilter(organization: NameFilter = AllPassFilter, name: NameFilter = AllPassFilter, revision: NameFilter = AllPassFilter): ModuleFilter =
new ModuleFilter {
def apply(m: ModuleID): Boolean = organization.accept(m.organization) && name.accept(m.name) && revision.accept(m.revision)
}
def artifactFilter(name: NameFilter = AllPassFilter, `type`: NameFilter = AllPassFilter, extension: NameFilter = AllPassFilter, classifier: NameFilter = AllPassFilter): ArtifactFilter =
new ArtifactFilter {
def apply(a: Artifact): Boolean = name.accept(a.name) && `type`.accept(a.`type`) && extension.accept(a.extension) && classifier.accept(a.classifier getOrElse "")
}
def configurationFilter(name: NameFilter = AllPassFilter): ConfigurationFilter =
new ConfigurationFilter {
def apply(c: String): Boolean = name.accept(c)
}
}
object DependencyFilter extends DependencyFilterExtra
{
def make(configuration: ConfigurationFilter = configurationFilter(), module: ModuleFilter = moduleFilter(), artifact: ArtifactFilter = artifactFilter()): DependencyFilter =
new DependencyFilter {
def apply(c: String, m: ModuleID, a: Artifact): Boolean = configuration(c) && module(m) && artifact(a)
}
def apply(x: DependencyFilter, y: DependencyFilter, combine: (Boolean, Boolean) => Boolean): DependencyFilter =
new DependencyFilter {
def apply(c: String, m: ModuleID, a: Artifact): Boolean = combine(x(c, m, a), y(c, m, a))
}
def allPass: DependencyFilter = configurationFilter()
implicit def fnToModuleFilter(f: ModuleID => Boolean): ModuleFilter = new ModuleFilter { def apply(m: ModuleID) = f(m) }
implicit def fnToArtifactFilter(f: Artifact => Boolean): ArtifactFilter = new ArtifactFilter { def apply(m: Artifact) = f(m) }
implicit def fnToConfigurationFilter(f: String => Boolean): ConfigurationFilter = new ConfigurationFilter { def apply(c: String) = f(c) }
implicit def subDepFilterToFn[Arg](f: SubDepFilter[Arg, _]): Arg => Boolean = f apply _
object DependencyFilter extends DependencyFilterExtra {
def make(configuration: ConfigurationFilter = configurationFilter(), module: ModuleFilter = moduleFilter(), artifact: ArtifactFilter = artifactFilter()): DependencyFilter =
new DependencyFilter {
def apply(c: String, m: ModuleID, a: Artifact): Boolean = configuration(c) && module(m) && artifact(a)
}
def apply(x: DependencyFilter, y: DependencyFilter, combine: (Boolean, Boolean) => Boolean): DependencyFilter =
new DependencyFilter {
def apply(c: String, m: ModuleID, a: Artifact): Boolean = combine(x(c, m, a), y(c, m, a))
}
def allPass: DependencyFilter = configurationFilter()
implicit def fnToModuleFilter(f: ModuleID => Boolean): ModuleFilter = new ModuleFilter { def apply(m: ModuleID) = f(m) }
implicit def fnToArtifactFilter(f: Artifact => Boolean): ArtifactFilter = new ArtifactFilter { def apply(m: Artifact) = f(m) }
implicit def fnToConfigurationFilter(f: String => Boolean): ConfigurationFilter = new ConfigurationFilter { def apply(c: String) = f(c) }
implicit def subDepFilterToFn[Arg](f: SubDepFilter[Arg, _]): Arg => Boolean = f apply _
}
trait DependencyFilter
{
def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean
final def &&(o: DependencyFilter) = DependencyFilter(this, o, _ && _)
final def ||(o: DependencyFilter) = DependencyFilter(this, o, _ || _)
final def -- (o: DependencyFilter) = DependencyFilter(this, o, _ && !_)
trait DependencyFilter {
def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean
final def &&(o: DependencyFilter) = DependencyFilter(this, o, _ && _)
final def ||(o: DependencyFilter) = DependencyFilter(this, o, _ || _)
final def --(o: DependencyFilter) = DependencyFilter(this, o, _ && !_)
}
sealed trait SubDepFilter[Arg, Self <: SubDepFilter[Arg, Self]] extends DependencyFilter
{ self: Self =>
def apply(a: Arg): Boolean
protected def make(f: Arg => Boolean): Self
final def &(o: Self): Self = combine(o, _ && _)
final def |(o: Self): Self = combine(o, _ || _)
final def -(o: Self): Self = combine(o, _ && !_)
private[this] def combine(o: Self, f: (Boolean, Boolean) => Boolean): Self = make( (m: Arg) => f(this(m), o(m)) )
sealed trait SubDepFilter[Arg, Self <: SubDepFilter[Arg, Self]] extends DependencyFilter { self: Self =>
def apply(a: Arg): Boolean
protected def make(f: Arg => Boolean): Self
final def &(o: Self): Self = combine(o, _ && _)
final def |(o: Self): Self = combine(o, _ || _)
final def -(o: Self): Self = combine(o, _ && !_)
private[this] def combine(o: Self, f: (Boolean, Boolean) => Boolean): Self = make((m: Arg) => f(this(m), o(m)))
}
trait ModuleFilter extends SubDepFilter[ModuleID, ModuleFilter]
{
protected final def make(f: ModuleID => Boolean) = new ModuleFilter { def apply(m: ModuleID) = f(m) }
final def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean = apply(module)
trait ModuleFilter extends SubDepFilter[ModuleID, ModuleFilter] {
protected final def make(f: ModuleID => Boolean) = new ModuleFilter { def apply(m: ModuleID) = f(m) }
final def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean = apply(module)
}
trait ArtifactFilter extends SubDepFilter[Artifact, ArtifactFilter]
{
protected final def make(f: Artifact => Boolean) = new ArtifactFilter { def apply(m: Artifact) = f(m) }
final def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean = apply(artifact)
trait ArtifactFilter extends SubDepFilter[Artifact, ArtifactFilter] {
protected final def make(f: Artifact => Boolean) = new ArtifactFilter { def apply(m: Artifact) = f(m) }
final def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean = apply(artifact)
}
trait ConfigurationFilter extends SubDepFilter[String, ConfigurationFilter]
{
protected final def make(f: String => Boolean) = new ConfigurationFilter { def apply(m: String) = f(m) }
final def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean = apply(configuration)
trait ConfigurationFilter extends SubDepFilter[String, ConfigurationFilter] {
protected final def make(f: String => Boolean) = new ConfigurationFilter { def apply(m: String) = f(m) }
final def apply(configuration: String, module: ModuleID, artifact: Artifact): Boolean = apply(configuration)
}

File diff suppressed because it is too large Load Diff

View File

@ -4,276 +4,269 @@
package sbt
import java.io.File
import scala.xml.{Node => XNode, NodeSeq}
import scala.xml.{ Node => XNode, NodeSeq }
import org.apache.ivy.{core, plugins, Ivy}
import core.{IvyPatternHelper, LogOptions}
import org.apache.ivy.{ core, plugins, Ivy }
import core.{ IvyPatternHelper, LogOptions }
import core.deliver.DeliverOptions
import core.install.InstallOptions
import core.module.descriptor.{Artifact => IArtifact, MDArtifact, ModuleDescriptor, DefaultModuleDescriptor}
import core.module.descriptor.{ Artifact => IArtifact, MDArtifact, ModuleDescriptor, DefaultModuleDescriptor }
import core.report.ResolveReport
import core.resolve.ResolveOptions
import plugins.resolver.{BasicResolver, DependencyResolver}
import plugins.resolver.{ BasicResolver, DependencyResolver }
final class DeliverConfiguration(val deliverIvyPattern: String, val status: String, val configurations: Option[Seq[Configuration]], val logging: UpdateLogging.Value)
final class PublishConfiguration(val ivyFile: Option[File], val resolverName: String, val artifacts: Map[Artifact, File], val checksums: Seq[String], val logging: UpdateLogging.Value,
val overwrite: Boolean) {
def this(ivyFile: Option[File], resolverName: String, artifacts: Map[Artifact, File], checksums: Seq[String], logging: UpdateLogging.Value) =
this(ivyFile, resolverName, artifacts, checksums, logging, false)
val overwrite: Boolean) {
def this(ivyFile: Option[File], resolverName: String, artifacts: Map[Artifact, File], checksums: Seq[String], logging: UpdateLogging.Value) =
this(ivyFile, resolverName, artifacts, checksums, logging, false)
}
final class UpdateConfiguration(val retrieve: Option[RetrieveConfiguration], val missingOk: Boolean, val logging: UpdateLogging.Value)
final class RetrieveConfiguration(val retrieveDirectory: File, val outputPattern: String)
final case class MakePomConfiguration(file: File, moduleInfo: ModuleInfo, configurations: Option[Seq[Configuration]] = None, extra: NodeSeq = NodeSeq.Empty, process: XNode => XNode = n => n, filterRepositories: MavenRepository => Boolean = _ => true, allRepositories: Boolean, includeTypes: Set[String] = Set(Artifact.DefaultType, Artifact.PomType))
// exclude is a map on a restricted ModuleID
// exclude is a map on a restricted ModuleID
final case class GetClassifiersConfiguration(module: GetClassifiersModule, exclude: Map[ModuleID, Set[String]], configuration: UpdateConfiguration, ivyScala: Option[IvyScala])
final case class GetClassifiersModule(id: ModuleID, modules: Seq[ModuleID], configurations: Seq[Configuration], classifiers: Seq[String])
/** Configures logging during an 'update'. `level` determines the amount of other information logged.
* `Full` is the default and logs the most.
* `DownloadOnly` only logs what is downloaded.
* `Quiet` only displays errors.*/
object UpdateLogging extends Enumeration
{
val Full, DownloadOnly, Quiet = Value
/**
* Configures logging during an 'update'. `level` determines the amount of other information logged.
* `Full` is the default and logs the most.
* `DownloadOnly` only logs what is downloaded.
* `Quiet` only displays errors.
*/
object UpdateLogging extends Enumeration {
val Full, DownloadOnly, Quiet = Value
}
object IvyActions
{
/** Installs the dependencies of the given 'module' from the resolver named 'from' to the resolver named 'to'.*/
def install(module: IvySbt#Module, from: String, to: String, log: Logger)
{
module.withModule(log) { (ivy, md, default) =>
for(dependency <- md.getDependencies)
{
log.info("Installing " + dependency)
val options = new InstallOptions
options.setValidate(module.moduleSettings.validate)
options.setTransitive(dependency.isTransitive)
ivy.install(dependency.getDependencyRevisionId, from, to, options)
}
}
}
object IvyActions {
/** Installs the dependencies of the given 'module' from the resolver named 'from' to the resolver named 'to'.*/
def install(module: IvySbt#Module, from: String, to: String, log: Logger) {
module.withModule(log) { (ivy, md, default) =>
for (dependency <- md.getDependencies) {
log.info("Installing " + dependency)
val options = new InstallOptions
options.setValidate(module.moduleSettings.validate)
options.setTransitive(dependency.isTransitive)
ivy.install(dependency.getDependencyRevisionId, from, to, options)
}
}
}
/** Clears the Ivy cache, as configured by 'config'. */
def cleanCache(ivy: IvySbt, log: Logger) = ivy.withIvy(log) { iv =>
iv.getSettings.getResolutionCacheManager.clean()
iv.getSettings.getRepositoryCacheManagers.foreach(_.clean())
}
/** Clears the Ivy cache, as configured by 'config'. */
def cleanCache(ivy: IvySbt, log: Logger) = ivy.withIvy(log) { iv =>
iv.getSettings.getResolutionCacheManager.clean()
iv.getSettings.getRepositoryCacheManagers.foreach(_.clean())
}
/** Creates a Maven pom from the given Ivy configuration*/
def makePom(module: IvySbt#Module, configuration: MakePomConfiguration, log: Logger)
{
import configuration.{allRepositories, moduleInfo, configurations, extra, file, filterRepositories, process, includeTypes}
module.withModule(log) { (ivy, md, default) =>
(new MakePom(log)).write(ivy, md, moduleInfo, configurations, includeTypes, extra, process, filterRepositories, allRepositories, file)
log.info("Wrote " + file.getAbsolutePath)
}
}
/** Creates a Maven pom from the given Ivy configuration*/
def makePom(module: IvySbt#Module, configuration: MakePomConfiguration, log: Logger) {
import configuration.{ allRepositories, moduleInfo, configurations, extra, file, filterRepositories, process, includeTypes }
module.withModule(log) { (ivy, md, default) =>
(new MakePom(log)).write(ivy, md, moduleInfo, configurations, includeTypes, extra, process, filterRepositories, allRepositories, file)
log.info("Wrote " + file.getAbsolutePath)
}
}
def deliver(module: IvySbt#Module, configuration: DeliverConfiguration, log: Logger): File =
{
import configuration._
module.withModule(log) { case (ivy, md, default) =>
val revID = md.getModuleRevisionId
val options = DeliverOptions.newInstance(ivy.getSettings).setStatus(status)
options.setConfs(IvySbt.getConfigurations(md, configurations))
ivy.deliver(revID, revID.getRevision, deliverIvyPattern, options)
deliveredFile(ivy, deliverIvyPattern, md)
}
}
def deliveredFile(ivy: Ivy, pattern: String, md: ModuleDescriptor): File =
ivy.getSettings.resolveFile(IvyPatternHelper.substitute(pattern, md.getResolvedModuleRevisionId))
def deliver(module: IvySbt#Module, configuration: DeliverConfiguration, log: Logger): File =
{
import configuration._
module.withModule(log) {
case (ivy, md, default) =>
val revID = md.getModuleRevisionId
val options = DeliverOptions.newInstance(ivy.getSettings).setStatus(status)
options.setConfs(IvySbt.getConfigurations(md, configurations))
ivy.deliver(revID, revID.getRevision, deliverIvyPattern, options)
deliveredFile(ivy, deliverIvyPattern, md)
}
}
def deliveredFile(ivy: Ivy, pattern: String, md: ModuleDescriptor): File =
ivy.getSettings.resolveFile(IvyPatternHelper.substitute(pattern, md.getResolvedModuleRevisionId))
def publish(module: IvySbt#Module, configuration: PublishConfiguration, log: Logger)
{
import configuration._
module.withModule(log) { case (ivy, md, default) =>
val resolver = ivy.getSettings.getResolver(resolverName)
if(resolver eq null) sys.error("Undefined resolver '" + resolverName + "'")
val ivyArtifact = ivyFile map { file => (MDArtifact.newIvyArtifact(md), file) }
val cross = crossVersionMap(module.moduleSettings)
val as = mapArtifacts(md, cross, artifacts) ++ ivyArtifact.toSeq
withChecksums(resolver, checksums) { publish(md, as, resolver, overwrite = overwrite) }
}
}
private[this] def withChecksums[T](resolver: DependencyResolver, checksums: Seq[String])(act: => T): T =
resolver match { case br: BasicResolver => withChecksums(br, checksums)(act); case _ => act }
private[this] def withChecksums[T](resolver: BasicResolver, checksums: Seq[String])(act: => T): T =
{
val previous = resolver.getChecksumAlgorithms
resolver.setChecksums(checksums mkString ",")
try { act }
finally { resolver.setChecksums(previous mkString ",") }
}
private def crossVersionMap(moduleSettings: ModuleSettings): Option[String => String] =
moduleSettings match {
case i: InlineConfiguration => CrossVersion(i.module, i.ivyScala)
case e: EmptyConfiguration => CrossVersion(e.module, e.ivyScala)
case _ => None
}
def mapArtifacts(module: ModuleDescriptor, cross: Option[String => String], artifacts: Map[Artifact, File]): Seq[(IArtifact, File)] =
{
val rawa = artifacts.keys.toSeq
val seqa = CrossVersion.substituteCross(rawa, cross)
val zipped = rawa zip IvySbt.mapArtifacts(module, seqa)
zipped map { case (a, ivyA) => (ivyA, artifacts(a)) }
}
/** Resolves and retrieves dependencies. 'ivyConfig' is used to produce an Ivy file and configuration.
* 'updateConfig' configures the actual resolution and retrieval process. */
def update(module: IvySbt#Module, configuration: UpdateConfiguration, log: Logger): UpdateReport =
module.withModule(log) { case (ivy, md, default) =>
val (report, err) = resolve(configuration.logging)(ivy, md, default)
err match
{
case Some(x) if !configuration.missingOk =>
processUnresolved(x, log)
throw x
case _ =>
val cachedDescriptor = ivy.getSettings.getResolutionCacheManager.getResolvedIvyFileInCache(md.getModuleRevisionId)
val uReport = IvyRetrieve.updateReport(report, cachedDescriptor)
configuration.retrieve match
{
case Some(rConf) => retrieve(ivy, uReport, rConf)
case None => uReport
}
}
}
def publish(module: IvySbt#Module, configuration: PublishConfiguration, log: Logger) {
import configuration._
module.withModule(log) {
case (ivy, md, default) =>
val resolver = ivy.getSettings.getResolver(resolverName)
if (resolver eq null) sys.error("Undefined resolver '" + resolverName + "'")
val ivyArtifact = ivyFile map { file => (MDArtifact.newIvyArtifact(md), file) }
val cross = crossVersionMap(module.moduleSettings)
val as = mapArtifacts(md, cross, artifacts) ++ ivyArtifact.toSeq
withChecksums(resolver, checksums) { publish(md, as, resolver, overwrite = overwrite) }
}
}
private[this] def withChecksums[T](resolver: DependencyResolver, checksums: Seq[String])(act: => T): T =
resolver match { case br: BasicResolver => withChecksums(br, checksums)(act); case _ => act }
private[this] def withChecksums[T](resolver: BasicResolver, checksums: Seq[String])(act: => T): T =
{
val previous = resolver.getChecksumAlgorithms
resolver.setChecksums(checksums mkString ",")
try { act }
finally { resolver.setChecksums(previous mkString ",") }
}
private def crossVersionMap(moduleSettings: ModuleSettings): Option[String => String] =
moduleSettings match {
case i: InlineConfiguration => CrossVersion(i.module, i.ivyScala)
case e: EmptyConfiguration => CrossVersion(e.module, e.ivyScala)
case _ => None
}
def mapArtifacts(module: ModuleDescriptor, cross: Option[String => String], artifacts: Map[Artifact, File]): Seq[(IArtifact, File)] =
{
val rawa = artifacts.keys.toSeq
val seqa = CrossVersion.substituteCross(rawa, cross)
val zipped = rawa zip IvySbt.mapArtifacts(module, seqa)
zipped map { case (a, ivyA) => (ivyA, artifacts(a)) }
}
/**
* Resolves and retrieves dependencies. 'ivyConfig' is used to produce an Ivy file and configuration.
* 'updateConfig' configures the actual resolution and retrieval process.
*/
def update(module: IvySbt#Module, configuration: UpdateConfiguration, log: Logger): UpdateReport =
module.withModule(log) {
case (ivy, md, default) =>
val (report, err) = resolve(configuration.logging)(ivy, md, default)
err match {
case Some(x) if !configuration.missingOk =>
processUnresolved(x, log)
throw x
case _ =>
val cachedDescriptor = ivy.getSettings.getResolutionCacheManager.getResolvedIvyFileInCache(md.getModuleRevisionId)
val uReport = IvyRetrieve.updateReport(report, cachedDescriptor)
configuration.retrieve match {
case Some(rConf) => retrieve(ivy, uReport, rConf)
case None => uReport
}
}
}
def processUnresolved(err: ResolveException, log: Logger)
{
val withExtra = err.failed.filter(!_.extraDependencyAttributes.isEmpty)
if(!withExtra.isEmpty)
{
log.warn("\n\tNote: Some unresolved dependencies have extra attributes. Check that these dependencies exist with the requested attributes.")
withExtra foreach { id => log.warn("\t\t" + id) }
log.warn("")
}
}
def groupedConflicts[T](moduleFilter: ModuleFilter, grouping: ModuleID => T)(report: UpdateReport): Map[T, Set[String]] =
report.configurations.flatMap { confReport =>
val evicted = confReport.evicted.filter(moduleFilter)
val evictedSet = evicted.map( m => (m.organization, m.name) ).toSet
val conflicted = confReport.allModules.filter( mod => evictedSet( (mod.organization, mod.name) ) )
grouped(grouping)(conflicted ++ evicted)
} toMap;
def processUnresolved(err: ResolveException, log: Logger) {
val withExtra = err.failed.filter(!_.extraDependencyAttributes.isEmpty)
if (!withExtra.isEmpty) {
log.warn("\n\tNote: Some unresolved dependencies have extra attributes. Check that these dependencies exist with the requested attributes.")
withExtra foreach { id => log.warn("\t\t" + id) }
log.warn("")
}
}
def groupedConflicts[T](moduleFilter: ModuleFilter, grouping: ModuleID => T)(report: UpdateReport): Map[T, Set[String]] =
report.configurations.flatMap { confReport =>
val evicted = confReport.evicted.filter(moduleFilter)
val evictedSet = evicted.map(m => (m.organization, m.name)).toSet
val conflicted = confReport.allModules.filter(mod => evictedSet((mod.organization, mod.name)))
grouped(grouping)(conflicted ++ evicted)
} toMap;
def grouped[T](grouping: ModuleID => T)(mods: Seq[ModuleID]): Map[T, Set[String]] =
mods groupBy(grouping) mapValues(_.map(_.revision).toSet)
def grouped[T](grouping: ModuleID => T)(mods: Seq[ModuleID]): Map[T, Set[String]] =
mods groupBy (grouping) mapValues (_.map(_.revision).toSet)
def transitiveScratch(ivySbt: IvySbt, label: String, config: GetClassifiersConfiguration, log: Logger): UpdateReport =
{
import config.{configuration => c, ivyScala, module => mod}
import mod.{id, modules => deps}
val base = restrictedCopy(id, true).copy(name = id.name + "$" + label)
val module = new ivySbt.Module(InlineConfiguration(base, ModuleInfo(base.name), deps).copy(ivyScala = ivyScala))
val report = update(module, c, log)
val newConfig = config.copy(module = mod.copy(modules = report.allModules))
updateClassifiers(ivySbt, newConfig, log)
}
def updateClassifiers(ivySbt: IvySbt, config: GetClassifiersConfiguration, log: Logger): UpdateReport =
{
import config.{configuration => c, module => mod, _}
import mod.{configurations => confs, _}
assert(!classifiers.isEmpty, "classifiers cannot be empty")
val baseModules = modules map { m => restrictedCopy(m, true) }
val deps = baseModules.distinct flatMap classifiedArtifacts(classifiers, exclude)
val base = restrictedCopy(id, true).copy(name = id.name + classifiers.mkString("$","_",""))
val module = new ivySbt.Module(InlineConfiguration(base, ModuleInfo(base.name), deps).copy(ivyScala = ivyScala, configurations = confs))
val upConf = new UpdateConfiguration(c.retrieve, true, c.logging)
update(module, upConf, log)
}
def classifiedArtifacts(classifiers: Seq[String], exclude: Map[ModuleID, Set[String]])(m: ModuleID): Option[ModuleID] =
{
val excluded = exclude getOrElse(restrictedCopy(m, false), Set.empty)
val included = classifiers filterNot excluded
if(included.isEmpty) None else Some(m.copy(isTransitive = false, explicitArtifacts = classifiedArtifacts(m.name, included) ))
}
def addExcluded(report: UpdateReport, classifiers: Seq[String], exclude: Map[ModuleID, Set[String]]): UpdateReport =
report.addMissing { id => classifiedArtifacts(id.name, classifiers filter getExcluded(id, exclude)) }
def classifiedArtifacts(name: String, classifiers: Seq[String]): Seq[Artifact] =
classifiers map { c => Artifact.classified(name, c) }
private[this] def getExcluded(id: ModuleID, exclude: Map[ModuleID, Set[String]]): Set[String] =
exclude.getOrElse(restrictedCopy(id, false), Set.empty[String])
def transitiveScratch(ivySbt: IvySbt, label: String, config: GetClassifiersConfiguration, log: Logger): UpdateReport =
{
import config.{ configuration => c, ivyScala, module => mod }
import mod.{ id, modules => deps }
val base = restrictedCopy(id, true).copy(name = id.name + "$" + label)
val module = new ivySbt.Module(InlineConfiguration(base, ModuleInfo(base.name), deps).copy(ivyScala = ivyScala))
val report = update(module, c, log)
val newConfig = config.copy(module = mod.copy(modules = report.allModules))
updateClassifiers(ivySbt, newConfig, log)
}
def updateClassifiers(ivySbt: IvySbt, config: GetClassifiersConfiguration, log: Logger): UpdateReport =
{
import config.{ configuration => c, module => mod, _ }
import mod.{ configurations => confs, _ }
assert(!classifiers.isEmpty, "classifiers cannot be empty")
val baseModules = modules map { m => restrictedCopy(m, true) }
val deps = baseModules.distinct flatMap classifiedArtifacts(classifiers, exclude)
val base = restrictedCopy(id, true).copy(name = id.name + classifiers.mkString("$", "_", ""))
val module = new ivySbt.Module(InlineConfiguration(base, ModuleInfo(base.name), deps).copy(ivyScala = ivyScala, configurations = confs))
val upConf = new UpdateConfiguration(c.retrieve, true, c.logging)
update(module, upConf, log)
}
def classifiedArtifacts(classifiers: Seq[String], exclude: Map[ModuleID, Set[String]])(m: ModuleID): Option[ModuleID] =
{
val excluded = exclude getOrElse (restrictedCopy(m, false), Set.empty)
val included = classifiers filterNot excluded
if (included.isEmpty) None else Some(m.copy(isTransitive = false, explicitArtifacts = classifiedArtifacts(m.name, included)))
}
def addExcluded(report: UpdateReport, classifiers: Seq[String], exclude: Map[ModuleID, Set[String]]): UpdateReport =
report.addMissing { id => classifiedArtifacts(id.name, classifiers filter getExcluded(id, exclude)) }
def classifiedArtifacts(name: String, classifiers: Seq[String]): Seq[Artifact] =
classifiers map { c => Artifact.classified(name, c) }
private[this] def getExcluded(id: ModuleID, exclude: Map[ModuleID, Set[String]]): Set[String] =
exclude.getOrElse(restrictedCopy(id, false), Set.empty[String])
def extractExcludes(report: UpdateReport): Map[ModuleID, Set[String]] =
report.allMissing flatMap { case (_, mod, art) => art.classifier.map { c => (restrictedCopy(mod, false), c) } } groupBy(_._1) map { case (mod, pairs) => (mod, pairs.map(_._2).toSet) }
def extractExcludes(report: UpdateReport): Map[ModuleID, Set[String]] =
report.allMissing flatMap { case (_, mod, art) => art.classifier.map { c => (restrictedCopy(mod, false), c) } } groupBy (_._1) map { case (mod, pairs) => (mod, pairs.map(_._2).toSet) }
private[this] def restrictedCopy(m: ModuleID, confs: Boolean) =
ModuleID(m.organization, m.name, m.revision, crossVersion = m.crossVersion, extraAttributes = m.extraAttributes, configurations = if(confs) m.configurations else None)
private[this] def resolve(logging: UpdateLogging.Value)(ivy: Ivy, module: DefaultModuleDescriptor, defaultConf: String): (ResolveReport, Option[ResolveException]) =
{
val resolveOptions = new ResolveOptions
val resolveId = ResolveOptions.getDefaultResolveId(module)
resolveOptions.setResolveId(resolveId)
resolveOptions.setLog(ivyLogLevel(logging))
ResolutionCache.cleanModule(module.getModuleRevisionId, resolveId, ivy.getSettings.getResolutionCacheManager)
val resolveReport = ivy.resolve(module, resolveOptions)
val err =
if(resolveReport.hasError)
{
val messages = resolveReport.getAllProblemMessages.toArray.map(_.toString).distinct
val failed = resolveReport.getUnresolvedDependencies.map(node => IvyRetrieve.toModuleID(node.getId))
Some(new ResolveException(messages, failed))
}
else None
(resolveReport, err)
}
private def retrieve(ivy: Ivy, report: UpdateReport, config: RetrieveConfiguration): UpdateReport =
retrieve(ivy, report, config.retrieveDirectory, config.outputPattern)
private[this] def restrictedCopy(m: ModuleID, confs: Boolean) =
ModuleID(m.organization, m.name, m.revision, crossVersion = m.crossVersion, extraAttributes = m.extraAttributes, configurations = if (confs) m.configurations else None)
private[this] def resolve(logging: UpdateLogging.Value)(ivy: Ivy, module: DefaultModuleDescriptor, defaultConf: String): (ResolveReport, Option[ResolveException]) =
{
val resolveOptions = new ResolveOptions
val resolveId = ResolveOptions.getDefaultResolveId(module)
resolveOptions.setResolveId(resolveId)
resolveOptions.setLog(ivyLogLevel(logging))
ResolutionCache.cleanModule(module.getModuleRevisionId, resolveId, ivy.getSettings.getResolutionCacheManager)
val resolveReport = ivy.resolve(module, resolveOptions)
val err =
if (resolveReport.hasError) {
val messages = resolveReport.getAllProblemMessages.toArray.map(_.toString).distinct
val failed = resolveReport.getUnresolvedDependencies.map(node => IvyRetrieve.toModuleID(node.getId))
Some(new ResolveException(messages, failed))
} else None
(resolveReport, err)
}
private def retrieve(ivy: Ivy, report: UpdateReport, config: RetrieveConfiguration): UpdateReport =
retrieve(ivy, report, config.retrieveDirectory, config.outputPattern)
private def retrieve(ivy: Ivy, report: UpdateReport, base: File, pattern: String): UpdateReport =
{
val toCopy = new collection.mutable.HashSet[(File,File)]
val retReport = report retrieve { (conf, mid, art, cached) =>
val to = retrieveTarget(conf, mid, art, base, pattern)
toCopy += ((cached, to))
to
}
IO.copy( toCopy )
retReport
}
private def retrieveTarget(conf: String, mid: ModuleID, art: Artifact, base: File, pattern: String): File =
new File(base, substitute(conf, mid, art, pattern))
private def retrieve(ivy: Ivy, report: UpdateReport, base: File, pattern: String): UpdateReport =
{
val toCopy = new collection.mutable.HashSet[(File, File)]
val retReport = report retrieve { (conf, mid, art, cached) =>
val to = retrieveTarget(conf, mid, art, base, pattern)
toCopy += ((cached, to))
to
}
IO.copy(toCopy)
retReport
}
private def retrieveTarget(conf: String, mid: ModuleID, art: Artifact, base: File, pattern: String): File =
new File(base, substitute(conf, mid, art, pattern))
private def substitute(conf: String, mid: ModuleID, art: Artifact, pattern: String): String =
{
val mextra = IvySbt.javaMap(mid.extraAttributes, true)
val aextra = IvySbt.extra(art, true)
IvyPatternHelper.substitute(pattern, mid.organization, mid.name, mid.revision, art.name, art.`type`, art.extension, conf, mextra, aextra)
}
private def substitute(conf: String, mid: ModuleID, art: Artifact, pattern: String): String =
{
val mextra = IvySbt.javaMap(mid.extraAttributes, true)
val aextra = IvySbt.extra(art, true)
IvyPatternHelper.substitute(pattern, mid.organization, mid.name, mid.revision, art.name, art.`type`, art.extension, conf, mextra, aextra)
}
import UpdateLogging.{Quiet, Full, DownloadOnly}
import LogOptions.{LOG_QUIET, LOG_DEFAULT, LOG_DOWNLOAD_ONLY}
private def ivyLogLevel(level: UpdateLogging.Value) =
level match
{
case Quiet => LOG_QUIET
case DownloadOnly => LOG_DOWNLOAD_ONLY
case Full => LOG_DEFAULT
}
import UpdateLogging.{ Quiet, Full, DownloadOnly }
import LogOptions.{ LOG_QUIET, LOG_DEFAULT, LOG_DOWNLOAD_ONLY }
private def ivyLogLevel(level: UpdateLogging.Value) =
level match {
case Quiet => LOG_QUIET
case DownloadOnly => LOG_DOWNLOAD_ONLY
case Full => LOG_DEFAULT
}
def publish(module: ModuleDescriptor, artifacts: Seq[(IArtifact, File)], resolver: DependencyResolver, overwrite: Boolean): Unit =
{
if (artifacts.nonEmpty) {
checkFilesPresent(artifacts)
try {
resolver.beginPublishTransaction(module.getModuleRevisionId(), overwrite);
for( (artifact, file) <- artifacts)
resolver.publish(artifact, file, overwrite)
resolver.commitPublishTransaction()
} catch {
case e: Throwable =>
try { resolver.abortPublishTransaction() }
finally { throw e }
}
}
}
private[this] def checkFilesPresent(artifacts: Seq[(IArtifact, File)])
{
val missing = artifacts filter { case (a, file) => !file.exists }
if(missing.nonEmpty)
error("Missing files for publishing:\n\t" + missing.map(_._2.getAbsolutePath).mkString("\n\t"))
}
def publish(module: ModuleDescriptor, artifacts: Seq[(IArtifact, File)], resolver: DependencyResolver, overwrite: Boolean): Unit =
{
if (artifacts.nonEmpty) {
checkFilesPresent(artifacts)
try {
resolver.beginPublishTransaction(module.getModuleRevisionId(), overwrite);
for ((artifact, file) <- artifacts)
resolver.publish(artifact, file, overwrite)
resolver.commitPublishTransaction()
} catch {
case e: Throwable =>
try { resolver.abortPublishTransaction() }
finally { throw e }
}
}
}
private[this] def checkFilesPresent(artifacts: Seq[(IArtifact, File)]) {
val missing = artifacts filter { case (a, file) => !file.exists }
if (missing.nonEmpty)
error("Missing files for publishing:\n\t" + missing.map(_._2.getAbsolutePath).mkString("\n\t"))
}
}
final class ResolveException(val messages: Seq[String], val failed: Seq[ModuleID]) extends RuntimeException(messages.mkString("\n"))

View File

@ -6,102 +6,93 @@ package sbt
import java.io.File
import java.net.URL
import org.apache.ivy.{core, plugins, util}
import core.cache.{ArtifactOrigin, CacheDownloadOptions, DefaultRepositoryCacheManager}
import core.module.descriptor.{Artifact => IvyArtifact, DefaultArtifact}
import plugins.repository.file.{FileRepository=>IvyFileRepository, FileResource}
import plugins.repository.{ArtifactResourceResolver, Resource, ResourceDownloader}
import org.apache.ivy.{ core, plugins, util }
import core.cache.{ ArtifactOrigin, CacheDownloadOptions, DefaultRepositoryCacheManager }
import core.module.descriptor.{ Artifact => IvyArtifact, DefaultArtifact }
import plugins.repository.file.{ FileRepository => IvyFileRepository, FileResource }
import plugins.repository.{ ArtifactResourceResolver, Resource, ResourceDownloader }
import plugins.resolver.util.ResolvedResource
import util.FileUtil
class NotInCache(val id: ModuleID, cause: Throwable)
extends RuntimeException(NotInCache(id, cause), cause)
{
def this(id: ModuleID) = this(id, null)
extends RuntimeException(NotInCache(id, cause), cause) {
def this(id: ModuleID) = this(id, null)
}
private object NotInCache
{
def apply(id: ModuleID, cause: Throwable) =
{
val postfix = if(cause == null) "" else (": " +cause.toString)
"File for " + id + " not in cache" + postfix
}
private object NotInCache {
def apply(id: ModuleID, cause: Throwable) =
{
val postfix = if (cause == null) "" else (": " + cause.toString)
"File for " + id + " not in cache" + postfix
}
}
/** Provides methods for working at the level of a single jar file with the default Ivy cache.*/
class IvyCache(val ivyHome: Option[File])
{
def lockFile = new File(ivyHome getOrElse Path.userHome, ".sbt.cache.lock")
/** Caches the given 'file' with the given ID. It may be retrieved or cleared using this ID.*/
def cacheJar(moduleID: ModuleID, file: File, lock: Option[xsbti.GlobalLock], log: Logger)
{
val artifact = defaultArtifact(moduleID)
val resolved = new ResolvedResource(new FileResource(new IvyFileRepository, file), moduleID.revision)
withDefaultCache(lock, log) { cache =>
val resolver = new ArtifactResourceResolver { def resolve(artifact: IvyArtifact) = resolved }
cache.download(artifact, resolver, new FileDownloader, new CacheDownloadOptions)
}
}
/** Clears the cache of the jar for the given ID.*/
def clearCachedJar(id: ModuleID, lock: Option[xsbti.GlobalLock], log: Logger)
{
try { withCachedJar(id, lock, log)(_.delete) }
catch { case e: Exception => log.debug("Error cleaning cached jar: " + e.toString) }
}
/** Copies the cached jar for the given ID to the directory 'toDirectory'. If the jar is not in the cache, NotInCache is thrown.*/
def retrieveCachedJar(id: ModuleID, toDirectory: File, lock: Option[xsbti.GlobalLock], log: Logger) =
withCachedJar(id, lock, log) { cachedFile =>
val copyTo = new File(toDirectory, cachedFile.getName)
FileUtil.copy(cachedFile, copyTo, null)
copyTo
}
class IvyCache(val ivyHome: Option[File]) {
def lockFile = new File(ivyHome getOrElse Path.userHome, ".sbt.cache.lock")
/** Caches the given 'file' with the given ID. It may be retrieved or cleared using this ID.*/
def cacheJar(moduleID: ModuleID, file: File, lock: Option[xsbti.GlobalLock], log: Logger) {
val artifact = defaultArtifact(moduleID)
val resolved = new ResolvedResource(new FileResource(new IvyFileRepository, file), moduleID.revision)
withDefaultCache(lock, log) { cache =>
val resolver = new ArtifactResourceResolver { def resolve(artifact: IvyArtifact) = resolved }
cache.download(artifact, resolver, new FileDownloader, new CacheDownloadOptions)
}
}
/** Clears the cache of the jar for the given ID.*/
def clearCachedJar(id: ModuleID, lock: Option[xsbti.GlobalLock], log: Logger) {
try { withCachedJar(id, lock, log)(_.delete) }
catch { case e: Exception => log.debug("Error cleaning cached jar: " + e.toString) }
}
/** Copies the cached jar for the given ID to the directory 'toDirectory'. If the jar is not in the cache, NotInCache is thrown.*/
def retrieveCachedJar(id: ModuleID, toDirectory: File, lock: Option[xsbti.GlobalLock], log: Logger) =
withCachedJar(id, lock, log) { cachedFile =>
val copyTo = new File(toDirectory, cachedFile.getName)
FileUtil.copy(cachedFile, copyTo, null)
copyTo
}
/** Get the location of the cached jar for the given ID in the Ivy cache. If the jar is not in the cache, NotInCache is thrown .*/
def withCachedJar[T](id: ModuleID, lock: Option[xsbti.GlobalLock], log: Logger)(f: File => T): T =
{
val cachedFile =
try
{
withDefaultCache(lock, log) { cache =>
val artifact = defaultArtifact(id)
cache.getArchiveFileInCache(artifact, unknownOrigin(artifact))
}
}
catch { case e: Exception => throw new NotInCache(id, e) }
/** Get the location of the cached jar for the given ID in the Ivy cache. If the jar is not in the cache, NotInCache is thrown .*/
def withCachedJar[T](id: ModuleID, lock: Option[xsbti.GlobalLock], log: Logger)(f: File => T): T =
{
val cachedFile =
try {
withDefaultCache(lock, log) { cache =>
val artifact = defaultArtifact(id)
cache.getArchiveFileInCache(artifact, unknownOrigin(artifact))
}
} catch { case e: Exception => throw new NotInCache(id, e) }
if(cachedFile.exists) f(cachedFile) else throw new NotInCache(id)
}
/** Calls the given function with the default Ivy cache.*/
def withDefaultCache[T](lock: Option[xsbti.GlobalLock], log: Logger)(f: DefaultRepositoryCacheManager => T): T =
{
val (ivy, local) = basicLocalIvy(lock, log)
ivy.withIvy(log) { ivy =>
val cache = ivy.getSettings.getDefaultRepositoryCacheManager.asInstanceOf[DefaultRepositoryCacheManager]
cache.setUseOrigin(false)
f(cache)
}
}
private def unknownOrigin(artifact: IvyArtifact) = ArtifactOrigin.unkwnown(artifact)
/** A minimal Ivy setup with only a local resolver and the current directory as the base directory.*/
private def basicLocalIvy(lock: Option[xsbti.GlobalLock], log: Logger) =
{
val local = Resolver.defaultLocal
val paths = new IvyPaths(new File("."), ivyHome)
val conf = new InlineIvyConfiguration(paths, Seq(local), Nil, Nil, false, lock, IvySbt.DefaultChecksums, None, log)
(new IvySbt(conf), local)
}
/** Creates a default jar artifact based on the given ID.*/
private def defaultArtifact(moduleID: ModuleID): IvyArtifact =
new DefaultArtifact(IvySbt.toID(moduleID), null, moduleID.name, "jar", "jar")
if (cachedFile.exists) f(cachedFile) else throw new NotInCache(id)
}
/** Calls the given function with the default Ivy cache.*/
def withDefaultCache[T](lock: Option[xsbti.GlobalLock], log: Logger)(f: DefaultRepositoryCacheManager => T): T =
{
val (ivy, local) = basicLocalIvy(lock, log)
ivy.withIvy(log) { ivy =>
val cache = ivy.getSettings.getDefaultRepositoryCacheManager.asInstanceOf[DefaultRepositoryCacheManager]
cache.setUseOrigin(false)
f(cache)
}
}
private def unknownOrigin(artifact: IvyArtifact) = ArtifactOrigin.unkwnown(artifact)
/** A minimal Ivy setup with only a local resolver and the current directory as the base directory.*/
private def basicLocalIvy(lock: Option[xsbti.GlobalLock], log: Logger) =
{
val local = Resolver.defaultLocal
val paths = new IvyPaths(new File("."), ivyHome)
val conf = new InlineIvyConfiguration(paths, Seq(local), Nil, Nil, false, lock, IvySbt.DefaultChecksums, None, log)
(new IvySbt(conf), local)
}
/** Creates a default jar artifact based on the given ID.*/
private def defaultArtifact(moduleID: ModuleID): IvyArtifact =
new DefaultArtifact(IvySbt.toID(moduleID), null, moduleID.name, "jar", "jar")
}
/** Required by Ivy for copying to the cache.*/
private class FileDownloader extends ResourceDownloader with NotNull
{
def download(artifact: IvyArtifact, resource: Resource, dest: File)
{
if(dest.exists()) dest.delete()
val part = new File(dest.getAbsolutePath + ".part")
FileUtil.copy(resource.openStream, part, null)
if(!part.renameTo(dest))
sys.error("Could not move temporary file " + part + " to final location " + dest)
}
private class FileDownloader extends ResourceDownloader with NotNull {
def download(artifact: IvyArtifact, resource: Resource, dest: File) {
if (dest.exists()) dest.delete()
val part = new File(dest.getAbsolutePath + ".part")
FileUtil.copy(resource.openStream, part, null)
if (!part.renameTo(dest))
sys.error("Could not move temporary file " + part + " to final location " + dest)
}
}

View File

@ -4,120 +4,104 @@
package sbt
import java.io.File
import java.net.{URI,URL}
import java.net.{ URI, URL }
import scala.xml.NodeSeq
final class IvyPaths(val baseDirectory: File, val ivyHome: Option[File])
{
def withBase(newBaseDirectory: File) = new IvyPaths(newBaseDirectory, ivyHome)
final class IvyPaths(val baseDirectory: File, val ivyHome: Option[File]) {
def withBase(newBaseDirectory: File) = new IvyPaths(newBaseDirectory, ivyHome)
}
sealed trait IvyConfiguration
{
type This <: IvyConfiguration
def lock: Option[xsbti.GlobalLock]
def baseDirectory: File
def log: Logger
def withBase(newBaseDirectory: File): This
sealed trait IvyConfiguration {
type This <: IvyConfiguration
def lock: Option[xsbti.GlobalLock]
def baseDirectory: File
def log: Logger
def withBase(newBaseDirectory: File): This
}
final class InlineIvyConfiguration(val paths: IvyPaths, val resolvers: Seq[Resolver], val otherResolvers: Seq[Resolver],
val moduleConfigurations: Seq[ModuleConfiguration], val localOnly: Boolean, val lock: Option[xsbti.GlobalLock],
val checksums: Seq[String], val resolutionCacheDir: Option[File], val log: Logger) extends IvyConfiguration
{
@deprecated("Use the variant that accepts the resolution cache location.", "0.13.0")
def this(paths: IvyPaths, resolvers: Seq[Resolver], otherResolvers: Seq[Resolver],
moduleConfigurations: Seq[ModuleConfiguration], localOnly: Boolean, lock: Option[xsbti.GlobalLock],
checksums: Seq[String], log: Logger) =
this(paths, resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, None, log)
val moduleConfigurations: Seq[ModuleConfiguration], val localOnly: Boolean, val lock: Option[xsbti.GlobalLock],
val checksums: Seq[String], val resolutionCacheDir: Option[File], val log: Logger) extends IvyConfiguration {
@deprecated("Use the variant that accepts the resolution cache location.", "0.13.0")
def this(paths: IvyPaths, resolvers: Seq[Resolver], otherResolvers: Seq[Resolver],
moduleConfigurations: Seq[ModuleConfiguration], localOnly: Boolean, lock: Option[xsbti.GlobalLock],
checksums: Seq[String], log: Logger) =
this(paths, resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, None, log)
type This = InlineIvyConfiguration
def baseDirectory = paths.baseDirectory
def withBase(newBase: File) = new InlineIvyConfiguration(paths.withBase(newBase), resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, resolutionCacheDir, log)
def changeResolvers(newResolvers: Seq[Resolver]) = new InlineIvyConfiguration(paths, newResolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, resolutionCacheDir, log)
type This = InlineIvyConfiguration
def baseDirectory = paths.baseDirectory
def withBase(newBase: File) = new InlineIvyConfiguration(paths.withBase(newBase), resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, resolutionCacheDir, log)
def changeResolvers(newResolvers: Seq[Resolver]) = new InlineIvyConfiguration(paths, newResolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, resolutionCacheDir, log)
}
final class ExternalIvyConfiguration(val baseDirectory: File, val uri: URI, val lock: Option[xsbti.GlobalLock], val extraResolvers: Seq[Resolver], val log: Logger) extends IvyConfiguration
{
type This = ExternalIvyConfiguration
def withBase(newBase: File) = new ExternalIvyConfiguration(newBase, uri, lock, extraResolvers, log)
final class ExternalIvyConfiguration(val baseDirectory: File, val uri: URI, val lock: Option[xsbti.GlobalLock], val extraResolvers: Seq[Resolver], val log: Logger) extends IvyConfiguration {
type This = ExternalIvyConfiguration
def withBase(newBase: File) = new ExternalIvyConfiguration(newBase, uri, lock, extraResolvers, log)
}
object ExternalIvyConfiguration
{
def apply(baseDirectory: File, file: File, lock: Option[xsbti.GlobalLock], log: Logger) = new ExternalIvyConfiguration(baseDirectory, file.toURI, lock, Nil, log)
object ExternalIvyConfiguration {
def apply(baseDirectory: File, file: File, lock: Option[xsbti.GlobalLock], log: Logger) = new ExternalIvyConfiguration(baseDirectory, file.toURI, lock, Nil, log)
}
object IvyConfiguration
{
/** Called to configure Ivy when inline resolvers are not specified.
* This will configure Ivy with an 'ivy-settings.xml' file if there is one or else use default resolvers.*/
@deprecated("Explicitly use either external or inline configuration.", "0.12.0")
def apply(paths: IvyPaths, lock: Option[xsbti.GlobalLock], localOnly: Boolean, checksums: Seq[String], log: Logger): IvyConfiguration =
{
log.debug("Autodetecting configuration.")
val defaultIvyConfigFile = IvySbt.defaultIvyConfiguration(paths.baseDirectory)
if(defaultIvyConfigFile.canRead)
ExternalIvyConfiguration(paths.baseDirectory, defaultIvyConfigFile, lock, log)
else
new InlineIvyConfiguration(paths, Resolver.withDefaultResolvers(Nil), Nil, Nil, localOnly, lock, checksums, None, log)
}
object IvyConfiguration {
/**
* Called to configure Ivy when inline resolvers are not specified.
* This will configure Ivy with an 'ivy-settings.xml' file if there is one or else use default resolvers.
*/
@deprecated("Explicitly use either external or inline configuration.", "0.12.0")
def apply(paths: IvyPaths, lock: Option[xsbti.GlobalLock], localOnly: Boolean, checksums: Seq[String], log: Logger): IvyConfiguration =
{
log.debug("Autodetecting configuration.")
val defaultIvyConfigFile = IvySbt.defaultIvyConfiguration(paths.baseDirectory)
if (defaultIvyConfigFile.canRead)
ExternalIvyConfiguration(paths.baseDirectory, defaultIvyConfigFile, lock, log)
else
new InlineIvyConfiguration(paths, Resolver.withDefaultResolvers(Nil), Nil, Nil, localOnly, lock, checksums, None, log)
}
}
sealed trait ModuleSettings
{
def validate: Boolean
def ivyScala: Option[IvyScala]
def noScala: ModuleSettings
sealed trait ModuleSettings {
def validate: Boolean
def ivyScala: Option[IvyScala]
def noScala: ModuleSettings
}
final case class IvyFileConfiguration(file: File, ivyScala: Option[IvyScala], validate: Boolean, autoScalaTools: Boolean = true) extends ModuleSettings
{
def noScala = copy(ivyScala = None)
final case class IvyFileConfiguration(file: File, ivyScala: Option[IvyScala], validate: Boolean, autoScalaTools: Boolean = true) extends ModuleSettings {
def noScala = copy(ivyScala = None)
}
final case class PomConfiguration(file: File, ivyScala: Option[IvyScala], validate: Boolean, autoScalaTools: Boolean = true) extends ModuleSettings
{
def noScala = copy(ivyScala = None)
final case class PomConfiguration(file: File, ivyScala: Option[IvyScala], validate: Boolean, autoScalaTools: Boolean = true) extends ModuleSettings {
def noScala = copy(ivyScala = None)
}
final case class InlineConfiguration(module: ModuleID, moduleInfo: ModuleInfo, dependencies: Seq[ModuleID], overrides: Set[ModuleID] = Set.empty, ivyXML: NodeSeq = NodeSeq.Empty, configurations: Seq[Configuration] = Nil, defaultConfiguration: Option[Configuration] = None, ivyScala: Option[IvyScala] = None, validate: Boolean = false, conflictManager: ConflictManager = ConflictManager.default) extends ModuleSettings
{
def withConfigurations(configurations: Seq[Configuration]) = copy(configurations = configurations)
def noScala = copy(ivyScala = None)
final case class InlineConfiguration(module: ModuleID, moduleInfo: ModuleInfo, dependencies: Seq[ModuleID], overrides: Set[ModuleID] = Set.empty, ivyXML: NodeSeq = NodeSeq.Empty, configurations: Seq[Configuration] = Nil, defaultConfiguration: Option[Configuration] = None, ivyScala: Option[IvyScala] = None, validate: Boolean = false, conflictManager: ConflictManager = ConflictManager.default) extends ModuleSettings {
def withConfigurations(configurations: Seq[Configuration]) = copy(configurations = configurations)
def noScala = copy(ivyScala = None)
}
@deprecated("Define a module using inline Scala (InlineConfiguration), a pom.xml (PomConfiguration), or an ivy.xml (IvyFileConfiguration).", "0.13.0")
final case class EmptyConfiguration(module: ModuleID, moduleInfo: ModuleInfo, ivyScala: Option[IvyScala], validate: Boolean) extends ModuleSettings
{
def noScala = copy(ivyScala = None)
final case class EmptyConfiguration(module: ModuleID, moduleInfo: ModuleInfo, ivyScala: Option[IvyScala], validate: Boolean) extends ModuleSettings {
def noScala = copy(ivyScala = None)
}
object InlineConfiguration
{
def configurations(explicitConfigurations: Iterable[Configuration], defaultConfiguration: Option[Configuration]) =
if(explicitConfigurations.isEmpty)
{
defaultConfiguration match
{
case Some(Configurations.DefaultIvyConfiguration) => Configurations.Default :: Nil
case Some(Configurations.DefaultMavenConfiguration) => Configurations.defaultMavenConfigurations
case _ => Nil
}
}
else
explicitConfigurations
object InlineConfiguration {
def configurations(explicitConfigurations: Iterable[Configuration], defaultConfiguration: Option[Configuration]) =
if (explicitConfigurations.isEmpty) {
defaultConfiguration match {
case Some(Configurations.DefaultIvyConfiguration) => Configurations.Default :: Nil
case Some(Configurations.DefaultMavenConfiguration) => Configurations.defaultMavenConfigurations
case _ => Nil
}
} else
explicitConfigurations
}
object ModuleSettings
{
@deprecated("Explicitly select configuration from pom.xml, ivy.xml, or inline Scala.", "0.13.0")
def apply(ivyScala: Option[IvyScala], validate: Boolean, module: => ModuleID, moduleInfo: => ModuleInfo)(baseDirectory: File, log: Logger): ModuleSettings =
{
log.debug("Autodetecting dependencies.")
val defaultPOMFile = IvySbt.defaultPOM(baseDirectory)
if(defaultPOMFile.canRead)
new PomConfiguration(defaultPOMFile, ivyScala, validate, true)
else
{
val defaultIvy = IvySbt.defaultIvyFile(baseDirectory)
if(defaultIvy.canRead)
new IvyFileConfiguration(defaultIvy, ivyScala, validate, true)
else
{
log.warn("No dependency configuration found, using defaults.")
new EmptyConfiguration(module, moduleInfo, ivyScala, validate)
}
}
}
object ModuleSettings {
@deprecated("Explicitly select configuration from pom.xml, ivy.xml, or inline Scala.", "0.13.0")
def apply(ivyScala: Option[IvyScala], validate: Boolean, module: => ModuleID, moduleInfo: => ModuleInfo)(baseDirectory: File, log: Logger): ModuleSettings =
{
log.debug("Autodetecting dependencies.")
val defaultPOMFile = IvySbt.defaultPOM(baseDirectory)
if (defaultPOMFile.canRead)
new PomConfiguration(defaultPOMFile, ivyScala, validate, true)
else {
val defaultIvy = IvySbt.defaultIvyFile(baseDirectory)
if (defaultIvy.canRead)
new IvyFileConfiguration(defaultIvy, ivyScala, validate, true)
else {
log.warn("No dependency configuration found, using defaults.")
new EmptyConfiguration(module, moduleInfo, ivyScala, validate)
}
}
}
}

View File

@ -4,18 +4,17 @@
package sbt
import java.io.File
import java.net.{URI, URL}
import java.net.{ URI, URL }
import scala.xml.NodeSeq
import org.apache.ivy.plugins.resolver.{DependencyResolver, IBiblioResolver}
import org.apache.ivy.plugins.resolver.{ DependencyResolver, IBiblioResolver }
import org.apache.ivy.util.url.CredentialsStore
/** Additional information about a project module */
final case class ModuleInfo(nameFormal: String, description: String = "", homepage: Option[URL] = None, startYear: Option[Int] = None, licenses: Seq[(String, URL)] = Nil, organizationName: String = "", organizationHomepage: Option[URL] = None, scmInfo: Option[ScmInfo] = None)
{
def formally(name: String) = copy(nameFormal = name)
def describing(desc: String, home: Option[URL]) = copy(description = desc, homepage = home)
def licensed(lics: (String, URL)*) = copy(licenses = lics)
def organization(name: String, home: Option[URL]) = copy(organizationName = name, organizationHomepage = home)
final case class ModuleInfo(nameFormal: String, description: String = "", homepage: Option[URL] = None, startYear: Option[Int] = None, licenses: Seq[(String, URL)] = Nil, organizationName: String = "", organizationHomepage: Option[URL] = None, scmInfo: Option[ScmInfo] = None) {
def formally(name: String) = copy(nameFormal = name)
def describing(desc: String, home: Option[URL]) = copy(description = desc, homepage = home)
def licensed(lics: (String, URL)*) = copy(licenses = lics)
def organization(name: String, home: Option[URL]) = copy(organizationName = name, organizationHomepage = home)
}
/** Basic SCM information for a project module */
@ -25,20 +24,19 @@ final case class ScmInfo(browseUrl: URL, connection: String, devConnection: Opti
final case class ExclusionRule(organization: String = "*", name: String = "*", artifact: String = "*", configurations: Seq[String] = Nil)
final case class ModuleConfiguration(organization: String, name: String, revision: String, resolver: Resolver)
object ModuleConfiguration
{
def apply(org: String, resolver: Resolver): ModuleConfiguration = apply(org, "*", "*", resolver)
def apply(org: String, name: String, resolver: Resolver): ModuleConfiguration = ModuleConfiguration(org, name, "*", resolver)
object ModuleConfiguration {
def apply(org: String, resolver: Resolver): ModuleConfiguration = apply(org, "*", "*", resolver)
def apply(org: String, name: String, resolver: Resolver): ModuleConfiguration = ModuleConfiguration(org, name, "*", resolver)
}
final case class ConflictManager(name: String, organization: String = "*", module: String = "*")
/** See http://ant.apache.org/ivy/history/latest-milestone/settings/conflict-managers.html for details of the different conflict managers.*/
object ConflictManager {
val all = ConflictManager("all")
val latestTime = ConflictManager("latest-time")
val latestRevision = ConflictManager("latest-revision")
val latestCompatible = ConflictManager("latest-compatible")
val strict = ConflictManager("strict")
val default = latestRevision
val all = ConflictManager("all")
val latestTime = ConflictManager("latest-time")
val latestRevision = ConflictManager("latest-revision")
val latestCompatible = ConflictManager("latest-compatible")
val strict = ConflictManager("strict")
val default = latestRevision
}

View File

@ -3,56 +3,51 @@
*/
package sbt
import org.apache.ivy.util.{Message, MessageLogger, MessageLoggerEngine}
import org.apache.ivy.util.{ Message, MessageLogger, MessageLoggerEngine }
/** Interface to Ivy logging. */
private final class IvyLoggerInterface(logger: Logger) extends MessageLogger
{
def rawlog(msg: String, level: Int) = log(msg, level)
def log(msg: String, level: Int)
{
import Message.{MSG_DEBUG, MSG_VERBOSE, MSG_INFO, MSG_WARN, MSG_ERR}
level match
{
case MSG_DEBUG => debug(msg)
case MSG_VERBOSE => verbose(msg)
case MSG_INFO => info(msg)
case MSG_WARN => warn(msg)
case MSG_ERR => error(msg)
}
}
//DEBUG level messages are very verbose and rarely useful to users.
// TODO: provide access to this information some other way
def debug(msg: String) {}
def verbose(msg: String) = logger.verbose(msg)
def deprecated(msg: String) = warn(msg)
def info(msg: String) = logger.info(msg)
def rawinfo(msg: String) = info(msg)
def warn(msg: String) = logger.warn(msg)
def error(msg: String) = if(SbtIvyLogger.acceptError(msg)) logger.error(msg)
private def emptyList = java.util.Collections.emptyList[String]
def getProblems = emptyList
def getWarns = emptyList
def getErrors = emptyList
private final class IvyLoggerInterface(logger: Logger) extends MessageLogger {
def rawlog(msg: String, level: Int) = log(msg, level)
def log(msg: String, level: Int) {
import Message.{ MSG_DEBUG, MSG_VERBOSE, MSG_INFO, MSG_WARN, MSG_ERR }
level match {
case MSG_DEBUG => debug(msg)
case MSG_VERBOSE => verbose(msg)
case MSG_INFO => info(msg)
case MSG_WARN => warn(msg)
case MSG_ERR => error(msg)
}
}
//DEBUG level messages are very verbose and rarely useful to users.
// TODO: provide access to this information some other way
def debug(msg: String) {}
def verbose(msg: String) = logger.verbose(msg)
def deprecated(msg: String) = warn(msg)
def info(msg: String) = logger.info(msg)
def rawinfo(msg: String) = info(msg)
def warn(msg: String) = logger.warn(msg)
def error(msg: String) = if (SbtIvyLogger.acceptError(msg)) logger.error(msg)
def clearProblems = ()
def sumupProblems = clearProblems()
def progress = ()
def endProgress = ()
private def emptyList = java.util.Collections.emptyList[String]
def getProblems = emptyList
def getWarns = emptyList
def getErrors = emptyList
def endProgress(msg: String) = info(msg)
def isShowProgress = false
def setShowProgress(progress: Boolean) {}
def clearProblems = ()
def sumupProblems = clearProblems()
def progress = ()
def endProgress = ()
def endProgress(msg: String) = info(msg)
def isShowProgress = false
def setShowProgress(progress: Boolean) {}
}
private final class SbtMessageLoggerEngine extends MessageLoggerEngine
{
/** This is a hack to filter error messages about 'unknown resolver ...'. */
override def error(msg: String) = if(SbtIvyLogger.acceptError(msg)) super.error(msg)
override def sumupProblems = clearProblems()
private final class SbtMessageLoggerEngine extends MessageLoggerEngine {
/** This is a hack to filter error messages about 'unknown resolver ...'. */
override def error(msg: String) = if (SbtIvyLogger.acceptError(msg)) super.error(msg)
override def sumupProblems = clearProblems()
}
private object SbtIvyLogger
{
val UnknownResolver = "unknown resolver"
def acceptError(msg: String) = (msg ne null) && !msg.startsWith(UnknownResolver)
private object SbtIvyLogger {
val UnknownResolver = "unknown resolver"
def acceptError(msg: String) = (msg ne null) && !msg.startsWith(UnknownResolver)
}

View File

@ -6,51 +6,49 @@ package sbt
import java.io.File
import collection.mutable
import org.apache.ivy.core.{module, report}
import module.descriptor.{Artifact => IvyArtifact}
import org.apache.ivy.core.{ module, report }
import module.descriptor.{ Artifact => IvyArtifact }
import module.id.ModuleRevisionId
import report.{ArtifactDownloadReport, ConfigurationResolveReport, ResolveReport}
import report.{ ArtifactDownloadReport, ConfigurationResolveReport, ResolveReport }
object IvyRetrieve
{
def reports(report: ResolveReport): Seq[ConfigurationResolveReport] =
report.getConfigurations map report.getConfigurationReport
object IvyRetrieve {
def reports(report: ResolveReport): Seq[ConfigurationResolveReport] =
report.getConfigurations map report.getConfigurationReport
def moduleReports(confReport: ConfigurationResolveReport): Seq[ModuleReport] =
for( revId <- confReport.getModuleRevisionIds.toArray collect { case revId: ModuleRevisionId => revId }) yield
artifactReports(toModuleID(revId), confReport getDownloadReports revId)
def moduleReports(confReport: ConfigurationResolveReport): Seq[ModuleReport] =
for (revId <- confReport.getModuleRevisionIds.toArray collect { case revId: ModuleRevisionId => revId }) yield artifactReports(toModuleID(revId), confReport getDownloadReports revId)
def artifactReports(mid: ModuleID, artReport: Seq[ArtifactDownloadReport]): ModuleReport =
{
val missing = new mutable.ListBuffer[Artifact]
val resolved = new mutable.ListBuffer[(Artifact,File)]
for(r <- artReport) {
val file = r.getLocalFile
val art = toArtifact(r.getArtifact)
if(file eq null)
missing += art
else
resolved += ((art,file))
}
new ModuleReport(mid, resolved.toSeq, missing.toSeq)
}
def artifactReports(mid: ModuleID, artReport: Seq[ArtifactDownloadReport]): ModuleReport =
{
val missing = new mutable.ListBuffer[Artifact]
val resolved = new mutable.ListBuffer[(Artifact, File)]
for (r <- artReport) {
val file = r.getLocalFile
val art = toArtifact(r.getArtifact)
if (file eq null)
missing += art
else
resolved += ((art, file))
}
new ModuleReport(mid, resolved.toSeq, missing.toSeq)
}
def evicted(confReport: ConfigurationResolveReport): Seq[ModuleID] =
confReport.getEvictedNodes.map(node => toModuleID(node.getId))
def toModuleID(revID: ModuleRevisionId): ModuleID =
ModuleID(revID.getOrganisation, revID.getName, revID.getRevision, extraAttributes = IvySbt.getExtraAttributes(revID))
def toArtifact(art: IvyArtifact): Artifact =
{
import art._
Artifact(getName, getType, getExt, Option(getExtraAttribute("classifier")), getConfigurations map Configurations.config, Option(getUrl))
}
def evicted(confReport: ConfigurationResolveReport): Seq[ModuleID] =
confReport.getEvictedNodes.map(node => toModuleID(node.getId))
def updateReport(report: ResolveReport, cachedDescriptor: File): UpdateReport =
new UpdateReport(cachedDescriptor, reports(report) map configurationReport, updateStats(report), Map.empty) recomputeStamps()
def updateStats(report: ResolveReport): UpdateStats =
new UpdateStats(report.getResolveTime, report.getDownloadTime, report.getDownloadSize, false)
def configurationReport(confReport: ConfigurationResolveReport): ConfigurationReport =
new ConfigurationReport(confReport.getConfiguration, moduleReports(confReport), evicted(confReport))
def toModuleID(revID: ModuleRevisionId): ModuleID =
ModuleID(revID.getOrganisation, revID.getName, revID.getRevision, extraAttributes = IvySbt.getExtraAttributes(revID))
def toArtifact(art: IvyArtifact): Artifact =
{
import art._
Artifact(getName, getType, getExt, Option(getExtraAttribute("classifier")), getConfigurations map Configurations.config, Option(getUrl))
}
def updateReport(report: ResolveReport, cachedDescriptor: File): UpdateReport =
new UpdateReport(cachedDescriptor, reports(report) map configurationReport, updateStats(report), Map.empty) recomputeStamps ()
def updateStats(report: ResolveReport): UpdateStats =
new UpdateStats(report.getResolveTime, report.getDownloadTime, report.getDownloadSize, false)
def configurationReport(confReport: ConfigurationResolveReport): ConfigurationReport =
new ConfigurationReport(confReport.getConfiguration, moduleReports(confReport), evicted(confReport))
}

View File

@ -6,111 +6,108 @@ package sbt
import java.util.Collections.emptyMap
import scala.collection.mutable.HashSet
import org.apache.ivy.{core, plugins}
import core.module.descriptor.{DefaultExcludeRule, ExcludeRule}
import core.module.descriptor.{DependencyDescriptor, DefaultModuleDescriptor, ModuleDescriptor, OverrideDependencyDescriptorMediator}
import core.module.id.{ArtifactId,ModuleId, ModuleRevisionId}
import org.apache.ivy.{ core, plugins }
import core.module.descriptor.{ DefaultExcludeRule, ExcludeRule }
import core.module.descriptor.{ DependencyDescriptor, DefaultModuleDescriptor, ModuleDescriptor, OverrideDependencyDescriptorMediator }
import core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId }
import plugins.matcher.ExactPatternMatcher
object ScalaArtifacts
{
import xsbti.ArtifactInfo._
val Organization = ScalaOrganization
val LibraryID = ScalaLibraryID
val CompilerID = ScalaCompilerID
def libraryDependency(version: String): ModuleID = ModuleID(Organization, LibraryID, version)
object ScalaArtifacts {
import xsbti.ArtifactInfo._
val Organization = ScalaOrganization
val LibraryID = ScalaLibraryID
val CompilerID = ScalaCompilerID
def libraryDependency(version: String): ModuleID = ModuleID(Organization, LibraryID, version)
private[sbt] def toolDependencies(org: String, version: String): Seq[ModuleID] = Seq(
scalaToolDependency(org, ScalaArtifacts.CompilerID, version),
scalaToolDependency(org, ScalaArtifacts.LibraryID, version)
)
private[this] def scalaToolDependency(org: String, id: String, version: String): ModuleID =
ModuleID(org, id, version, Some(Configurations.ScalaTool.name + "->default,optional(default)") )
private[sbt] def toolDependencies(org: String, version: String): Seq[ModuleID] = Seq(
scalaToolDependency(org, ScalaArtifacts.CompilerID, version),
scalaToolDependency(org, ScalaArtifacts.LibraryID, version)
)
private[this] def scalaToolDependency(org: String, id: String, version: String): ModuleID =
ModuleID(org, id, version, Some(Configurations.ScalaTool.name + "->default,optional(default)"))
}
object SbtArtifacts
{
import xsbti.ArtifactInfo._
val Organization = SbtOrganization
object SbtArtifacts {
import xsbti.ArtifactInfo._
val Organization = SbtOrganization
}
import ScalaArtifacts._
final case class IvyScala(scalaFullVersion: String, scalaBinaryVersion: String, configurations: Iterable[Configuration], checkExplicit: Boolean, filterImplicit: Boolean, overrideScalaVersion: Boolean, scalaOrganization: String = ScalaArtifacts.Organization)
private object IvyScala
{
/** Performs checks/adds filters on Scala dependencies (if enabled in IvyScala). */
def checkModule(module: DefaultModuleDescriptor, conf: String, log: Logger)(check: IvyScala)
{
if(check.checkExplicit)
checkDependencies(module, check.scalaBinaryVersion, check.configurations, log)
if(check.filterImplicit)
excludeScalaJars(module, check.configurations)
if(check.overrideScalaVersion)
overrideScalaVersion(module, check.scalaFullVersion)
}
def overrideScalaVersion(module: DefaultModuleDescriptor, version: String)
{
overrideVersion(module, Organization, LibraryID, version)
overrideVersion(module, Organization, CompilerID, version)
}
def overrideVersion(module: DefaultModuleDescriptor, org: String, name: String, version: String)
{
val id = new ModuleId(org, name)
val over = new OverrideDependencyDescriptorMediator(null, version)
module.addDependencyDescriptorMediator(id, ExactPatternMatcher.INSTANCE, over)
}
/** Checks the immediate dependencies of module for dependencies on scala jars and verifies that the version on the
* dependencies matches scalaVersion. */
private def checkDependencies(module: ModuleDescriptor, scalaBinaryVersion: String, configurations: Iterable[Configuration], log: Logger)
{
val configSet = if(configurations.isEmpty) (c: String) => true else configurationSet(configurations)
def binaryScalaWarning(dep: DependencyDescriptor): Option[String] =
{
val id = dep.getDependencyRevisionId
val depBinaryVersion = CrossVersion.binaryScalaVersion(id.getRevision)
val mismatched = id.getOrganisation == Organization && depBinaryVersion != scalaBinaryVersion && dep.getModuleConfigurations.exists(configSet)
if(mismatched)
Some("Binary version (" + depBinaryVersion + ") for dependency " + id +
"\n\tin " + module.getModuleRevisionId +
" differs from Scala binary version in project (" + scalaBinaryVersion + ").")
else
None
}
module.getDependencies.toList.flatMap(binaryScalaWarning).toSet foreach { (s: String) => log.warn(s) }
}
private def configurationSet(configurations: Iterable[Configuration]) = configurations.map(_.toString).toSet
private object IvyScala {
/** Performs checks/adds filters on Scala dependencies (if enabled in IvyScala). */
def checkModule(module: DefaultModuleDescriptor, conf: String, log: Logger)(check: IvyScala) {
if (check.checkExplicit)
checkDependencies(module, check.scalaBinaryVersion, check.configurations, log)
if (check.filterImplicit)
excludeScalaJars(module, check.configurations)
if (check.overrideScalaVersion)
overrideScalaVersion(module, check.scalaFullVersion)
}
def overrideScalaVersion(module: DefaultModuleDescriptor, version: String) {
overrideVersion(module, Organization, LibraryID, version)
overrideVersion(module, Organization, CompilerID, version)
}
def overrideVersion(module: DefaultModuleDescriptor, org: String, name: String, version: String) {
val id = new ModuleId(org, name)
val over = new OverrideDependencyDescriptorMediator(null, version)
module.addDependencyDescriptorMediator(id, ExactPatternMatcher.INSTANCE, over)
}
/** Adds exclusions for the scala library and compiler jars so that they are not downloaded. This is
* done because these jars are provided by the ScalaInstance of the project. The version of Scala to use
* is done by setting scalaVersion in the project definition. */
private def excludeScalaJars(module: DefaultModuleDescriptor, configurations: Iterable[Configuration])
{
val configurationNames =
{
val names = module.getConfigurationsNames
if(configurations.isEmpty)
names
else
{
val configSet = configurationSet(configurations)
configSet.intersect(HashSet(names : _*))
configSet.toArray
}
}
def excludeScalaJar(name: String): Unit =
module.addExcludeRule(excludeRule(Organization, name, configurationNames, "jar"))
excludeScalaJar(LibraryID)
excludeScalaJar(CompilerID)
}
/** Creates an ExcludeRule that excludes artifacts with the given module organization and name for
* the given configurations. */
private[sbt] def excludeRule(organization: String, name: String, configurationNames: Iterable[String], excludeTypePattern: String): ExcludeRule =
{
val artifact = new ArtifactId(ModuleId.newInstance(organization, name), "*", excludeTypePattern, "*")
val rule = new DefaultExcludeRule(artifact, ExactPatternMatcher.INSTANCE, emptyMap[AnyRef,AnyRef])
configurationNames.foreach(rule.addConfiguration)
rule
}
/**
* Checks the immediate dependencies of module for dependencies on scala jars and verifies that the version on the
* dependencies matches scalaVersion.
*/
private def checkDependencies(module: ModuleDescriptor, scalaBinaryVersion: String, configurations: Iterable[Configuration], log: Logger) {
val configSet = if (configurations.isEmpty) (c: String) => true else configurationSet(configurations)
def binaryScalaWarning(dep: DependencyDescriptor): Option[String] =
{
val id = dep.getDependencyRevisionId
val depBinaryVersion = CrossVersion.binaryScalaVersion(id.getRevision)
val mismatched = id.getOrganisation == Organization && depBinaryVersion != scalaBinaryVersion && dep.getModuleConfigurations.exists(configSet)
if (mismatched)
Some("Binary version (" + depBinaryVersion + ") for dependency " + id +
"\n\tin " + module.getModuleRevisionId +
" differs from Scala binary version in project (" + scalaBinaryVersion + ").")
else
None
}
module.getDependencies.toList.flatMap(binaryScalaWarning).toSet foreach { (s: String) => log.warn(s) }
}
private def configurationSet(configurations: Iterable[Configuration]) = configurations.map(_.toString).toSet
/**
* Adds exclusions for the scala library and compiler jars so that they are not downloaded. This is
* done because these jars are provided by the ScalaInstance of the project. The version of Scala to use
* is done by setting scalaVersion in the project definition.
*/
private def excludeScalaJars(module: DefaultModuleDescriptor, configurations: Iterable[Configuration]) {
val configurationNames =
{
val names = module.getConfigurationsNames
if (configurations.isEmpty)
names
else {
val configSet = configurationSet(configurations)
configSet.intersect(HashSet(names: _*))
configSet.toArray
}
}
def excludeScalaJar(name: String): Unit =
module.addExcludeRule(excludeRule(Organization, name, configurationNames, "jar"))
excludeScalaJar(LibraryID)
excludeScalaJar(CompilerID)
}
/**
* Creates an ExcludeRule that excludes artifacts with the given module organization and name for
* the given configurations.
*/
private[sbt] def excludeRule(organization: String, name: String, configurationNames: Iterable[String], excludeTypePattern: String): ExcludeRule =
{
val artifact = new ArtifactId(ModuleId.newInstance(organization, name), "*", excludeTypePattern, "*")
val rule = new DefaultExcludeRule(artifact, ExactPatternMatcher.INSTANCE, emptyMap[AnyRef, AnyRef])
configurationNames.foreach(rule.addConfiguration)
rule
}
}

View File

@ -1,7 +1,6 @@
package sbt
private[sbt] object IvyUtil
{
def separate[A,B](l: Seq[Either[A,B]]): (Seq[A], Seq[B]) =
(l.flatMap(_.left.toOption), l.flatMap(_.right.toOption))
private[sbt] object IvyUtil {
def separate[A, B](l: Seq[Either[A, B]]): (Seq[A], Seq[B]) =
(l.flatMap(_.left.toOption), l.flatMap(_.right.toOption))
}

View File

@ -10,349 +10,348 @@ package sbt
import java.io.File
// 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}
import scala.xml.{ Elem, Node => XNode, NodeSeq, PrettyPrinter, PrefixedAttribute }
import Configurations.Optional
import org.apache.ivy.{core, plugins, Ivy}
import org.apache.ivy.{ core, plugins, Ivy }
import core.settings.IvySettings
import core.module.descriptor.{DependencyArtifactDescriptor, DependencyDescriptor, License, ModuleDescriptor, ExcludeRule}
import plugins.resolver.{ChainResolver, DependencyResolver, IBiblioResolver}
import core.module.descriptor.{ DependencyArtifactDescriptor, DependencyDescriptor, License, ModuleDescriptor, ExcludeRule }
import plugins.resolver.{ ChainResolver, DependencyResolver, IBiblioResolver }
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")
def write(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], extra: NodeSeq, process: XNode => XNode, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean, output: File): Unit =
write(ivy, module, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], Set(Artifact.DefaultType), extra, process, filterRepositories, allRepositories, output)
def write(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], includeTypes: Set[String], extra: NodeSeq, process: XNode => XNode, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean, output: File): Unit =
write(process(toPom(ivy, module, moduleInfo, configurations, includeTypes, extra, filterRepositories, allRepositories)), output)
// use \n as newline because toString uses PrettyPrinter, which hard codes line endings to be \n
def write(node: XNode, output: File): Unit = write(toString(node), output, "\n")
def write(xmlString: String, output: File, newline: String)
{
IO.write(output, "<?xml version='1.0' encoding='" + IO.utf8.name + "'?>" + newline + xmlString)
}
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")
def write(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], extra: NodeSeq, process: XNode => XNode, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean, output: File): Unit =
write(ivy, module, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], Set(Artifact.DefaultType), extra, process, filterRepositories, allRepositories, output)
def write(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], includeTypes: Set[String], extra: NodeSeq, process: XNode => XNode, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean, output: File): Unit =
write(process(toPom(ivy, module, moduleInfo, configurations, includeTypes, extra, filterRepositories, allRepositories)), output)
// use \n as newline because toString uses PrettyPrinter, which hard codes line endings to be \n
def write(node: XNode, output: File): Unit = write(toString(node), output, "\n")
def write(xmlString: String, output: File, newline: String) {
IO.write(output, "<?xml version='1.0' encoding='" + IO.utf8.name + "'?>" + newline + xmlString)
}
def toString(node: XNode): String = new PrettyPrinter(1000, 4).format(node)
@deprecated("Use `toPom(Ivy, ModuleDescriptor, ModuleInfo, Option[Iterable[Configuration]], Set[String], NodeSeq, MavenRepository => Boolean, Boolean)` instead", "0.11.2")
def toPom(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], extra: NodeSeq, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean): XNode =
toPom(ivy, module, moduleInfo, configurations, Set(Artifact.DefaultType), extra, filterRepositories, allRepositories)
def toPom(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], includeTypes: Set[String], extra: NodeSeq, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean): XNode =
(<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
{ makeModuleID(module) }
<name>{moduleInfo.nameFormal}</name>
{ makeStartYear(moduleInfo) }
{ makeOrganization(moduleInfo) }
{ makeScmInfo(moduleInfo) }
{ extra }
{
val deps = depsInConfs(module, configurations)
makeProperties(module, deps) ++
makeDependencies(deps, includeTypes)
}
{ makeRepositories(ivy.getSettings, allRepositories, filterRepositories) }
</project>)
def toString(node: XNode): String = new PrettyPrinter(1000, 4).format(node)
@deprecated("Use `toPom(Ivy, ModuleDescriptor, ModuleInfo, Option[Iterable[Configuration]], Set[String], NodeSeq, MavenRepository => Boolean, Boolean)` instead", "0.11.2")
def toPom(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], extra: NodeSeq, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean): XNode =
toPom(ivy, module, moduleInfo, configurations, Set(Artifact.DefaultType), extra, filterRepositories, allRepositories)
def toPom(ivy: Ivy, module: ModuleDescriptor, moduleInfo: ModuleInfo, configurations: Option[Iterable[Configuration]], includeTypes: Set[String], extra: NodeSeq, filterRepositories: MavenRepository => Boolean, allRepositories: Boolean): XNode =
(<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
{ makeModuleID(module) }
<name>{ moduleInfo.nameFormal }</name>
{ makeStartYear(moduleInfo) }
{ makeOrganization(moduleInfo) }
{ makeScmInfo(moduleInfo) }
{ extra }
{
val deps = depsInConfs(module, configurations)
makeProperties(module, deps) ++
makeDependencies(deps, includeTypes)
}
{ makeRepositories(ivy.getSettings, allRepositories, filterRepositories) }
</project>)
def makeModuleID(module: ModuleDescriptor): NodeSeq =
{
val mrid = moduleDescriptor(module)
val a: NodeSeq =
(<groupId>{ mrid.getOrganisation }</groupId>
<artifactId>{ mrid.getName }</artifactId>
<packaging>{ packaging(module) }</packaging>)
val b: NodeSeq =
( (description(module.getDescription) ++
homePage(module.getHomePage) ++
revision(mrid.getRevision) ++
licenses(module.getLicenses)) : NodeSeq )
a ++ b
}
def makeModuleID(module: ModuleDescriptor): NodeSeq =
{
val mrid = moduleDescriptor(module)
val a: NodeSeq =
(<groupId>{ mrid.getOrganisation }</groupId>
<artifactId>{ mrid.getName }</artifactId>
<packaging>{ packaging(module) }</packaging>)
val b: NodeSeq =
((description(module.getDescription) ++
homePage(module.getHomePage) ++
revision(mrid.getRevision) ++
licenses(module.getLicenses)): NodeSeq)
a ++ b
}
def makeStartYear(moduleInfo: ModuleInfo): NodeSeq =
moduleInfo.startYear match {
case Some(y) => <inceptionYear>{y}</inceptionYear>
case _ => NodeSeq.Empty
}
def makeOrganization(moduleInfo: ModuleInfo): NodeSeq =
{
<organization>
<name>{moduleInfo.organizationName}</name>
{ moduleInfo.organizationHomepage match {
case Some(h) => <url>{h}</url>
case _ => NodeSeq.Empty
}}
</organization>
}
def makeScmInfo(moduleInfo: ModuleInfo): NodeSeq =
{
moduleInfo.scmInfo match {
case Some(s) =>
<scm>
<url>{s.browseUrl}</url>
<connection>{s.connection}</connection>
{s.devConnection match {
case Some(d) => <developerConnection>{d}</developerConnection>
case _ => NodeSeq.Empty
}}
</scm>
case _ => NodeSeq.Empty
}
}
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)
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
<properties> {
for( (key,value) <- extra ) yield
(<x>{value}</x>).copy(label = key, attributes = _extraAttributes(key))
} </properties>
}
def makeStartYear(moduleInfo: ModuleInfo): NodeSeq =
moduleInfo.startYear match {
case Some(y) => <inceptionYear>{ y }</inceptionYear>
case _ => NodeSeq.Empty
}
def makeOrganization(moduleInfo: ModuleInfo): NodeSeq =
{
<organization>
<name>{ moduleInfo.organizationName }</name>
{
moduleInfo.organizationHomepage match {
case Some(h)=> <url>{ h }</url>
case _ => NodeSeq.Empty
}
}
</organization>
}
def makeScmInfo(moduleInfo: ModuleInfo): NodeSeq =
{
moduleInfo.scmInfo match {
case Some(s) =>
<scm>
<url>{ s.browseUrl }</url>
<connection>{ s.connection }</connection>
{
s.devConnection match {
case Some(d)=> <developerConnection>{ d }</developerConnection>
case _=> NodeSeq.Empty
}
}
</scm>
case _ => NodeSeq.Empty
}
}
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)
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
<properties> {
for ((key, value) <- extra) yield (<x>{ value }</x>).copy(label = key, attributes = _extraAttributes(key))
} </properties>
}
/**
* Attribute tag that PrettyPrinter won't ignore, saying "don't mess with my spaces"
* Without this, PrettyPrinter will flatten multiple entries for ExtraDependencyAttributes and make them
* unparseable. (e.g. a plugin that depends on multiple plugins will fail)
*/
def xmlSpacePreserve = new PrefixedAttribute("xml", "space", "preserve", scala.xml.Null)
/**
* Attribute tag that PrettyPrinter won't ignore, saying "don't mess with my spaces"
* Without this, PrettyPrinter will flatten multiple entries for ExtraDependencyAttributes and make them
* unparseable. (e.g. a plugin that depends on multiple plugins will fail)
*/
def xmlSpacePreserve = new PrefixedAttribute("xml", "space", "preserve", scala.xml.Null)
def description(d: String) = if((d eq null) || d.isEmpty) NodeSeq.Empty else <description>{d}</description>
def licenses(ls: Array[License]) = if(ls == null || ls.isEmpty) NodeSeq.Empty else <licenses>{ls.map(license)}</licenses>
def license(l: License) =
<license>
<name>{l.getName}</name>
<url>{l.getUrl}</url>
<distribution>repo</distribution>
</license>
def homePage(homePage: String) = if(homePage eq null) NodeSeq.Empty else <url>{homePage}</url>
def revision(version: String) = if(version ne null) <version>{version}</version> else NodeSeq.Empty
def packaging(module: ModuleDescriptor) =
module.getAllArtifacts match
{
case Array() => "pom"
case Array(x) => x.getType
case xs =>
val types = xs.map(_.getType).toList.filterNot(IgnoreTypes)
types match {
case Nil => Artifact.PomType
case xs if xs.contains(Artifact.DefaultType) => Artifact.DefaultType
case x :: xs => x
}
}
val IgnoreTypes: Set[String] = Set(Artifact.SourceType, Artifact.DocType, Artifact.PomType)
def description(d: String) = if ((d eq null) || d.isEmpty) NodeSeq.Empty else <description>{ d }</description>
def licenses(ls: Array[License]) = if (ls == null || ls.isEmpty) NodeSeq.Empty else <licenses>{ ls.map(license) }</licenses>
def license(l: License) =
<license>
<name>{ l.getName }</name>
<url>{ l.getUrl }</url>
<distribution>repo</distribution>
</license>
def homePage(homePage: String) = if (homePage eq null) NodeSeq.Empty else <url>{ homePage }</url>
def revision(version: String) = if (version ne null) <version>{ version }</version> else NodeSeq.Empty
def packaging(module: ModuleDescriptor) =
module.getAllArtifacts match {
case Array() => "pom"
case Array(x) => x.getType
case xs =>
val types = xs.map(_.getType).toList.filterNot(IgnoreTypes)
types match {
case Nil => Artifact.PomType
case xs if xs.contains(Artifact.DefaultType) => Artifact.DefaultType
case x :: xs => x
}
}
val IgnoreTypes: Set[String] = Set(Artifact.SourceType, Artifact.DocType, Artifact.PomType)
def makeDependencies(dependencies: Seq[DependencyDescriptor], includeTypes: Set[String]): NodeSeq =
if(dependencies.isEmpty)
NodeSeq.Empty
else
<dependencies>
{ dependencies.map(makeDependency(_, includeTypes)) }
</dependencies>
def makeDependencies(dependencies: Seq[DependencyDescriptor], includeTypes: Set[String]): NodeSeq =
if (dependencies.isEmpty)
NodeSeq.Empty
else
<dependencies>
{ dependencies.map(makeDependency(_, includeTypes)) }
</dependencies>
def makeDependency(dependency: DependencyDescriptor, includeTypes: Set[String]): NodeSeq =
{
val artifacts = dependency.getAllDependencyArtifacts
val includeArtifacts = artifacts.filter(d => includeTypes(d.getType))
if(artifacts.isEmpty) {
val (scope, optional) = getScopeAndOptional(dependency.getModuleConfigurations)
makeDependencyElem(dependency, scope, optional, None, None)
}
else if(includeArtifacts.isEmpty)
NodeSeq.Empty
else
NodeSeq.fromSeq(artifacts.map( a => makeDependencyElem(dependency, a) ))
}
def makeDependency(dependency: DependencyDescriptor, includeTypes: Set[String]): NodeSeq =
{
val artifacts = dependency.getAllDependencyArtifacts
val includeArtifacts = artifacts.filter(d => includeTypes(d.getType))
if (artifacts.isEmpty) {
val (scope, optional) = getScopeAndOptional(dependency.getModuleConfigurations)
makeDependencyElem(dependency, scope, optional, None, None)
} else if (includeArtifacts.isEmpty)
NodeSeq.Empty
else
NodeSeq.fromSeq(artifacts.map(a => makeDependencyElem(dependency, a)))
}
def makeDependencyElem(dependency: DependencyDescriptor, artifact: DependencyArtifactDescriptor): Elem =
{
val configs = artifact.getConfigurations.toList match {
case Nil | "*" :: Nil => dependency.getModuleConfigurations
case x => x.toArray
}
val (scope, optional) = getScopeAndOptional(configs)
val classifier = artifactClassifier(artifact)
val baseType = artifactType(artifact)
val tpe = (classifier, baseType) match {
case (Some(c), Some(tpe)) if Artifact.classifierType(c) == tpe => None
case _ => baseType
}
makeDependencyElem(dependency, scope, optional, classifier, tpe)
}
def makeDependencyElem(dependency: DependencyDescriptor, scope: Option[String], optional: Boolean, classifier: Option[String], tpe: Option[String]): Elem =
{
val mrid = dependency.getDependencyRevisionId
<dependency>
<groupId>{mrid.getOrganisation}</groupId>
<artifactId>{mrid.getName}</artifactId>
<version>{makeDependencyVersion(mrid.getRevision)}</version>
{ scopeElem(scope) }
{ optionalElem(optional) }
{ classifierElem(classifier) }
{ typeElem(tpe) }
{ exclusions(dependency) }
</dependency>
}
def makeDependencyElem(dependency: DependencyDescriptor, artifact: DependencyArtifactDescriptor): Elem =
{
val configs = artifact.getConfigurations.toList match {
case Nil | "*" :: Nil => dependency.getModuleConfigurations
case x => x.toArray
}
val (scope, optional) = getScopeAndOptional(configs)
val classifier = artifactClassifier(artifact)
val baseType = artifactType(artifact)
val tpe = (classifier, baseType) match {
case (Some(c), Some(tpe)) if Artifact.classifierType(c) == tpe => None
case _ => baseType
}
makeDependencyElem(dependency, scope, optional, classifier, tpe)
}
def makeDependencyElem(dependency: DependencyDescriptor, scope: Option[String], optional: Boolean, classifier: Option[String], tpe: Option[String]): Elem =
{
val mrid = dependency.getDependencyRevisionId
<dependency>
<groupId>{ mrid.getOrganisation }</groupId>
<artifactId>{ mrid.getName }</artifactId>
<version>{ makeDependencyVersion(mrid.getRevision) }</version>
{ scopeElem(scope) }
{ optionalElem(optional) }
{ classifierElem(classifier) }
{ typeElem(tpe) }
{ exclusions(dependency) }
</dependency>
}
def makeDependencyVersion(revision: String): String = {
def plusRange(s: String, shift: Int = 0) = {
def pow(i: Int): Int = if (i > 0) 10 * pow(i - 1) else 1
val (prefixVersion, lastVersion) = (s + "0" * shift).reverse.split("\\.", 2) match {
case Array(revLast, revRest) =>
(revRest.reverse + ".", revLast.reverse)
case Array(revLast) => ("", revLast.reverse)
}
val lastVersionInt = lastVersion.toInt
s"[${prefixVersion}${lastVersion},${prefixVersion}${lastVersionInt + pow(shift)})"
}
val startSym = Set(']', '[', '(')
val stopSym = Set(']', '[', ')')
try {
if (revision endsWith ".+") {
plusRange(revision.substring(0, revision.length - 2))
} else if (revision endsWith "+") {
val base = revision.take(revision.length - 1)
// This is a heuristic. Maven just doesn't support Ivy's notions of 1+, so
// we assume version ranges never go beyond 5 siginificant digits.
(0 to 5).map(plusRange(base, _)).mkString(",")
} else if (startSym(revision(0)) && stopSym(revision(revision.length - 1))) {
val start = revision(0)
val stop = revision(revision.length - 1)
val mid = revision.substring(1, revision.length - 1)
(if (start == ']') "(" else start) + mid + (if (stop == '[') ")" else stop)
} else revision
} catch {
case e: NumberFormatException =>
// TODO - if the version doesn't meet our expectations, maybe we just issue a hard
// error instead of softly ignoring the attempt to rewrite.
//sys.error(s"Could not fix version [$revision] into maven style version")
revision
}
}
@deprecated("No longer used and will be removed.", "0.12.1")
def classifier(dependency: DependencyDescriptor, includeTypes: Set[String]): NodeSeq =
{
val jarDep = dependency.getAllDependencyArtifacts.filter(d => includeTypes(d.getType)).headOption
jarDep match {
case Some(a) => classifierElem(artifactClassifier(a))
case None => NodeSeq.Empty
}
}
def artifactType(artifact: DependencyArtifactDescriptor): Option[String] =
Option(artifact.getType).flatMap { tpe => if (tpe == "jar") None else Some(tpe) }
def typeElem(tpe: Option[String]): NodeSeq =
tpe match {
case Some(t) => <type>{ t }</type>
case None => NodeSeq.Empty
}
def makeDependencyVersion(revision: String): String = {
def plusRange(s:String, shift:Int = 0) = {
def pow(i:Int):Int = if (i>0) 10 * pow(i-1) else 1
val (prefixVersion, lastVersion) = (s+"0"*shift).reverse.split("\\.",2) match {
case Array(revLast,revRest) =>
( revRest.reverse + ".", revLast.reverse )
case Array(revLast) => ("", revLast.reverse)
}
val lastVersionInt = lastVersion.toInt
s"[${prefixVersion}${lastVersion},${prefixVersion}${lastVersionInt+pow(shift)})"
}
val startSym=Set(']','[','(')
val stopSym=Set(']','[',')')
try {
if (revision endsWith ".+") {
plusRange(revision.substring(0,revision.length-2))
} else if (revision endsWith "+") {
val base = revision.take(revision.length-1)
// This is a heuristic. Maven just doesn't support Ivy's notions of 1+, so
// we assume version ranges never go beyond 5 siginificant digits.
(0 to 5).map(plusRange(base,_)).mkString(",")
} else if (startSym(revision(0)) && stopSym(revision(revision.length-1))) {
val start = revision(0)
val stop = revision(revision.length-1)
val mid = revision.substring(1,revision.length-1)
(if (start == ']') "(" else start) + mid + (if (stop == '[') ")" else stop)
} else revision
} catch {
case e: NumberFormatException =>
// TODO - if the version doesn't meet our expectations, maybe we just issue a hard
// error instead of softly ignoring the attempt to rewrite.
//sys.error(s"Could not fix version [$revision] into maven style version")
revision
}
}
def artifactClassifier(artifact: DependencyArtifactDescriptor): Option[String] =
Option(artifact.getExtraAttribute("classifier"))
def classifierElem(classifier: Option[String]): NodeSeq =
classifier match {
case Some(c) => <classifier>{ c }</classifier>
case None => NodeSeq.Empty
}
@deprecated("No longer used and will be removed.", "0.12.1")
def classifier(dependency: DependencyDescriptor, includeTypes: Set[String]): NodeSeq =
{
val jarDep = dependency.getAllDependencyArtifacts.filter(d => includeTypes(d.getType)).headOption
jarDep match {
case Some(a) => classifierElem(artifactClassifier(a))
case None => NodeSeq.Empty
}
}
def artifactType(artifact: DependencyArtifactDescriptor): Option[String] =
Option(artifact.getType).flatMap { tpe => if(tpe == "jar") None else Some(tpe) }
def typeElem(tpe: Option[String]): NodeSeq =
tpe match {
case Some(t) => <type>{t}</type>
case None => NodeSeq.Empty
}
def artifactClassifier(artifact: DependencyArtifactDescriptor): Option[String] =
Option(artifact.getExtraAttribute("classifier"))
def classifierElem(classifier: Option[String]): NodeSeq =
classifier match {
case Some(c) => <classifier>{c}</classifier>
case None => NodeSeq.Empty
}
@deprecated("No longer used and will be removed.", "0.12.1")
def scopeAndOptional(dependency: DependencyDescriptor): NodeSeq =
{
val (scope, opt) = getScopeAndOptional(dependency.getModuleConfigurations)
scopeElem(scope) ++ optionalElem(opt)
}
def scopeElem(scope: Option[String]): NodeSeq = scope match {
case None | Some(Configurations.Compile.name) => NodeSeq.Empty
case Some(s) => <scope>{ s }</scope>
}
def optionalElem(opt: Boolean) = if (opt) <optional>true</optional> else NodeSeq.Empty
def moduleDescriptor(module: ModuleDescriptor) = module.getModuleRevisionId
@deprecated("No longer used and will be removed.", "0.12.1")
def scopeAndOptional(dependency: DependencyDescriptor): NodeSeq =
{
val (scope, opt) = getScopeAndOptional(dependency.getModuleConfigurations)
scopeElem(scope) ++ optionalElem(opt)
}
def scopeElem(scope: Option[String]): NodeSeq = scope match {
case None | Some(Configurations.Compile.name) => NodeSeq.Empty
case Some(s) => <scope>{s}</scope>
}
def optionalElem(opt: Boolean) = if(opt) <optional>true</optional> else NodeSeq.Empty
def moduleDescriptor(module: ModuleDescriptor) = module.getModuleRevisionId
def getScopeAndOptional(confs: Array[String]): (Option[String], Boolean) =
{
val (opt, notOptional) = confs.partition(_ == Optional.name)
val defaultNotOptional = Configurations.defaultMavenConfigurations.find(notOptional contains _.name)
val scope = defaultNotOptional.map(_.name)
(scope, !opt.isEmpty)
}
def getScopeAndOptional(confs: Array[String]): (Option[String], Boolean) =
{
val (opt, notOptional) = confs.partition(_ == Optional.name)
val defaultNotOptional = Configurations.defaultMavenConfigurations.find(notOptional contains _.name)
val scope = defaultNotOptional.map(_.name)
(scope, !opt.isEmpty)
}
def exclusions(dependency: DependencyDescriptor): NodeSeq =
{
val excl = dependency.getExcludeRules(dependency.getModuleConfigurations)
val (warns, excls) = IvyUtil.separate(excl.map(makeExclusion))
if (!warns.isEmpty) log.warn(warns.mkString(IO.Newline))
if (!excls.isEmpty) <exclusions>{ excls }</exclusions>
else NodeSeq.Empty
}
def makeExclusion(exclRule: ExcludeRule): Either[String, NodeSeq] =
{
val m = exclRule.getId.getModuleId
val (g, a) = (m.getOrganisation, m.getName)
if (g == null || g.isEmpty || g == "*" || a.isEmpty || a == "*")
Left("Skipped generating '<exclusion/>' for %s. Dependency exclusion should have both 'org' and 'module' to comply with Maven POM's schema.".format(m))
else
Right(
<exclusion>
<groupId>{ g }</groupId>
<artifactId>{ a }</artifactId>
</exclusion>
)
}
def exclusions(dependency: DependencyDescriptor): NodeSeq =
{
val excl = dependency.getExcludeRules(dependency.getModuleConfigurations)
val (warns, excls) = IvyUtil.separate(excl.map(makeExclusion))
if(!warns.isEmpty) log.warn(warns.mkString(IO.Newline))
if(!excls.isEmpty) <exclusions>{excls}</exclusions>
else NodeSeq.Empty
}
def makeExclusion(exclRule: ExcludeRule): Either[String, NodeSeq] =
{
val m = exclRule.getId.getModuleId
val (g, a) = (m.getOrganisation, m.getName)
if(g == null || g.isEmpty || g == "*" || a.isEmpty || a == "*")
Left("Skipped generating '<exclusion/>' for %s. Dependency exclusion should have both 'org' and 'module' to comply with Maven POM's schema.".format(m))
else
Right(
<exclusion>
<groupId>{g}</groupId>
<artifactId>{a}</artifactId>
</exclusion>
)
}
def makeRepositories(settings: IvySettings, includeAll: Boolean, filterRepositories: MavenRepository => Boolean) =
{
class MavenRepo(name: String, snapshots: Boolean, releases: Boolean)
val repositories = if (includeAll) allResolvers(settings) else resolvers(settings.getDefaultResolver)
val mavenRepositories =
repositories.flatMap {
case m: IBiblioResolver if m.isM2compatible && m.getRoot != IBiblioResolver.DEFAULT_M2_ROOT =>
MavenRepository(m.getName, m.getRoot) :: Nil
case _ => Nil
}
val repositoryElements = mavenRepositories.filter(filterRepositories).map(mavenRepository)
if (repositoryElements.isEmpty) repositoryElements else <repositories>{ repositoryElements }</repositories>
}
def allResolvers(settings: IvySettings): Seq[DependencyResolver] = flatten(castResolvers(settings.getResolvers)).distinct
def flatten(rs: Seq[DependencyResolver]): Seq[DependencyResolver] = if (rs eq null) Nil else rs.flatMap(resolvers)
def resolvers(r: DependencyResolver): Seq[DependencyResolver] =
r match { case c: ChainResolver => flatten(castResolvers(c.getResolvers)); case _ => r :: Nil }
def makeRepositories(settings: IvySettings, includeAll: Boolean, filterRepositories: MavenRepository => Boolean) =
{
class MavenRepo(name: String, snapshots: Boolean, releases: Boolean)
val repositories = if(includeAll) allResolvers(settings) else resolvers(settings.getDefaultResolver)
val mavenRepositories =
repositories.flatMap {
case m: IBiblioResolver if m.isM2compatible && m.getRoot != IBiblioResolver.DEFAULT_M2_ROOT =>
MavenRepository(m.getName, m.getRoot) :: Nil
case _ => Nil
}
val repositoryElements = mavenRepositories.filter(filterRepositories).map(mavenRepository)
if(repositoryElements.isEmpty) repositoryElements else <repositories>{repositoryElements}</repositories>
}
def allResolvers(settings: IvySettings): Seq[DependencyResolver] = flatten(castResolvers(settings.getResolvers)).distinct
def flatten(rs: Seq[DependencyResolver]): Seq[DependencyResolver] = if(rs eq null) Nil else rs.flatMap(resolvers)
def resolvers(r: DependencyResolver): Seq[DependencyResolver] =
r match { case c: ChainResolver => flatten(castResolvers(c.getResolvers)); case _ => r :: Nil }
// cast the contents of a pre-generics collection
private def castResolvers(s: java.util.Collection[_]): Seq[DependencyResolver] =
s.toArray.map(_.asInstanceOf[DependencyResolver])
// cast the contents of a pre-generics collection
private def castResolvers(s: java.util.Collection[_]): Seq[DependencyResolver] =
s.toArray.map(_.asInstanceOf[DependencyResolver])
def toID(name: String) = checkID(name.filter(isValidIDCharacter).mkString, name)
def isValidIDCharacter(c: Char) = c.isLetterOrDigit
private def checkID(id: String, name: String) = if (id.isEmpty) sys.error("Could not convert '" + name + "' to an ID") else id
def mavenRepository(repo: MavenRepository): XNode =
mavenRepository(toID(repo.name), repo.name, repo.root)
def mavenRepository(id: String, name: String, root: String): XNode =
<repository>
<id>{ id }</id>
<name>{ name }</name>
<url>{ root }</url>
<layout>{ if (name == JavaNet1Repository.name) "legacy" else "default" }</layout>
</repository>
def toID(name: String) = checkID(name.filter(isValidIDCharacter).mkString, name)
def isValidIDCharacter(c: Char) = c.isLetterOrDigit
private def checkID(id: String, name: String) = if(id.isEmpty) sys.error("Could not convert '" + name + "' to an ID") else id
def mavenRepository(repo: MavenRepository): XNode =
mavenRepository(toID(repo.name), repo.name, repo.root)
def mavenRepository(id: String, name: String, root: String): XNode =
<repository>
<id>{id}</id>
<name>{name}</name>
<url>{root}</url>
<layout>{ if(name == JavaNet1Repository.name) "legacy" else "default" }</layout>
</repository>
/** Retain dependencies only with the configurations given, or all public configurations of `module` if `configurations` is None.
* This currently only preserves the information required by makePom*/
private def depsInConfs(module: ModuleDescriptor, configurations: Option[Iterable[Configuration]]): Seq[DependencyDescriptor] =
{
val keepConfigurations = IvySbt.getConfigurations(module, configurations)
val keepSet = Set(keepConfigurations.toSeq : _*)
def translate(dependency: DependencyDescriptor) =
{
val keep = dependency.getModuleConfigurations.filter(keepSet.contains)
if(keep.isEmpty)
None
else // TODO: translate the dependency to contain only configurations to keep
Some(dependency)
}
module.getDependencies flatMap translate
}
/**
* Retain dependencies only with the configurations given, or all public configurations of `module` if `configurations` is None.
* This currently only preserves the information required by makePom
*/
private def depsInConfs(module: ModuleDescriptor, configurations: Option[Iterable[Configuration]]): Seq[DependencyDescriptor] =
{
val keepConfigurations = IvySbt.getConfigurations(module, configurations)
val keepSet = Set(keepConfigurations.toSeq: _*)
def translate(dependency: DependencyDescriptor) =
{
val keep = dependency.getModuleConfigurations.filter(keepSet.contains)
if (keep.isEmpty)
None
else // TODO: translate the dependency to contain only configurations to keep
Some(dependency)
}
module.getDependencies flatMap translate
}
}

View File

@ -3,103 +3,121 @@
*/
package sbt
import java.net.URL
import java.net.URL
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 +
(configurations match { case Some(s) => ":" + s; case None => "" }) +
(if(extraAttributes.isEmpty) "" else " " + extraString)
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 +
(configurations match { case Some(s) => ":" + s; case None => "" }) +
(if (extraAttributes.isEmpty) "" else " " + extraString)
/** String representation of the extra attributes, excluding any information only attributes. */
def extraString: String = extraDependencyAttributes.map { case (k,v) => k + "=" + v } mkString("(",", ",")")
/** String representation of the extra attributes, excluding any information only attributes. */
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))
/** 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))
@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)
@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)
@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, verRemap: String => String): ModuleID = cross(if(v) CrossVersion.binaryMapped(verRemap) else CrossVersion.Disabled)
@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, verRemap: String => String): ModuleID = cross(if (v) CrossVersion.binaryMapped(verRemap) else CrossVersion.Disabled)
/** Specifies the cross-version behavior for this module. See [CrossVersion] for details.*/
def cross(v: CrossVersion): ModuleID = copy(crossVersion = v)
/** Specifies the cross-version behavior for this module. See [CrossVersion] for details.*/
def cross(v: CrossVersion): ModuleID = copy(crossVersion = v)
// () required for chaining
/** Do not follow dependencies of this module. Synonym for `intransitive`.*/
def notTransitive() = intransitive()
// () required for chaining
/** Do not follow dependencies of this module. Synonym for `intransitive`.*/
def notTransitive() = intransitive()
/** Do not follow dependencies of this module. Synonym for `notTransitive`.*/
def intransitive() = copy(isTransitive = false)
/** Do not follow dependencies of this module. Synonym for `notTransitive`.*/
def intransitive() = copy(isTransitive = false)
/** Marks this dependency as "changing". Ivy will always check if the metadata has changed and then if the artifact has changed,
* redownload it. sbt configures all -SNAPSHOT dependencies to be changing.
*
* See the "Changes in artifacts" section of https://ant.apache.org/ivy/history/trunk/concept.html for full details.
* */
def changing() = copy(isChanging = true)
/**
* Marks this dependency as "changing". Ivy will always check if the metadata has changed and then if the artifact has changed,
* redownload it. sbt configures all -SNAPSHOT dependencies to be changing.
*
* See the "Changes in artifacts" section of https://ant.apache.org/ivy/history/trunk/concept.html for full details.
*/
def changing() = copy(isChanging = true)
/** Indicates that conflict resolution should only select this module's revision.
* This prevents a newer revision from being pulled in by a transitive dependency, for example.*/
def force() = copy(isForce = true)
/**
* Indicates that conflict resolution should only select this module's revision.
* This prevents a newer revision from being pulled in by a transitive dependency, for example.
*/
def force() = copy(isForce = true)
/** Specifies a URL from which the main artifact for this dependency can be downloaded.
* This value is only consulted if the module is not found in a repository.
* It is not included in published metadata.*/
def from(url: String) = artifacts(Artifact(name, new URL(url)))
/**
* Specifies a URL from which the main artifact for this dependency can be downloaded.
* This value is only consulted if the module is not found in a repository.
* It is not included in published metadata.
*/
def from(url: String) = artifacts(Artifact(name, new URL(url)))
/** Adds a dependency on the artifact for this module with classifier `c`. */
def classifier(c: String) = artifacts(Artifact(name, c))
/** Adds a dependency on the artifact for this module with classifier `c`. */
def classifier(c: String) = artifacts(Artifact(name, c))
/** Declares the explicit artifacts for this module. If this ModuleID represents a dependency,
* these artifact definitions override the information in the dependency's published metadata. */
def artifacts(newArtifacts: Artifact*) = copy(explicitArtifacts = newArtifacts ++ this.explicitArtifacts)
/**
* Declares the explicit artifacts for this module. If this ModuleID represents a dependency,
* these artifact definitions override the information in the dependency's published metadata.
*/
def artifacts(newArtifacts: Artifact*) = copy(explicitArtifacts = newArtifacts ++ this.explicitArtifacts)
/** Applies the provided exclusions to dependencies of this module. Note that only exclusions that specify
* both the exact organization and name and nothing else will be included in a pom.xml.*/
def excludeAll(rules: ExclusionRule*) = copy(exclusions = this.exclusions ++ rules)
/**
* Applies the provided exclusions to dependencies of this module. Note that only exclusions that specify
* both the exact organization and name and nothing else will be included in a pom.xml.
*/
def excludeAll(rules: ExclusionRule*) = copy(exclusions = this.exclusions ++ rules)
/** Excludes the dependency with organization `org` and `name` from being introduced by this dependency during resolution. */
def exclude(org: String, name: String) = excludeAll(ExclusionRule(org, name))
/** Excludes the dependency with organization `org` and `name` from being introduced by this dependency during resolution. */
def exclude(org: String, name: String) = excludeAll(ExclusionRule(org, name))
/** Adds extra attributes for this module. All keys are prefixed with `e:` if they are not already so prefixed.
* This information will only be published in an ivy.xml and not in a pom.xml. */
def extra(attributes: (String,String)*) = copy(extraAttributes = this.extraAttributes ++ ModuleID.checkE(attributes))
/**
* Adds extra attributes for this module. All keys are prefixed with `e:` if they are not already so prefixed.
* This information will only be published in an ivy.xml and not in a pom.xml.
*/
def extra(attributes: (String, String)*) = copy(extraAttributes = this.extraAttributes ++ ModuleID.checkE(attributes))
/** Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred
* for performance and correctness. This method adds a dependency on this module's artifact with the "sources"
* classifier. If you want to also depend on the main artifact, be sure to also call `jar()` or use `withSources()` instead.*/
def sources() = artifacts(Artifact.sources(name))
/**
* Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred
* for performance and correctness. This method adds a dependency on this module's artifact with the "sources"
* classifier. If you want to also depend on the main artifact, be sure to also call `jar()` or use `withSources()` instead.
*/
def sources() = artifacts(Artifact.sources(name))
/** Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred
* for performance and correctness. This method adds a dependency on this module's artifact with the "javadoc"
* classifier. If you want to also depend on the main artifact, be sure to also call `jar()` or use `withJavadoc()` instead.*/
def javadoc() = artifacts(Artifact.javadoc(name))
/**
* Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred
* for performance and correctness. This method adds a dependency on this module's artifact with the "javadoc"
* classifier. If you want to also depend on the main artifact, be sure to also call `jar()` or use `withJavadoc()` instead.
*/
def javadoc() = artifacts(Artifact.javadoc(name))
def pomOnly() = artifacts(Artifact.pom(name))
def pomOnly() = artifacts(Artifact.pom(name))
/** Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred
* for performance and correctness. This method adds a dependency on this module's artifact with the "sources"
* classifier. If there is not already an explicit dependency on the main artifact, this adds one.*/
def withSources() = jarIfEmpty.sources()
/**
* Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred
* for performance and correctness. This method adds a dependency on this module's artifact with the "sources"
* classifier. If there is not already an explicit dependency on the main artifact, this adds one.
*/
def withSources() = jarIfEmpty.sources()
/** Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred
* for performance and correctness. This method adds a dependency on this module's artifact with the "javadoc"
* classifier. If there is not already an explicit dependency on the main artifact, this adds one.*/
def withJavadoc() = jarIfEmpty.javadoc()
/**
* Not recommended for new use. This method is not deprecated, but the `update-classifiers` task is preferred
* for performance and correctness. This method adds a dependency on this module's artifact with the "javadoc"
* classifier. If there is not already an explicit dependency on the main artifact, this adds one.
*/
def withJavadoc() = jarIfEmpty.javadoc()
private def jarIfEmpty = if(explicitArtifacts.isEmpty) jar() else this
private def jarIfEmpty = if (explicitArtifacts.isEmpty) jar() else this
/** Declares a dependency on the main artifact. This is implied by default unless artifacts are explicitly declared, such
* as when adding a dependency on an artifact with a classifier.*/
def jar() = artifacts(Artifact(name))
/**
* Declares a dependency on the main artifact. This is implied by default unless artifacts are explicitly declared, such
* as when adding a dependency on an artifact with a classifier.
*/
def jar() = artifacts(Artifact(name))
}
object ModuleID
{
/** Prefixes all keys with `e:` if they are not already so prefixed. */
def checkE(attributes: Seq[(String, String)]) =
for ( (key, value) <- attributes) yield
if(key.startsWith("e:")) (key, value) else ("e:" + key, value)
object ModuleID {
/** Prefixes all keys with `e:` if they are not already so prefixed. */
def checkE(attributes: Seq[(String, String)]) =
for ((key, value) <- attributes) yield if (key.startsWith("e:")) (key, value) else ("e:" + key, value)
}

View File

@ -3,87 +3,88 @@
*/
package sbt
import java.io.File
import java.util.Date
import java.io.File
import java.util.Date
import org.apache.ivy.{core,plugins}
import core.{cache,module, report, resolve,search}
import cache.{ArtifactOrigin,RepositoryCacheManager}
import search.{ModuleEntry, OrganisationEntry, RevisionEntry}
import module.id.ModuleRevisionId
import module.descriptor.{Artifact => IArtifact, DefaultArtifact, DependencyDescriptor, ModuleDescriptor}
import plugins.namespace.Namespace
import plugins.resolver.{DependencyResolver,ResolverSettings}
import report.{ArtifactDownloadReport, DownloadReport, DownloadStatus, MetadataArtifactDownloadReport}
import resolve.{DownloadOptions, ResolveData, ResolvedModuleRevision}
import org.apache.ivy.{ core, plugins }
import core.{ cache, module, report, resolve, search }
import cache.{ ArtifactOrigin, RepositoryCacheManager }
import search.{ ModuleEntry, OrganisationEntry, RevisionEntry }
import module.id.ModuleRevisionId
import module.descriptor.{ Artifact => IArtifact, DefaultArtifact, DependencyDescriptor, ModuleDescriptor }
import plugins.namespace.Namespace
import plugins.resolver.{ DependencyResolver, ResolverSettings }
import report.{ ArtifactDownloadReport, DownloadReport, DownloadStatus, MetadataArtifactDownloadReport }
import resolve.{ DownloadOptions, ResolveData, ResolvedModuleRevision }
/**A Resolver that uses a predefined mapping from module ids to in-memory descriptors.
* It does not handle artifacts.*/
class ProjectResolver(name: String, map: Map[ModuleRevisionId, ModuleDescriptor]) extends ResolverAdapter
{
def getName = name
def setName(name: String) = sys.error("Setting name not supported by ProjectResolver")
override def toString = "ProjectResolver(" + name + ", mapped: " + map.keys.mkString(", ") + ")"
/**
* A Resolver that uses a predefined mapping from module ids to in-memory descriptors.
* It does not handle artifacts.
*/
class ProjectResolver(name: String, map: Map[ModuleRevisionId, ModuleDescriptor]) extends ResolverAdapter {
def getName = name
def setName(name: String) = sys.error("Setting name not supported by ProjectResolver")
override def toString = "ProjectResolver(" + name + ", mapped: " + map.keys.mkString(", ") + ")"
def getDependency(dd: DependencyDescriptor, data: ResolveData): ResolvedModuleRevision =
getDependency(dd.getDependencyRevisionId).orNull
def getDependency(dd: DependencyDescriptor, data: ResolveData): ResolvedModuleRevision =
getDependency(dd.getDependencyRevisionId).orNull
private[this] def getDependency(revisionId: ModuleRevisionId): Option[ResolvedModuleRevision] =
{
def constructResult(descriptor: ModuleDescriptor) = new ResolvedModuleRevision(this, this, descriptor, report(revisionId), true)
map get revisionId map constructResult
}
private[this] def getDependency(revisionId: ModuleRevisionId): Option[ResolvedModuleRevision] =
{
def constructResult(descriptor: ModuleDescriptor) = new ResolvedModuleRevision(this, this, descriptor, report(revisionId), true)
map get revisionId map constructResult
}
def report(revisionId: ModuleRevisionId): MetadataArtifactDownloadReport =
{
val artifact = DefaultArtifact.newIvyArtifact(revisionId, new Date)
val r = new MetadataArtifactDownloadReport(artifact)
r.setSearched(false)
r.setDownloadStatus(DownloadStatus.FAILED)
r
}
def report(revisionId: ModuleRevisionId): MetadataArtifactDownloadReport =
{
val artifact = DefaultArtifact.newIvyArtifact(revisionId, new Date)
val r = new MetadataArtifactDownloadReport(artifact)
r.setSearched(false)
r.setDownloadStatus(DownloadStatus.FAILED)
r
}
// this resolver nevers locates artifacts, only resolves dependencies
def exists(artifact: IArtifact) = false
def locate(artifact: IArtifact) = null
def download(artifacts: Array[IArtifact], options: DownloadOptions): DownloadReport =
{
val r = new DownloadReport
for(artifact <- artifacts)
if(getDependency(artifact.getModuleRevisionId).isEmpty)
r.addArtifactReport(notDownloaded(artifact))
r
}
// this resolver nevers locates artifacts, only resolves dependencies
def exists(artifact: IArtifact) = false
def locate(artifact: IArtifact) = null
def download(artifacts: Array[IArtifact], options: DownloadOptions): DownloadReport =
{
val r = new DownloadReport
for (artifact <- artifacts)
if (getDependency(artifact.getModuleRevisionId).isEmpty)
r.addArtifactReport(notDownloaded(artifact))
r
}
def download(artifact: ArtifactOrigin, options: DownloadOptions): ArtifactDownloadReport =
notDownloaded(artifact.getArtifact)
def findIvyFileRef(dd: DependencyDescriptor, data: ResolveData) = null
def download(artifact: ArtifactOrigin, options: DownloadOptions): ArtifactDownloadReport =
notDownloaded(artifact.getArtifact)
def findIvyFileRef(dd: DependencyDescriptor, data: ResolveData) = null
def notDownloaded(artifact: IArtifact): ArtifactDownloadReport=
{
val r = new ArtifactDownloadReport(artifact)
r.setDownloadStatus(DownloadStatus.FAILED)
r
}
def notDownloaded(artifact: IArtifact): ArtifactDownloadReport =
{
val r = new ArtifactDownloadReport(artifact)
r.setDownloadStatus(DownloadStatus.FAILED)
r
}
// doesn't support publishing
def publish(artifact: IArtifact, src: File, overwrite: Boolean) = sys.error("Publish not supported by ProjectResolver")
def beginPublishTransaction(module: ModuleRevisionId, overwrite: Boolean) {}
def abortPublishTransaction() {}
def commitPublishTransaction() {}
// doesn't support publishing
def publish(artifact: IArtifact, src: File, overwrite: Boolean) = sys.error("Publish not supported by ProjectResolver")
def beginPublishTransaction(module: ModuleRevisionId, overwrite: Boolean) {}
def abortPublishTransaction() {}
def commitPublishTransaction() {}
def reportFailure() {}
def reportFailure(art: IArtifact) {}
def reportFailure() {}
def reportFailure(art: IArtifact) {}
def listOrganisations() = new Array[OrganisationEntry](0)
def listModules(org: OrganisationEntry) = new Array[ModuleEntry](0)
def listRevisions(module: ModuleEntry) = new Array[RevisionEntry](0)
def listOrganisations() = new Array[OrganisationEntry](0)
def listModules(org: OrganisationEntry) = new Array[ModuleEntry](0)
def listRevisions(module: ModuleEntry) = new Array[RevisionEntry](0)
def getNamespace = Namespace.SYSTEM_NAMESPACE
def getNamespace = Namespace.SYSTEM_NAMESPACE
private[this] var settings: Option[ResolverSettings] = None
private[this] var settings: Option[ResolverSettings] = None
def dumpSettings() {}
def setSettings(settings: ResolverSettings) { this.settings = Some(settings) }
def getRepositoryCacheManager = settings match { case Some(s) => s.getDefaultRepositoryCacheManager; case None => sys.error("No settings defined for ProjectResolver") }
def dumpSettings() {}
def setSettings(settings: ResolverSettings) { this.settings = Some(settings) }
def getRepositoryCacheManager = settings match { case Some(s) => s.getDefaultRepositoryCacheManager; case None => sys.error("No settings defined for ProjectResolver") }
}

View File

@ -7,84 +7,84 @@ import org.apache.ivy.core
import org.apache.ivy.plugins.parser
import core.IvyPatternHelper
import core.settings.IvySettings
import core.cache.{CacheMetadataOptions, DefaultRepositoryCacheManager, DefaultResolutionCacheManager, ResolutionCacheManager}
import core.cache.{ CacheMetadataOptions, DefaultRepositoryCacheManager, DefaultResolutionCacheManager, ResolutionCacheManager }
import core.module.id.ModuleRevisionId
import core.module.descriptor.ModuleDescriptor
import ResolutionCache.{Name, ReportDirectory, ResolvedName, ResolvedPattern}
import ResolutionCache.{ Name, ReportDirectory, ResolvedName, ResolvedPattern }
import parser.xml.XmlModuleDescriptorParser
/** Replaces the standard Ivy resolution cache in order to:
* 1. Separate cached resolved Ivy files from resolution reports, making the resolution reports easier to find.
* 2. Have them per-project for easier cleaning (possible with standard cache, but central to this custom one).
* 3. Cache location includes extra attributes so that cross builds of a plugin do not overwrite each other.
*/
private[sbt] final class ResolutionCache(base: File, settings: IvySettings) extends ResolutionCacheManager
{
private[this] def resolvedFileInCache(m: ModuleRevisionId, name: String, ext: String): File = {
val p = ResolvedPattern
val f = IvyPatternHelper.substitute(p, m.getOrganisation, m.getName, m.getBranch, m.getRevision, name, name, ext, null, null, m.getAttributes, null)
new File(base, f)
}
private[this] val reportBase: File = new File(base, ReportDirectory)
/**
* Replaces the standard Ivy resolution cache in order to:
* 1. Separate cached resolved Ivy files from resolution reports, making the resolution reports easier to find.
* 2. Have them per-project for easier cleaning (possible with standard cache, but central to this custom one).
* 3. Cache location includes extra attributes so that cross builds of a plugin do not overwrite each other.
*/
private[sbt] final class ResolutionCache(base: File, settings: IvySettings) extends ResolutionCacheManager {
private[this] def resolvedFileInCache(m: ModuleRevisionId, name: String, ext: String): File = {
val p = ResolvedPattern
val f = IvyPatternHelper.substitute(p, m.getOrganisation, m.getName, m.getBranch, m.getRevision, name, name, ext, null, null, m.getAttributes, null)
new File(base, f)
}
private[this] val reportBase: File = new File(base, ReportDirectory)
def getResolutionCacheRoot: File = base
def clean() { IO.delete(base) }
override def toString = Name
def getResolutionCacheRoot: File = base
def clean() { IO.delete(base) }
override def toString = Name
def getResolvedIvyFileInCache(mrid: ModuleRevisionId): File =
resolvedFileInCache(mrid, ResolvedName, "xml")
def getResolvedIvyPropertiesInCache(mrid: ModuleRevisionId): File =
resolvedFileInCache(mrid, ResolvedName, "properties")
// name needs to be the same as Ivy's default because the ivy-report.xsl stylesheet assumes this
// when making links to reports for other configurations
def getConfigurationResolveReportInCache(resolveId: String, conf: String): File =
new File(reportBase, resolveId + "-" + conf + ".xml")
def getConfigurationResolveReportsInCache(resolveId: String): Array[File] =
IO.listFiles(reportBase).filter(_.getName.startsWith(resolveId + "-"))
// XXX: this method is required by ResolutionCacheManager in Ivy 2.3.0 final,
// but it is apparently unused by Ivy as sbt uses Ivy. Therefore, it is
// unexercised in tests. Note that the implementation of this method in Ivy 2.3.0's
// DefaultResolutionCache also resolves parent properties for a given mrid
def getResolvedModuleDescriptor(mrid: ModuleRevisionId): ModuleDescriptor = {
val ivyFile = getResolvedIvyFileInCache(mrid)
if (!ivyFile.exists()) {
throw new IllegalStateException("Ivy file not found in cache for " + mrid + "!")
}
return XmlModuleDescriptorParser.getInstance().parseDescriptor(settings, ivyFile.toURI().toURL(), false)
}
def saveResolvedModuleDescriptor(md: ModuleDescriptor): Unit = {
val mrid = md.getResolvedModuleRevisionId
val cachedIvyFile = getResolvedIvyFileInCache(mrid)
md.toIvyFile(cachedIvyFile)
}
def getResolvedIvyFileInCache(mrid: ModuleRevisionId): File =
resolvedFileInCache(mrid, ResolvedName, "xml")
def getResolvedIvyPropertiesInCache(mrid: ModuleRevisionId): File =
resolvedFileInCache(mrid, ResolvedName, "properties")
// name needs to be the same as Ivy's default because the ivy-report.xsl stylesheet assumes this
// when making links to reports for other configurations
def getConfigurationResolveReportInCache(resolveId: String, conf: String): File =
new File(reportBase, resolveId + "-" + conf + ".xml")
def getConfigurationResolveReportsInCache(resolveId: String): Array[File] =
IO.listFiles(reportBase).filter(_.getName.startsWith(resolveId + "-"))
// XXX: this method is required by ResolutionCacheManager in Ivy 2.3.0 final,
// but it is apparently unused by Ivy as sbt uses Ivy. Therefore, it is
// unexercised in tests. Note that the implementation of this method in Ivy 2.3.0's
// DefaultResolutionCache also resolves parent properties for a given mrid
def getResolvedModuleDescriptor(mrid: ModuleRevisionId): ModuleDescriptor = {
val ivyFile = getResolvedIvyFileInCache(mrid)
if (!ivyFile.exists()) {
throw new IllegalStateException("Ivy file not found in cache for " + mrid + "!")
}
return XmlModuleDescriptorParser.getInstance().parseDescriptor(settings, ivyFile.toURI().toURL(), false)
}
def saveResolvedModuleDescriptor(md: ModuleDescriptor): Unit = {
val mrid = md.getResolvedModuleRevisionId
val cachedIvyFile = getResolvedIvyFileInCache(mrid)
md.toIvyFile(cachedIvyFile)
}
}
private[sbt] object ResolutionCache
{
/** Removes cached files from the resolution cache for the module with ID `mrid`
* and the resolveId (as set on `ResolveOptions`). */
private[sbt] def cleanModule(mrid: ModuleRevisionId, resolveId: String, manager: ResolutionCacheManager)
{
val files =
Option(manager.getResolvedIvyFileInCache(mrid)).toList :::
Option(manager.getResolvedIvyPropertiesInCache(mrid)).toList :::
Option(manager.getConfigurationResolveReportsInCache(resolveId)).toList.flatten
IO.delete(files)
}
private[sbt] object ResolutionCache {
/**
* Removes cached files from the resolution cache for the module with ID `mrid`
* and the resolveId (as set on `ResolveOptions`).
*/
private[sbt] def cleanModule(mrid: ModuleRevisionId, resolveId: String, manager: ResolutionCacheManager) {
val files =
Option(manager.getResolvedIvyFileInCache(mrid)).toList :::
Option(manager.getResolvedIvyPropertiesInCache(mrid)).toList :::
Option(manager.getConfigurationResolveReportsInCache(resolveId)).toList.flatten
IO.delete(files)
}
private val ReportDirectory = "reports"
private val ReportDirectory = "reports"
// name of the file providing a dependency resolution report for a configuration
private val ReportFileName = "report.xml"
// name of the file providing a dependency resolution report for a configuration
private val ReportFileName = "report.xml"
// base name (name except for extension) of resolution report file
private val ResolvedName = "resolved.xml"
// base name (name except for extension) of resolution report file
private val ResolvedName = "resolved.xml"
// Cache name
private val Name = "sbt-resolution-cache"
// Cache name
private val Name = "sbt-resolution-cache"
// use sbt-specific extra attributes so that resolved xml files do not get overwritten when using different Scala/sbt versions
private val ResolvedPattern = "[organisation]/[module]/" + Resolver.PluginPattern + "[revision]/[artifact].[ext]"
// use sbt-specific extra attributes so that resolved xml files do not get overwritten when using different Scala/sbt versions
private val ResolvedPattern = "[organisation]/[module]/" + Resolver.PluginPattern + "[revision]/[artifact].[ext]"
}

View File

@ -6,145 +6,131 @@ package sbt
import java.io.File
import java.net.URL
import scala.xml.NodeSeq
import org.apache.ivy.plugins.resolver.{DependencyResolver, IBiblioResolver}
import org.apache.ivy.plugins.resolver.{ DependencyResolver, IBiblioResolver }
sealed trait Resolver
{
def name: String
sealed trait Resolver {
def name: String
}
final class RawRepository(val resolver: DependencyResolver) extends Resolver
{
def name = resolver.getName
override def toString = "Raw(" + resolver.toString + ")"
final class RawRepository(val resolver: DependencyResolver) extends Resolver {
def name = resolver.getName
override def toString = "Raw(" + resolver.toString + ")"
}
sealed case class ChainedResolver(name: String, resolvers: Seq[Resolver]) extends Resolver
sealed case class MavenRepository(name: String, root: String) extends Resolver
{
override def toString = name + ": " + root
sealed case class MavenRepository(name: String, root: String) extends Resolver {
override def toString = name + ": " + root
}
final class Patterns(val ivyPatterns: Seq[String], val artifactPatterns: Seq[String], val isMavenCompatible: Boolean, val descriptorOptional: Boolean, val skipConsistencyCheck: Boolean)
{
private[sbt] def mavenStyle(): Patterns = Patterns(ivyPatterns, artifactPatterns, true)
private[sbt] def withDescriptorOptional(): Patterns = Patterns(ivyPatterns, artifactPatterns, isMavenCompatible, true, skipConsistencyCheck)
private[sbt] def withoutConsistencyCheck(): Patterns = Patterns(ivyPatterns, artifactPatterns, isMavenCompatible, descriptorOptional, true)
private[sbt] def withIvys(patterns: Seq[String]): Patterns = Patterns(patterns ++ ivyPatterns, artifactPatterns, isMavenCompatible)
private[sbt] def withArtifacts(patterns: Seq[String]): Patterns = Patterns(ivyPatterns, patterns ++ artifactPatterns, isMavenCompatible)
override def toString = "Patterns(ivyPatterns=%s, artifactPatterns=%s, isMavenCompatible=%s, descriptorOptional=%s, skipConsistencyCheck=%s)".format(ivyPatterns, artifactPatterns, isMavenCompatible, descriptorOptional, skipConsistencyCheck)
override def equals(obj: Any): Boolean = {
obj match {
case other: Patterns =>
ivyPatterns == other.ivyPatterns && artifactPatterns == other.artifactPatterns && isMavenCompatible == other.isMavenCompatible && descriptorOptional == other.descriptorOptional && skipConsistencyCheck == other.skipConsistencyCheck
case _ => false
}
}
override def hashCode: Int = (ivyPatterns, artifactPatterns, isMavenCompatible, descriptorOptional, skipConsistencyCheck).hashCode
final class Patterns(val ivyPatterns: Seq[String], val artifactPatterns: Seq[String], val isMavenCompatible: Boolean, val descriptorOptional: Boolean, val skipConsistencyCheck: Boolean) {
private[sbt] def mavenStyle(): Patterns = Patterns(ivyPatterns, artifactPatterns, true)
private[sbt] def withDescriptorOptional(): Patterns = Patterns(ivyPatterns, artifactPatterns, isMavenCompatible, true, skipConsistencyCheck)
private[sbt] def withoutConsistencyCheck(): Patterns = Patterns(ivyPatterns, artifactPatterns, isMavenCompatible, descriptorOptional, true)
private[sbt] def withIvys(patterns: Seq[String]): Patterns = Patterns(patterns ++ ivyPatterns, artifactPatterns, isMavenCompatible)
private[sbt] def withArtifacts(patterns: Seq[String]): Patterns = Patterns(ivyPatterns, patterns ++ artifactPatterns, isMavenCompatible)
override def toString = "Patterns(ivyPatterns=%s, artifactPatterns=%s, isMavenCompatible=%s, descriptorOptional=%s, skipConsistencyCheck=%s)".format(ivyPatterns, artifactPatterns, isMavenCompatible, descriptorOptional, skipConsistencyCheck)
override def equals(obj: Any): Boolean = {
obj match {
case other: Patterns =>
ivyPatterns == other.ivyPatterns && artifactPatterns == other.artifactPatterns && isMavenCompatible == other.isMavenCompatible && descriptorOptional == other.descriptorOptional && skipConsistencyCheck == other.skipConsistencyCheck
case _ => false
}
}
override def hashCode: Int = (ivyPatterns, artifactPatterns, isMavenCompatible, descriptorOptional, skipConsistencyCheck).hashCode
@deprecated
def this(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean) = this(ivyPatterns, artifactPatterns, isMavenCompatible, false, false)
@deprecated
def this(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean) = this(ivyPatterns, artifactPatterns, isMavenCompatible, false, false)
}
object Patterns
{
implicit def defaultPatterns: Patterns = Resolver.defaultPatterns
object Patterns {
implicit def defaultPatterns: Patterns = Resolver.defaultPatterns
def apply(artifactPatterns: String*): Patterns = Patterns(true, artifactPatterns : _*)
def apply(isMavenCompatible: Boolean, artifactPatterns: String*): Patterns = Patterns(artifactPatterns, artifactPatterns, isMavenCompatible)
def apply(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean): Patterns = apply(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean, false, false)
def apply(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean, descriptorOptional: Boolean, skipConsistencyCheck: Boolean): Patterns = new Patterns(ivyPatterns, artifactPatterns, isMavenCompatible, descriptorOptional, skipConsistencyCheck)
def apply(artifactPatterns: String*): Patterns = Patterns(true, artifactPatterns: _*)
def apply(isMavenCompatible: Boolean, artifactPatterns: String*): Patterns = Patterns(artifactPatterns, artifactPatterns, isMavenCompatible)
def apply(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean): Patterns = apply(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean, false, false)
def apply(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean, descriptorOptional: Boolean, skipConsistencyCheck: Boolean): Patterns = new Patterns(ivyPatterns, artifactPatterns, isMavenCompatible, descriptorOptional, skipConsistencyCheck)
}
object RepositoryHelpers
{
final case class SshConnection(authentication: Option[SshAuthentication], hostname: Option[String], port: Option[Int])
{
def copy(authentication: Option[SshAuthentication]) = SshConnection(authentication, hostname, port)
}
/** Configuration specific to an Ivy filesystem resolver. */
final case class FileConfiguration(isLocal: Boolean, isTransactional: Option[Boolean])
{
def transactional() = FileConfiguration(isLocal, Some(true))
def nontransactional() = FileConfiguration(isLocal, Some(false))
def nonlocal() = FileConfiguration(false, isTransactional)
}
sealed trait SshAuthentication extends NotNull
final case class PasswordAuthentication(user: String, password: Option[String]) extends SshAuthentication
final case class KeyFileAuthentication(user: String, keyfile: File, password: Option[String]) extends SshAuthentication
object RepositoryHelpers {
final case class SshConnection(authentication: Option[SshAuthentication], hostname: Option[String], port: Option[Int]) {
def copy(authentication: Option[SshAuthentication]) = SshConnection(authentication, hostname, port)
}
/** Configuration specific to an Ivy filesystem resolver. */
final case class FileConfiguration(isLocal: Boolean, isTransactional: Option[Boolean]) {
def transactional() = FileConfiguration(isLocal, Some(true))
def nontransactional() = FileConfiguration(isLocal, Some(false))
def nonlocal() = FileConfiguration(false, isTransactional)
}
sealed trait SshAuthentication extends NotNull
final case class PasswordAuthentication(user: String, password: Option[String]) extends SshAuthentication
final case class KeyFileAuthentication(user: String, keyfile: File, password: Option[String]) extends SshAuthentication
}
import RepositoryHelpers.{SshConnection, FileConfiguration}
import RepositoryHelpers.{KeyFileAuthentication, PasswordAuthentication, SshAuthentication}
import RepositoryHelpers.{ SshConnection, FileConfiguration }
import RepositoryHelpers.{ KeyFileAuthentication, PasswordAuthentication, SshAuthentication }
/** sbt interface to an Ivy repository based on patterns, which is most Ivy repositories.*/
sealed abstract class PatternsBasedRepository extends Resolver
{
type RepositoryType <: PatternsBasedRepository
/** Should be implemented to create a new copy of this repository but with `patterns` as given.*/
protected def copy(patterns: Patterns): RepositoryType
sealed abstract class PatternsBasedRepository extends Resolver {
type RepositoryType <: PatternsBasedRepository
/** Should be implemented to create a new copy of this repository but with `patterns` as given.*/
protected def copy(patterns: Patterns): RepositoryType
/** The object representing the configured patterns for this repository. */
def patterns: Patterns
/** The object representing the configured patterns for this repository. */
def patterns: Patterns
/** Enables maven 2 compatibility for this repository. */
def mavenStyle() = copy(patterns.mavenStyle())
/** Enables maven 2 compatibility for this repository. */
def mavenStyle() = copy(patterns.mavenStyle())
/** Makes descriptor metadata optional for this repository. */
def descriptorOptional() = copy(patterns.withDescriptorOptional())
/** Makes descriptor metadata optional for this repository. */
def descriptorOptional() = copy(patterns.withDescriptorOptional())
/** Disables consistency checking for this repository. */
def skipConsistencyCheck() = copy(patterns.withoutConsistencyCheck())
/** Disables consistency checking for this repository. */
def skipConsistencyCheck() = copy(patterns.withoutConsistencyCheck())
/** Adds the given patterns for resolving/publishing Ivy files.*/
def ivys(ivyPatterns: String*): RepositoryType = copy(patterns.withIvys(ivyPatterns))
/** Adds the given patterns for resolving/publishing artifacts.*/
def artifacts(artifactPatterns: String*): RepositoryType = copy(patterns.withArtifacts(artifactPatterns))
/** Adds the given patterns for resolving/publishing Ivy files.*/
def ivys(ivyPatterns: String*): RepositoryType = copy(patterns.withIvys(ivyPatterns))
/** Adds the given patterns for resolving/publishing artifacts.*/
def artifacts(artifactPatterns: String*): RepositoryType = copy(patterns.withArtifacts(artifactPatterns))
}
/** sbt interface for an Ivy filesystem repository. More convenient construction is done using Resolver.file. */
final case class FileRepository(name: String, configuration: FileConfiguration, patterns: Patterns) extends PatternsBasedRepository
{
type RepositoryType = FileRepository
protected def copy(patterns: Patterns): FileRepository = FileRepository(name, configuration, patterns)
private def copy(configuration: FileConfiguration) = FileRepository(name, configuration, patterns)
def transactional() = copy(configuration.transactional())
def nonlocal() = copy(configuration.nonlocal())
final case class FileRepository(name: String, configuration: FileConfiguration, patterns: Patterns) extends PatternsBasedRepository {
type RepositoryType = FileRepository
protected def copy(patterns: Patterns): FileRepository = FileRepository(name, configuration, patterns)
private def copy(configuration: FileConfiguration) = FileRepository(name, configuration, patterns)
def transactional() = copy(configuration.transactional())
def nonlocal() = copy(configuration.nonlocal())
}
final case class URLRepository(name: String, patterns: Patterns) extends PatternsBasedRepository
{
type RepositoryType = URLRepository
protected def copy(patterns: Patterns): URLRepository = URLRepository(name, patterns)
final case class URLRepository(name: String, patterns: Patterns) extends PatternsBasedRepository {
type RepositoryType = URLRepository
protected def copy(patterns: Patterns): URLRepository = URLRepository(name, patterns)
}
/** sbt interface for an Ivy ssh-based repository (ssh and sftp). Requires the Jsch library.. */
sealed abstract class SshBasedRepository extends PatternsBasedRepository
{
type RepositoryType <: SshBasedRepository
protected def copy(connection: SshConnection): RepositoryType
private def copy(authentication: SshAuthentication): RepositoryType = copy(connection.copy(Some(authentication)))
sealed abstract class SshBasedRepository extends PatternsBasedRepository {
type RepositoryType <: SshBasedRepository
protected def copy(connection: SshConnection): RepositoryType
private def copy(authentication: SshAuthentication): RepositoryType = copy(connection.copy(Some(authentication)))
/** The object representing the configured ssh connection for this repository. */
def connection: SshConnection
/** The object representing the configured ssh connection for this repository. */
def connection: SshConnection
/** Configures this to use the specified user name and password when connecting to the remote repository. */
def as(user: String, password: String): RepositoryType = as(user, Some(password))
def as(user: String): RepositoryType = as(user, None)
def as(user: String, password: Option[String]) = copy(new PasswordAuthentication(user, password))
/** Configures this to use the specified keyfile and password for the keyfile when connecting to the remote repository. */
def as(user: String, keyfile: File): RepositoryType = as(user, keyfile, None)
def as(user: String, keyfile: File, password: String): RepositoryType = as(user, keyfile, Some(password))
def as(user: String, keyfile: File, password: Option[String]): RepositoryType = copy(new KeyFileAuthentication(user, keyfile, password))
/** Configures this to use the specified user name and password when connecting to the remote repository. */
def as(user: String, password: String): RepositoryType = as(user, Some(password))
def as(user: String): RepositoryType = as(user, None)
def as(user: String, password: Option[String]) = copy(new PasswordAuthentication(user, password))
/** Configures this to use the specified keyfile and password for the keyfile when connecting to the remote repository. */
def as(user: String, keyfile: File): RepositoryType = as(user, keyfile, None)
def as(user: String, keyfile: File, password: String): RepositoryType = as(user, keyfile, Some(password))
def as(user: String, keyfile: File, password: Option[String]): RepositoryType = copy(new KeyFileAuthentication(user, keyfile, password))
}
/** sbt interface for an Ivy repository over ssh. More convenient construction is done using Resolver.ssh. */
final case class SshRepository(name: String, connection: SshConnection, patterns: Patterns, publishPermissions: Option[String]) extends SshBasedRepository
{
type RepositoryType = SshRepository
protected def copy(patterns: Patterns): SshRepository = SshRepository(name, connection, patterns, publishPermissions)
protected def copy(connection: SshConnection): SshRepository = SshRepository(name, connection, patterns, publishPermissions)
/** Defines the permissions to set when publishing to this repository. */
def withPermissions(publishPermissions: String): SshRepository = withPermissions(Some(publishPermissions))
def withPermissions(publishPermissions: Option[String]): SshRepository = SshRepository(name, connection, patterns, publishPermissions)
final case class SshRepository(name: String, connection: SshConnection, patterns: Patterns, publishPermissions: Option[String]) extends SshBasedRepository {
type RepositoryType = SshRepository
protected def copy(patterns: Patterns): SshRepository = SshRepository(name, connection, patterns, publishPermissions)
protected def copy(connection: SshConnection): SshRepository = SshRepository(name, connection, patterns, publishPermissions)
/** Defines the permissions to set when publishing to this repository. */
def withPermissions(publishPermissions: String): SshRepository = withPermissions(Some(publishPermissions))
def withPermissions(publishPermissions: Option[String]): SshRepository = SshRepository(name, connection, patterns, publishPermissions)
}
/** sbt interface for an Ivy repository over sftp. More convenient construction is done using Resolver.sftp. */
final case class SftpRepository(name: String, connection: SshConnection, patterns: Patterns) extends SshBasedRepository
{
type RepositoryType = SftpRepository
protected def copy(patterns: Patterns): SftpRepository = SftpRepository(name, connection, patterns)
protected def copy(connection: SshConnection): SftpRepository = SftpRepository(name, connection, patterns)
final case class SftpRepository(name: String, connection: SshConnection, patterns: Patterns) extends SshBasedRepository {
type RepositoryType = SftpRepository
protected def copy(patterns: Patterns): SftpRepository = SftpRepository(name, connection, patterns)
protected def copy(connection: SshConnection): SftpRepository = SftpRepository(name, connection, patterns)
}
import Resolver._
@ -152,151 +138,163 @@ import Resolver._
object DefaultMavenRepository extends MavenRepository("public", IBiblioResolver.DEFAULT_M2_ROOT)
object JavaNet2Repository extends MavenRepository(JavaNet2RepositoryName, JavaNet2RepositoryRoot)
object JavaNet1Repository extends JavaNet1Repository
sealed trait JavaNet1Repository extends Resolver
{
def name = "java.net Maven1 Repository"
sealed trait JavaNet1Repository extends Resolver {
def name = "java.net Maven1 Repository"
}
object Resolver
{
val TypesafeRepositoryRoot = "http://repo.typesafe.com/typesafe"
val SbtPluginRepositoryRoot = "http://repo.scala-sbt.org/scalasbt"
val SonatypeRepositoryRoot = "https://oss.sonatype.org/content/repositories"
object Resolver {
val TypesafeRepositoryRoot = "http://repo.typesafe.com/typesafe"
val SbtPluginRepositoryRoot = "http://repo.scala-sbt.org/scalasbt"
val SonatypeRepositoryRoot = "https://oss.sonatype.org/content/repositories"
// obsolete: kept only for launcher compatibility
private[sbt] val ScalaToolsReleasesName = "Sonatype OSS Releases"
private[sbt] val ScalaToolsSnapshotsName = "Sonatype OSS Snapshots"
private[sbt] val ScalaToolsReleasesRoot = SonatypeRepositoryRoot + "/releases"
private[sbt] val ScalaToolsSnapshotsRoot = SonatypeRepositoryRoot + "/snapshots"
private[sbt] val ScalaToolsReleases = new MavenRepository(ScalaToolsReleasesName, ScalaToolsReleasesRoot)
private[sbt] val ScalaToolsSnapshots = new MavenRepository(ScalaToolsSnapshotsName, ScalaToolsSnapshotsRoot)
// obsolete: kept only for launcher compatibility
private[sbt] val ScalaToolsReleasesName = "Sonatype OSS Releases"
private[sbt] val ScalaToolsSnapshotsName = "Sonatype OSS Snapshots"
private[sbt] val ScalaToolsReleasesRoot = SonatypeRepositoryRoot + "/releases"
private[sbt] val ScalaToolsSnapshotsRoot = SonatypeRepositoryRoot + "/snapshots"
private[sbt] val ScalaToolsReleases = new MavenRepository(ScalaToolsReleasesName, ScalaToolsReleasesRoot)
private[sbt] val ScalaToolsSnapshots = new MavenRepository(ScalaToolsSnapshotsName, ScalaToolsSnapshotsRoot)
val JavaNet2RepositoryName = "java.net Maven2 Repository"
val JavaNet2RepositoryRoot = "http://download.java.net/maven/2"
val JavaNet2RepositoryName = "java.net Maven2 Repository"
val JavaNet2RepositoryRoot = "http://download.java.net/maven/2"
def typesafeRepo(status: String) = new MavenRepository("typesafe-" + status, TypesafeRepositoryRoot + "/" + status)
def typesafeIvyRepo(status: String) = url("typesafe-ivy-" + status, new URL(TypesafeRepositoryRoot + "/ivy-" + status + "/"))(ivyStylePatterns)
def sbtPluginRepo(status: String) = url("sbt-plugin-" + status, new URL(SbtPluginRepositoryRoot + "/sbt-plugin-" + status + "/"))(ivyStylePatterns)
def sonatypeRepo(status: String) = new MavenRepository("sonatype-" + status, SonatypeRepositoryRoot + "/" + status)
def typesafeRepo(status: String) = new MavenRepository("typesafe-" + status, TypesafeRepositoryRoot + "/" + status)
def typesafeIvyRepo(status: String) = url("typesafe-ivy-" + status, new URL(TypesafeRepositoryRoot + "/ivy-" + status + "/"))(ivyStylePatterns)
def sbtPluginRepo(status: String) = url("sbt-plugin-" + status, new URL(SbtPluginRepositoryRoot + "/sbt-plugin-" + status + "/"))(ivyStylePatterns)
def sonatypeRepo(status: String) = new MavenRepository("sonatype-" + status, SonatypeRepositoryRoot + "/" + status)
/** Add the local and Maven Central repositories to the user repositories. */
def withDefaultResolvers(userResolvers: Seq[Resolver]): Seq[Resolver] =
withDefaultResolvers(userResolvers, true)
/** Add the local Ivy repository to the user repositories.
* If `mavenCentral` is true, add the Maven Central repository. */
def withDefaultResolvers(userResolvers: Seq[Resolver], mavenCentral: Boolean): Seq[Resolver] =
Seq(Resolver.defaultLocal) ++
userResolvers ++
single(DefaultMavenRepository, mavenCentral)
private def single[T](value: T, nonEmpty: Boolean): Seq[T] = if(nonEmpty) Seq(value) else Nil
/** Add the local and Maven Central repositories to the user repositories. */
def withDefaultResolvers(userResolvers: Seq[Resolver]): Seq[Resolver] =
withDefaultResolvers(userResolvers, true)
/**
* Add the local Ivy repository to the user repositories.
* If `mavenCentral` is true, add the Maven Central repository.
*/
def withDefaultResolvers(userResolvers: Seq[Resolver], mavenCentral: Boolean): Seq[Resolver] =
Seq(Resolver.defaultLocal) ++
userResolvers ++
single(DefaultMavenRepository, mavenCentral)
private def single[T](value: T, nonEmpty: Boolean): Seq[T] = if (nonEmpty) Seq(value) else Nil
/** A base class for defining factories for interfaces to Ivy repositories that require a hostname , port, and patterns. */
sealed abstract class Define[RepositoryType <: SshBasedRepository] extends NotNull
{
/** Subclasses should implement this method to */
protected def construct(name: String, connection: SshConnection, patterns: Patterns): RepositoryType
/** Constructs this repository type with the given `name`. `basePatterns` are the initial patterns to use. A ManagedProject
* has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/
def apply(name: String)(implicit basePatterns: Patterns): RepositoryType =
apply(name, None, None, None)
/** Constructs this repository type with the given `name` and `hostname`. `basePatterns` are the initial patterns to use.
* A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/
def apply(name: String, hostname: String)(implicit basePatterns: Patterns): RepositoryType =
apply(name, Some(hostname), None, None)
/** Constructs this repository type with the given `name`, `hostname`, and the `basePath` against which the initial
* patterns will be resolved. `basePatterns` are the initial patterns to use.
* A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/
def apply(name: String, hostname: String, basePath: String)(implicit basePatterns: Patterns): RepositoryType =
apply(name, Some(hostname), None, Some(basePath))
/** Constructs this repository type with the given `name`, `hostname`, and `port`. `basePatterns` are the initial patterns to use.
* A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/
def apply(name: String, hostname: String, port: Int)(implicit basePatterns: Patterns): RepositoryType =
apply(name, Some(hostname), Some(port), None)
/** Constructs this repository type with the given `name`, `hostname`, `port`, and the `basePath` against which the initial
* patterns will be resolved. `basePatterns` are the initial patterns to use.
* A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/
def apply(name: String, hostname: String, port: Int, basePath: String)(implicit basePatterns: Patterns): RepositoryType =
apply(name, Some(hostname), Some(port), Some(basePath))
/** Constructs this repository type with the given `name`, `hostname`, `port`, and the `basePath` against which the initial
* patterns will be resolved. `basePatterns` are the initial patterns to use. All but the `name` are optional (use None).
* A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.*/
def apply(name: String, hostname: Option[String], port: Option[Int], basePath: Option[String])(implicit basePatterns: Patterns): RepositoryType =
construct(name, SshConnection(None, hostname, port), resolvePatterns(basePath, basePatterns))
}
/** A factory to construct an interface to an Ivy SSH resolver.*/
object ssh extends Define[SshRepository]
{
protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SshRepository(name, connection, patterns, None)
}
/** A factory to construct an interface to an Ivy SFTP resolver.*/
object sftp extends Define[SftpRepository]
{
protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SftpRepository(name, connection, patterns)
}
/** A factory to construct an interface to an Ivy filesytem resolver. */
object file
{
/** Constructs a file resolver with the given name. The patterns to use must be explicitly specified
* using the `ivys` or `artifacts` methods on the constructed resolver object.*/
def apply(name: String): FileRepository = FileRepository(name, defaultFileConfiguration, Patterns(false))
/** Constructs a file resolver with the given name and base directory. */
def apply(name: String, baseDirectory: File)(implicit basePatterns: Patterns): FileRepository =
baseRepository(new File(baseDirectory.toURI.normalize) getAbsolutePath)(FileRepository(name, defaultFileConfiguration, _))
}
object url
{
/** Constructs a URL resolver with the given name. The patterns to use must be explicitly specified
* using the `ivys` or `artifacts` methods on the constructed resolver object.*/
def apply(name: String): URLRepository = URLRepository(name, Patterns(false))
/** Constructs a file resolver with the given name and base directory. */
def apply(name: String, baseURL: URL)(implicit basePatterns: Patterns): URLRepository =
baseRepository(baseURL.toURI.normalize.toString)(URLRepository(name, _))
}
private def baseRepository[T](base: String)(construct: Patterns => T)(implicit basePatterns: Patterns): T =
construct(resolvePatterns(base, basePatterns))
/** A base class for defining factories for interfaces to Ivy repositories that require a hostname , port, and patterns. */
sealed abstract class Define[RepositoryType <: SshBasedRepository] extends NotNull {
/** Subclasses should implement this method to */
protected def construct(name: String, connection: SshConnection, patterns: Patterns): RepositoryType
/**
* Constructs this repository type with the given `name`. `basePatterns` are the initial patterns to use. A ManagedProject
* has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.
*/
def apply(name: String)(implicit basePatterns: Patterns): RepositoryType =
apply(name, None, None, None)
/**
* Constructs this repository type with the given `name` and `hostname`. `basePatterns` are the initial patterns to use.
* A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.
*/
def apply(name: String, hostname: String)(implicit basePatterns: Patterns): RepositoryType =
apply(name, Some(hostname), None, None)
/**
* Constructs this repository type with the given `name`, `hostname`, and the `basePath` against which the initial
* patterns will be resolved. `basePatterns` are the initial patterns to use.
* A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.
*/
def apply(name: String, hostname: String, basePath: String)(implicit basePatterns: Patterns): RepositoryType =
apply(name, Some(hostname), None, Some(basePath))
/**
* Constructs this repository type with the given `name`, `hostname`, and `port`. `basePatterns` are the initial patterns to use.
* A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.
*/
def apply(name: String, hostname: String, port: Int)(implicit basePatterns: Patterns): RepositoryType =
apply(name, Some(hostname), Some(port), None)
/**
* Constructs this repository type with the given `name`, `hostname`, `port`, and the `basePath` against which the initial
* patterns will be resolved. `basePatterns` are the initial patterns to use.
* A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.
*/
def apply(name: String, hostname: String, port: Int, basePath: String)(implicit basePatterns: Patterns): RepositoryType =
apply(name, Some(hostname), Some(port), Some(basePath))
/**
* Constructs this repository type with the given `name`, `hostname`, `port`, and the `basePath` against which the initial
* patterns will be resolved. `basePatterns` are the initial patterns to use. All but the `name` are optional (use None).
* A ManagedProject has an implicit defining these initial patterns based on a setting for either Maven or Ivy style patterns.
*/
def apply(name: String, hostname: Option[String], port: Option[Int], basePath: Option[String])(implicit basePatterns: Patterns): RepositoryType =
construct(name, SshConnection(None, hostname, port), resolvePatterns(basePath, basePatterns))
}
/** A factory to construct an interface to an Ivy SSH resolver.*/
object ssh extends Define[SshRepository] {
protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SshRepository(name, connection, patterns, None)
}
/** A factory to construct an interface to an Ivy SFTP resolver.*/
object sftp extends Define[SftpRepository] {
protected def construct(name: String, connection: SshConnection, patterns: Patterns) = SftpRepository(name, connection, patterns)
}
/** A factory to construct an interface to an Ivy filesytem resolver. */
object file {
/**
* Constructs a file resolver with the given name. The patterns to use must be explicitly specified
* using the `ivys` or `artifacts` methods on the constructed resolver object.
*/
def apply(name: String): FileRepository = FileRepository(name, defaultFileConfiguration, Patterns(false))
/** Constructs a file resolver with the given name and base directory. */
def apply(name: String, baseDirectory: File)(implicit basePatterns: Patterns): FileRepository =
baseRepository(new File(baseDirectory.toURI.normalize) getAbsolutePath)(FileRepository(name, defaultFileConfiguration, _))
}
object url {
/**
* Constructs a URL resolver with the given name. The patterns to use must be explicitly specified
* using the `ivys` or `artifacts` methods on the constructed resolver object.
*/
def apply(name: String): URLRepository = URLRepository(name, Patterns(false))
/** Constructs a file resolver with the given name and base directory. */
def apply(name: String, baseURL: URL)(implicit basePatterns: Patterns): URLRepository =
baseRepository(baseURL.toURI.normalize.toString)(URLRepository(name, _))
}
private def baseRepository[T](base: String)(construct: Patterns => T)(implicit basePatterns: Patterns): T =
construct(resolvePatterns(base, basePatterns))
/** If `base` is None, `patterns` is returned unchanged.
* Otherwise, the ivy file and artifact patterns in `patterns` are resolved against the given base. */
private def resolvePatterns(base: Option[String], patterns: Patterns): Patterns =
base match
{
case Some(path) => resolvePatterns(path, patterns)
case None => patterns
}
/** Resolves the ivy file and artifact patterns in `patterns` against the given base. */
private def resolvePatterns(base: String, basePatterns: Patterns): Patterns =
{
def resolveAll(patterns: Seq[String]) = patterns.map(p => resolvePattern(base, p))
Patterns(resolveAll(basePatterns.ivyPatterns), resolveAll(basePatterns.artifactPatterns), basePatterns.isMavenCompatible)
}
private[sbt] def resolvePattern(base: String, pattern: String): String =
{
val normBase = base.replace('\\', '/')
if(normBase.endsWith("/") || pattern.startsWith("/")) normBase + pattern else normBase + "/" + pattern
}
def defaultFileConfiguration = FileConfiguration(true, None)
def mavenStylePatterns = Patterns(Nil, mavenStyleBasePattern :: Nil, true)
def ivyStylePatterns = defaultIvyPatterns//Patterns(Nil, Nil, false)
/**
* If `base` is None, `patterns` is returned unchanged.
* Otherwise, the ivy file and artifact patterns in `patterns` are resolved against the given base.
*/
private def resolvePatterns(base: Option[String], patterns: Patterns): Patterns =
base match {
case Some(path) => resolvePatterns(path, patterns)
case None => patterns
}
/** Resolves the ivy file and artifact patterns in `patterns` against the given base. */
private def resolvePatterns(base: String, basePatterns: Patterns): Patterns =
{
def resolveAll(patterns: Seq[String]) = patterns.map(p => resolvePattern(base, p))
Patterns(resolveAll(basePatterns.ivyPatterns), resolveAll(basePatterns.artifactPatterns), basePatterns.isMavenCompatible)
}
private[sbt] def resolvePattern(base: String, pattern: String): String =
{
val normBase = base.replace('\\', '/')
if (normBase.endsWith("/") || pattern.startsWith("/")) normBase + pattern else normBase + "/" + pattern
}
def defaultFileConfiguration = FileConfiguration(true, None)
def mavenStylePatterns = Patterns(Nil, mavenStyleBasePattern :: Nil, true)
def ivyStylePatterns = defaultIvyPatterns //Patterns(Nil, Nil, false)
def defaultPatterns = mavenStylePatterns
def mavenStyleBasePattern = "[organisation]/[module](_[scalaVersion])(_[sbtVersion])/[revision]/[artifact]-[revision](-[classifier]).[ext]"
def localBasePattern = "[organisation]/[module]/" + PluginPattern + "[revision]/[type]s/[artifact](-[classifier]).[ext]"
def defaultRetrievePattern = "[type]s/[organisation]/[module]/" + PluginPattern + "[artifact](-[revision])(-[classifier]).[ext]"
final val PluginPattern = "(scala_[scalaVersion]/)(sbt_[sbtVersion]/)"
def defaultPatterns = mavenStylePatterns
def mavenStyleBasePattern = "[organisation]/[module](_[scalaVersion])(_[sbtVersion])/[revision]/[artifact]-[revision](-[classifier]).[ext]"
def localBasePattern = "[organisation]/[module]/" + PluginPattern + "[revision]/[type]s/[artifact](-[classifier]).[ext]"
def defaultRetrievePattern = "[type]s/[organisation]/[module]/" + PluginPattern + "[artifact](-[revision])(-[classifier]).[ext]"
final val PluginPattern = "(scala_[scalaVersion]/)(sbt_[sbtVersion]/)"
private[this] def mavenLocalDir = new File(Path.userHome, ".m2/repository/")
def publishMavenLocal = Resolver.file("publish-m2-local", mavenLocalDir)
def mavenLocal = MavenRepository("Maven2 Local", mavenLocalDir.toURI.toString)
def defaultLocal = defaultUserFileRepository("local")
def defaultShared = defaultUserFileRepository("shared")
def defaultUserFileRepository(id: String) =
{
val pList = ("${ivy.home}/" + id + "/" + localBasePattern) :: Nil
FileRepository(id, defaultFileConfiguration, Patterns(pList, pList, false))
}
def defaultIvyPatterns =
{
val pList = List(localBasePattern)
Patterns(pList, pList, false)
}
private[this] def mavenLocalDir = new File(Path.userHome, ".m2/repository/")
def publishMavenLocal = Resolver.file("publish-m2-local", mavenLocalDir)
def mavenLocal = MavenRepository("Maven2 Local", mavenLocalDir.toURI.toString)
def defaultLocal = defaultUserFileRepository("local")
def defaultShared = defaultUserFileRepository("shared")
def defaultUserFileRepository(id: String) =
{
val pList = ("${ivy.home}/" + id + "/" + localBasePattern) :: Nil
FileRepository(id, defaultFileConfiguration, Patterns(pList, pList, false))
}
def defaultIvyPatterns =
{
val pList = List(localBasePattern)
Patterns(pList, pList, false)
}
}

View File

@ -5,13 +5,11 @@ package sbt
import java.util.Locale
object StringUtilities
{
@deprecated("Different use cases require different normalization. Use Project.normalizeModuleID or normalizeProjectID instead.", "0.13.0")
def normalize(s: String) = s.toLowerCase(Locale.ENGLISH).replaceAll("""\W+""", "-")
def nonEmpty(s: String, label: String)
{
require(s.trim.length > 0, label + " cannot be empty.")
}
def appendable(s: String) = if(s.isEmpty) "" else "_" + s
object StringUtilities {
@deprecated("Different use cases require different normalization. Use Project.normalizeModuleID or normalizeProjectID instead.", "0.13.0")
def normalize(s: String) = s.toLowerCase(Locale.ENGLISH).replaceAll("""\W+""", "-")
def nonEmpty(s: String, label: String) {
require(s.trim.length > 0, label + " cannot be empty.")
}
def appendable(s: String) = if (s.isEmpty) "" else "_" + s
}

View File

@ -3,142 +3,138 @@
*/
package sbt
import java.io.File
import java.io.File
/** Provides information about dependency resolution.
* It does not include information about evicted modules, only about the modules ultimately selected by the conflict manager.
* This means that for a given configuration, there should only be one revision for a given organization and module name.
* @param cachedDescriptor the location of the resolved module descriptor in the cache
* @param configurations a sequence containing one report for each configuration resolved.
* @param stats information about the update that produced this report
* @see sbt.RichUpdateReport
*/
final class UpdateReport(val cachedDescriptor: File, val configurations: Seq[ConfigurationReport], val stats: UpdateStats, private[sbt] val stamps: Map[File,Long])
{
@deprecated("Use the variant that provides timestamps of files.", "0.13.0")
def this(cachedDescriptor: File, configurations: Seq[ConfigurationReport], stats: UpdateStats) =
this(cachedDescriptor, configurations, stats, Map.empty)
/**
* Provides information about dependency resolution.
* It does not include information about evicted modules, only about the modules ultimately selected by the conflict manager.
* This means that for a given configuration, there should only be one revision for a given organization and module name.
* @param cachedDescriptor the location of the resolved module descriptor in the cache
* @param configurations a sequence containing one report for each configuration resolved.
* @param stats information about the update that produced this report
* @see sbt.RichUpdateReport
*/
final class UpdateReport(val cachedDescriptor: File, val configurations: Seq[ConfigurationReport], val stats: UpdateStats, private[sbt] val stamps: Map[File, Long]) {
@deprecated("Use the variant that provides timestamps of files.", "0.13.0")
def this(cachedDescriptor: File, configurations: Seq[ConfigurationReport], stats: UpdateStats) =
this(cachedDescriptor, configurations, stats, Map.empty)
override def toString = "Update report:\n\t" + stats + "\n" + configurations.mkString
override def toString = "Update report:\n\t" + stats + "\n" + configurations.mkString
/** All resolved modules in all configurations. */
def allModules: Seq[ModuleID] = configurations.flatMap(_.allModules).distinct
/** All resolved modules in all configurations. */
def allModules: Seq[ModuleID] = configurations.flatMap(_.allModules).distinct
def retrieve(f: (String, ModuleID, Artifact, File) => File): UpdateReport =
new UpdateReport(cachedDescriptor, configurations map { _ retrieve f}, stats, stamps )
def retrieve(f: (String, ModuleID, Artifact, File) => File): UpdateReport =
new UpdateReport(cachedDescriptor, configurations map { _ retrieve f }, stats, stamps)
/** Gets the report for the given configuration, or `None` if the configuration was not resolved.*/
def configuration(s: String) = configurations.find(_.configuration == s)
/** Gets the report for the given configuration, or `None` if the configuration was not resolved.*/
def configuration(s: String) = configurations.find(_.configuration == s)
/** Gets the names of all resolved configurations. This `UpdateReport` contains one `ConfigurationReport` for each configuration in this list. */
def allConfigurations: Seq[String] = configurations.map(_.configuration)
/** Gets the names of all resolved configurations. This `UpdateReport` contains one `ConfigurationReport` for each configuration in this list. */
def allConfigurations: Seq[String] = configurations.map(_.configuration)
}
/** Provides information about resolution of a single configuration.
* @param configuration the configuration this report is for.
* @param modules a seqeuence containing one report for each module resolved for this configuration.
*/
final class ConfigurationReport(val configuration: String, val modules: Seq[ModuleReport], val evicted: Seq[ModuleID])
{
override def toString = "\t" + configuration + ":\n" + modules.mkString + evicted.map("\t\t(EVICTED) " + _ + "\n").mkString
/**
* Provides information about resolution of a single configuration.
* @param configuration the configuration this report is for.
* @param modules a seqeuence containing one report for each module resolved for this configuration.
*/
final class ConfigurationReport(val configuration: String, val modules: Seq[ModuleReport], val evicted: Seq[ModuleID]) {
override def toString = "\t" + configuration + ":\n" + modules.mkString + evicted.map("\t\t(EVICTED) " + _ + "\n").mkString
/** All resolved modules for this configuration.
* For a given organization and module name, there is only one revision/`ModuleID` in this sequence.
*/
def allModules: Seq[ModuleID] = modules.map(mr => addConfiguration(mr.module))
private[this] def addConfiguration(mod: ModuleID): ModuleID = if(mod.configurations.isEmpty) mod.copy(configurations = Some(configuration)) else mod
def retrieve(f: (String, ModuleID, Artifact, File) => File): ConfigurationReport =
new ConfigurationReport(configuration, modules map { _.retrieve( (mid,art,file) => f(configuration, mid, art, file)) }, evicted)
/**
* All resolved modules for this configuration.
* For a given organization and module name, there is only one revision/`ModuleID` in this sequence.
*/
def allModules: Seq[ModuleID] = modules.map(mr => addConfiguration(mr.module))
private[this] def addConfiguration(mod: ModuleID): ModuleID = if (mod.configurations.isEmpty) mod.copy(configurations = Some(configuration)) else mod
def retrieve(f: (String, ModuleID, Artifact, File) => File): ConfigurationReport =
new ConfigurationReport(configuration, modules map { _.retrieve((mid, art, file) => f(configuration, mid, art, file)) }, evicted)
}
/** Provides information about the resolution of a module.
* This information is in the context of a specific configuration.
* @param module the `ModuleID` this report is for.
* @param artifacts the resolved artifacts for this module, paired with the File the artifact was retrieved to. This File may be in the
*/
final class ModuleReport(val module: ModuleID, val artifacts: Seq[(Artifact, File)], val missingArtifacts: Seq[Artifact])
{
override def toString =
{
val arts = artifacts.map(_.toString) ++ missingArtifacts.map(art => "(MISSING) " + art)
"\t\t" + module + ": " +
(if(arts.size <= 1) "" else "\n\t\t\t") + arts.mkString("\n\t\t\t") + "\n"
}
def retrieve(f: (ModuleID, Artifact, File) => File): ModuleReport =
new ModuleReport(module, artifacts.map { case (art,file) => (art, f(module, art, file)) }, missingArtifacts)
/**
* Provides information about the resolution of a module.
* This information is in the context of a specific configuration.
* @param module the `ModuleID` this report is for.
* @param artifacts the resolved artifacts for this module, paired with the File the artifact was retrieved to. This File may be in the
*/
final class ModuleReport(val module: ModuleID, val artifacts: Seq[(Artifact, File)], val missingArtifacts: Seq[Artifact]) {
override def toString =
{
val arts = artifacts.map(_.toString) ++ missingArtifacts.map(art => "(MISSING) " + art)
"\t\t" + module + ": " +
(if (arts.size <= 1) "" else "\n\t\t\t") + arts.mkString("\n\t\t\t") + "\n"
}
def retrieve(f: (ModuleID, Artifact, File) => File): ModuleReport =
new ModuleReport(module, artifacts.map { case (art, file) => (art, f(module, art, file)) }, missingArtifacts)
}
object UpdateReport
{
implicit def richUpdateReport(report: UpdateReport): RichUpdateReport = new RichUpdateReport(report)
object UpdateReport {
implicit def richUpdateReport(report: UpdateReport): RichUpdateReport = new RichUpdateReport(report)
/** Provides extra methods for filtering the contents of an `UpdateReport` and for obtaining references to a selected subset of the underlying files. */
final class RichUpdateReport(report: UpdateReport)
{
def recomputeStamps(): UpdateReport =
{
val files = report.cachedDescriptor +: allFiles
val stamps = files.map(f => (f, f.lastModified)).toMap
new UpdateReport(report.cachedDescriptor, report.configurations, report.stats, stamps)
}
/** Provides extra methods for filtering the contents of an `UpdateReport` and for obtaining references to a selected subset of the underlying files. */
final class RichUpdateReport(report: UpdateReport) {
def recomputeStamps(): UpdateReport =
{
val files = report.cachedDescriptor +: allFiles
val stamps = files.map(f => (f, f.lastModified)).toMap
new UpdateReport(report.cachedDescriptor, report.configurations, report.stats, stamps)
}
import DependencyFilter._
/** Obtains all successfully retrieved files in all configurations and modules. */
def allFiles: Seq[File] = matching(DependencyFilter.allPass)
import DependencyFilter._
/** Obtains all successfully retrieved files in all configurations and modules. */
def allFiles: Seq[File] = matching(DependencyFilter.allPass)
/** Obtains all successfully retrieved files in configurations, modules, and artifacts matching the specified filter. */
def matching(f: DependencyFilter): Seq[File] = select0(f).distinct
/** Obtains all successfully retrieved files in configurations, modules, and artifacts matching the specified filter. */
def matching(f: DependencyFilter): Seq[File] = select0(f).distinct
/** Obtains all successfully retrieved files matching all provided filters. An unspecified argument matches all files. */
def select(configuration: ConfigurationFilter = configurationFilter(), module: ModuleFilter = moduleFilter(), artifact: ArtifactFilter = artifactFilter()): Seq[File] =
matching(DependencyFilter.make(configuration, module, artifact))
/** Obtains all successfully retrieved files matching all provided filters. An unspecified argument matches all files. */
def select(configuration: ConfigurationFilter = configurationFilter(), module: ModuleFilter = moduleFilter(), artifact: ArtifactFilter = artifactFilter()): Seq[File] =
matching(DependencyFilter.make(configuration, module, artifact))
private[this] def select0(f: DependencyFilter): Seq[File] =
for(cReport <- report.configurations; mReport <- cReport.modules; (artifact, file) <- mReport.artifacts if f(cReport.configuration, mReport.module, artifact)) yield {
if(file == null) error("Null file: conf=" + cReport.configuration + ", module=" + mReport.module + ", art: " + artifact)
file
}
/** Constructs a new report that only contains files matching the specified filter.*/
def filter(f: DependencyFilter): UpdateReport =
moduleReportMap { (configuration, modReport) =>
import modReport._
val newArtifacts = artifacts filter { case (art, file) => f(configuration, module, art) }
val newMissing = missingArtifacts filter { art => f(configuration, module, art) }
new ModuleReport(module, newArtifacts, newMissing)
}
def substitute(f: (String, ModuleID, Seq[(Artifact, File)]) => Seq[(Artifact, File)]): UpdateReport =
moduleReportMap { (configuration, modReport) =>
val newArtifacts = f(configuration, modReport.module, modReport.artifacts)
new ModuleReport(modReport.module, newArtifacts, Nil)
}
private[this] def select0(f: DependencyFilter): Seq[File] =
for (cReport <- report.configurations; mReport <- cReport.modules; (artifact, file) <- mReport.artifacts if f(cReport.configuration, mReport.module, artifact)) yield {
if (file == null) error("Null file: conf=" + cReport.configuration + ", module=" + mReport.module + ", art: " + artifact)
file
}
def toSeq: Seq[(String, ModuleID, Artifact, File)] =
for(confReport <- report.configurations; modReport <- confReport.modules; (artifact, file) <- modReport.artifacts) yield
(confReport.configuration, modReport.module, artifact, file)
/** Constructs a new report that only contains files matching the specified filter.*/
def filter(f: DependencyFilter): UpdateReport =
moduleReportMap { (configuration, modReport) =>
import modReport._
val newArtifacts = artifacts filter { case (art, file) => f(configuration, module, art) }
val newMissing = missingArtifacts filter { art => f(configuration, module, art) }
new ModuleReport(module, newArtifacts, newMissing)
}
def substitute(f: (String, ModuleID, Seq[(Artifact, File)]) => Seq[(Artifact, File)]): UpdateReport =
moduleReportMap { (configuration, modReport) =>
val newArtifacts = f(configuration, modReport.module, modReport.artifacts)
new ModuleReport(modReport.module, newArtifacts, Nil)
}
def allMissing: Seq[(String, ModuleID, Artifact)] =
for(confReport <- report.configurations; modReport <- confReport.modules; artifact <- modReport.missingArtifacts) yield
(confReport.configuration, modReport.module, artifact)
def toSeq: Seq[(String, ModuleID, Artifact, File)] =
for (confReport <- report.configurations; modReport <- confReport.modules; (artifact, file) <- modReport.artifacts) yield (confReport.configuration, modReport.module, artifact, file)
def addMissing(f: ModuleID => Seq[Artifact]): UpdateReport =
moduleReportMap { (configuration, modReport) =>
import modReport._
new ModuleReport(module, artifacts, (missingArtifacts ++ f(module)).distinct)
}
def allMissing: Seq[(String, ModuleID, Artifact)] =
for (confReport <- report.configurations; modReport <- confReport.modules; artifact <- modReport.missingArtifacts) yield (confReport.configuration, modReport.module, artifact)
def moduleReportMap(f: (String, ModuleReport) => ModuleReport): UpdateReport =
{
val newConfigurations = report.configurations.map { confReport =>
import confReport._
val newModules = modules map { modReport => f(configuration, modReport) }
new ConfigurationReport(configuration, newModules, evicted)
}
new UpdateReport(report.cachedDescriptor, newConfigurations, report.stats, report.stamps)
}
}
def addMissing(f: ModuleID => Seq[Artifact]): UpdateReport =
moduleReportMap { (configuration, modReport) =>
import modReport._
new ModuleReport(module, artifacts, (missingArtifacts ++ f(module)).distinct)
}
def moduleReportMap(f: (String, ModuleReport) => ModuleReport): UpdateReport =
{
val newConfigurations = report.configurations.map { confReport =>
import confReport._
val newModules = modules map { modReport => f(configuration, modReport) }
new ConfigurationReport(configuration, newModules, evicted)
}
new UpdateReport(report.cachedDescriptor, newConfigurations, report.stats, report.stamps)
}
}
}
final class UpdateStats(val resolveTime: Long, val downloadTime: Long, val downloadSize: Long, val cached: Boolean)
{
override def toString = Seq("Resolve time: " + resolveTime + " ms", "Download time: " + downloadTime + " ms", "Download size: " + downloadSize + " bytes").mkString(", ")
final class UpdateStats(val resolveTime: Long, val downloadTime: Long, val downloadSize: Long, val cached: Boolean) {
override def toString = Seq("Resolve time: " + resolveTime + " ms", "Download time: " + downloadTime + " ms", "Download size: " + downloadSize + " bytes").mkString(", ")
}

View File

@ -6,67 +6,62 @@ package impl
import StringUtilities.nonEmpty
trait DependencyBuilders
{
final implicit def toGroupID(groupID: String): GroupID =
{
nonEmpty(groupID, "Group ID")
new GroupID(groupID)
}
final implicit def toRepositoryName(name: String): RepositoryName =
{
nonEmpty(name, "Repository name")
new RepositoryName(name)
}
final implicit def moduleIDConfigurable(m: ModuleID): ModuleIDConfigurable =
{
require(m.configurations.isEmpty, "Configurations already specified for module " + m)
new ModuleIDConfigurable(m)
}
trait DependencyBuilders {
final implicit def toGroupID(groupID: String): GroupID =
{
nonEmpty(groupID, "Group ID")
new GroupID(groupID)
}
final implicit def toRepositoryName(name: String): RepositoryName =
{
nonEmpty(name, "Repository name")
new RepositoryName(name)
}
final implicit def moduleIDConfigurable(m: ModuleID): ModuleIDConfigurable =
{
require(m.configurations.isEmpty, "Configurations already specified for module " + m)
new ModuleIDConfigurable(m)
}
}
final class GroupID private[sbt] (groupID: String)
{
def % (artifactID: String) = groupArtifact(artifactID, CrossVersion.Disabled)
def %% (artifactID: String): GroupArtifactID = groupArtifact(artifactID, CrossVersion.binary)
final class GroupID private[sbt] (groupID: String) {
def %(artifactID: String) = groupArtifact(artifactID, CrossVersion.Disabled)
def %%(artifactID: String): GroupArtifactID = groupArtifact(artifactID, CrossVersion.binary)
@deprecated(deprecationMessage, "0.12.0")
def %% (artifactID: String, crossVersion: String => String) = groupArtifact(artifactID, CrossVersion.binaryMapped(crossVersion))
@deprecated(deprecationMessage, "0.12.0")
def %% (artifactID: String, alternatives: (String, String)*) = groupArtifact(artifactID, CrossVersion.binaryMapped(Map(alternatives: _*) orElse { case s => s }))
@deprecated(deprecationMessage, "0.12.0")
def %%(artifactID: String, crossVersion: String => String) = groupArtifact(artifactID, CrossVersion.binaryMapped(crossVersion))
@deprecated(deprecationMessage, "0.12.0")
def %%(artifactID: String, alternatives: (String, String)*) = groupArtifact(artifactID, CrossVersion.binaryMapped(Map(alternatives: _*) orElse { case s => s }))
private def groupArtifact(artifactID: String, cross: CrossVersion) =
{
nonEmpty(artifactID, "Artifact ID")
new GroupArtifactID(groupID, artifactID, cross)
}
private def groupArtifact(artifactID: String, cross: CrossVersion) =
{
nonEmpty(artifactID, "Artifact ID")
new GroupArtifactID(groupID, artifactID, cross)
}
private[this] def deprecationMessage = """Use the cross method on the constructed ModuleID. For example: ("a" % "b" % "1").cross(...)"""
private[this] def deprecationMessage = """Use the cross method on the constructed ModuleID. For example: ("a" % "b" % "1").cross(...)"""
}
final class GroupArtifactID private[sbt] (groupID: String, artifactID: String, crossVersion: CrossVersion)
{
def % (revision: String): ModuleID =
{
nonEmpty(revision, "Revision")
ModuleID(groupID, artifactID, revision).cross(crossVersion)
}
final class GroupArtifactID private[sbt] (groupID: String, artifactID: String, crossVersion: CrossVersion) {
def %(revision: String): ModuleID =
{
nonEmpty(revision, "Revision")
ModuleID(groupID, artifactID, revision).cross(crossVersion)
}
}
final class ModuleIDConfigurable private[sbt] (moduleID: ModuleID)
{
def % (configuration: Configuration): ModuleID = %(configuration.name)
final class ModuleIDConfigurable private[sbt] (moduleID: ModuleID) {
def %(configuration: Configuration): ModuleID = %(configuration.name)
def % (configurations: String): ModuleID =
{
nonEmpty(configurations, "Configurations")
val c = configurations
moduleID.copy(configurations = Some(c))
}
def %(configurations: String): ModuleID =
{
nonEmpty(configurations, "Configurations")
val c = configurations
moduleID.copy(configurations = Some(c))
}
}
final class RepositoryName private[sbt] (name: String)
{
def at (location: String) =
{
nonEmpty(location, "Repository location")
new MavenRepository(name, location)
}
final class RepositoryName private[sbt] (name: String) {
def at(location: String) =
{
nonEmpty(location, "Repository location")
new MavenRepository(name, location)
}
}

View File

@ -16,7 +16,7 @@ import org.apache.ivy.util.url.CredentialsStore
*/
object ErrorMessageAuthenticator {
private var securityWarningLogged = false
private def originalAuthenticator: Option[Authenticator] = {
try {
val f = classOf[Authenticator].getDeclaredField("theAuthenticator");
@ -29,7 +29,7 @@ object ErrorMessageAuthenticator {
None
}
}
private lazy val ivyOriginalField = {
val field = classOf[IvyAuthenticator].getDeclaredField("original")
field.setAccessible(true)
@ -42,17 +42,17 @@ object ErrorMessageAuthenticator {
val newOriginal = new ErrorMessageAuthenticator(original)
ivyOriginalField.set(ivy, newOriginal)
}
try Option(ivyOriginalField.get(ivy).asInstanceOf[Authenticator]) match {
case Some(alreadyThere: ErrorMessageAuthenticator) => // We're already installed, no need to do the work again.
case originalOpt => installIntoIvyImpl(originalOpt)
case originalOpt => installIntoIvyImpl(originalOpt)
} catch {
case t: Throwable =>
case t: Throwable =>
Message.debug("Error occurred will trying to install debug messages into Ivy Authentication" + t.getMessage)
}
Some(ivy)
}
/** Installs the error message authenticator so we have nicer error messages when using java's URL for downloading. */
def install() {
// Actually installs the error message authenticator.
@ -62,67 +62,68 @@ object ErrorMessageAuthenticator {
case e: SecurityException if !securityWarningLogged =>
securityWarningLogged = true;
Message.warn("Not enough permissions to set the ErorrMessageAuthenticator. "
+ "Helpful debug messages disabled!");
}
// We will try to use the original authenticator as backup authenticator.
// Since there is no getter available, so try to use some reflection to
// obtain it. If that doesn't work, assume there is no original authenticator
def doInstallIfIvy(original: Option[Authenticator]): Unit =
original match {
case Some(installed: ErrorMessageAuthenticator) => // Ignore, we're already installed
case Some(ivy: IvyAuthenticator) => installIntoIvy(ivy)
case original => doInstall(original)
}
doInstallIfIvy(originalAuthenticator)
+ "Helpful debug messages disabled!");
}
// We will try to use the original authenticator as backup authenticator.
// Since there is no getter available, so try to use some reflection to
// obtain it. If that doesn't work, assume there is no original authenticator
def doInstallIfIvy(original: Option[Authenticator]): Unit =
original match {
case Some(installed: ErrorMessageAuthenticator) => // Ignore, we're already installed
case Some(ivy: IvyAuthenticator) => installIntoIvy(ivy)
case original => doInstall(original)
}
doInstallIfIvy(originalAuthenticator)
}
}
/**
* An authenticator which just delegates to a previous authenticator and issues *nice*
* error messages on failure to find credentials.
*
* Since ivy installs its own credentials handler EVERY TIME it resolves or publishes, we want to
* An authenticator which just delegates to a previous authenticator and issues *nice*
* error messages on failure to find credentials.
*
* Since ivy installs its own credentials handler EVERY TIME it resolves or publishes, we want to
* install this one at some point and eventually ivy will capture it and use it.
*/
private[sbt] final class ErrorMessageAuthenticator(original: Option[Authenticator]) extends Authenticator {
protected override def getPasswordAuthentication(): PasswordAuthentication = {
// We're guaranteed to only get here if Ivy's authentication fails
if (!isProxyAuthentication) {
val host = getRequestingHost
// TODO - levenshtein distance "did you mean" message.
Message.error(s"Unable to find credentials for [${getRequestingPrompt} @ ${host}].")
val configuredRealms = IvyCredentialsLookup.realmsForHost.getOrElse(host, Set.empty)
if(!configuredRealms.isEmpty) {
Message.error(s" Is one of these realms mispelled for host [${host}]:")
configuredRealms foreach { realm =>
Message.error(s" * ${realm}")
}
}
protected override def getPasswordAuthentication(): PasswordAuthentication = {
// We're guaranteed to only get here if Ivy's authentication fails
if (!isProxyAuthentication) {
val host = getRequestingHost
// TODO - levenshtein distance "did you mean" message.
Message.error(s"Unable to find credentials for [${getRequestingPrompt} @ ${host}].")
val configuredRealms = IvyCredentialsLookup.realmsForHost.getOrElse(host, Set.empty)
if (!configuredRealms.isEmpty) {
Message.error(s" Is one of these realms mispelled for host [${host}]:")
configuredRealms foreach { realm =>
Message.error(s" * ${realm}")
}
// TODO - Maybe we should work on a helpful proxy message...
// TODO - To be more maven friendly, we may want to also try to grab the "first" authentication that shows up for a server and try it.
// or maybe allow that behavior to be configured, since maven users aren't used to realms (which they should be).
// Grabs the authentication that would have been provided had we not been installed...
def originalAuthentication: Option[PasswordAuthentication] = {
Authenticator.setDefault(original.getOrElse(null))
try Option(Authenticator.requestPasswordAuthentication(
getRequestingHost,
getRequestingSite,
getRequestingPort,
getRequestingProtocol,
getRequestingPrompt,
getRequestingScheme))
finally Authenticator.setDefault(this)
}
originalAuthentication.getOrElse(null)
}
}
// TODO - Maybe we should work on a helpful proxy message...
/** Returns true if this authentication if for a proxy and not for an HTTP server.
* We want to display different error messages, depending.
*/
private def isProxyAuthentication: Boolean =
getRequestorType == Authenticator.RequestorType.PROXY
// TODO - To be more maven friendly, we may want to also try to grab the "first" authentication that shows up for a server and try it.
// or maybe allow that behavior to be configured, since maven users aren't used to realms (which they should be).
// Grabs the authentication that would have been provided had we not been installed...
def originalAuthentication: Option[PasswordAuthentication] = {
Authenticator.setDefault(original.getOrElse(null))
try Option(Authenticator.requestPasswordAuthentication(
getRequestingHost,
getRequestingSite,
getRequestingPort,
getRequestingProtocol,
getRequestingPrompt,
getRequestingScheme))
finally Authenticator.setDefault(this)
}
originalAuthentication.getOrElse(null)
}
/**
* Returns true if this authentication if for a proxy and not for an HTTP server.
* We want to display different error messages, depending.
*/
private def isProxyAuthentication: Boolean =
getRequestorType == Authenticator.RequestorType.PROXY
}

View File

@ -13,7 +13,7 @@ private[sbt] case class Realm(host: String, realm: String) extends CredentialKey
/**
* Helper mechanism to improve credential related error messages.
*
*
* This evil class exposes to us the necessary information to warn on credential failure and offer
* spelling/typo suggestions.
*/
@ -21,17 +21,18 @@ private[sbt] object IvyCredentialsLookup {
/** Helper extractor for Ivy's key-value store of credentials. */
private object KeySplit {
def unapply(key: String): Option[(String,String)] = {
def unapply(key: String): Option[(String, String)] = {
key.indexOf('@') match {
case -1 => None
case n => Some(key.take(n) -> key.drop(n+1))
case n => Some(key.take(n) -> key.drop(n + 1))
}
}
}
/** Here we cheat runtime private so we can look in the credentials store.
/**
* Here we cheat runtime private so we can look in the credentials store.
*
* TODO - Don't bomb at class load time...
* TODO - Don't bomb at class load time...
*/
private val credKeyringField = {
val tmp = classOf[CredentialsStore].getDeclaredField("KEYRING")
@ -45,10 +46,10 @@ private[sbt] object IvyCredentialsLookup {
// make a clone of the set...
(map.keySet.asScala.map {
case KeySplit(realm, host) => Realm(host, realm)
case host => Host(host)
case host => Host(host)
})(collection.breakOut)
}
/**
* A mapping of host -> realms in the ivy credentials store.
*/
@ -58,6 +59,6 @@ private[sbt] object IvyCredentialsLookup {
} groupBy { realm =>
realm.host
} mapValues { realms =>
realms map (_.realm)
realms map (_.realm)
}
}

View File

@ -3,134 +3,132 @@ package ivyint
import java.io.File
import java.net.URI
import java.util.{Collection, Collections => CS}
import java.util.{ Collection, Collections => CS }
import CS.singleton
import org.apache.ivy.{core, plugins, util, Ivy}
import core.module.descriptor.{DependencyArtifactDescriptor, DefaultDependencyArtifactDescriptor}
import core.module.descriptor.{DefaultDependencyDescriptor => DDD, DependencyDescriptor}
import core.module.id.{ArtifactId,ModuleId, ModuleRevisionId}
import org.apache.ivy.{ core, plugins, util, Ivy }
import core.module.descriptor.{ DependencyArtifactDescriptor, DefaultDependencyArtifactDescriptor }
import core.module.descriptor.{ DefaultDependencyDescriptor => DDD, DependencyDescriptor }
import core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId }
import plugins.namespace.Namespace
import util.extendable.ExtendableItem
private[sbt] object MergeDescriptors
{
def mergeable(a: DependencyDescriptor, b: DependencyDescriptor): Boolean =
a.isForce == b.isForce &&
a.isChanging == b.isChanging &&
a.isTransitive == b.isTransitive &&
a.getParentRevisionId == b.getParentRevisionId &&
a.getNamespace == b.getNamespace && {
val amrid = a.getDependencyRevisionId
val bmrid = b.getDependencyRevisionId
amrid == bmrid
} && {
val adyn = a.getDynamicConstraintDependencyRevisionId
val bdyn = b.getDynamicConstraintDependencyRevisionId
adyn == bdyn
}
private[sbt] object MergeDescriptors {
def mergeable(a: DependencyDescriptor, b: DependencyDescriptor): Boolean =
a.isForce == b.isForce &&
a.isChanging == b.isChanging &&
a.isTransitive == b.isTransitive &&
a.getParentRevisionId == b.getParentRevisionId &&
a.getNamespace == b.getNamespace && {
val amrid = a.getDependencyRevisionId
val bmrid = b.getDependencyRevisionId
amrid == bmrid
} && {
val adyn = a.getDynamicConstraintDependencyRevisionId
val bdyn = b.getDynamicConstraintDependencyRevisionId
adyn == bdyn
}
def apply(a: DependencyDescriptor, b: DependencyDescriptor): DependencyDescriptor =
{
assert(mergeable(a,b))
new MergedDescriptors(a,b)
}
def apply(a: DependencyDescriptor, b: DependencyDescriptor): DependencyDescriptor =
{
assert(mergeable(a, b))
new MergedDescriptors(a, b)
}
}
// combines the artifacts, configurations, includes, and excludes for DependencyDescriptors `a` and `b`
// that otherwise have equal IDs
private final class MergedDescriptors(a: DependencyDescriptor, b: DependencyDescriptor) extends DependencyDescriptor
{
def getDependencyId = a.getDependencyId
def isForce = a.isForce
def isChanging = a.isChanging
def isTransitive = a.isTransitive
def getNamespace = a.getNamespace
def getParentRevisionId = a.getParentRevisionId
def getDependencyRevisionId = a.getDependencyRevisionId
def getDynamicConstraintDependencyRevisionId = a.getDynamicConstraintDependencyRevisionId
private final class MergedDescriptors(a: DependencyDescriptor, b: DependencyDescriptor) extends DependencyDescriptor {
def getDependencyId = a.getDependencyId
def isForce = a.isForce
def isChanging = a.isChanging
def isTransitive = a.isTransitive
def getNamespace = a.getNamespace
def getParentRevisionId = a.getParentRevisionId
def getDependencyRevisionId = a.getDependencyRevisionId
def getDynamicConstraintDependencyRevisionId = a.getDynamicConstraintDependencyRevisionId
def getModuleConfigurations = concat(a.getModuleConfigurations, b.getModuleConfigurations)
def getModuleConfigurations = concat(a.getModuleConfigurations, b.getModuleConfigurations)
def getDependencyConfigurations(moduleConfiguration: String, requestedConfiguration: String) =
concat(a.getDependencyConfigurations(moduleConfiguration, requestedConfiguration), b.getDependencyConfigurations(moduleConfiguration))
def getDependencyConfigurations(moduleConfiguration: String, requestedConfiguration: String) =
concat(a.getDependencyConfigurations(moduleConfiguration, requestedConfiguration), b.getDependencyConfigurations(moduleConfiguration))
def getDependencyConfigurations(moduleConfiguration: String) =
concat(a.getDependencyConfigurations(moduleConfiguration), b.getDependencyConfigurations(moduleConfiguration))
def getDependencyConfigurations(moduleConfiguration: String) =
concat(a.getDependencyConfigurations(moduleConfiguration), b.getDependencyConfigurations(moduleConfiguration))
def getDependencyConfigurations(moduleConfigurations: Array[String]) =
concat(a.getDependencyConfigurations(moduleConfigurations), b.getDependencyConfigurations(moduleConfigurations))
def getDependencyConfigurations(moduleConfigurations: Array[String]) =
concat(a.getDependencyConfigurations(moduleConfigurations), b.getDependencyConfigurations(moduleConfigurations))
def getAllDependencyArtifacts = concatArtifacts(a, a.getAllDependencyArtifacts, b, b.getAllDependencyArtifacts)
def getAllDependencyArtifacts = concatArtifacts(a, a.getAllDependencyArtifacts, b, b.getAllDependencyArtifacts)
def getDependencyArtifacts(moduleConfigurations: String) =
concatArtifacts(a, a.getDependencyArtifacts(moduleConfigurations), b, b.getDependencyArtifacts(moduleConfigurations))
def getDependencyArtifacts(moduleConfigurations: String) =
concatArtifacts(a, a.getDependencyArtifacts(moduleConfigurations), b, b.getDependencyArtifacts(moduleConfigurations))
def getDependencyArtifacts(moduleConfigurations: Array[String]) =
concatArtifacts(a, a.getDependencyArtifacts(moduleConfigurations), b, b.getDependencyArtifacts(moduleConfigurations))
def getDependencyArtifacts(moduleConfigurations: Array[String]) =
concatArtifacts(a, a.getDependencyArtifacts(moduleConfigurations), b, b.getDependencyArtifacts(moduleConfigurations))
def getAllIncludeRules = concat(a.getAllIncludeRules, b.getAllIncludeRules)
def getAllIncludeRules = concat(a.getAllIncludeRules, b.getAllIncludeRules)
def getIncludeRules(moduleConfigurations: String) =
concat(a.getIncludeRules(moduleConfigurations), b.getIncludeRules(moduleConfigurations))
def getIncludeRules(moduleConfigurations: String) =
concat(a.getIncludeRules(moduleConfigurations), b.getIncludeRules(moduleConfigurations))
def getIncludeRules(moduleConfigurations: Array[String]) =
concat(a.getIncludeRules(moduleConfigurations), b.getIncludeRules(moduleConfigurations))
def getIncludeRules(moduleConfigurations: Array[String]) =
concat(a.getIncludeRules(moduleConfigurations), b.getIncludeRules(moduleConfigurations))
private[this] def concatArtifacts(a: DependencyDescriptor, as: Array[DependencyArtifactDescriptor], b: DependencyDescriptor, bs: Array[DependencyArtifactDescriptor]) =
{
if(as.isEmpty)
if(bs.isEmpty) as
else defaultArtifact(a) +: explicitConfigurations(b, bs)
else if(bs.isEmpty) explicitConfigurations(a, as) :+ defaultArtifact(b)
else concat(explicitConfigurations(a, as), explicitConfigurations(b, bs))
}
private[this] def explicitConfigurations(base: DependencyDescriptor, arts: Array[DependencyArtifactDescriptor]): Array[DependencyArtifactDescriptor] =
arts map { art => explicitConfigurations(base, art) }
private[this] def explicitConfigurations(base: DependencyDescriptor, art: DependencyArtifactDescriptor): DependencyArtifactDescriptor =
{
val aConfs = art.getConfigurations
if(aConfs == null || aConfs.isEmpty)
copyWithConfigurations(art, base.getModuleConfigurations)
else
art
}
private[this] def defaultArtifact(a: DependencyDescriptor): DependencyArtifactDescriptor =
{
val dd = new DefaultDependencyArtifactDescriptor(a, a.getDependencyRevisionId.getName, "jar", "jar", null, null)
addConfigurations(dd, a.getModuleConfigurations)
dd
}
private[this] def copyWithConfigurations(dd: DependencyArtifactDescriptor, confs: Seq[String]): DependencyArtifactDescriptor =
{
val dextra = dd.getQualifiedExtraAttributes
val newd = new DefaultDependencyArtifactDescriptor(dd.getDependencyDescriptor, dd.getName, dd.getType, dd.getExt, dd.getUrl, dextra)
addConfigurations(newd, confs)
newd
}
private[this] def addConfigurations(dd: DefaultDependencyArtifactDescriptor, confs: Seq[String]): Unit =
confs foreach dd.addConfiguration
private[this] def concatArtifacts(a: DependencyDescriptor, as: Array[DependencyArtifactDescriptor], b: DependencyDescriptor, bs: Array[DependencyArtifactDescriptor]) =
{
if (as.isEmpty)
if (bs.isEmpty) as
else defaultArtifact(a) +: explicitConfigurations(b, bs)
else if (bs.isEmpty) explicitConfigurations(a, as) :+ defaultArtifact(b)
else concat(explicitConfigurations(a, as), explicitConfigurations(b, bs))
}
private[this] def explicitConfigurations(base: DependencyDescriptor, arts: Array[DependencyArtifactDescriptor]): Array[DependencyArtifactDescriptor] =
arts map { art => explicitConfigurations(base, art) }
private[this] def explicitConfigurations(base: DependencyDescriptor, art: DependencyArtifactDescriptor): DependencyArtifactDescriptor =
{
val aConfs = art.getConfigurations
if (aConfs == null || aConfs.isEmpty)
copyWithConfigurations(art, base.getModuleConfigurations)
else
art
}
private[this] def defaultArtifact(a: DependencyDescriptor): DependencyArtifactDescriptor =
{
val dd = new DefaultDependencyArtifactDescriptor(a, a.getDependencyRevisionId.getName, "jar", "jar", null, null)
addConfigurations(dd, a.getModuleConfigurations)
dd
}
private[this] def copyWithConfigurations(dd: DependencyArtifactDescriptor, confs: Seq[String]): DependencyArtifactDescriptor =
{
val dextra = dd.getQualifiedExtraAttributes
val newd = new DefaultDependencyArtifactDescriptor(dd.getDependencyDescriptor, dd.getName, dd.getType, dd.getExt, dd.getUrl, dextra)
addConfigurations(newd, confs)
newd
}
private[this] def addConfigurations(dd: DefaultDependencyArtifactDescriptor, confs: Seq[String]): Unit =
confs foreach dd.addConfiguration
private[this] def concat[T: ClassManifest](a: Array[T], b: Array[T]): Array[T] = (a ++ b).distinct.toArray
private[this] def concat[T: ClassManifest](a: Array[T], b: Array[T]): Array[T] = (a ++ b).distinct.toArray
def getAllExcludeRules = concat(a.getAllExcludeRules, b.getAllExcludeRules)
def getAllExcludeRules = concat(a.getAllExcludeRules, b.getAllExcludeRules)
def getExcludeRules(moduleConfigurations: String) = concat(a.getExcludeRules(moduleConfigurations), b.getExcludeRules(moduleConfigurations))
def getExcludeRules(moduleConfigurations: String) = concat(a.getExcludeRules(moduleConfigurations), b.getExcludeRules(moduleConfigurations))
def getExcludeRules(moduleConfigurations: Array[String]) = concat(a.getExcludeRules(moduleConfigurations), b.getExcludeRules(moduleConfigurations))
def getExcludeRules(moduleConfigurations: Array[String]) = concat(a.getExcludeRules(moduleConfigurations), b.getExcludeRules(moduleConfigurations))
def doesExclude(moduleConfigurations: Array[String], artifactId: ArtifactId) = a.doesExclude(moduleConfigurations, artifactId) || b.doesExclude(moduleConfigurations, artifactId)
def doesExclude(moduleConfigurations: Array[String], artifactId: ArtifactId) = a.doesExclude(moduleConfigurations, artifactId) || b.doesExclude(moduleConfigurations, artifactId)
def canExclude = a.canExclude || b.canExclude
def canExclude = a.canExclude || b.canExclude
def asSystem = this
def asSystem = this
def clone(revision: ModuleRevisionId) = new MergedDescriptors(a.clone(revision), b.clone(revision))
def clone(revision: ModuleRevisionId) = new MergedDescriptors(a.clone(revision), b.clone(revision))
def getAttribute(name: String): String = a.getAttribute(name)
def getAttributes = a.getAttributes
def getExtraAttribute(name: String) = a.getExtraAttribute(name)
def getExtraAttributes = a.getExtraAttributes
def getQualifiedExtraAttributes = a.getQualifiedExtraAttributes
def getSourceModule = a.getSourceModule
def getAttribute(name: String): String = a.getAttribute(name)
def getAttributes = a.getAttributes
def getExtraAttribute(name: String) = a.getExtraAttribute(name)
def getExtraAttributes = a.getExtraAttributes
def getQualifiedExtraAttributes = a.getQualifiedExtraAttributes
def getSourceModule = a.getSourceModule
}