From 24d97aa10410e7d571a8a5aef8a74ba059f7f7dd Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sun, 16 Jul 2017 18:48:28 -0400 Subject: [PATCH 1/2] Fixes Tracked.inputChanged Tracked.inputChanged stores and reads hash correctly. Fixes #96 --- .../src/main/scala/sbt/util/Tracked.scala | 14 +++++++++----- .../src/test/scala/sbt/util/TrackedSpec.scala | 6 +++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/util-tracking/src/main/scala/sbt/util/Tracked.scala b/util-tracking/src/main/scala/sbt/util/Tracked.scala index 66e84e8fb..823e9bde9 100644 --- a/util-tracking/src/main/scala/sbt/util/Tracked.scala +++ b/util-tracking/src/main/scala/sbt/util/Tracked.scala @@ -96,16 +96,20 @@ object Tracked { private final class CacheHelp[I: JsonFormat](val sc: SingletonCache[Long]) { import CacheImplicits.implicitHashWriter + import CacheImplicits.LongJsonFormat def save(store: CacheStore, value: I): Unit = { - store.write(value) + Hasher.hash(value) match { + case Success(keyHash) => store.write[Long](keyHash.toLong) + case Failure(e) => () + } } def changed(store: CacheStore, value: I): Boolean = - Try { store.read[I] } match { - case Success(prev) => + Try { store.read[Long] } match { + case Success(prev: Long) => Hasher.hash(value) match { - case Success(keyHash) => keyHash.toLong != prev - case Failure(_) => true + case Success(keyHash: Int) => keyHash.toLong != prev + case Failure(_) => true } case Failure(_) => true } diff --git a/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala b/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala index 90513cad6..4fa480ca7 100644 --- a/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala +++ b/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala @@ -51,10 +51,10 @@ class TrackedSpec extends UnitSpec { "inputChanged" should "detect that the input has not changed" in { withStore { store => - val input0 = 0 + val input0 = "foo" val res0 = - Tracked.inputChanged[Int, Int](store) { + Tracked.inputChanged[String, String](store) { case (true, in) => assert(in === input0) in @@ -64,7 +64,7 @@ class TrackedSpec extends UnitSpec { assert(res0 === input0) val res1 = - Tracked.inputChanged[Int, Int](store) { + Tracked.inputChanged[String, String](store) { case (true, in) => fail() case (false, in) => From f653800cb30a7c5b0c675ec759da20673f6b1546 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sun, 16 Jul 2017 19:28:02 -0400 Subject: [PATCH 2/2] Add back outputChanged Fixes #79 --- .../src/main/scala/sbt/util/Tracked.scala | 68 +++++++++++++++++++ .../src/test/scala/sbt/util/TrackedSpec.scala | 29 ++++++++ 2 files changed, 97 insertions(+) diff --git a/util-tracking/src/main/scala/sbt/util/Tracked.scala b/util-tracking/src/main/scala/sbt/util/Tracked.scala index 823e9bde9..83ac2df0e 100644 --- a/util-tracking/src/main/scala/sbt/util/Tracked.scala +++ b/util-tracking/src/main/scala/sbt/util/Tracked.scala @@ -70,9 +70,66 @@ object Tracked { def lastOutput[I, O: JsonFormat](cacheFile: File)(f: (I, Option[O]) => O): I => O = lastOutput(CacheStore(cacheFile))(f) + /** + * Creates a tracker that indicates whether the output returned from `p` has changed or not. + * + * {{{ + * val cachedTask = inputChanged(cache / "inputs") { (inChanged, in: Inputs) => + * Tracked.outputChanged(cache / "output") { (outChanged, outputs: FilesInfo[PlainFileInfo]) => + * if (inChanged || outChanged) { + * doSomething(label, sources, classpath, outputDirectory, options, log) + * } + * } + * } + * cachedDoc(inputs)(() => exists(outputDirectory.allPaths.get.toSet)) + * }}} + */ + def outputChanged[A1: JsonFormat, A2](store: CacheStore)(f: (Boolean, A1) => A2): (() => A1) => A2 = p => { + val cache: SingletonCache[Long] = { + import CacheImplicits.LongJsonFormat + implicitly + } + val initial = p() + val help = new CacheHelp(cache) + val changed = help.changed(store, initial) + val result = f(changed, initial) + if (changed) { + help.save(store, initial) + } + result + } + + /** + * Creates a tracker that indicates whether the output returned from `p` has changed or not. + * + * {{{ + * val cachedTask = inputChanged(cache / "inputs") { (inChanged, in: Inputs) => + * Tracked.outputChanged(cache / "output") { (outChanged, outputs: FilesInfo[PlainFileInfo]) => + * if (inChanged || outChanged) { + * doSomething(label, sources, classpath, outputDirectory, options, log) + * } + * } + * } + * cachedDoc(inputs)(() => exists(outputDirectory.allPaths.get.toSet)) + * }}} + */ + def outputChanged[A1: JsonFormat, A2](cacheFile: File)(f: (Boolean, A1) => A2): (() => A1) => A2 = + outputChanged[A1, A2](CacheStore(cacheFile))(f) + /** * Creates a tracker that indicates whether the arguments given to f have changed since the most * recent invocation. + * + * {{{ + * val cachedTask = inputChanged(cache / "inputs") { (inChanged, in: Inputs) => + * Tracked.outputChanged(cache / "output") { (outChanged, outputs: FilesInfo[PlainFileInfo]) => + * if (inChanged || outChanged) { + * doSomething(label, sources, classpath, outputDirectory, options, log) + * } + * } + * } + * cachedDoc(inputs)(() => exists(outputDirectory.allPaths.get.toSet)) + * }}} */ def inputChanged[I: JsonFormat: SingletonCache, O](store: CacheStore)(f: (Boolean, I) => O): I => O = { in => val cache: SingletonCache[Long] = { @@ -90,6 +147,17 @@ object Tracked { /** * Creates a tracker that indicates whether the arguments given to f have changed since the most * recent invocation. + * + * {{{ + * val cachedTask = inputChanged(cache / "inputs") { (inChanged, in: Inputs) => + * Tracked.outputChanged(cache / "output") { (outChanged, outputs: FilesInfo[PlainFileInfo]) => + * if (inChanged || outChanged) { + * doSomething(label, sources, classpath, outputDirectory, options, log) + * } + * } + * } + * cachedDoc(inputs)(() => exists(outputDirectory.allPaths.get.toSet)) + * }}} */ def inputChanged[I: JsonFormat: SingletonCache, O](cacheFile: File)(f: (Boolean, I) => O): I => O = inputChanged(CacheStore(cacheFile))(f) diff --git a/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala b/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala index 4fa480ca7..e0cf74558 100644 --- a/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala +++ b/util-tracking/src/test/scala/sbt/util/TrackedSpec.scala @@ -104,6 +104,35 @@ class TrackedSpec extends UnitSpec { } } + "outputChanged" should "detect that the output has not changed" in { + withStore { store => + val input0: String = "foo" + val p0: () => String = () => input0 + + val res0 = + Tracked.outputChanged[String, String](store) { + case (true, in) => + assert(in === input0) + in + case (false, in) => + fail() + }(implicitly)(p0) + assert(res0 === input0) + + val res1 = + Tracked.outputChanged[String, String](store) { + case (true, in) => + fail() + case (false, in) => + assert(in === input0) + in + }(implicitly)(p0) + assert(res1 === input0) + + } + } + + "tstamp tracker" should "have a timestamp of 0 on first invocation" in { withStore { store => Tracked.tstamp(store) { last =>