detailed UpdateReport replaces Map[String,Seq[File]]

the information included is:
  configuration -> module -> artifact -> file
This commit is contained in:
Mark Harrah 2011-03-13 21:40:49 -04:00
parent 26d834e926
commit 787f00985b
6 changed files with 96 additions and 50 deletions

View File

@ -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

View File

@ -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"
}

View File

@ -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

View File

@ -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)
}

View File

@ -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")

View File

@ -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]