diff --git a/ivy/src/main/scala/sbt/Artifact.scala b/ivy/src/main/scala/sbt/Artifact.scala index 5bd17ef0d..325ef7b6b 100644 --- a/ivy/src/main/scala/sbt/Artifact.scala +++ b/ivy/src/main/scala/sbt/Artifact.scala @@ -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) } diff --git a/ivy/src/main/scala/sbt/ComponentManager.scala b/ivy/src/main/scala/sbt/ComponentManager.scala index de87a9890..2d6c5a447 100644 --- a/ivy/src/main/scala/sbt/ComponentManager.scala +++ b/ivy/src/main/scala/sbt/ComponentManager.scala @@ -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") } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/Configuration.scala b/ivy/src/main/scala/sbt/Configuration.scala index a259f99d7..db4ada450 100644 --- a/ivy/src/main/scala/sbt/Configuration.scala +++ b/ivy/src/main/scala/sbt/Configuration.scala @@ -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 } diff --git a/ivy/src/main/scala/sbt/ConflictWarning.scala b/ivy/src/main/scala/sbt/ConflictWarning.scala index 51c69e988..7c5c9a918 100644 --- a/ivy/src/main/scala/sbt/ConflictWarning.scala +++ b/ivy/src/main/scala/sbt/ConflictWarning.scala @@ -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 _ => "" - } + 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 _ => "" + } } diff --git a/ivy/src/main/scala/sbt/ConvertResolver.scala b/ivy/src/main/scala/sbt/ConvertResolver.scala index 74c5c119c..613de970e 100644 --- a/ivy/src/main/scala/sbt/ConvertResolver.scala +++ b/ivy/src/main/scala/sbt/ConvertResolver.scala @@ -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) + } + } + } } diff --git a/ivy/src/main/scala/sbt/Credentials.scala b/ivy/src/main/scala/sbt/Credentials.scala index 882e1c22b..fd6a85538 100644 --- a/ivy/src/main/scala/sbt/Credentials.scala +++ b/ivy/src/main/scala/sbt/Credentials.scala @@ -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 diff --git a/ivy/src/main/scala/sbt/CrossVersion.scala b/ivy/src/main/scala/sbt/CrossVersion.scala index 3425bb1f5..79dfe091e 100644 --- a/ivy/src/main/scala/sbt/CrossVersion.scala +++ b/ivy/src/main/scala/sbt/CrossVersion.scala @@ -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) } diff --git a/ivy/src/main/scala/sbt/CustomPomParser.scala b/ivy/src/main/scala/sbt/CustomPomParser.scala index 871c1f07c..12ae48675 100644 --- a/ivy/src/main/scala/sbt/CustomPomParser.scala +++ b/ivy/src/main/scala/sbt/CustomPomParser.scala @@ -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 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 element of the pom. + // These are attached to the module itself. + val filtered = shouldBeUnqualified(properties) - // Extracts extra attributes for the dependencies. - // Because the 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 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 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 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 element - // with the extra attributes from the 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 element + // with the extra attributes from the 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 + } } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/CustomXmlParser.scala b/ivy/src/main/scala/sbt/CustomXmlParser.scala index da44743f6..0afa87fbd 100644 --- a/ivy/src/main/scala/sbt/CustomXmlParser.scala +++ b/ivy/src/main/scala/sbt/CustomXmlParser.scala @@ -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) + } } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/DependencyFilter.scala b/ivy/src/main/scala/sbt/DependencyFilter.scala index e31cd4e24..fee8da46c 100644 --- a/ivy/src/main/scala/sbt/DependencyFilter.scala +++ b/ivy/src/main/scala/sbt/DependencyFilter.scala @@ -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) } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/Ivy.scala b/ivy/src/main/scala/sbt/Ivy.scala index a6519c6bc..be029c422 100644 --- a/ivy/src/main/scala/sbt/Ivy.scala +++ b/ivy/src/main/scala/sbt/Ivy.scala @@ -8,636 +8,617 @@ import Resolver.PluginPattern import java.io.File import java.net.URI import java.util.concurrent.Callable -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.{IvyPatternHelper, LogOptions} -import core.cache.{CacheMetadataOptions, DefaultRepositoryCacheManager, ModuleDescriptorWriter} -import core.module.descriptor.{Artifact => IArtifact, DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact} -import core.module.descriptor.{DefaultDependencyDescriptor, DefaultModuleDescriptor, DependencyDescriptor, ModuleDescriptor, License} -import core.module.descriptor.{OverrideDependencyDescriptorMediator} -import core.module.id.{ArtifactId,ModuleId, ModuleRevisionId} -import core.resolve.{IvyNode, ResolveData, ResolvedModuleRevision} +import org.apache.ivy.{ core, plugins, util, Ivy } +import core.{ IvyPatternHelper, LogOptions } +import core.cache.{ CacheMetadataOptions, DefaultRepositoryCacheManager, ModuleDescriptorWriter } +import core.module.descriptor.{ Artifact => IArtifact, DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact } +import core.module.descriptor.{ DefaultDependencyDescriptor, DefaultModuleDescriptor, DependencyDescriptor, ModuleDescriptor, License } +import core.module.descriptor.{ OverrideDependencyDescriptorMediator } +import core.module.id.{ ArtifactId, ModuleId, ModuleRevisionId } +import core.resolve.{ IvyNode, ResolveData, ResolvedModuleRevision } import core.settings.IvySettings import plugins.latest.LatestRevisionStrategy import plugins.matcher.PatternMatcher import plugins.parser.m2.PomModuleDescriptorParser -import plugins.resolver.{ChainResolver, DependencyResolver} -import util.{Message, MessageLogger} +import plugins.resolver.{ ChainResolver, DependencyResolver } +import util.{ Message, MessageLogger } import util.extendable.ExtendableItem -import scala.xml.{NodeSeq, Text} +import scala.xml.{ NodeSeq, Text } -final class IvySbt(val configuration: IvyConfiguration) -{ - import configuration.baseDirectory +final class IvySbt(val configuration: IvyConfiguration) { + import configuration.baseDirectory - /** ========== Configuration/Setup ============ - * This part configures the Ivy instance by first creating the logger interface to ivy, then IvySettings, and then the Ivy instance. - * These are lazy so that they are loaded within the right context. This is important so that no Ivy XML configuration needs to be loaded, - * saving some time. This is necessary because Ivy has global state (IvyContext, Message, DocumentBuilder, ...). - */ - private def withDefaultLogger[T](logger: MessageLogger)(f: => T): T = - { - def action() = - IvySbt.synchronized - { - val originalLogger = Message.getDefaultLogger - Message.setDefaultLogger(logger) - try { f } - finally { Message.setDefaultLogger(originalLogger) } - } - // Ivy is not thread-safe nor can the cache be used concurrently. - // If provided a GlobalLock, we can use that to ensure safe access to the cache. - // Otherwise, we can at least synchronize within the JVM. - // For thread-safety in particular, Ivy uses a static DocumentBuilder, which is not thread-safe. - configuration.lock match - { - case Some(lock) => lock(ivyLockFile, new Callable[T] { def call = action() }) - case None => action() - } - } - private lazy val settings: IvySettings = - { - val is = new IvySettings - is.setBaseDir(baseDirectory) - CustomPomParser.registerDefault - configuration match - { - case e: ExternalIvyConfiguration => - IvySbt.addResolvers(e.extraResolvers, is, configuration.log) - IvySbt.loadURI(is, e.uri) - case i: InlineIvyConfiguration => - is.setVariable("ivy.checksums", i.checksums mkString ",") - i.paths.ivyHome foreach is.setDefaultIvyUserDir - IvySbt.configureCache(is, i.localOnly, i.resolutionCacheDir) - IvySbt.setResolvers(is, i.resolvers, i.otherResolvers, i.localOnly, configuration.log) - IvySbt.setModuleConfigurations(is, i.moduleConfigurations, configuration.log) - } - is - } - private lazy val ivy: Ivy = - { - val i = new Ivy() { private val loggerEngine = new SbtMessageLoggerEngine; override def getLoggerEngine = loggerEngine } - i.setSettings(settings) - i.bind() - i.getLoggerEngine.pushLogger(new IvyLoggerInterface(configuration.log)) - i - } - // Must be the same file as is used in Update in the launcher - private lazy val ivyLockFile = new File(settings.getDefaultIvyUserDir, ".sbt.ivy.lock") - /** ========== End Configuration/Setup ============*/ + /** + * ========== Configuration/Setup ============ + * This part configures the Ivy instance by first creating the logger interface to ivy, then IvySettings, and then the Ivy instance. + * These are lazy so that they are loaded within the right context. This is important so that no Ivy XML configuration needs to be loaded, + * saving some time. This is necessary because Ivy has global state (IvyContext, Message, DocumentBuilder, ...). + */ + private def withDefaultLogger[T](logger: MessageLogger)(f: => T): T = + { + def action() = + IvySbt.synchronized { + val originalLogger = Message.getDefaultLogger + Message.setDefaultLogger(logger) + try { f } + finally { Message.setDefaultLogger(originalLogger) } + } + // Ivy is not thread-safe nor can the cache be used concurrently. + // If provided a GlobalLock, we can use that to ensure safe access to the cache. + // Otherwise, we can at least synchronize within the JVM. + // For thread-safety in particular, Ivy uses a static DocumentBuilder, which is not thread-safe. + configuration.lock match { + case Some(lock) => lock(ivyLockFile, new Callable[T] { def call = action() }) + case None => action() + } + } + private lazy val settings: IvySettings = + { + val is = new IvySettings + is.setBaseDir(baseDirectory) + CustomPomParser.registerDefault + configuration match { + case e: ExternalIvyConfiguration => + IvySbt.addResolvers(e.extraResolvers, is, configuration.log) + IvySbt.loadURI(is, e.uri) + case i: InlineIvyConfiguration => + is.setVariable("ivy.checksums", i.checksums mkString ",") + i.paths.ivyHome foreach is.setDefaultIvyUserDir + IvySbt.configureCache(is, i.localOnly, i.resolutionCacheDir) + IvySbt.setResolvers(is, i.resolvers, i.otherResolvers, i.localOnly, configuration.log) + IvySbt.setModuleConfigurations(is, i.moduleConfigurations, configuration.log) + } + is + } + private lazy val ivy: Ivy = + { + val i = new Ivy() { private val loggerEngine = new SbtMessageLoggerEngine; override def getLoggerEngine = loggerEngine } + i.setSettings(settings) + i.bind() + i.getLoggerEngine.pushLogger(new IvyLoggerInterface(configuration.log)) + i + } + // Must be the same file as is used in Update in the launcher + private lazy val ivyLockFile = new File(settings.getDefaultIvyUserDir, ".sbt.ivy.lock") + /** ========== End Configuration/Setup ============*/ - /** Uses the configured Ivy instance within a safe context.*/ - def withIvy[T](log: Logger)(f: Ivy => T): T = - withIvy(new IvyLoggerInterface(log))(f) + /** Uses the configured Ivy instance within a safe context.*/ + def withIvy[T](log: Logger)(f: Ivy => T): T = + withIvy(new IvyLoggerInterface(log))(f) - def withIvy[T](log: MessageLogger)(f: Ivy => T): T = - withDefaultLogger(log) - { - // See #429 - We always insert a helper authenticator here which lets us get more useful authentication errors. - ivyint.ErrorMessageAuthenticator.install() - ivy.pushContext() - ivy.getLoggerEngine.pushLogger(log) - try { f(ivy) } - finally { - ivy.getLoggerEngine.popLogger() - ivy.popContext() - } - } + def withIvy[T](log: MessageLogger)(f: Ivy => T): T = + withDefaultLogger(log) { + // See #429 - We always insert a helper authenticator here which lets us get more useful authentication errors. + ivyint.ErrorMessageAuthenticator.install() + ivy.pushContext() + ivy.getLoggerEngine.pushLogger(log) + try { f(ivy) } + finally { + ivy.getLoggerEngine.popLogger() + ivy.popContext() + } + } - final class Module(rawModuleSettings: ModuleSettings) - { - val moduleSettings: ModuleSettings = IvySbt.substituteCross(rawModuleSettings) - def owner = IvySbt.this - def withModule[T](log: Logger)(f: (Ivy,DefaultModuleDescriptor,String) => T): T = - withIvy[T](log) { ivy => f(ivy, moduleDescriptor0, defaultConfig0) } + final class Module(rawModuleSettings: ModuleSettings) { + val moduleSettings: ModuleSettings = IvySbt.substituteCross(rawModuleSettings) + def owner = IvySbt.this + def withModule[T](log: Logger)(f: (Ivy, DefaultModuleDescriptor, String) => T): T = + withIvy[T](log) { ivy => f(ivy, moduleDescriptor0, defaultConfig0) } - def moduleDescriptor(log: Logger): DefaultModuleDescriptor = withModule(log)((_,md,_) => md) - def dependencyMapping(log: Logger): (ModuleRevisionId, ModuleDescriptor) = - { - val md = moduleDescriptor(log) - (md.getModuleRevisionId, md) - } - def defaultConfig(log: Logger): String = withModule(log)( (_,_,dc) => dc) - // these should only be referenced by withModule because lazy vals synchronize on this object - // withIvy explicitly locks the IvySbt object, so they have to be done in the right order to avoid deadlock - private[this] lazy val (moduleDescriptor0: DefaultModuleDescriptor, defaultConfig0: String) = - { - val (baseModule, baseConfiguration) = - moduleSettings match - { - case ic: InlineConfiguration => configureInline(ic, configuration.log) - case ec: EmptyConfiguration => configureEmpty(ec) - case pc: PomConfiguration => configurePom(pc) - case ifc: IvyFileConfiguration => configureIvyFile(ifc) - } - moduleSettings.ivyScala.foreach(IvyScala.checkModule(baseModule, baseConfiguration, configuration.log)) - IvySbt.addExtraNamespace(baseModule) - (baseModule, baseConfiguration) - } - private def configureInline(ic: InlineConfiguration, log: Logger) = - { - import ic._ - val moduleID = newConfiguredModuleID(module, moduleInfo, configurations) - IvySbt.setConflictManager(moduleID, conflictManager, ivy.getSettings) - val defaultConf = defaultConfiguration getOrElse Configurations.config(ModuleDescriptor.DEFAULT_CONFIGURATION) - log.debug("Using inline dependencies specified in Scala" + (if(ivyXML.isEmpty) "." else " and XML.")) + def moduleDescriptor(log: Logger): DefaultModuleDescriptor = withModule(log)((_, md, _) => md) + def dependencyMapping(log: Logger): (ModuleRevisionId, ModuleDescriptor) = + { + val md = moduleDescriptor(log) + (md.getModuleRevisionId, md) + } + def defaultConfig(log: Logger): String = withModule(log)((_, _, dc) => dc) + // these should only be referenced by withModule because lazy vals synchronize on this object + // withIvy explicitly locks the IvySbt object, so they have to be done in the right order to avoid deadlock + private[this] lazy val (moduleDescriptor0: DefaultModuleDescriptor, defaultConfig0: String) = + { + val (baseModule, baseConfiguration) = + moduleSettings match { + case ic: InlineConfiguration => configureInline(ic, configuration.log) + case ec: EmptyConfiguration => configureEmpty(ec) + case pc: PomConfiguration => configurePom(pc) + case ifc: IvyFileConfiguration => configureIvyFile(ifc) + } + moduleSettings.ivyScala.foreach(IvyScala.checkModule(baseModule, baseConfiguration, configuration.log)) + IvySbt.addExtraNamespace(baseModule) + (baseModule, baseConfiguration) + } + private def configureInline(ic: InlineConfiguration, log: Logger) = + { + import ic._ + val moduleID = newConfiguredModuleID(module, moduleInfo, configurations) + IvySbt.setConflictManager(moduleID, conflictManager, ivy.getSettings) + val defaultConf = defaultConfiguration getOrElse Configurations.config(ModuleDescriptor.DEFAULT_CONFIGURATION) + log.debug("Using inline dependencies specified in Scala" + (if (ivyXML.isEmpty) "." else " and XML.")) - val parser = IvySbt.parseIvyXML(ivy.getSettings, IvySbt.wrapped(module, ivyXML), moduleID, defaultConf.name, validate) - IvySbt.addMainArtifact(moduleID) - IvySbt.addOverrides(moduleID, overrides, ivy.getSettings.getMatcher(PatternMatcher.EXACT)) - val transformedDeps = IvySbt.overrideDirect(dependencies, overrides) - IvySbt.addDependencies(moduleID, transformedDeps, parser) - (moduleID, parser.getDefaultConf) - } - private def newConfiguredModuleID(module: ModuleID, moduleInfo: ModuleInfo, configurations: Iterable[Configuration]) = - { - val mod = new DefaultModuleDescriptor(IvySbt.toID(module), "release", null, false) - mod.setLastModified(System.currentTimeMillis) - mod.setDescription(moduleInfo.description) - moduleInfo.homepage foreach { h => mod.setHomePage(h.toString) } - moduleInfo.licenses foreach { l => mod.addLicense(new License(l._1, l._2.toString)) } - IvySbt.addConfigurations(mod, configurations) - IvySbt.addArtifacts(mod, module.explicitArtifacts) - mod - } + val parser = IvySbt.parseIvyXML(ivy.getSettings, IvySbt.wrapped(module, ivyXML), moduleID, defaultConf.name, validate) + IvySbt.addMainArtifact(moduleID) + IvySbt.addOverrides(moduleID, overrides, ivy.getSettings.getMatcher(PatternMatcher.EXACT)) + val transformedDeps = IvySbt.overrideDirect(dependencies, overrides) + IvySbt.addDependencies(moduleID, transformedDeps, parser) + (moduleID, parser.getDefaultConf) + } + private def newConfiguredModuleID(module: ModuleID, moduleInfo: ModuleInfo, configurations: Iterable[Configuration]) = + { + val mod = new DefaultModuleDescriptor(IvySbt.toID(module), "release", null, false) + mod.setLastModified(System.currentTimeMillis) + mod.setDescription(moduleInfo.description) + moduleInfo.homepage foreach { h => mod.setHomePage(h.toString) } + moduleInfo.licenses foreach { l => mod.addLicense(new License(l._1, l._2.toString)) } + IvySbt.addConfigurations(mod, configurations) + IvySbt.addArtifacts(mod, module.explicitArtifacts) + mod + } - /** Parses the Maven pom 'pomFile' from the given `PomConfiguration`.*/ - private def configurePom(pc: PomConfiguration) = - { - val md = CustomPomParser.default.parseDescriptor(settings, toURL(pc.file), pc.validate) - val dmd = IvySbt.toDefaultModuleDescriptor(md) - IvySbt.addConfigurations(dmd, Configurations.defaultInternal) - val defaultConf = Configurations.DefaultMavenConfiguration.name - for( is <- pc.ivyScala) if(pc.autoScalaTools) { - val confParser = new CustomXmlParser.CustomParser(settings, Some(defaultConf)) - confParser.setMd(dmd) - addScalaToolDependencies(dmd, confParser, is) - } - (dmd, defaultConf) - } - /** Parses the Ivy file 'ivyFile' from the given `IvyFileConfiguration`.*/ - private def configureIvyFile(ifc: IvyFileConfiguration) = - { - val parser = new CustomXmlParser.CustomParser(settings, None) - parser.setValidate(ifc.validate) - parser.setSource(toURL(ifc.file)) - parser.parse() - val dmd = IvySbt.toDefaultModuleDescriptor(parser.getModuleDescriptor()) - for( is <- ifc.ivyScala ) if(ifc.autoScalaTools) - addScalaToolDependencies(dmd, parser, is) - (dmd, parser.getDefaultConf) - } - private def addScalaToolDependencies(dmd: DefaultModuleDescriptor, parser: CustomXmlParser.CustomParser, is: IvyScala) { - IvySbt.addConfigurations(dmd, Configurations.ScalaTool :: Nil) - IvySbt.addDependencies(dmd, ScalaArtifacts.toolDependencies(is.scalaOrganization, is.scalaFullVersion), parser) - } - private def toURL(file: File) = file.toURI.toURL - private def configureEmpty(ec: EmptyConfiguration) = - { - val defaultConf = ModuleDescriptor.DEFAULT_CONFIGURATION - val mod = newConfiguredModuleID(ec.module, ec.moduleInfo, Seq(Configurations.Default)) - IvySbt.addMainArtifact(mod) - (mod, defaultConf) - } - } + /** Parses the Maven pom 'pomFile' from the given `PomConfiguration`.*/ + private def configurePom(pc: PomConfiguration) = + { + val md = CustomPomParser.default.parseDescriptor(settings, toURL(pc.file), pc.validate) + val dmd = IvySbt.toDefaultModuleDescriptor(md) + IvySbt.addConfigurations(dmd, Configurations.defaultInternal) + val defaultConf = Configurations.DefaultMavenConfiguration.name + for (is <- pc.ivyScala) if (pc.autoScalaTools) { + val confParser = new CustomXmlParser.CustomParser(settings, Some(defaultConf)) + confParser.setMd(dmd) + addScalaToolDependencies(dmd, confParser, is) + } + (dmd, defaultConf) + } + /** Parses the Ivy file 'ivyFile' from the given `IvyFileConfiguration`.*/ + private def configureIvyFile(ifc: IvyFileConfiguration) = + { + val parser = new CustomXmlParser.CustomParser(settings, None) + parser.setValidate(ifc.validate) + parser.setSource(toURL(ifc.file)) + parser.parse() + val dmd = IvySbt.toDefaultModuleDescriptor(parser.getModuleDescriptor()) + for (is <- ifc.ivyScala) if (ifc.autoScalaTools) + addScalaToolDependencies(dmd, parser, is) + (dmd, parser.getDefaultConf) + } + private def addScalaToolDependencies(dmd: DefaultModuleDescriptor, parser: CustomXmlParser.CustomParser, is: IvyScala) { + IvySbt.addConfigurations(dmd, Configurations.ScalaTool :: Nil) + IvySbt.addDependencies(dmd, ScalaArtifacts.toolDependencies(is.scalaOrganization, is.scalaFullVersion), parser) + } + private def toURL(file: File) = file.toURI.toURL + private def configureEmpty(ec: EmptyConfiguration) = + { + val defaultConf = ModuleDescriptor.DEFAULT_CONFIGURATION + val mod = newConfiguredModuleID(ec.module, ec.moduleInfo, Seq(Configurations.Default)) + IvySbt.addMainArtifact(mod) + (mod, defaultConf) + } + } } -private object IvySbt -{ - val DefaultIvyConfigFilename = "ivysettings.xml" - val DefaultIvyFilename = "ivy.xml" - val DefaultMavenFilename = "pom.xml" - val DefaultChecksums = Seq("sha1", "md5") +private object IvySbt { + val DefaultIvyConfigFilename = "ivysettings.xml" + val DefaultIvyFilename = "ivy.xml" + val DefaultMavenFilename = "pom.xml" + val DefaultChecksums = Seq("sha1", "md5") - def defaultIvyFile(project: File) = new File(project, DefaultIvyFilename) - def defaultIvyConfiguration(project: File) = new File(project, DefaultIvyConfigFilename) - def defaultPOM(project: File) = new File(project, DefaultMavenFilename) + def defaultIvyFile(project: File) = new File(project, DefaultIvyFilename) + def defaultIvyConfiguration(project: File) = new File(project, DefaultIvyConfigFilename) + def defaultPOM(project: File) = new File(project, DefaultMavenFilename) - def loadURI(is: IvySettings, uri: URI) - { - if(uri.getScheme == "file") - is.load(new File(uri)) // IVY-1114 - else - is.load(uri.toURL) - } + def loadURI(is: IvySettings, uri: URI) { + if (uri.getScheme == "file") + is.load(new File(uri)) // IVY-1114 + else + is.load(uri.toURL) + } - /** Sets the resolvers for 'settings' to 'resolvers'. This is done by creating a new chain and making it the default. - * 'other' is for resolvers that should be in a different chain. These are typically used for publishing or other actions. */ - private def setResolvers(settings: IvySettings, resolvers: Seq[Resolver], other: Seq[Resolver], localOnly: Boolean, log: Logger) - { - def makeChain(label: String, name: String, rs: Seq[Resolver]) = { - log.debug(label + " repositories:") - val chain = resolverChain(name, rs, localOnly, settings, log) - settings.addResolver(chain) - chain - } - val otherChain = makeChain("Other", "sbt-other", other) - val mainChain = makeChain("Default", "sbt-chain", resolvers) - settings.setDefaultResolver(mainChain.getName) - } - def resolverChain(name: String, resolvers: Seq[Resolver], localOnly: Boolean, settings: IvySettings, log: Logger): DependencyResolver = - { - val newDefault = new ChainResolver { - // Technically, this should be applied to module configurations. - // That would require custom subclasses of all resolver types in ConvertResolver (a delegation approach does not work). - // It would be better to get proper support into Ivy. - // A workaround is to configure the ModuleConfiguration resolver to be a ChainResolver. - // - // This method is only used by the pom parsing code in Ivy to find artifacts it doesn't know about. - // In particular, a) it looks up source and javadoc classifiers b) it looks up a main artifact for packaging="pom" - // sbt now provides the update-classifiers or requires explicitly specifying classifiers explicitly - // Providing a main artifact for packaging="pom" does not seem to be correct and the lookup can be expensive. - // - // Ideally this could just skip the lookup, but unfortunately several artifacts in practice do not follow the - // correct behavior for packaging="pom" and so it is only skipped for source/javadoc classifiers. - override def locate(artifact: IArtifact) = if(hasImplicitClassifier(artifact)) null else super.locate(artifact) + /** + * Sets the resolvers for 'settings' to 'resolvers'. This is done by creating a new chain and making it the default. + * 'other' is for resolvers that should be in a different chain. These are typically used for publishing or other actions. + */ + private def setResolvers(settings: IvySettings, resolvers: Seq[Resolver], other: Seq[Resolver], localOnly: Boolean, log: Logger) { + def makeChain(label: String, name: String, rs: Seq[Resolver]) = { + log.debug(label + " repositories:") + val chain = resolverChain(name, rs, localOnly, settings, log) + settings.addResolver(chain) + chain + } + val otherChain = makeChain("Other", "sbt-other", other) + val mainChain = makeChain("Default", "sbt-chain", resolvers) + settings.setDefaultResolver(mainChain.getName) + } + def resolverChain(name: String, resolvers: Seq[Resolver], localOnly: Boolean, settings: IvySettings, log: Logger): DependencyResolver = + { + val newDefault = new ChainResolver { + // Technically, this should be applied to module configurations. + // That would require custom subclasses of all resolver types in ConvertResolver (a delegation approach does not work). + // It would be better to get proper support into Ivy. + // A workaround is to configure the ModuleConfiguration resolver to be a ChainResolver. + // + // This method is only used by the pom parsing code in Ivy to find artifacts it doesn't know about. + // In particular, a) it looks up source and javadoc classifiers b) it looks up a main artifact for packaging="pom" + // sbt now provides the update-classifiers or requires explicitly specifying classifiers explicitly + // Providing a main artifact for packaging="pom" does not seem to be correct and the lookup can be expensive. + // + // Ideally this could just skip the lookup, but unfortunately several artifacts in practice do not follow the + // correct behavior for packaging="pom" and so it is only skipped for source/javadoc classifiers. + override def locate(artifact: IArtifact) = if (hasImplicitClassifier(artifact)) null else super.locate(artifact) - override def getDependency(dd: DependencyDescriptor, data: ResolveData) = - { - if(data.getOptions.getLog != LogOptions.LOG_QUIET) - Message.info("Resolving " + dd.getDependencyRevisionId + " ...") - val gd = super.getDependency(dd, data) - resetArtifactResolver(gd) - } - } - newDefault.setName(name) - newDefault.setReturnFirst(true) - newDefault.setCheckmodified(false) - for(sbtResolver <- resolvers) { - log.debug("\t" + sbtResolver) - newDefault.add(ConvertResolver(sbtResolver, settings, log)) - } - newDefault - } - def addResolvers(resolvers: Seq[Resolver], settings: IvySettings, log: Logger) - { - for(r <- resolvers) { - log.debug("\t" + r) - settings.addResolver(ConvertResolver(r, settings, log)) - } - } - /** A hack to detect if the given artifact is an automatically generated request for a classifier, - * as opposed to a user-initiated declaration. It relies on Ivy prefixing classifier with m:, while sbt uses e:. - * Clearly, it would be better to have an explicit option in Ivy to control this.*/ - def hasImplicitClassifier(artifact: IArtifact): Boolean = - { - import collection.JavaConversions._ - artifact.getQualifiedExtraAttributes.keys.exists(_.asInstanceOf[String] startsWith "m:") - } - private def setModuleConfigurations(settings: IvySettings, moduleConfigurations: Seq[ModuleConfiguration], log: Logger) - { - val existing = settings.getResolverNames - for(moduleConf <- moduleConfigurations) - { - import moduleConf._ - import IvyPatternHelper._ - import PatternMatcher._ - if(!existing.contains(resolver.name)) - settings.addResolver(ConvertResolver(resolver, settings, log)) - val attributes = javaMap(Map(MODULE_KEY -> name, ORGANISATION_KEY -> organization, REVISION_KEY -> revision)) - settings.addModuleConfiguration(attributes, settings.getMatcher(EXACT_OR_REGEXP), resolver.name, null, null, null) - } - } - private def configureCache(settings: IvySettings, localOnly: Boolean, resCacheDir: Option[File]) - { - configureResolutionCache(settings, localOnly, resCacheDir) - configureRepositoryCache(settings, localOnly) - } - private[this] def configureResolutionCache(settings: IvySettings, localOnly: Boolean, resCacheDir: Option[File]) - { - val base = resCacheDir getOrElse settings.getDefaultResolutionCacheBasedir - settings.setResolutionCacheManager(new ResolutionCache(base, settings)) - } - // set the artifact resolver to be the main resolver. - // this is because sometimes the artifact resolver saved in the cache is not correct - // the common case is for resolved.getArtifactResolver to be inter-project from a different project's publish-local - // if there are problems with this, a less aggressive fix might be to only reset the artifact resolver when it is a ProjectResolver - // a possible problem is that fetching artifacts is slower, due to the full chain being the artifact resolver instead of the specific resolver - // This also fixes #760, which occurs when metadata exists in a repository, but the artifact doesn't. - private[this] def resetArtifactResolver(resolved: ResolvedModuleRevision): ResolvedModuleRevision = - if(resolved eq null) - null - else { - val desc = resolved.getDescriptor - val updatedDescriptor = CustomPomParser.defaultTransform(desc.getParser, desc) - new ResolvedModuleRevision(resolved.getResolver, resolved.getResolver, updatedDescriptor, resolved.getReport, resolved.isForce) - } + override def getDependency(dd: DependencyDescriptor, data: ResolveData) = + { + if (data.getOptions.getLog != LogOptions.LOG_QUIET) + Message.info("Resolving " + dd.getDependencyRevisionId + " ...") + val gd = super.getDependency(dd, data) + resetArtifactResolver(gd) + } + } + newDefault.setName(name) + newDefault.setReturnFirst(true) + newDefault.setCheckmodified(false) + for (sbtResolver <- resolvers) { + log.debug("\t" + sbtResolver) + newDefault.add(ConvertResolver(sbtResolver, settings, log)) + } + newDefault + } + def addResolvers(resolvers: Seq[Resolver], settings: IvySettings, log: Logger) { + for (r <- resolvers) { + log.debug("\t" + r) + settings.addResolver(ConvertResolver(r, settings, log)) + } + } + /** + * A hack to detect if the given artifact is an automatically generated request for a classifier, + * as opposed to a user-initiated declaration. It relies on Ivy prefixing classifier with m:, while sbt uses e:. + * Clearly, it would be better to have an explicit option in Ivy to control this. + */ + def hasImplicitClassifier(artifact: IArtifact): Boolean = + { + import collection.JavaConversions._ + artifact.getQualifiedExtraAttributes.keys.exists(_.asInstanceOf[String] startsWith "m:") + } + private def setModuleConfigurations(settings: IvySettings, moduleConfigurations: Seq[ModuleConfiguration], log: Logger) { + val existing = settings.getResolverNames + for (moduleConf <- moduleConfigurations) { + import moduleConf._ + import IvyPatternHelper._ + import PatternMatcher._ + if (!existing.contains(resolver.name)) + settings.addResolver(ConvertResolver(resolver, settings, log)) + val attributes = javaMap(Map(MODULE_KEY -> name, ORGANISATION_KEY -> organization, REVISION_KEY -> revision)) + settings.addModuleConfiguration(attributes, settings.getMatcher(EXACT_OR_REGEXP), resolver.name, null, null, null) + } + } + private def configureCache(settings: IvySettings, localOnly: Boolean, resCacheDir: Option[File]) { + configureResolutionCache(settings, localOnly, resCacheDir) + configureRepositoryCache(settings, localOnly) + } + private[this] def configureResolutionCache(settings: IvySettings, localOnly: Boolean, resCacheDir: Option[File]) { + val base = resCacheDir getOrElse settings.getDefaultResolutionCacheBasedir + settings.setResolutionCacheManager(new ResolutionCache(base, settings)) + } + // set the artifact resolver to be the main resolver. + // this is because sometimes the artifact resolver saved in the cache is not correct + // the common case is for resolved.getArtifactResolver to be inter-project from a different project's publish-local + // if there are problems with this, a less aggressive fix might be to only reset the artifact resolver when it is a ProjectResolver + // a possible problem is that fetching artifacts is slower, due to the full chain being the artifact resolver instead of the specific resolver + // This also fixes #760, which occurs when metadata exists in a repository, but the artifact doesn't. + private[this] def resetArtifactResolver(resolved: ResolvedModuleRevision): ResolvedModuleRevision = + if (resolved eq null) + null + else { + val desc = resolved.getDescriptor + val updatedDescriptor = CustomPomParser.defaultTransform(desc.getParser, desc) + new ResolvedModuleRevision(resolved.getResolver, resolved.getResolver, updatedDescriptor, resolved.getReport, resolved.isForce) + } - private[this] def configureRepositoryCache(settings: IvySettings, localOnly: Boolean) //, artifactResolver: DependencyResolver) - { - val cacheDir = settings.getDefaultRepositoryCacheBasedir() - val manager = new DefaultRepositoryCacheManager("default-cache", settings, cacheDir) { - override def findModuleInCache(dd: DependencyDescriptor, revId: ModuleRevisionId, options: CacheMetadataOptions, r: String) = { - // ignore and reset the resolver- not ideal, but avoids thrashing. - val resolved = resetArtifactResolver(super.findModuleInCache(dd,revId,options,null)) - // invalidate the cache if the artifact was removed from the local repository - if(resolved == null) null - else if(isProjectResolver(resolved.getResolver)) { - resolved.getReport.getLocalFile.delete() - null - } else { - val origin = resolved.getReport.getArtifactOrigin - if(!origin.isLocal) resolved - else { - val file = new File(origin.getLocation) - if(file == null || file.exists) resolved - else { - resolved.getReport.getLocalFile.delete() - null - } - } - } - } - private[this] def isProjectResolver(r: DependencyResolver): Boolean = r match { - case pr: ProjectResolver => true - case _ => false - } - // ignore the original resolver wherever possible to avoid issues like #704 - override def saveResolvers(descriptor: ModuleDescriptor, metadataResolverName: String, artifactResolverName: String) {} - } - manager.setArtifactPattern(PluginPattern + manager.getArtifactPattern) - manager.setDataFilePattern(PluginPattern + manager.getDataFilePattern) - manager.setIvyPattern(PluginPattern + manager.getIvyPattern) - manager.setUseOrigin(true) - if(localOnly) - manager.setDefaultTTL(java.lang.Long.MAX_VALUE) - else - { - manager.setChangingMatcher(PatternMatcher.REGEXP) - manager.setChangingPattern(".*-SNAPSHOT") - } - settings.addRepositoryCacheManager(manager) - settings.setDefaultRepositoryCacheManager(manager) - } - def toIvyConfiguration(configuration: Configuration) = - { - import org.apache.ivy.core.module.descriptor.{Configuration => IvyConfig} - import IvyConfig.Visibility._ - import configuration._ - new IvyConfig(name, if(isPublic) PUBLIC else PRIVATE, description, extendsConfigs.map(_.name).toArray, transitive, null) - } - def addExtraNamespace(dmd: DefaultModuleDescriptor): Unit = - dmd.addExtraAttributeNamespace("e", "http://ant.apache.org/ivy/extra") + private[this] def configureRepositoryCache(settings: IvySettings, localOnly: Boolean) //, artifactResolver: DependencyResolver) + { + val cacheDir = settings.getDefaultRepositoryCacheBasedir() + val manager = new DefaultRepositoryCacheManager("default-cache", settings, cacheDir) { + override def findModuleInCache(dd: DependencyDescriptor, revId: ModuleRevisionId, options: CacheMetadataOptions, r: String) = { + // ignore and reset the resolver- not ideal, but avoids thrashing. + val resolved = resetArtifactResolver(super.findModuleInCache(dd, revId, options, null)) + // invalidate the cache if the artifact was removed from the local repository + if (resolved == null) null + else if (isProjectResolver(resolved.getResolver)) { + resolved.getReport.getLocalFile.delete() + null + } else { + val origin = resolved.getReport.getArtifactOrigin + if (!origin.isLocal) resolved + else { + val file = new File(origin.getLocation) + if (file == null || file.exists) resolved + else { + resolved.getReport.getLocalFile.delete() + null + } + } + } + } + private[this] def isProjectResolver(r: DependencyResolver): Boolean = r match { + case pr: ProjectResolver => true + case _ => false + } + // ignore the original resolver wherever possible to avoid issues like #704 + override def saveResolvers(descriptor: ModuleDescriptor, metadataResolverName: String, artifactResolverName: String) {} + } + manager.setArtifactPattern(PluginPattern + manager.getArtifactPattern) + manager.setDataFilePattern(PluginPattern + manager.getDataFilePattern) + manager.setIvyPattern(PluginPattern + manager.getIvyPattern) + manager.setUseOrigin(true) + if (localOnly) + manager.setDefaultTTL(java.lang.Long.MAX_VALUE) + else { + manager.setChangingMatcher(PatternMatcher.REGEXP) + manager.setChangingPattern(".*-SNAPSHOT") + } + settings.addRepositoryCacheManager(manager) + settings.setDefaultRepositoryCacheManager(manager) + } + def toIvyConfiguration(configuration: Configuration) = + { + import org.apache.ivy.core.module.descriptor.{ Configuration => IvyConfig } + import IvyConfig.Visibility._ + import configuration._ + new IvyConfig(name, if (isPublic) PUBLIC else PRIVATE, description, extendsConfigs.map(_.name).toArray, transitive, null) + } + def addExtraNamespace(dmd: DefaultModuleDescriptor): Unit = + dmd.addExtraAttributeNamespace("e", "http://ant.apache.org/ivy/extra") - /** Adds the ivy.xml main artifact. */ - private def addMainArtifact(moduleID: DefaultModuleDescriptor) - { - val artifact = DefaultArtifact.newIvyArtifact(moduleID.getResolvedModuleRevisionId, moduleID.getPublicationDate) - moduleID.setModuleArtifact(artifact) - moduleID.check() - } - private def setConflictManager(moduleID: DefaultModuleDescriptor, conflict: ConflictManager, is: IvySettings) - { - val mid = ModuleId.newInstance(conflict.organization, conflict.module) - val matcher = is.getMatcher(PatternMatcher.EXACT_OR_REGEXP) - val manager = is.getConflictManager(conflict.name) - moduleID.addConflictManager(mid, matcher, manager) - } + /** Adds the ivy.xml main artifact. */ + private def addMainArtifact(moduleID: DefaultModuleDescriptor) { + val artifact = DefaultArtifact.newIvyArtifact(moduleID.getResolvedModuleRevisionId, moduleID.getPublicationDate) + moduleID.setModuleArtifact(artifact) + moduleID.check() + } + private def setConflictManager(moduleID: DefaultModuleDescriptor, conflict: ConflictManager, is: IvySettings) { + val mid = ModuleId.newInstance(conflict.organization, conflict.module) + val matcher = is.getMatcher(PatternMatcher.EXACT_OR_REGEXP) + val manager = is.getConflictManager(conflict.name) + moduleID.addConflictManager(mid, matcher, manager) + } - /** Converts the given sbt module id into an Ivy ModuleRevisionId.*/ - def toID(m: ModuleID) = - { - import m._ - ModuleRevisionId.newInstance(organization, name, revision, javaMap(extraAttributes)) - } + /** Converts the given sbt module id into an Ivy ModuleRevisionId.*/ + def toID(m: ModuleID) = + { + import m._ + ModuleRevisionId.newInstance(organization, name, revision, javaMap(extraAttributes)) + } - private def substituteCross(m: ModuleSettings): ModuleSettings = - m.ivyScala match { - case None => m - case Some(is) => substituteCross(m, is.scalaFullVersion, is.scalaBinaryVersion) - } - private def substituteCross(m: ModuleSettings, scalaFullVersion: String, scalaBinaryVersion: String): ModuleSettings = - { - val sub = CrossVersion(scalaFullVersion, scalaBinaryVersion) - m match { - case ec: EmptyConfiguration => ec.copy(module = sub(ec.module)) - case ic: InlineConfiguration => ic.copy(module = sub(ic.module), dependencies = ic.dependencies map sub, overrides = ic.overrides map sub) - case _ => m - } - } + private def substituteCross(m: ModuleSettings): ModuleSettings = + m.ivyScala match { + case None => m + case Some(is) => substituteCross(m, is.scalaFullVersion, is.scalaBinaryVersion) + } + private def substituteCross(m: ModuleSettings, scalaFullVersion: String, scalaBinaryVersion: String): ModuleSettings = + { + val sub = CrossVersion(scalaFullVersion, scalaBinaryVersion) + m match { + case ec: EmptyConfiguration => ec.copy(module = sub(ec.module)) + case ic: InlineConfiguration => ic.copy(module = sub(ic.module), dependencies = ic.dependencies map sub, overrides = ic.overrides map sub) + case _ => m + } + } - private def toIvyArtifact(moduleID: ModuleDescriptor, a: Artifact, allConfigurations: Iterable[String]): MDArtifact = - { - val artifact = new MDArtifact(moduleID, a.name, a.`type`, a.extension, null, extra(a, false)) - copyConfigurations(a, artifact.addConfiguration, allConfigurations) - artifact - } - def getExtraAttributes(revID: ExtendableItem): Map[String,String] = - { - import collection.JavaConverters._ - revID.getExtraAttributes.asInstanceOf[java.util.Map[String,String]].asScala.toMap - } - private[sbt] def extra(artifact: Artifact, unqualify: Boolean = false): java.util.Map[String, String] = - { - val ea = artifact.classifier match { case Some(c) => artifact.extra("e:classifier" -> c); case None => artifact } - javaMap(ea.extraAttributes, unqualify) - } - private[sbt] def javaMap(m: Map[String,String], unqualify: Boolean = false) = - { - val map = if(unqualify) m map { case (k, v) => (k.stripPrefix("e:"), v) } else m - if(map.isEmpty) null else scala.collection.JavaConversions.mapAsJavaMap(map) - } + private def toIvyArtifact(moduleID: ModuleDescriptor, a: Artifact, allConfigurations: Iterable[String]): MDArtifact = + { + val artifact = new MDArtifact(moduleID, a.name, a.`type`, a.extension, null, extra(a, false)) + copyConfigurations(a, artifact.addConfiguration, allConfigurations) + artifact + } + def getExtraAttributes(revID: ExtendableItem): Map[String, String] = + { + import collection.JavaConverters._ + revID.getExtraAttributes.asInstanceOf[java.util.Map[String, String]].asScala.toMap + } + private[sbt] def extra(artifact: Artifact, unqualify: Boolean = false): java.util.Map[String, String] = + { + val ea = artifact.classifier match { case Some(c) => artifact.extra("e:classifier" -> c); case None => artifact } + javaMap(ea.extraAttributes, unqualify) + } + private[sbt] def javaMap(m: Map[String, String], unqualify: Boolean = false) = + { + val map = if (unqualify) m map { case (k, v) => (k.stripPrefix("e:"), v) } else m + if (map.isEmpty) null else scala.collection.JavaConversions.mapAsJavaMap(map) + } - private object javaMap - { - import java.util.{HashMap, Map} - def apply[K,V](pairs: (K,V)*): Map[K,V] = - { - val map = new HashMap[K,V] - pairs.foreach { case (key, value) => map.put(key, value) } - map - } - } - /** Creates a full ivy file for 'module' using the 'dependencies' XML as the part after the <info>...</info> section. */ - private def wrapped(module: ModuleID, dependencies: NodeSeq) = - { - - { if(hasInfo(module, dependencies)) - NodeSeq.Empty - else - addExtraAttributes(defaultInfo(module), module.extraAttributes) - } - {dependencies} - { - // this is because Ivy adds a default artifact if none are specified. - if(dependencies \\ "publications" isEmpty) else NodeSeq.Empty - } - - } - private[this] def defaultInfo(module: ModuleID): scala.xml.Elem = { - import module._ - - } - private[this] def addExtraAttributes(elem: scala.xml.Elem, extra: Map[String, String]): scala.xml.Elem = - (elem /: extra) { case (e, (key,value) ) => e % new scala.xml.UnprefixedAttribute(key, value, scala.xml.Null) } - private def hasInfo(module: ModuleID, x: scala.xml.NodeSeq) = - { - val info = {x} \ "info" - if(!info.isEmpty) - { - def check(found: NodeSeq, expected: String, label: String) = - if(found.isEmpty) - sys.error("Missing " + label + " in inline Ivy XML.") - else { - val str = found.text - if(str != expected) sys.error("Inconsistent " + label + " in inline Ivy XML. Expected '" + expected + "', got '" + str + "'") - } - check(info \ "@organisation", module.organization, "organisation") - check(info \ "@module", module.name, "name") - check(info \ "@revision", module.revision, "version") - } - !info.isEmpty - } - /** Parses the given in-memory Ivy file 'xml', using the existing 'moduleID' and specifying the given 'defaultConfiguration'. */ - private def parseIvyXML(settings: IvySettings, xml: scala.xml.NodeSeq, moduleID: DefaultModuleDescriptor, defaultConfiguration: String, validate: Boolean): CustomXmlParser.CustomParser = - parseIvyXML(settings, xml.toString, moduleID, defaultConfiguration, validate) - /** Parses the given in-memory Ivy file 'xml', using the existing 'moduleID' and specifying the given 'defaultConfiguration'. */ - private def parseIvyXML(settings: IvySettings, xml: String, moduleID: DefaultModuleDescriptor, defaultConfiguration: String, validate: Boolean): CustomXmlParser.CustomParser = - { - val parser = new CustomXmlParser.CustomParser(settings, Some(defaultConfiguration)) - parser.setMd(moduleID) - parser.setValidate(validate) - parser.setInput(xml.getBytes) - parser.parse() - parser - } + private object javaMap { + import java.util.{ HashMap, Map } + def apply[K, V](pairs: (K, V)*): Map[K, V] = + { + val map = new HashMap[K, V] + pairs.foreach { case (key, value) => map.put(key, value) } + map + } + } + /** Creates a full ivy file for 'module' using the 'dependencies' XML as the part after the <info>...</info> section. */ + private def wrapped(module: ModuleID, dependencies: NodeSeq) = + { + + { + if (hasInfo(module, dependencies)) + NodeSeq.Empty + else + addExtraAttributes(defaultInfo(module), module.extraAttributes) + } + { dependencies } + { + // this is because Ivy adds a default artifact if none are specified. + if (dependencies \\ "publications" isEmpty) else NodeSeq.Empty + } + + } + private[this] def defaultInfo(module: ModuleID): scala.xml.Elem = { + import module._ + + } + private[this] def addExtraAttributes(elem: scala.xml.Elem, extra: Map[String, String]): scala.xml.Elem = + (elem /: extra) { case (e, (key, value)) => e % new scala.xml.UnprefixedAttribute(key, value, scala.xml.Null) } + private def hasInfo(module: ModuleID, x: scala.xml.NodeSeq) = + { + val info = { x } \ "info" + if (!info.isEmpty) { + def check(found: NodeSeq, expected: String, label: String) = + if (found.isEmpty) + sys.error("Missing " + label + " in inline Ivy XML.") + else { + val str = found.text + if (str != expected) sys.error("Inconsistent " + label + " in inline Ivy XML. Expected '" + expected + "', got '" + str + "'") + } + check(info \ "@organisation", module.organization, "organisation") + check(info \ "@module", module.name, "name") + check(info \ "@revision", module.revision, "version") + } + !info.isEmpty + } + /** Parses the given in-memory Ivy file 'xml', using the existing 'moduleID' and specifying the given 'defaultConfiguration'. */ + private def parseIvyXML(settings: IvySettings, xml: scala.xml.NodeSeq, moduleID: DefaultModuleDescriptor, defaultConfiguration: String, validate: Boolean): CustomXmlParser.CustomParser = + parseIvyXML(settings, xml.toString, moduleID, defaultConfiguration, validate) + /** Parses the given in-memory Ivy file 'xml', using the existing 'moduleID' and specifying the given 'defaultConfiguration'. */ + private def parseIvyXML(settings: IvySettings, xml: String, moduleID: DefaultModuleDescriptor, defaultConfiguration: String, validate: Boolean): CustomXmlParser.CustomParser = + { + val parser = new CustomXmlParser.CustomParser(settings, Some(defaultConfiguration)) + parser.setMd(moduleID) + parser.setValidate(validate) + parser.setInput(xml.getBytes) + parser.parse() + parser + } - /** This method is used to add inline dependencies to the provided module. */ - def addDependencies(moduleID: DefaultModuleDescriptor, dependencies: Seq[ModuleID], parser: CustomXmlParser.CustomParser) - { - val converted = dependencies map { dependency => convertDependency(moduleID, dependency, parser) } - val unique = if(hasDuplicateDependencies(converted)) mergeDuplicateDefinitions(converted) else converted - unique foreach moduleID.addDependency - } - /** Determines if there are multiple dependency definitions for the same dependency ID. */ - def hasDuplicateDependencies(dependencies: Seq[DependencyDescriptor]): Boolean = - { - val ids = dependencies.map(_.getDependencyRevisionId) - ids.toSet.size != ids.size - } + /** This method is used to add inline dependencies to the provided module. */ + def addDependencies(moduleID: DefaultModuleDescriptor, dependencies: Seq[ModuleID], parser: CustomXmlParser.CustomParser) { + val converted = dependencies map { dependency => convertDependency(moduleID, dependency, parser) } + val unique = if (hasDuplicateDependencies(converted)) mergeDuplicateDefinitions(converted) else converted + unique foreach moduleID.addDependency + } + /** Determines if there are multiple dependency definitions for the same dependency ID. */ + def hasDuplicateDependencies(dependencies: Seq[DependencyDescriptor]): Boolean = + { + val ids = dependencies.map(_.getDependencyRevisionId) + ids.toSet.size != ids.size + } - /** Combines the artifacts, includes, and excludes of duplicate dependency definitions. - * This is somewhat fragile and is only intended to workaround Ivy (or sbt's use of Ivy) not handling this case properly. - * In particular, Ivy will create multiple dependency entries when converting a pom with a dependency on a classified artifact and a non-classified artifact: - * https://github.com/sbt/sbt/issues/468 - * It will also allow users to declare dependencies on classified modules in different configurations: - * https://groups.google.com/d/topic/simple-build-tool/H2MdAARz6e0/discussion - * as well as basic multi-classifier handling: #285, #419, #480. - * Multiple dependency definitions should otherwise be avoided as much as possible. - */ - def mergeDuplicateDefinitions(dependencies: Seq[DependencyDescriptor]): Seq[DependencyDescriptor] = - { - // need to preserve basic order of dependencies: can't use dependencies.groupBy - val deps = new java.util.LinkedHashMap[ModuleRevisionId, List[DependencyDescriptor]] - for( dd <- dependencies ) - { - val id = dd.getDependencyRevisionId - val updated = deps get id match { - case null => dd :: Nil - case v => dd :: v - } - deps.put(id, updated) - } + /** + * Combines the artifacts, includes, and excludes of duplicate dependency definitions. + * This is somewhat fragile and is only intended to workaround Ivy (or sbt's use of Ivy) not handling this case properly. + * In particular, Ivy will create multiple dependency entries when converting a pom with a dependency on a classified artifact and a non-classified artifact: + * https://github.com/sbt/sbt/issues/468 + * It will also allow users to declare dependencies on classified modules in different configurations: + * https://groups.google.com/d/topic/simple-build-tool/H2MdAARz6e0/discussion + * as well as basic multi-classifier handling: #285, #419, #480. + * Multiple dependency definitions should otherwise be avoided as much as possible. + */ + def mergeDuplicateDefinitions(dependencies: Seq[DependencyDescriptor]): Seq[DependencyDescriptor] = + { + // need to preserve basic order of dependencies: can't use dependencies.groupBy + val deps = new java.util.LinkedHashMap[ModuleRevisionId, List[DependencyDescriptor]] + for (dd <- dependencies) { + val id = dd.getDependencyRevisionId + val updated = deps get id match { + case null => dd :: Nil + case v => dd :: v + } + deps.put(id, updated) + } - import collection.JavaConverters._ - deps.values.asScala.toSeq.flatMap { dds => - val mergeable = (dds, dds.tail).zipped.forall( ivyint.MergeDescriptors.mergeable _) - if(mergeable) dds.reverse.reduceLeft(ivyint.MergeDescriptors.apply _) :: Nil else dds - } - } + import collection.JavaConverters._ + deps.values.asScala.toSeq.flatMap { dds => + val mergeable = (dds, dds.tail).zipped.forall(ivyint.MergeDescriptors.mergeable _) + if (mergeable) dds.reverse.reduceLeft(ivyint.MergeDescriptors.apply _) :: Nil else dds + } + } - /** Transforms an sbt ModuleID into an Ivy DefaultDependencyDescriptor.*/ - def convertDependency(moduleID: DefaultModuleDescriptor, dependency: ModuleID, parser: CustomXmlParser.CustomParser): DefaultDependencyDescriptor = - { - val dependencyDescriptor = new DefaultDependencyDescriptor(moduleID, toID(dependency), dependency.isForce, dependency.isChanging, dependency.isTransitive) - dependency.configurations match - { - case None => // The configuration for this dependency was not explicitly specified, so use the default - parser.parseDepsConfs(parser.getDefaultConf, dependencyDescriptor) - case Some(confs) => // The configuration mapping (looks like: test->default) was specified for this dependency - parser.parseDepsConfs(confs, dependencyDescriptor) - } - for(artifact <- dependency.explicitArtifacts) - { - import artifact.{name, classifier, `type`, extension, url} - val extraMap = extra(artifact) - val ivyArtifact = new DefaultDependencyArtifactDescriptor(dependencyDescriptor, name, `type`, extension, url.getOrElse(null), extraMap) - copyConfigurations(artifact, ivyArtifact.addConfiguration) - for(conf <- dependencyDescriptor.getModuleConfigurations) - dependencyDescriptor.addDependencyArtifact(conf, ivyArtifact) - } - for(excls <- dependency.exclusions) - { - for(conf <- dependencyDescriptor.getModuleConfigurations) - { - dependencyDescriptor.addExcludeRule(conf, IvyScala.excludeRule(excls.organization, excls.name, excls.configurations, excls.artifact)) - } - } - dependencyDescriptor - } - def copyConfigurations(artifact: Artifact, addConfiguration: String => Unit): Unit = - copyConfigurations(artifact, addConfiguration, "*" :: Nil) - - private[this] def copyConfigurations(artifact: Artifact, addConfiguration: String => Unit, allConfigurations: Iterable[String]): Unit = - { - val confs = if(artifact.configurations.isEmpty) allConfigurations else artifact.configurations.map(_.name) - confs foreach addConfiguration - } + /** Transforms an sbt ModuleID into an Ivy DefaultDependencyDescriptor.*/ + def convertDependency(moduleID: DefaultModuleDescriptor, dependency: ModuleID, parser: CustomXmlParser.CustomParser): DefaultDependencyDescriptor = + { + val dependencyDescriptor = new DefaultDependencyDescriptor(moduleID, toID(dependency), dependency.isForce, dependency.isChanging, dependency.isTransitive) + dependency.configurations match { + case None => // The configuration for this dependency was not explicitly specified, so use the default + parser.parseDepsConfs(parser.getDefaultConf, dependencyDescriptor) + case Some(confs) => // The configuration mapping (looks like: test->default) was specified for this dependency + parser.parseDepsConfs(confs, dependencyDescriptor) + } + for (artifact <- dependency.explicitArtifacts) { + import artifact.{ name, classifier, `type`, extension, url } + val extraMap = extra(artifact) + val ivyArtifact = new DefaultDependencyArtifactDescriptor(dependencyDescriptor, name, `type`, extension, url.getOrElse(null), extraMap) + copyConfigurations(artifact, ivyArtifact.addConfiguration) + for (conf <- dependencyDescriptor.getModuleConfigurations) + dependencyDescriptor.addDependencyArtifact(conf, ivyArtifact) + } + for (excls <- dependency.exclusions) { + for (conf <- dependencyDescriptor.getModuleConfigurations) { + dependencyDescriptor.addExcludeRule(conf, IvyScala.excludeRule(excls.organization, excls.name, excls.configurations, excls.artifact)) + } + } + dependencyDescriptor + } + def copyConfigurations(artifact: Artifact, addConfiguration: String => Unit): Unit = + copyConfigurations(artifact, addConfiguration, "*" :: Nil) - def addOverrides(moduleID: DefaultModuleDescriptor, overrides: Set[ModuleID], matcher: PatternMatcher): Unit = - overrides foreach addOverride(moduleID, matcher) - def addOverride(moduleID: DefaultModuleDescriptor, matcher: PatternMatcher)(overrideDef: ModuleID): Unit = - { - val overrideID = new ModuleId(overrideDef.organization, overrideDef.name) - val overrideWith = new OverrideDependencyDescriptorMediator(null, overrideDef.revision) - moduleID.addDependencyDescriptorMediator(overrideID, matcher, overrideWith) - } - /** It is necessary to explicitly modify direct dependencies because Ivy gives - * "IllegalStateException: impossible to get artifacts when data has not been loaded." - * when a direct dependency is overridden with a newer version."*/ - def overrideDirect(dependencies: Seq[ModuleID], overrides: Set[ModuleID]): Seq[ModuleID] = - { - def key(id: ModuleID) = (id.organization, id.name) - val overridden = overrides.map(id => (key(id), id.revision)).toMap - dependencies map { dep => - overridden get key(dep) match { - case Some(rev) => dep.copy(revision = rev) - case None => dep - } - } - } + private[this] def copyConfigurations(artifact: Artifact, addConfiguration: String => Unit, allConfigurations: Iterable[String]): Unit = + { + val confs = if (artifact.configurations.isEmpty) allConfigurations else artifact.configurations.map(_.name) + confs foreach addConfiguration + } - /** This method is used to add inline artifacts to the provided module. */ - def addArtifacts(moduleID: DefaultModuleDescriptor, artifacts: Iterable[Artifact]): Unit = - for(art <- mapArtifacts(moduleID, artifacts.toSeq); c <- art.getConfigurations) - moduleID.addArtifact(c, art) + def addOverrides(moduleID: DefaultModuleDescriptor, overrides: Set[ModuleID], matcher: PatternMatcher): Unit = + overrides foreach addOverride(moduleID, matcher) + def addOverride(moduleID: DefaultModuleDescriptor, matcher: PatternMatcher)(overrideDef: ModuleID): Unit = + { + val overrideID = new ModuleId(overrideDef.organization, overrideDef.name) + val overrideWith = new OverrideDependencyDescriptorMediator(null, overrideDef.revision) + moduleID.addDependencyDescriptorMediator(overrideID, matcher, overrideWith) + } + /** + * It is necessary to explicitly modify direct dependencies because Ivy gives + * "IllegalStateException: impossible to get artifacts when data has not been loaded." + * when a direct dependency is overridden with a newer version." + */ + def overrideDirect(dependencies: Seq[ModuleID], overrides: Set[ModuleID]): Seq[ModuleID] = + { + def key(id: ModuleID) = (id.organization, id.name) + val overridden = overrides.map(id => (key(id), id.revision)).toMap + dependencies map { dep => + overridden get key(dep) match { + case Some(rev) => dep.copy(revision = rev) + case None => dep + } + } + } - def addConfigurations(mod: DefaultModuleDescriptor, configurations: Iterable[Configuration]): Unit = - configurations.foreach(config => mod.addConfiguration(toIvyConfiguration(config))) + /** This method is used to add inline artifacts to the provided module. */ + def addArtifacts(moduleID: DefaultModuleDescriptor, artifacts: Iterable[Artifact]): Unit = + for (art <- mapArtifacts(moduleID, artifacts.toSeq); c <- art.getConfigurations) + moduleID.addArtifact(c, art) - def mapArtifacts(moduleID: ModuleDescriptor, artifacts: Seq[Artifact]): Seq[IArtifact] = - { - lazy val allConfigurations = moduleID.getPublicConfigurationsNames - for(artifact <- artifacts) yield - toIvyArtifact(moduleID, artifact, allConfigurations) - } + def addConfigurations(mod: DefaultModuleDescriptor, configurations: Iterable[Configuration]): Unit = + configurations.foreach(config => mod.addConfiguration(toIvyConfiguration(config))) + def mapArtifacts(moduleID: ModuleDescriptor, artifacts: Seq[Artifact]): Seq[IArtifact] = + { + lazy val allConfigurations = moduleID.getPublicConfigurationsNames + for (artifact <- artifacts) yield toIvyArtifact(moduleID, artifact, allConfigurations) + } - /** This code converts the given ModuleDescriptor to a DefaultModuleDescriptor by casting or generating an error. - * Ivy 2.0.0 always produces a DefaultModuleDescriptor. */ - private def toDefaultModuleDescriptor(md: ModuleDescriptor) = - md match - { - case dmd: DefaultModuleDescriptor => dmd - case _ => sys.error("Unknown ModuleDescriptor type.") - } - def getConfigurations(module: ModuleDescriptor, configurations: Option[Iterable[Configuration]]) = - configurations match - { - case Some(confs) => confs.map(_.name).toList.toArray - case None => module.getPublicConfigurationsNames - } + /** + * This code converts the given ModuleDescriptor to a DefaultModuleDescriptor by casting or generating an error. + * Ivy 2.0.0 always produces a DefaultModuleDescriptor. + */ + private def toDefaultModuleDescriptor(md: ModuleDescriptor) = + md match { + case dmd: DefaultModuleDescriptor => dmd + case _ => sys.error("Unknown ModuleDescriptor type.") + } + def getConfigurations(module: ModuleDescriptor, configurations: Option[Iterable[Configuration]]) = + configurations match { + case Some(confs) => confs.map(_.name).toList.toArray + case None => module.getPublicConfigurationsNames + } } diff --git a/ivy/src/main/scala/sbt/IvyActions.scala b/ivy/src/main/scala/sbt/IvyActions.scala index 716579e0f..5e2d0359c 100644 --- a/ivy/src/main/scala/sbt/IvyActions.scala +++ b/ivy/src/main/scala/sbt/IvyActions.scala @@ -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")) diff --git a/ivy/src/main/scala/sbt/IvyCache.scala b/ivy/src/main/scala/sbt/IvyCache.scala index 3ff8432f0..fe520d96c 100644 --- a/ivy/src/main/scala/sbt/IvyCache.scala +++ b/ivy/src/main/scala/sbt/IvyCache.scala @@ -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) + } } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/IvyConfigurations.scala b/ivy/src/main/scala/sbt/IvyConfigurations.scala index 256af5f2e..93025e2d7 100644 --- a/ivy/src/main/scala/sbt/IvyConfigurations.scala +++ b/ivy/src/main/scala/sbt/IvyConfigurations.scala @@ -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) + } + } + } } diff --git a/ivy/src/main/scala/sbt/IvyInterface.scala b/ivy/src/main/scala/sbt/IvyInterface.scala index 2772b98c3..05720e6aa 100644 --- a/ivy/src/main/scala/sbt/IvyInterface.scala +++ b/ivy/src/main/scala/sbt/IvyInterface.scala @@ -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 } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/IvyLogger.scala b/ivy/src/main/scala/sbt/IvyLogger.scala index 6e1b136fe..42131a174 100644 --- a/ivy/src/main/scala/sbt/IvyLogger.scala +++ b/ivy/src/main/scala/sbt/IvyLogger.scala @@ -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) } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/IvyRetrieve.scala b/ivy/src/main/scala/sbt/IvyRetrieve.scala index dd0ffe910..5d0f3f6ec 100644 --- a/ivy/src/main/scala/sbt/IvyRetrieve.scala +++ b/ivy/src/main/scala/sbt/IvyRetrieve.scala @@ -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)) } diff --git a/ivy/src/main/scala/sbt/IvyScala.scala b/ivy/src/main/scala/sbt/IvyScala.scala index 23b7597ff..5cc3bdd48 100644 --- a/ivy/src/main/scala/sbt/IvyScala.scala +++ b/ivy/src/main/scala/sbt/IvyScala.scala @@ -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 + } } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/IvyUtil.scala b/ivy/src/main/scala/sbt/IvyUtil.scala index 1c99bb5d3..0f06b6b84 100644 --- a/ivy/src/main/scala/sbt/IvyUtil.scala +++ b/ivy/src/main/scala/sbt/IvyUtil.scala @@ -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)) } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/MakePom.scala b/ivy/src/main/scala/sbt/MakePom.scala index 16ec28333..6e51befca 100644 --- a/ivy/src/main/scala/sbt/MakePom.scala +++ b/ivy/src/main/scala/sbt/MakePom.scala @@ -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, "" + 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, "" + 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 = - ( - 4.0.0 - { makeModuleID(module) } - {moduleInfo.nameFormal} - { makeStartYear(moduleInfo) } - { makeOrganization(moduleInfo) } - { makeScmInfo(moduleInfo) } - { extra } - { - val deps = depsInConfs(module, configurations) - makeProperties(module, deps) ++ - makeDependencies(deps, includeTypes) - } - { makeRepositories(ivy.getSettings, allRepositories, filterRepositories) } - ) + 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 = + ( + 4.0.0 + { makeModuleID(module) } + { moduleInfo.nameFormal } + { makeStartYear(moduleInfo) } + { makeOrganization(moduleInfo) } + { makeScmInfo(moduleInfo) } + { extra } + { + val deps = depsInConfs(module, configurations) + makeProperties(module, deps) ++ + makeDependencies(deps, includeTypes) + } + { makeRepositories(ivy.getSettings, allRepositories, filterRepositories) } + ) - def makeModuleID(module: ModuleDescriptor): NodeSeq = - { - val mrid = moduleDescriptor(module) - val a: NodeSeq = - ({ mrid.getOrganisation } - { mrid.getName } - { packaging(module) }) - 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 = + ({ mrid.getOrganisation } + { mrid.getName } + { packaging(module) }) + 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) => {y} - case _ => NodeSeq.Empty - } - def makeOrganization(moduleInfo: ModuleInfo): NodeSeq = - { - - {moduleInfo.organizationName} - { moduleInfo.organizationHomepage match { - case Some(h) => {h} - case _ => NodeSeq.Empty - }} - - } - def makeScmInfo(moduleInfo: ModuleInfo): NodeSeq = - { - moduleInfo.scmInfo match { - case Some(s) => - - {s.browseUrl} - {s.connection} - {s.devConnection match { - case Some(d) => {d} - case _ => NodeSeq.Empty - }} - - 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 - { - for( (key,value) <- extra ) yield - ({value}).copy(label = key, attributes = _extraAttributes(key)) - } - } + def makeStartYear(moduleInfo: ModuleInfo): NodeSeq = + moduleInfo.startYear match { + case Some(y) => { y } + case _ => NodeSeq.Empty + } + def makeOrganization(moduleInfo: ModuleInfo): NodeSeq = + { + + { moduleInfo.organizationName } + { + moduleInfo.organizationHomepage match { + case Some(h)=> { h } + case _ => NodeSeq.Empty + } + } + + } + def makeScmInfo(moduleInfo: ModuleInfo): NodeSeq = + { + moduleInfo.scmInfo match { + case Some(s) => + + { s.browseUrl } + { s.connection } + { + s.devConnection match { + case Some(d)=> { d } + case _=> NodeSeq.Empty + } + } + + 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 + { + for ((key, value) <- extra) yield ({ value }).copy(label = key, attributes = _extraAttributes(key)) + } + } - /** - * 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 {d} - def licenses(ls: Array[License]) = if(ls == null || ls.isEmpty) NodeSeq.Empty else {ls.map(license)} - def license(l: License) = - - {l.getName} - {l.getUrl} - repo - - def homePage(homePage: String) = if(homePage eq null) NodeSeq.Empty else {homePage} - def revision(version: String) = if(version ne null) {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 { d } + def licenses(ls: Array[License]) = if (ls == null || ls.isEmpty) NodeSeq.Empty else { ls.map(license) } + def license(l: License) = + + { l.getName } + { l.getUrl } + repo + + def homePage(homePage: String) = if (homePage eq null) NodeSeq.Empty else { homePage } + def revision(version: String) = if (version ne null) { 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.map(makeDependency(_, includeTypes)) } - + def makeDependencies(dependencies: Seq[DependencyDescriptor], includeTypes: Set[String]): NodeSeq = + if (dependencies.isEmpty) + NodeSeq.Empty + else + + { dependencies.map(makeDependency(_, includeTypes)) } + - 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 - - {mrid.getOrganisation} - {mrid.getName} - {makeDependencyVersion(mrid.getRevision)} - { scopeElem(scope) } - { optionalElem(optional) } - { classifierElem(classifier) } - { typeElem(tpe) } - { exclusions(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 + + { mrid.getOrganisation } + { mrid.getName } + { makeDependencyVersion(mrid.getRevision) } + { scopeElem(scope) } + { optionalElem(optional) } + { classifierElem(classifier) } + { typeElem(tpe) } + { exclusions(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) => { t } + 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) => { c } + 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) => {t} - 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) => {c} - 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) => { s } + } + def optionalElem(opt: Boolean) = if (opt) true 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) => {s} - } - def optionalElem(opt: Boolean) = if(opt) true 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) { excls } + 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 '' for %s. Dependency exclusion should have both 'org' and 'module' to comply with Maven POM's schema.".format(m)) + else + Right( + + { g } + { a } + + ) + } - 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) {excls} - 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 '' for %s. Dependency exclusion should have both 'org' and 'module' to comply with Maven POM's schema.".format(m)) - else - Right( - - {g} - {a} - - ) - } + 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 { repositoryElements } + } + 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 {repositoryElements} - } - 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 = + + { id } + { name } + { root } + { if (name == JavaNet1Repository.name) "legacy" else "default" } + - 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 = - - {id} - {name} - {root} - { if(name == JavaNet1Repository.name) "legacy" else "default" } - - - /** 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 + } } diff --git a/ivy/src/main/scala/sbt/ModuleID.scala b/ivy/src/main/scala/sbt/ModuleID.scala index e37c84da2..178940666 100644 --- a/ivy/src/main/scala/sbt/ModuleID.scala +++ b/ivy/src/main/scala/sbt/ModuleID.scala @@ -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) } diff --git a/ivy/src/main/scala/sbt/ProjectResolver.scala b/ivy/src/main/scala/sbt/ProjectResolver.scala index 917889db8..caff3d496 100644 --- a/ivy/src/main/scala/sbt/ProjectResolver.scala +++ b/ivy/src/main/scala/sbt/ProjectResolver.scala @@ -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") } } diff --git a/ivy/src/main/scala/sbt/ResolutionCache.scala b/ivy/src/main/scala/sbt/ResolutionCache.scala index d79f25a4c..41721a784 100644 --- a/ivy/src/main/scala/sbt/ResolutionCache.scala +++ b/ivy/src/main/scala/sbt/ResolutionCache.scala @@ -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]" } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/Resolver.scala b/ivy/src/main/scala/sbt/Resolver.scala index e72db1e46..f6250705e 100644 --- a/ivy/src/main/scala/sbt/Resolver.scala +++ b/ivy/src/main/scala/sbt/Resolver.scala @@ -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) + } } diff --git a/ivy/src/main/scala/sbt/StringUtilities.scala b/ivy/src/main/scala/sbt/StringUtilities.scala index caf373d3b..fc8327304 100644 --- a/ivy/src/main/scala/sbt/StringUtilities.scala +++ b/ivy/src/main/scala/sbt/StringUtilities.scala @@ -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 } diff --git a/ivy/src/main/scala/sbt/UpdateReport.scala b/ivy/src/main/scala/sbt/UpdateReport.scala index 071643eb2..71d58390b 100644 --- a/ivy/src/main/scala/sbt/UpdateReport.scala +++ b/ivy/src/main/scala/sbt/UpdateReport.scala @@ -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(", ") } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/impl/DependencyBuilders.scala b/ivy/src/main/scala/sbt/impl/DependencyBuilders.scala index 7d5812aaf..16a1fa504 100755 --- a/ivy/src/main/scala/sbt/impl/DependencyBuilders.scala +++ b/ivy/src/main/scala/sbt/impl/DependencyBuilders.scala @@ -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) + } } diff --git a/ivy/src/main/scala/sbt/ivyint/ErrorMessageAuthenticator.scala b/ivy/src/main/scala/sbt/ivyint/ErrorMessageAuthenticator.scala index 3d0d174d8..e59e36e34 100644 --- a/ivy/src/main/scala/sbt/ivyint/ErrorMessageAuthenticator.scala +++ b/ivy/src/main/scala/sbt/ivyint/ErrorMessageAuthenticator.scala @@ -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 } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/ivyint/IvyCredentialsLookup.scala b/ivy/src/main/scala/sbt/ivyint/IvyCredentialsLookup.scala index 365ffe698..aeef4d428 100644 --- a/ivy/src/main/scala/sbt/ivyint/IvyCredentialsLookup.scala +++ b/ivy/src/main/scala/sbt/ivyint/IvyCredentialsLookup.scala @@ -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) } } \ No newline at end of file diff --git a/ivy/src/main/scala/sbt/ivyint/MergeDescriptors.scala b/ivy/src/main/scala/sbt/ivyint/MergeDescriptors.scala index e1c07c780..486d8b7bd 100644 --- a/ivy/src/main/scala/sbt/ivyint/MergeDescriptors.scala +++ b/ivy/src/main/scala/sbt/ivyint/MergeDescriptors.scala @@ -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 }