From f653800cb30a7c5b0c675ec759da20673f6b1546 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Sun, 16 Jul 2017 19:28:02 -0400 Subject: [PATCH] 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 =>