From 7ed0c85097d941e4b3513b9185c1923bccb669e3 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Wed, 29 Aug 2012 11:13:48 -0400 Subject: [PATCH] Second part of resolution cache work. Fixes #532. Replaces the standard Ivy resolution cache: 1. Separate the cached resolved Ivy files from resolution reports, making resolution reports easier to find (target/resolution-cache/reports/) 2. Cache location includes extra attributes so that cross builds of a plugin do not overwrite each other. --- ivy/Ivy.scala | 21 +++++++------ ivy/IvyActions.scala | 2 +- ivy/ResolutionCache.scala | 64 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 76 insertions(+), 11 deletions(-) create mode 100644 ivy/ResolutionCache.scala diff --git a/ivy/Ivy.scala b/ivy/Ivy.scala index 20d3fd5e5..f512efef0 100644 --- a/ivy/Ivy.scala +++ b/ivy/Ivy.scala @@ -13,7 +13,7 @@ import CS.singleton import org.apache.ivy.{core, plugins, util, Ivy} import core.{IvyPatternHelper, LogOptions} -import core.cache.{CacheMetadataOptions, DefaultRepositoryCacheManager, ResolutionCacheManager} +import core.cache.{CacheMetadataOptions, DefaultRepositoryCacheManager} import core.module.descriptor.{Artifact => IArtifact, DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact} import core.module.descriptor.{DefaultDependencyDescriptor, DefaultModuleDescriptor, DependencyDescriptor, ModuleDescriptor, License} import core.module.descriptor.{OverrideDependencyDescriptorMediator} @@ -277,17 +277,18 @@ private object IvySbt settings.addModuleConfiguration(attributes, settings.getMatcher(EXACT_OR_REGEXP), resolver.name, null, null, null) } } - private[sbt] def cleanResolutionCache(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 def configureCache(settings: IvySettings, localOnly: Boolean, resCacheDir: Option[File]) { - resCacheDir foreach { dir => settings.setDefaultResolutionCacheBasedir(dir.getAbsolutePath) } + 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)) + } + private[this] def configureRepositoryCache(settings: IvySettings, localOnly: Boolean) + { val cacheDir = settings.getDefaultRepositoryCacheBasedir() val manager = new DefaultRepositoryCacheManager("default-cache", settings, cacheDir) { override def findModuleInCache(dd: DependencyDescriptor, revId: ModuleRevisionId, options: CacheMetadataOptions, r: String) = diff --git a/ivy/IvyActions.scala b/ivy/IvyActions.scala index 8bb952d06..e01cfba90 100644 --- a/ivy/IvyActions.scala +++ b/ivy/IvyActions.scala @@ -204,7 +204,7 @@ object IvyActions val resolveId = ResolveOptions.getDefaultResolveId(module) resolveOptions.setResolveId(resolveId) resolveOptions.setLog(ivyLogLevel(logging)) - IvySbt.cleanResolutionCache(module.getModuleRevisionId, resolveId, ivy.getSettings.getResolutionCacheManager) + ResolutionCache.cleanModule(module.getModuleRevisionId, resolveId, ivy.getSettings.getResolutionCacheManager) val resolveReport = ivy.resolve(module, resolveOptions) val err = if(resolveReport.hasError) diff --git a/ivy/ResolutionCache.scala b/ivy/ResolutionCache.scala new file mode 100644 index 000000000..c8846e251 --- /dev/null +++ b/ivy/ResolutionCache.scala @@ -0,0 +1,64 @@ +package sbt + +import java.io.File +import org.apache.ivy.core +import core.IvyPatternHelper +import core.cache.{CacheMetadataOptions, DefaultRepositoryCacheManager, DefaultResolutionCacheManager, ResolutionCacheManager} +import core.module.id.ModuleRevisionId +import ResolutionCache.{Name, ReportDirectory, ResolvedName, ResolvedPattern} + +/** 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) 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] def reportBase(resolveId: String): File = + new File(new File(base, ReportDirectory), resolveId) + + 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") + def getConfigurationResolveReportInCache(resolveId: String, conf: String): File = + new File(reportBase(resolveId), "/" + conf + "-" + ResolvedName) + def getConfigurationResolveReportsInCache(resolveId: String): Array[File] = + IO.listFiles(reportBase(resolveId)).flatMap(d => IO.listFiles(d)) +} +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" + + // 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" + + // 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]" +} \ No newline at end of file