Merge pull request #5513 from bjaglin/jsonwriter

add context-bound-relaxed helpers for write-only Tracked
This commit is contained in:
eugene yokota 2020-04-24 12:05:49 -04:00 committed by GitHub
commit 04a0b10ac4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 140 additions and 8 deletions

View File

@ -11,7 +11,6 @@
[asking]: https://stackoverflow.com/questions/ask?tags=sbt
[LICENSE]: LICENSE
[sbt/io]: https://github.com/sbt/io
[sbt/util]: https://github.com/sbt/util
[sbt/librarymanagement]: https://github.com/sbt/librarymanagement
[sbt/zinc]: https://github.com/sbt/zinc
[sbt/sbt]: https://github.com/sbt/sbt
@ -30,7 +29,6 @@ This is the 1.x series of sbt. The source code of sbt is split across
several GitHub repositories, including this one.
- [sbt/io][sbt/io] hosts `sbt.io` module.
- [sbt/util][sbt/util] hosts a collection of internally used modules.
- [sbt/librarymanagement][sbt/librarymanagement] hosts `sbt.librarymanagement` module that wraps Ivy.
- [sbt/zinc][sbt/zinc] hosts Zinc, an incremental compiler for Scala.
- [sbt/sbt][sbt/sbt], this repository hosts modules that implements the build tool.

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 {
@ -83,8 +83,8 @@ object Tracked {
* 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]) =>
* val cachedTask = inputChanged(cacheStoreFactory.make("inputs")) { (inChanged, in: Inputs) =>
* Tracked.outputChanged(cacheStoreFactory.make("output")) { (outChanged, outputs: FilesInfo[PlainFileInfo]) =>
* if (inChanged || outChanged) {
* doSomething(label, sources, classpath, outputDirectory, options, log)
* }
@ -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(cacheStoreFactory.make("inputs")) { (inChanged, in: Inputs) =>
* Tracked.outputChanged(cacheStoreFactory.make("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
@ -128,8 +150,7 @@ object Tracked {
outputChanged[A1, A2](CacheStore(cacheFile))(f)
/**
* Creates a tracker that indicates whether the arguments given to f have changed since the most
* recent invocation.
* Creates a tracker that indicates whether the output returned from `p` has changed or not.
*
* {{{
* val cachedTask = inputChanged(cache / "inputs") { (inChanged, in: Inputs) =>
@ -141,9 +162,53 @@ object Tracked {
* }
* 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.
*
* {{{
* val cachedTask = inputChanged(cacheStoreFactory.make("inputs")) { (inChanged, in: Inputs) =>
* Tracked.outputChanged(cacheStoreFactory.make("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 =
inputChangedW(store)(f)
/**
* Creates a tracker that indicates whether the arguments given to f have changed since the most
* recent invocation.
*
* {{{
* val cachedTask = inputChanged(cacheStoreFactory.make("inputs")) { (inChanged, in: Inputs) =>
* Tracked.outputChanged(cacheStoreFactory.make("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"