From 3dd3a18996908ba59345dce985ceb31681341ac6 Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Wed, 29 Aug 2012 11:13:48 -0400 Subject: [PATCH] Force update on change to last modified time of artifacts or cached descriptor. This is part 2 of the fix for #532. It may also fix issues when working with multiple local projects via 'publish-local' and binary dependencies. --- ivy/IvyRetrieve.scala | 2 +- ivy/ResolutionCache.scala | 2 +- ivy/UpdateReport.scala | 17 ++++++++++++++--- main/Defaults.scala | 6 ++++-- main/actions/CacheIvy.scala | 2 +- .../pom-scope/project/PomTest.scala | 2 +- 6 files changed, 22 insertions(+), 9 deletions(-) diff --git a/ivy/IvyRetrieve.scala b/ivy/IvyRetrieve.scala index cd619afaf..dd0ffe910 100644 --- a/ivy/IvyRetrieve.scala +++ b/ivy/IvyRetrieve.scala @@ -48,7 +48,7 @@ object IvyRetrieve } def updateReport(report: ResolveReport, cachedDescriptor: File): UpdateReport = - new UpdateReport(cachedDescriptor, reports(report) map configurationReport, updateStats(report)) + 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 = diff --git a/ivy/ResolutionCache.scala b/ivy/ResolutionCache.scala index c8846e251..a5026882b 100644 --- a/ivy/ResolutionCache.scala +++ b/ivy/ResolutionCache.scala @@ -54,7 +54,7 @@ private[sbt] object ResolutionCache private val ReportFileName = "report.xml" // base name (name except for extension) of resolution report file - private val ResolvedName = "resolved" + private val ResolvedName = "resolved.xml" // Cache name private val Name = "sbt-resolution-cache" diff --git a/ivy/UpdateReport.scala b/ivy/UpdateReport.scala index 6f8485c9d..071643eb2 100644 --- a/ivy/UpdateReport.scala +++ b/ivy/UpdateReport.scala @@ -13,15 +13,19 @@ package sbt * @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) +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 /** 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 ) + 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) @@ -71,6 +75,13 @@ object UpdateReport /** 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) @@ -123,7 +134,7 @@ object UpdateReport val newModules = modules map { modReport => f(configuration, modReport) } new ConfigurationReport(configuration, newModules, evicted) } - new UpdateReport(report.cachedDescriptor, newConfigurations, report.stats) + new UpdateReport(report.cachedDescriptor, newConfigurations, report.stats, report.stamps) } } } diff --git a/main/Defaults.scala b/main/Defaults.scala index 82a97d5f0..7edf2fa71 100755 --- a/main/Defaults.scala +++ b/main/Defaults.scala @@ -956,8 +956,8 @@ object Classpaths !force && !depsUpdated && !inChanged && - out.allFiles.forall(_.exists) && - out.cachedDescriptor.exists + out.allFiles.forall(f => fileUptodate(f,out.stamps)) && + fileUptodate(out.cachedDescriptor, out.stamps) val outCacheFile = cacheFile / "output" def skipWork: In => UpdateReport = @@ -976,6 +976,8 @@ object Classpaths val f = if(skip && !force) skipWork else doWork f(module.owner.configuration :+: module.moduleSettings :+: config :+: HNil) } + private[this] def fileUptodate(file: File, stamps: Map[File, Long]): Boolean = + stamps.get(file).forall(_ == file.lastModified) /* // can't cache deliver/publish easily since files involved are hidden behind patterns. publish will be difficult to verify target-side anyway def cachedPublish(cacheFile: File)(g: (IvySbt#Module, PublishConfiguration) => Unit, module: IvySbt#Module, config: PublishConfiguration) => Unit = diff --git a/main/actions/CacheIvy.scala b/main/actions/CacheIvy.scala index 5dd5a2e2f..ec823bb12 100644 --- a/main/actions/CacheIvy.scala +++ b/main/actions/CacheIvy.scala @@ -59,7 +59,7 @@ object CacheIvy implicit lazy val updateReportFormat: Format[UpdateReport] = { import DefaultProtocol.{StringFormat, FileFormat} - wrap[UpdateReport, (File, Seq[ConfigurationReport], UpdateStats)](rep => (rep.cachedDescriptor, rep.configurations, rep.stats), { case (cd, cs, stats) => new UpdateReport(cd, cs, stats) }) + wrap[UpdateReport, (File, Seq[ConfigurationReport], UpdateStats, Map[File,Long])](rep => (rep.cachedDescriptor, rep.configurations, rep.stats, rep.stamps), { case (cd, cs, stats, stamps) => new UpdateReport(cd, cs, stats, stamps) }) } implicit def updateStatsFormat: Format[UpdateStats] = wrap[UpdateStats, (Long,Long,Long)]( us => (us.resolveTime, us.downloadTime, us.downloadSize), { case (rt, dt, ds) => new UpdateStats(rt, dt, ds, true) }) diff --git a/sbt/src/sbt-test/dependency-management/pom-scope/project/PomTest.scala b/sbt/src/sbt-test/dependency-management/pom-scope/project/PomTest.scala index 7c937dd03..cfb3fc7f4 100644 --- a/sbt/src/sbt-test/dependency-management/pom-scope/project/PomTest.scala +++ b/sbt/src/sbt-test/dependency-management/pom-scope/project/PomTest.scala @@ -22,7 +22,7 @@ object PomTest extends Build def checkPom = makePom map { pom => val expected = Seq( - ("a", Some("compile"), false, None), + ("a", None, false, None), ("b", Some("runtime"), true, None), ("c", None, true, None), ("d", Some("test"), false, None),