From b70fa6e0c2e756ec72dd8866aae7f7320b95e6a8 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 4 Mar 2015 05:41:57 -0500 Subject: [PATCH] Use pickler to cache UpdateReport for update task. #1763 --- build.sbt | 2 +- .../tracking/src/main/scala/sbt/Tracked.scala | 54 +++++++++++++++++++ main/src/main/scala/sbt/Defaults.scala | 4 +- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/build.sbt b/build.sbt index 7b79a8b20..bc2cf4cb0 100644 --- a/build.sbt +++ b/build.sbt @@ -305,7 +305,7 @@ lazy val cacheProj = (project in cachePath). settings(baseSettings: _*). settings( name := "Cache", - libraryDependencies ++= Seq(sbinary) ++ scalaXml.value + libraryDependencies ++= Seq(sbinary, sbtSerialization) ++ scalaXml.value ) // Builds on cache to provide caching for filesystem-related operations diff --git a/cache/tracking/src/main/scala/sbt/Tracked.scala b/cache/tracking/src/main/scala/sbt/Tracked.scala index c851ef9a5..028d385c2 100644 --- a/cache/tracking/src/main/scala/sbt/Tracked.scala +++ b/cache/tracking/src/main/scala/sbt/Tracked.scala @@ -9,6 +9,7 @@ import sbinary.Format import scala.reflect.Manifest import scala.collection.mutable import IO.{ delete, read, write } +import sbt.serialization._ object Tracked { /** @@ -36,6 +37,25 @@ object Tracked { toFile(next)(cacheFile) next } + private[sbt] def lastOuputWithJson[I, O: Pickler: Unpickler](cacheFile: File)(f: (I, Option[O]) => O): I => O = in => + { + val previous: Option[O] = fromJsonFile[O](cacheFile) + val next = f(in, previous) + toJsonFile(next)(cacheFile) + next + } + private[sbt] def fromJsonFile[A: Unpickler](file: File): Option[A] = + try { + val s = IO.read(file, IO.utf8) + fromJsonString[A](s).toOption + } catch { + case e: Throwable => None + } + private[sbt] def toJsonFile[A: Pickler](a: A)(file: File): Unit = + { + val str = toJsonString(a) + IO.write(file, str, IO.utf8) + } def inputChanged[I, O](cacheFile: File)(f: (Boolean, I) => O)(implicit ic: InputCache[I]): I => O = in => { @@ -44,6 +64,18 @@ object Tracked { val changed = help.changed(cacheFile, conv) val result = f(changed, in) + if (changed) + help.save(cacheFile, conv) + + result + } + private[sbt] def inputChangedWithJson[I: Pickler: Unpickler, O](cacheFile: File)(f: (Boolean, I) => O): I => O = in => + { + val help = new JsonCacheHelp[I] + val conv = help.convert(in) + val changed = help.changed(cacheFile, conv) + val result = f(changed, in) + if (changed) help.save(cacheFile, conv) @@ -56,6 +88,18 @@ object Tracked { val changed = help.changed(cacheFile, help.convert(initial)) val result = f(changed, initial) + if (changed) + help.save(cacheFile, help.convert(in())) + + result + } + private[sbt] def outputChangedWithJson[I: Pickler, O](cacheFile: File)(f: (Boolean, I) => O): (() => I) => O = in => + { + val initial = in() + val help = new JsonCacheHelp[I] + val changed = help.changed(cacheFile, help.convert(initial)) + val result = f(changed, initial) + if (changed) help.save(cacheFile, help.convert(in())) @@ -71,6 +115,16 @@ object Tracked { !ic.equiv.equiv(converted, prev) } catch { case e: Exception => true } } + private[sbt] final class JsonCacheHelp[I: Pickler] { + def convert(i: I): String = toJsonString(i) + def save(cacheFile: File, value: String): Unit = + IO.write(cacheFile, value, IO.utf8) + def changed(cacheFile: File, converted: String): Boolean = + try { + val prev = IO.read(cacheFile, IO.utf8) + converted != prev + } catch { case e: Exception => true } + } } trait Tracked { diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 968ef1960..d171e1765 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1388,13 +1388,13 @@ object Classpaths { val outCacheFile = cacheFile / "output" def skipWork: In => UpdateReport = - Tracked.lastOutput[In, UpdateReport](outCacheFile) { + Tracked.lastOuputWithJson[In, UpdateReport](outCacheFile) { case (_, Some(out)) => out case _ => sys.error("Skipping update requested, but update has not previously run successfully.") } def doWork: In => UpdateReport = Tracked.inputChanged(cacheFile / "inputs") { (inChanged: Boolean, in: In) => - val outCache = Tracked.lastOutput[In, UpdateReport](outCacheFile) { + val outCache = Tracked.lastOuputWithJson[In, UpdateReport](outCacheFile) { case (_, Some(out)) if uptodate(inChanged, out) => out case _ => work(in) }