add context-bound-relaxed helpers for write-only Tracked

This commit is contained in:
Brice Jaglin 2020-04-24 11:53:38 +02:00
parent 8429187ecb
commit 6be10901de
3 changed files with 136 additions and 2 deletions

View File

@ -419,6 +419,10 @@ lazy val utilTracking = (project in file("util-tracking"))
name := "Util Tracking",
libraryDependencies ++= Seq(scalatest % "test"),
utilMimaSettings,
mimaBinaryIssueFilters ++= Seq(
// Private final class constructors changed
ProblemFilters.exclude[IncompatibleMethTypeProblem]("sbt.util.Tracked#CacheHelp.this"),
)
)
.configure(addSbtIO)

View File

@ -14,7 +14,7 @@ import sbt.io.IO
import sbt.io.syntax._
import sbt.internal.util.EmptyCacheError
import sjsonnew.JsonFormat
import sjsonnew.{ JsonFormat, JsonWriter }
import sjsonnew.support.murmurhash.Hasher
object Tracked {
@ -95,6 +95,28 @@ object Tracked {
*/
def outputChanged[A1: JsonFormat, A2](store: CacheStore)(
f: (Boolean, A1) => A2
): (() => A1) => A2 = {
outputChangedW(store)(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))
* }}}
*
* This is a variant of `outputChanged` that takes `A1: JsonWriter` as opposed to `A1: JsonFormat`.
*/
def outputChangedW[A1: JsonWriter, A2](store: CacheStore)(
f: (Boolean, A1) => A2
): (() => A1) => A2 = p => {
val cache: SingletonCache[Long] = {
import CacheImplicits.LongJsonFormat
@ -127,6 +149,27 @@ object Tracked {
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 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))
* }}}
*
* This is a variant of `outputChanged` that takes `A1: JsonWriter` as opposed to `A1: JsonFormat`.
*/
def outputChangedW[A1: JsonWriter, A2](
cacheFile: File
)(f: (Boolean, A1) => A2): (() => A1) => A2 =
outputChangedW[A1, A2](CacheStore(cacheFile))(f)
/**
* Creates a tracker that indicates whether the arguments given to f have changed since the most
* recent invocation.
@ -144,6 +187,28 @@ object Tracked {
*/
def inputChanged[I: JsonFormat: SingletonCache, O](store: CacheStore)(
f: (Boolean, I) => O
): I => O =
inputChangedW(store)(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))
* }}}
*
* This is a variant of `inputChanged` that takes `I: JsonWriter` as opposed to `I: JsonFormat`.
*/
def inputChangedW[I: JsonWriter, O](store: CacheStore)(
f: (Boolean, I) => O
): I => O = { in =>
val cache: SingletonCache[Long] = {
import CacheImplicits.LongJsonFormat
@ -177,7 +242,29 @@ object Tracked {
): I => O =
inputChanged(CacheStore(cacheFile))(f)
private final class CacheHelp[I: JsonFormat](val sc: SingletonCache[Long]) {
/**
* 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))
* }}}
*
* This is a variant of `inputChanged` that takes `I: JsonWriter` as opposed to `I: JsonFormat`.
*/
def inputChangedW[I: JsonWriter, O](cacheFile: File)(
f: (Boolean, I) => O
): I => O =
inputChangedW(CacheStore(cacheFile))(f)
private final class CacheHelp[I: JsonWriter](val sc: SingletonCache[Long]) {
import CacheImplicits.implicitHashWriter
import CacheImplicits.LongJsonFormat
def save(store: CacheStore, value: I): Unit = {

View File

@ -11,6 +11,7 @@ import org.scalatest.FlatSpec
import sbt.io.IO
import sbt.io.syntax._
import sbt.util.CacheImplicits._
import sjsonnew.{ Builder, JsonWriter }
import scala.concurrent.Promise
@ -56,6 +57,27 @@ class TrackedSpec extends FlatSpec {
}
}
"inputChangedW" should "not require the input to have a JsonReader instance" in {
case class Input(v: Int)
implicit val writer = new JsonWriter[Input] {
override def write[J](obj: Input, builder: Builder[J]): Unit = builder.writeInt(obj.v)
}
withStore { store =>
val input0 = Input(1)
val cachedFun = Tracked.inputChangedW[Input, Int](store) {
case (_, in) => in.v
}
val res0 = cachedFun(input0)
assert(res0 === input0.v)
()
}
}
"inputChanged" should "detect that the input has not changed" in {
withStore { store =>
val input0 = "foo"
@ -113,6 +135,27 @@ class TrackedSpec extends FlatSpec {
}
}
"outputChangedW" should "not require the input to have a JsonReader instance" in {
case class Input(v: Int)
implicit val writer = new JsonWriter[Input] {
override def write[J](obj: Input, builder: Builder[J]): Unit = builder.writeInt(obj.v)
}
withStore { store =>
val input0 = Input(1)
val cachedFun = Tracked.outputChangedW[Input, Int](store) {
case (_, in) => in.v
}
val res0 = cachedFun(() => input0)
assert(res0 === input0.v)
()
}
}
"outputChanged" should "detect that the output has not changed" in {
withStore { store =>
val beforeCompletion: String = "before-completion"