summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2017-10-28 17:14:45 -0700
committerLi Haoyi <haoyi.sg@gmail.com>2017-10-28 17:14:45 -0700
commit8a85ac816b5514c481341456061ed26a83f72bb0 (patch)
treefb56b211e95b6b504bdb649176bd66dc10a668b7 /src
parentfee05e6163fadff27521807cc3d97d61b74ab466 (diff)
downloadmill-8a85ac816b5514c481341456061ed26a83f72bb0.tar.gz
mill-8a85ac816b5514c481341456061ed26a83f72bb0.tar.bz2
mill-8a85ac816b5514c481341456061ed26a83f72bb0.zip
Get rid of `DefCtx` for good. All tests passing entirely using the new `Discovered` labeling mechanism
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/forge/DefCtx.scala79
-rw-r--r--src/main/scala/forge/Evaluator.scala23
-rw-r--r--src/main/scala/forge/Target.scala41
-rw-r--r--src/test/scala/forge/EvaluationTests.scala49
-rw-r--r--src/test/scala/forge/GraphTests.scala80
-rw-r--r--src/test/scala/forge/Main.scala32
-rw-r--r--src/test/scala/forge/TestUtil.scala39
7 files changed, 99 insertions, 244 deletions
diff --git a/src/main/scala/forge/DefCtx.scala b/src/main/scala/forge/DefCtx.scala
deleted file mode 100644
index 3439f755..00000000
--- a/src/main/scala/forge/DefCtx.scala
+++ /dev/null
@@ -1,79 +0,0 @@
-package forge
-
-
-import scala.annotation.compileTimeOnly
-import scala.language.experimental.macros
-import scala.reflect.macros.blackbox._
-
-
-final case class DefCtx(baseLabel: String, anonId: Option[Int]){
- def label = baseLabel + anonId.getOrElse("")
-}
-object DefCtx{
- @compileTimeOnly("A DefCtx can only be provided directly within a T{} macro")
- implicit def dummy: DefCtx with Int = ???
-}
-
-object T{
- def apply[T](expr: T): T = macro applyImpl[T]
-
- def applyImpl[T: c.WeakTypeTag](c: Context)(expr: c.Expr[T]): c.Expr[T] = {
- import c.universe._
- var count = 0
- object transformer extends c.universe.Transformer {
- override def transform(tree: c.Tree): c.Tree = {
- if (tree.toString.startsWith("forge.") && tree.toString.endsWith(".DefCtx.dummy")) {
- count += 1
- c.typecheck(q"forge.DefCtx(sourcecode.Enclosing(), Some($count))")
- }else tree match{
- case Apply(fun, args) =>
- val extendedParams = fun.tpe.paramLists.head.padTo(
- args.length,
- fun.tpe.paramLists.head.lastOption.getOrElse(null)
- )
- val newArgs =
- for((sym, tree) <- extendedParams.zip(args))
- yield {
- if (sym.asTerm.isByNameParam) tree
- else transform(tree)
- }
- treeCopy.Apply(tree, transform(fun), newArgs)
-
- case t: DefDef => t
- case t: ClassDef => t
- case t: Function => t
- case t: LabelDef => t
- case t => super.transform(t)
- }
-
- }
- }
-
-
- def transformTerminal(tree: c.Tree): c.Tree = tree match{
- case Block(stats, returnExpr) =>
- treeCopy.Block(
- tree,
- stats.map(transformer.transform(_)),
- transformTerminal(returnExpr)
- )
-
- case Apply(fun, args) =>
- var isTransformed = false
- val newArgs = for(x <- args) yield {
- if (x.toString.startsWith("forge.") && x.toString.endsWith(".DefCtx.dummy")) {
- isTransformed = true
- c.typecheck(q"forge.DefCtx(sourcecode.Enclosing(), None)")
- }else transformer.transform(x)
- }
-
- assert(isTransformed)
- treeCopy.Apply(tree, transformer.transform(fun), newArgs)
-
- case _ => ???
- }
-
- val transformed = transformTerminal(expr.tree)
- c.Expr[T](transformed)
- }
-} \ No newline at end of file
diff --git a/src/main/scala/forge/Evaluator.scala b/src/main/scala/forge/Evaluator.scala
index 32a3c9d0..4d9a435e 100644
--- a/src/main/scala/forge/Evaluator.scala
+++ b/src/main/scala/forge/Evaluator.scala
@@ -8,7 +8,7 @@ import sourcecode.Enclosing
import scala.collection.mutable
class Evaluator(workspacePath: jnio.Path,
- enclosingBase: DefCtx){
+ labeling: Map[Target[_], Seq[String]]){
/**
* Cache from the ID of the first terminal target in a group to the has of
@@ -20,12 +20,14 @@ class Evaluator(workspacePath: jnio.Path,
jnio.Files.createDirectories(workspacePath)
val sortedGroups = Evaluator.groupAroundNamedTargets(
- Evaluator.topoSortedTransitiveTargets(targets)
+ Evaluator.topoSortedTransitiveTargets(targets),
+ labeling
)
val evaluated = new MutableOSet[Target[_]]
val results = mutable.Map.empty[Target[_], Any]
for (group <- sortedGroups){
+ println("Evaluating group " + group)
val (newResults, newEvaluated) = evaluateGroup(group, results)
evaluated.appendAll(newEvaluated)
for((k, v) <- newResults) results.put(k, v)
@@ -46,19 +48,19 @@ class Evaluator(workspacePath: jnio.Path,
val terminals = group.filter(!internalInputSet(_))
val primeTerminal = terminals.items(0)
- val enclosingStr = primeTerminal.defCtx.label
+
+ val primeLabel = labeling(primeTerminal).mkString("/")
val targetDestPath = workspacePath.resolve(
- jnio.Paths.get(enclosingStr.stripSuffix(enclosingBase.label))
+ jnio.Paths.get(primeLabel)
)
val anyDirty = group.exists(_.dirty)
deleteRec(targetDestPath)
val inputsHash = inputResults.hashCode
- resultCache.get(primeTerminal.defCtx.label) match{
+ resultCache.get(primeLabel) match{
case Some((hash, terminalResults))
if hash == inputsHash && !anyDirty =>
for((terminal, res) <- terminals.items.zip(terminalResults)){
-
newResults(terminal) = primeTerminal.formatter.reads(Json.parse(res)).get
}
@@ -69,7 +71,7 @@ class Evaluator(workspacePath: jnio.Path,
val targetInputValues = target.inputs.toVector.map(x =>
newResults.getOrElse(x, results(x))
)
- if (target.defCtx.anonId.isDefined) {
+ if (!labeling.contains(target)) {
val res = target.evaluate(new Args(targetInputValues, targetDestPath))
newResults(target) = res
}else{
@@ -83,7 +85,7 @@ class Evaluator(workspacePath: jnio.Path,
newResults(target) = res
}
}
- resultCache(primeTerminal.defCtx.label) = (inputsHash, terminalResults)
+ resultCache(primeLabel) = (inputsHash, terminalResults)
}
@@ -105,7 +107,8 @@ class Evaluator(workspacePath: jnio.Path,
object Evaluator{
class TopoSorted private[Evaluator] (val values: OSet[Target[_]])
case class Results(values: Seq[Any], evaluated: OSet[Target[_]])
- def groupAroundNamedTargets(topoSortedTargets: TopoSorted): OSet[OSet[Target[_]]] = {
+ def groupAroundNamedTargets(topoSortedTargets: TopoSorted,
+ labeling: Map[Target[_], Seq[String]]): OSet[OSet[Target[_]]] = {
val grouping = new MultiBiMap[Int, Target[_]]()
var groupCount = 0
@@ -120,7 +123,7 @@ object Evaluator{
val targetGroup = grouping.lookupValue(target)
for(upstream <- target.inputs){
grouping.lookupValueOpt(upstream) match{
- case None if upstream.defCtx.anonId.nonEmpty =>
+ case None if !labeling.contains(upstream) =>
grouping.add(targetGroup, upstream)
case Some(upstreamGroup) if upstreamGroup == targetGroup =>
val upstreamTargets = grouping.removeAll(upstreamGroup)
diff --git a/src/main/scala/forge/Target.scala b/src/main/scala/forge/Target.scala
index 6f434030..8966a729 100644
--- a/src/main/scala/forge/Target.scala
+++ b/src/main/scala/forge/Target.scala
@@ -5,10 +5,6 @@ import java.nio.{file => jnio}
import play.api.libs.json.{Format, Json}
abstract class Target[T](implicit formatter: Format[T]) extends Target.Ops[T]{
/**
- * Where in the Scala codebase was this target defined?
- */
- val defCtx: DefCtx
- /**
* What other Targets does this Target depend on?
*/
val inputs: Seq[Target[_]]
@@ -33,22 +29,19 @@ object Target{
val str = formatter.writes(res)
(res, Json.stringify(str))
}
- val defCtx: DefCtx
- def map[V: Format](f: T => V)(implicit defCtx: DefCtx) = {
- new Target.Mapped(this, f, defCtx)
+ def map[V: Format](f: T => V) = {
+ new Target.Mapped(this, f)
}
- def zip[V: Format](other: Target[V])(implicit defCtx: DefCtx) = {
- new Target.Zipped(this, other, defCtx)
+ def zip[V: Format](other: Target[V]) = {
+ new Target.Zipped(this, other)
}
def ~[V: Format, R: Format](other: Target[V])
- (implicit s: Implicits.Sequencer[T, V, R], defCtx: DefCtx): Target[R] = {
+ (implicit s: Implicits.Sequencer[T, V, R]): Target[R] = {
this.zip(other).map(s.apply _ tupled)
}
-
- override def toString = defCtx.label.split('.').last
}
- def test(inputs: Target[Int]*)(implicit defCtx: DefCtx) = {
- new Test(inputs, defCtx, pure = inputs.nonEmpty)
+ def test(inputs: Target[Int]*) = {
+ new Test(inputs, pure = inputs.nonEmpty)
}
/**
@@ -57,7 +50,6 @@ object Target{
* test how changes propagate.
*/
class Test(val inputs: Seq[Target[Int]],
- val defCtx: DefCtx,
val pure: Boolean) extends Target[Int]{
var counter = 0
var lastCounter = counter
@@ -67,36 +59,33 @@ object Target{
}
override def dirty = lastCounter != counter
}
- def traverse[T: Format](source: Seq[Target[T]])(implicit defCtx: DefCtx) = {
- new Traverse[T](source, defCtx)
+ def traverse[T: Format](source: Seq[Target[T]]) = {
+ new Traverse[T](source)
}
- class Traverse[T: Format](val inputs: Seq[Target[T]], val defCtx: DefCtx) extends Target[Seq[T]]{
+ class Traverse[T: Format](val inputs: Seq[Target[T]]) extends Target[Seq[T]]{
def evaluate(args: Args) = {
for (i <- 0 until args.length)
yield args(i).asInstanceOf[T]
}
}
- class Mapped[T, V: Format](source: Target[T], f: T => V,
- val defCtx: DefCtx) extends Target[V]{
+ class Mapped[T, V: Format](source: Target[T], f: T => V) extends Target[V]{
def evaluate(args: Args) = f(args(0))
val inputs = List(source)
}
class Zipped[T: Format, V: Format](source1: Target[T],
- source2: Target[V],
- val defCtx: DefCtx) extends Target[(T, V)]{
+ source2: Target[V]) extends Target[(T, V)]{
def evaluate(args: Args) = (args(0), args(0))
val inputs = List(source1, source1)
}
- def path(path: jnio.Path)(implicit defCtx: DefCtx) = new Path(path, defCtx)
- class Path(path: jnio.Path, val defCtx: DefCtx) extends Target[jnio.Path]{
+ def path(path: jnio.Path) = new Path(path)
+ class Path(path: jnio.Path) extends Target[jnio.Path]{
def evaluate(args: Args) = path
val inputs = Nil
}
class Subprocess(val inputs: Seq[Target[_]],
- command: Args => Seq[String],
- val defCtx: DefCtx) extends Target[Subprocess.Result] {
+ command: Args => Seq[String]) extends Target[Subprocess.Result] {
def evaluate(args: Args) = {
jnio.Files.createDirectories(args.dest)
diff --git a/src/test/scala/forge/EvaluationTests.scala b/src/test/scala/forge/EvaluationTests.scala
index cecbf5f1..d80781c0 100644
--- a/src/test/scala/forge/EvaluationTests.scala
+++ b/src/test/scala/forge/EvaluationTests.scala
@@ -7,30 +7,36 @@ import utest._
object EvaluationTests extends TestSuite{
+ val workspace = jnio.Paths.get("target/workspace")
val tests = Tests{
- val baseCtx = DefCtx("forge.ForgeTests.tests ", None)
-
val graphs = new TestUtil.TestGraphs()
import graphs._
'evaluateSingle - {
- val evaluator = new Evaluator(jnio.Paths.get("target/workspace"), baseCtx)
- def check(target: Target[_], expValue: Any, expEvaled: OSet[Target[_]]) = {
- val Evaluator.Results(returnedValues, returnedEvaluated) = evaluator.evaluate(OSet(target))
- assert(
- returnedValues == Seq(expValue),
- returnedEvaluated == expEvaled
- )
- // Second time the value is already cached, so no evaluation needed
- val Evaluator.Results(returnedValues2, returnedEvaluated2) = evaluator.evaluate(OSet(target))
- assert(
- returnedValues2 == returnedValues,
- returnedEvaluated2 == OSet()
+
+ class Checker[T: Discovered](base: T) {
+ val evaluator = new Evaluator(
+ workspace,
+ implicitly[Discovered[T]].apply(base).map(_.swap).toMap
)
+ def apply(target: Target[_], expValue: Any, expEvaled: OSet[Target[_]]) = {
+ val Evaluator.Results(returnedValues, returnedEvaluated) = evaluator.evaluate(OSet(target))
+ assert(
+ returnedValues == Seq(expValue),
+ returnedEvaluated == expEvaled
+ )
+ // Second time the value is already cached, so no evaluation needed
+ val Evaluator.Results(returnedValues2, returnedEvaluated2) = evaluator.evaluate(OSet(target))
+ assert(
+ returnedValues2 == returnedValues,
+ returnedEvaluated2 == OSet()
+ )
+ }
}
'singleton - {
import singleton._
+ val check = new Checker(singleton)
// First time the target is evaluated
check(single, expValue = 0, expEvaled = OSet(single))
@@ -40,16 +46,20 @@ object EvaluationTests extends TestSuite{
}
'pair - {
import pair._
+ val check = new Checker(pair)
check(down, expValue = 0, expEvaled = OSet(up, down))
+ println("=" * 20 + "incrementing down.counter" + "=" * 20)
down.counter += 1
check(down, expValue = 1, expEvaled = OSet(down))
+ println("=" * 20 + "incrementing up.counter" + "=" * 20)
up.counter += 1
check(down, expValue = 2, expEvaled = OSet(up, down))
}
'anonTriple - {
import anonTriple._
+ val check = new Checker(anonTriple)
val middle = down.inputs(0)
check(down, expValue = 0, expEvaled = OSet(up, middle, down))
@@ -65,6 +75,7 @@ object EvaluationTests extends TestSuite{
}
'diamond - {
import diamond._
+ val check = new Checker(diamond)
check(down, expValue = 0, expEvaled = OSet(up, left, right, down))
down.counter += 1
@@ -82,6 +93,7 @@ object EvaluationTests extends TestSuite{
}
'anonDiamond - {
import anonDiamond._
+ val check = new Checker(anonDiamond)
val left = down.inputs(0).asInstanceOf[Target.Test]
val right = down.inputs(1).asInstanceOf[Target.Test]
check(down, expValue = 0, expEvaled = OSet(up, left, right, down))
@@ -99,15 +111,6 @@ object EvaluationTests extends TestSuite{
right.counter += 1
check(down, expValue = 5, expEvaled = OSet(left, right, down))
}
-// 'anonImpureDiamond - {
-// import AnonImpureDiamond._
-// val left = down.inputs(0).asInstanceOf[Target.Test]
-// val right = down.inputs(1).asInstanceOf[Target.Test]
-// check(down, expValue = 0, expEvaled = Seq(up, left, right, down))
-//
-// down.counter += 1
-// check(down, expValue = 1, expEvaled = Seq(left, down))
-// }
}
diff --git a/src/test/scala/forge/GraphTests.scala b/src/test/scala/forge/GraphTests.scala
index 55763909..aead2d0c 100644
--- a/src/test/scala/forge/GraphTests.scala
+++ b/src/test/scala/forge/GraphTests.scala
@@ -14,15 +14,15 @@ object GraphTests extends TestSuite{
'discovery{
class CanNest{
- val single = T{ test() }
- val invisible: Any = T{ test() }
+ val single = test()
+ val invisible: Any = test()
}
object outer {
- val single = T{ test() }
- val invisible: Any = T{ test() }
+ val single = test()
+ val invisible: Any = test()
object nested{
- val single = T{ test() }
- val invisible: Any = T{ test() }
+ val single = test()
+ val invisible: Any = test()
}
val classInstance = new CanNest
@@ -36,66 +36,6 @@ object GraphTests extends TestSuite{
)
assert(discovered == expected)
}
- 'syntaxLimits - {
- // Make sure that we properly prohibit cases where a `test()` target can
- // be created more than once with the same `DefCtx`, while still allowing
- // cases where the `test()` target is created exactly one time, or even
- // zero-or-one times (since that's ok, as long as it's not more than once)
-
- 'neg - {
- 'nakedTest - {
- compileError("test()")
- ()
- }
- 'notFunctionCall - {
- compileError("T{ 123 }")
- ()
- }
- 'functionCallWithoutImplicit - {
- compileError("T{ println() }")
- ()
- }
- // Make sure the snippets without `test()`s compile, but the same snippets
- // *with* the `test()` calls do not (presumably due to the `@compileTimeOnly`
- // annotation)
- //
- // For some reason, `if(false)` isn't good enough because scalac constant
- // folds the conditional, eliminates the entire code block, and makes any
- // `@compileTimeOnly`s annotations disappear...
-
-
- 'canEvaluateMoreThanOnce - {
- if (math.random() > 10) T{ Seq(1, 2).map(_ => ???); test() }
- compileError("T{ Seq(1, 2).map(_ => test()); test() }")
-
- if (math.random() > 10) T{ class Foo{ ??? }; test() }
- compileError("T{ class Foo{ test() }; test() }")
-
- if (math.random() > 10) T{ test({while(true){ }; ???}) }
- compileError("T{ test({while(true){ test() }; ???}) }")
-
- if (math.random() > 10) T{ do{ } while(true); test() }
- compileError("T{ do{ test() } while(true); test() }")
-
- if (math.random() > 10) T{ def foo() = ???; test() }
- compileError("T{ def foo() = test(); test() }")
-
- if (math.random() > 10) T{ None.getOrElse(???); test() }
- if (math.random() > 10) T{ None.contains(test()); test() }
- compileError("T{ None.getOrElse(test()); test() }")
-
- ()
- }
- }
- 'pos - {
- T{ test({val x = test(); x}) }
- T{ test({lazy val x = test(); x}) }
- T { object foo {val x = test()}; test(foo.x) }
- T{ test({val x = if (math.random() > 0.5) test() else test(); x}) }
-
- ()
- }
- }
'topoSortedTransitiveTargets - {
@@ -142,15 +82,19 @@ object GraphTests extends TestSuite{
target: Target.Test,
expected: OSet[(OSet[Target.Test], Int)]) = {
+ val mapping: Map[Target[_], Seq[String]] = {
+ implicitly[Discovered[T]].apply(base).map(_.swap).toMap
+ }
val grouped = Evaluator.groupAroundNamedTargets(
- Evaluator.topoSortedTransitiveTargets(OSet(target))
+ Evaluator.topoSortedTransitiveTargets(OSet(target)),
+ mapping
)
TestUtil.checkTopological(grouped.flatMap(_.items))
for(((expectedPresent, expectedSize), i) <- expected.items.zipWithIndex){
val grouping = grouped.items(i)
assert(
grouping.size == expectedSize,
- expectedPresent.forall(grouping.contains)
+ grouping.filter(mapping.contains) == expectedPresent
)
}
}
diff --git a/src/test/scala/forge/Main.scala b/src/test/scala/forge/Main.scala
index d9262627..b65099ec 100644
--- a/src/test/scala/forge/Main.scala
+++ b/src/test/scala/forge/Main.scala
@@ -7,34 +7,32 @@ object Main{
def main(args: Array[String]): Unit = {
- val sourceRoot = T{ Target.path(jnio.Paths.get("src/test/resources/example/src")) }
- val resourceRoot = T{ Target.path(jnio.Paths.get("src/test/resources/example/resources")) }
- val allSources = T{ list(sourceRoot) }
- val classFiles = T{ compileAll(allSources) }
- val jar = T{ jarUp(resourceRoot, classFiles) }
-
- val evaluator = new Evaluator(
- jnio.Paths.get("target/workspace"),
- DefCtx("forge.Main ", None)
- )
- evaluator.evaluate(OSet(jar))
+ val sourceRoot = Target.path(jnio.Paths.get("src/test/resources/example/src"))
+ val resourceRoot = Target.path(jnio.Paths.get("src/test/resources/example/resources"))
+ val allSources = list(sourceRoot)
+ val classFiles = compileAll(allSources)
+ val jar = jarUp(resourceRoot, classFiles)
+
+// val evaluator = new Evaluator(
+// jnio.Paths.get("target/workspace"),
+// DefCtx("forge.Main ", None)
+// )
+// evaluator.evaluate(OSet(jar))
}
- def compileAll(sources: Target[Seq[jnio.Path]])
- (implicit defCtx: DefCtx) = {
+ def compileAll(sources: Target[Seq[jnio.Path]]) = {
new Target.Subprocess(
Seq(sources),
args =>
Seq("javac") ++
args[Seq[jnio.Path]](0).map(_.toAbsolutePath.toString) ++
- Seq("-d", args.dest.toAbsolutePath.toString),
- defCtx
+ Seq("-d", args.dest.toAbsolutePath.toString)
).map(_.dest)
}
- def list(root: Target[jnio.Path])(implicit defCtx: DefCtx): Target[Seq[jnio.Path]] = {
+ def list(root: Target[jnio.Path]): Target[Seq[jnio.Path]] = {
root.map(jnio.Files.list(_).iterator().asScala.toArray[jnio.Path])
}
- case class jarUp(roots: Target[jnio.Path]*)(implicit val defCtx: DefCtx) extends Target[jnio.Path]{
+ case class jarUp(roots: Target[jnio.Path]*) extends Target[jnio.Path]{
val inputs = roots
def evaluate(args: Args): jnio.Path = {
diff --git a/src/test/scala/forge/TestUtil.scala b/src/test/scala/forge/TestUtil.scala
index 2eea1e0d..f06dc6b6 100644
--- a/src/test/scala/forge/TestUtil.scala
+++ b/src/test/scala/forge/TestUtil.scala
@@ -19,26 +19,26 @@ object TestUtil {
class TestGraphs(){
object singleton {
- val single = T{ test() }
+ val single = test()
}
object pair {
- val up = T{ test() }
- val down = T{ test(up) }
+ val up = test()
+ val down = test(up)
}
object anonTriple{
- val up = T{ test() }
- val down = T{ test(test(up)) }
+ val up = test()
+ val down = test(test(up))
}
object diamond{
- val up = T{ test() }
- val left = T{ test(up) }
- val right = T{ test(up) }
- val down = T{ test(left, right) }
+ val up = test()
+ val left = test(up)
+ val right = test(up)
+ val down = test(left, right)
}
object anonDiamond{
- val up = T{ test() }
- val down = T{ test(test(up), test(up)) }
+ val up = test()
+ val down = test(test(up), test(up))
}
// o g-----o
@@ -53,24 +53,21 @@ object TestUtil {
// / /
// o--B o
object bigSingleTerminal{
- val a = T{ test(test(), test()) }
- val b = T{ test(test()) }
- val e = T{
+ val a = test(test(), test())
+ val b = test(test())
+ val e = {
val c = test(a)
val d = test(a)
test(test(test(), test(c)), test(test(c, test(d, b))))
}
- val f = T{
- test(test(test(), test(e)))
- }
- val i = T{
+ val f = test(test(test(), test(e)))
+
+ val i = {
val g = test()
val h = test(g, e)
test(test(g), test(test(h)))
}
- val j = T{
- test(test(i), test(i, f), test(f))
- }
+ val j = test(test(i), test(i, f), test(f))
}
(singleton, pair, anonTriple, diamond, anonDiamond, bigSingleTerminal)