From 787f00985be73e31408de4578c0eaddf6c5c6e95 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Sun, 13 Mar 2011 21:40:49 -0400 Subject: [PATCH] detailed UpdateReport replaces Map[String,Seq[File]] the information included is: configuration -> module -> artifact -> file --- ivy/IvyActions.scala | 34 ++------------------------ ivy/IvyRetrieve.scala | 55 +++++++++++++++++++++++++++++++++---------- main/CacheIvy.scala | 27 +++++++++++++++++++++ main/Defaults.scala | 26 +++++++++++++++++--- main/Keys.scala | 2 +- sbt/package.scala | 2 +- 6 files changed, 96 insertions(+), 50 deletions(-) diff --git a/ivy/IvyActions.scala b/ivy/IvyActions.scala index a685ed2e7..8310fb955 100644 --- a/ivy/IvyActions.scala +++ b/ivy/IvyActions.scala @@ -105,44 +105,14 @@ object IvyActions } /** 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) = + def update(module: IvySbt#Module, configuration: UpdateConfiguration): UpdateReport = { module.withModule { case (ivy, md, default) => import configuration.{retrieve => rConf, logging} val report = resolve(logging)(ivy, md, default) - rConf match - { - case None => IvyRetrieve.cachePaths(report) - case Some(conf) => retrieve(ivy, md, conf, logging) - } + IvyRetrieve.updateReport(report) } } - // doesn't work. perhaps replace retrieve/determineArtifactsToCopy with custom code - private def retrieve(ivy: Ivy, md: ModuleDescriptor, conf: RetrieveConfiguration, logging: UpdateLogging.Value) = - { - import conf._ - val retrieveOptions = new RetrieveOptions - retrieveOptions.setSync(synchronize) - val patternBase = retrieveDirectory.getAbsolutePath - val pattern = - if(patternBase.endsWith(File.separator)) - patternBase + outputPattern - else - patternBase + File.separatorChar + outputPattern - - val engine = ivy.getRetrieveEngine - engine.retrieve(md.getModuleRevisionId, pattern, retrieveOptions) - - //TODO: eliminate the duplication for better efficiency (retrieve already calls determineArtifactsToCopy once) - val rawMap = engine.determineArtifactsToCopy(md.getModuleRevisionId, pattern, retrieveOptions) - val map = rawMap.asInstanceOf[java.util.Map[ArtifactDownloadReport,java.util.Set[String]]] - val confMap = new collection.mutable.HashMap[String, Seq[File]] - - import collection.JavaConversions._ - for( (report, all) <- map; retrieved <- all; val file = new File(retrieved); conf <- report.getArtifact.getConfigurations) - confMap.put(conf, file +: confMap.getOrElse(conf, Nil)) - confMap.toMap - } private def resolve(logging: UpdateLogging.Value)(ivy: Ivy, module: DefaultModuleDescriptor, defaultConf: String): ResolveReport = { val resolveOptions = new ResolveOptions diff --git a/ivy/IvyRetrieve.scala b/ivy/IvyRetrieve.scala index 7cfd7f026..f9eecfff4 100644 --- a/ivy/IvyRetrieve.scala +++ b/ivy/IvyRetrieve.scala @@ -6,6 +6,7 @@ package sbt import java.io.File import org.apache.ivy.core.{module, report} +import module.descriptor.{Artifact => IvyArtifact} import module.id.ModuleRevisionId import report.{ArtifactDownloadReport, ConfigurationResolveReport, ResolveReport} @@ -14,21 +15,49 @@ object IvyRetrieve def reports(report: ResolveReport): Map[String, ConfigurationResolveReport] = ( for( conf <- report.getConfigurations) yield (conf, report.getConfigurationReport(conf)) ).toMap - def artifactReports(confReport: ConfigurationResolveReport): Seq[ArtifactDownloadReport] = + def moduleReports(confReport: ConfigurationResolveReport): Map[ModuleID, ModuleReport] = + moduleReportMap(confReport) map { case (mid, arts) => (mid, new ModuleReport(mid, artifactReports(arts)) ) } + + def moduleReportMap(confReport: ConfigurationResolveReport): Map[ModuleID, Seq[ArtifactDownloadReport]] = { - val all = new scala.collection.mutable.HashSet[ArtifactDownloadReport] - for( revId <- confReport.getModuleRevisionIds.toArray collect { case revId: ModuleRevisionId => revId }) - all ++= confReport.getDownloadReports(revId) - all.toSeq + val modules = + for( revId <- confReport.getModuleRevisionIds.toArray collect { case revId: ModuleRevisionId => revId }) yield + (toModuleID(revId), (confReport getDownloadReports revId).toSeq) + modules.toMap + } + def artifactReports(artReport: Seq[ArtifactDownloadReport]): Map[Artifact, File] = + artReport map { r => + val art = r.getArtifact + val file0 = r.getLocalFile + val file = if(file0 eq null) error("No file for " + art) else file0 + (toArtifact(art), file) + } toMap; + + def toModuleID(revID: ModuleRevisionId): ModuleID = + ModuleID(revID.getOrganisation, revID.getName, revID.getRevision) + + def toArtifact(art: IvyArtifact): Artifact = + { + import art._ + Artifact(getName, getType, getExt, Option(getExtraAttribute("classifier")), getConfigurations map Configurations.config, Option(getUrl)) } - def cachePath(reports: Seq[ArtifactDownloadReport]): Seq[File] = - for(r <- reports) yield - { - val file = r.getLocalFile - if(file eq null) error("No file for " + r.getArtifact) else file - } + def updateReport(report: ResolveReport): UpdateReport = + new UpdateReport(reports(report) mapValues configurationReport) - def cachePaths(report: ResolveReport): Map[String, Seq[File]] = - reports(report).mapValues(confReport => cachePath(artifactReports(confReport))) + def configurationReport(confReport: ConfigurationResolveReport): ConfigurationReport = + new ConfigurationReport(confReport.getConfiguration, moduleReports(confReport)) +} + +final class UpdateReport(val configurations: Map[String, ConfigurationReport]) +{ + override def toString = "Update report:\n" + configurations.values.mkString +} +final class ConfigurationReport(val configuration: String, val modules: Map[ModuleID, ModuleReport]) +{ + override def toString = "\t" + configuration + ":\n" + modules.values.mkString +} +final class ModuleReport(val module: ModuleID, val artifacts: Map[Artifact, File]) +{ + override def toString = "\t\t" + module + ": " + (if(artifacts.size <= 1) "" else "\n") + artifacts.mkString("\n\t\t\t") + "\n" } \ No newline at end of file diff --git a/main/CacheIvy.scala b/main/CacheIvy.scala index 83108097b..1e1808998 100644 --- a/main/CacheIvy.scala +++ b/main/CacheIvy.scala @@ -7,9 +7,11 @@ package sbt import FileInfo.{exists, hash} import java.io.File + import java.net.URL import Types.:+: import scala.xml.NodeSeq import sbinary.{DefaultProtocol,Format} + import DefaultProtocol.{immutableMapFormat, optionsAreFormat} import RepositoryHelpers._ import Ordering._ @@ -53,6 +55,31 @@ object CacheIvy def updateIC: InputCache[IvyConfiguration :+: ModuleSettings :+: UpdateConfiguration :+: HNil] = implicitly def publishIC: InputCache[IvyConfiguration :+: ModuleSettings :+: PublishConfiguration :+: HNil] = implicitly + def updateReportF: Format[UpdateReport] = + { + import DefaultProtocol.{BooleanFormat, FileFormat, StringFormat} + updateReportFormat + } + implicit def updateReportFormat(implicit m: Format[String], cr: Format[ConfigurationReport]): Format[UpdateReport] = + wrap[UpdateReport, Map[String,ConfigurationReport]](_.configurations, c => new UpdateReport(c)) + implicit def confReportFormat(implicit mf: Format[ModuleID], mr: Format[ModuleReport]): Format[ConfigurationReport] = + wrap[ConfigurationReport, (String,Map[ModuleID,ModuleReport])]( r => (r.configuration, r.modules), { case (c,m) => new ConfigurationReport(c,m) }) + implicit def moduleReportFormat(implicit f: Format[Artifact], ff: Format[File], mid: Format[ModuleID]): Format[ModuleReport] = + wrap[ModuleReport, (ModuleID, Map[Artifact, File])]( m => (m.module, m.artifacts), { case (m, as) => new ModuleReport(m, as) }) + implicit def artifactFormat(implicit sf: Format[String], of: Format[Seq[Configuration]], cf: Format[Configuration], uf: Format[Option[URL]]): Format[Artifact] = + wrap[Artifact, (String,String,String,Option[String],Seq[Configuration],Option[URL],Map[String,String])]( + a => (a.name, a.`type`, a.extension, a.classifier, a.configurations.toSeq, a.url, a.extraAttributes), + { case (n,t,x,c,cs,u,e) => Artifact(n,t,x,c,cs,u,e) } + ) + implicit def moduleIDFormat(implicit sf: Format[String], af: Format[Artifact], bf: Format[Boolean]): Format[ModuleID] = + wrap[ModuleID, (String,String,String,Option[String],Boolean,Boolean,Seq[Artifact],Map[String,String],Boolean)]( + m => (m.organization,m.name,m.revision,m.configurations, m.isChanging, m.isTransitive, m.explicitArtifacts, m.extraAttributes, m.crossVersion), + { case (o,n,r,cs,ch,t,as,x,cv) => ModuleID(o,n,r,cs,ch,t,as,x,cv) } + ) + + implicit def configurationFormat(implicit sf: Format[String]): Format[Configuration] = + wrap[Configuration, String](_.name, s => new Configuration(s)) + implicit def classpathFormat = { import DefaultProtocol.FileFormat diff --git a/main/Defaults.scala b/main/Defaults.scala index bbccc912b..239ad91b3 100755 --- a/main/Defaults.scala +++ b/main/Defaults.scala @@ -421,7 +421,7 @@ object Classpaths internalDependencyClasspath <<= internalDependencies, unmanagedClasspath <<= unmanagedDependencies, products <<= makeProducts, - managedClasspath <<= (configuration, update) map { (config, up) => up.getOrElse(config.name, error("Configuration '" + config.name + "' unresolved by 'update'.")) }, + managedClasspath <<= (configuration, update) map managedJars, unmanagedJars <<= (configuration, unmanagedBase, classpathFilter, defaultExcludes) map { (config, base, filter, excl) => (base * (filter -- excl) +++ (base / config.name).descendentsExcept(filter, excl)).getFiles.toSeq } @@ -500,11 +500,12 @@ object Classpaths (ivyModule, config, deliverKey) map { (module, config, _) => IvyActions.publish(module, config) } import Cache._ - import CacheIvy.{classpathFormat, publishIC, updateIC} + import CacheIvy.{classpathFormat, publishIC, updateIC, updateReportF} - def cachedUpdate(cacheFile: File, module: IvySbt#Module, config: UpdateConfiguration, log: Logger): Map[String, Seq[File]] = + def cachedUpdate(cacheFile: File, module: IvySbt#Module, config: UpdateConfiguration, log: Logger): UpdateReport = { implicit val updateCache = updateIC + implicit val updateReport = updateReportF val work = (_: IvyConfiguration :+: ModuleSettings :+: UpdateConfiguration :+: HNil) match { case conf :+: settings :+: config :+: HNil => log.info("Updating...") val r = IvyActions.update(module, config) @@ -667,4 +668,23 @@ object Classpaths def defaultConfigurationTask(p: ResolvedReference, data: Settings[Scope]): Configuration = flatten(defaultConfiguration in p get data) getOrElse Configurations.Default def flatten[T](o: Option[Option[T]]): Option[T] = o flatMap identity + + def managedJars(config: Configuration, up: UpdateReport): Classpath = + allJars( confReport(config.name, up) ) + def confReport(config: String, up: UpdateReport): ConfigurationReport = + up.configurations.getOrElse(config, error("Configuration '" + config + "' unresolved by 'update'.")) + def allJars(cr: ConfigurationReport): Seq[File] = cr.modules.values.toSeq.flatMap(mr => allJars(mr.artifacts)) + def allJars(as: Iterable[(Artifact,File)]): Iterable[File] = as collect { case (a, f) if isJar(a) => f } + def isJar(a: Artifact): Boolean = a.`type` == "jar" } +trait Defaults +{ + def addSbtDependency: Setting[Seq[ModuleID]] = + libraryDependencies <<= (libraryDependencies, appConfiguration) { (libs, app) => + val id = app.provider.id + libs :+ ModuleID(id.groupID, id.name, id.version, crossVersion = true) + } + + def addSbtRepository: Setting[Seq[Resolver]] = + resolvers += Resolver.url("sbt-db", new URL("http://databinder.net/repo/"))(Resolver.ivyStylePatterns) +} \ No newline at end of file diff --git a/main/Keys.scala b/main/Keys.scala index 9765be12b..7d81ac019 100644 --- a/main/Keys.scala +++ b/main/Keys.scala @@ -149,7 +149,7 @@ object Keys val ivySbt = TaskKey[IvySbt]("ivy-sbt") val ivyModule = TaskKey[IvySbt#Module]("ivy-module") val classpathFilter = SettingKey[FileFilter]("classpath-filter") - val update = TaskKey[Map[String,Seq[File]]]("update") + val update = TaskKey[UpdateReport]("update") val publishConfiguration = TaskKey[PublishConfiguration]("publish-configuration") val publishLocalConfiguration = TaskKey[PublishConfiguration]("publish-local-configuration") diff --git a/sbt/package.scala b/sbt/package.scala index d5cdbc1c3..3bdf57267 100644 --- a/sbt/package.scala +++ b/sbt/package.scala @@ -1,7 +1,7 @@ /* sbt -- Simple Build Tool * Copyright 2010 Mark Harrah */ -package object sbt extends sbt.std.TaskExtra with sbt.Types with sbt.ProcessExtra with sbt.impl.DependencyBuilders with sbt.PathExtra with sbt.ProjectConstructors +package object sbt extends sbt.std.TaskExtra with sbt.Types with sbt.ProcessExtra with sbt.impl.DependencyBuilders with sbt.PathExtra with sbt.ProjectConstructors with sbt.Defaults { type Setting[T] = Project.Setting[T] type ScopedKey[T] = Project.ScopedKey[T]