From b12c3f0f040a2b6caf1f94ad789d80b91313e381 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sun, 12 Nov 2017 11:28:52 -0800 Subject: Rename out `Task.path` to `Task.source`, use it more aggressively to get `amm -w` watch-n-rebuild functionality working on `build.sc` --- core/src/main/scala/mill/Main.scala | 14 +++++--- core/src/main/scala/mill/define/Task.scala | 42 ++++++++++------------ core/src/main/scala/mill/eval/Evaluator.scala | 21 ++++++----- core/src/main/scala/mill/modules/Jvm.scala | 2 +- core/src/test/scala/mill/EvaluationTests.scala | 8 +++-- core/src/test/scala/mill/GraphTests.scala | 6 ++-- core/src/test/scala/mill/JavaCompileJarTests.scala | 30 ++++++++-------- 7 files changed, 67 insertions(+), 56 deletions(-) (limited to 'core') diff --git a/core/src/main/scala/mill/Main.scala b/core/src/main/scala/mill/Main.scala index 3112ec27..2b33fb78 100644 --- a/core/src/main/scala/mill/Main.scala +++ b/core/src/main/scala/mill/Main.scala @@ -10,7 +10,7 @@ import play.api.libs.json.Format object Main { - def apply[T: Discovered](args: Seq[String], obj: T) = { + def apply[T: Discovered](args: Seq[String], obj: T, watch: Path => Unit) = { val Seq(selectorString, rest @_*) = args val selector = selectorString.split('.') val discovered = implicitly[Discovered[T]] @@ -36,7 +36,13 @@ object Main { case mill.discover.Router.Result.Success(target) => println("Found target! " + target) val evaluated = evaluator.evaluate(OSet(target)) - pprint.log(evaluated) + + evaluated.transitive.foreach{ + case t: define.Source => + println("Watching " + t.handle.path) + watch(t.handle.path) + case _ => // do nothing + } } case Some(labelled: LabelInfo[T, _]) => @@ -46,8 +52,8 @@ object Main { case None => println("Unknown selector: " + selector) } } - } + def main(args: Array[String]): Unit = { val List(buildFile, rest @_*) = args.toList @@ -60,7 +66,7 @@ object Main { if (!result.isSuccess) println(result) else{ val (obj, discovered) = result.asInstanceOf[Res.Success[(Any, Discovered[Any])]].s - apply(rest, obj)(discovered) + apply(rest, obj, _ => ())(discovered) } } diff --git a/core/src/main/scala/mill/define/Task.scala b/core/src/main/scala/mill/define/Task.scala index c1568a8f..ea197039 100644 --- a/core/src/main/scala/mill/define/Task.scala +++ b/core/src/main/scala/mill/define/Task.scala @@ -34,10 +34,22 @@ class Command[+T](t: Task[T]) extends Task[T] { val inputs = Seq(t) def evaluate(args: Args) = args[T](0) } +object Source{ + implicit def apply(p: ammonite.ops.Path) = new Source(p) +} +class Source(path: ammonite.ops.Path) extends Task[PathRef]{ + def handle = PathRef(path) + def evaluate(args: Args) = handle + override def sideHash = handle.hashCode() + val inputs = Nil +} + object Task extends Applicative.Applyer[Task, Task, Args]{ def underlying[A](v: Task[A]) = v + def source(path: ammonite.ops.Path) = new Source(path) + trait Cacher extends mill.define.Cacher[Task, Target]{ def wrapCached[T](t: Task[T], enclosing: String): Target[T] = new TargetImpl(t, enclosing) } @@ -47,42 +59,32 @@ object Task extends Applicative.Applyer[Task, Task, Args]{ def evaluate(args: Args) = t0 } - implicit def apply[T](t: T): Target[T] = macro targetCachedImpl[T] + implicit def apply[T](t: T): Target[T] = macro targetImpl[T] def apply[T](t: Task[T]): Target[T] = macro Cacher.impl0[Task, T] - def command[T](t: T): Command[T] = macro targetCommandImpl[T] + def command[T](t: T): Command[T] = macro commandImpl[T] + def command[T](t: Task[T]): Command[T] = new Command(t) def task[T](t: T): Task[T] = macro Applicative.impl[Task, T, Args] def task[T](t: Task[T]): Task[T] = t - - def targetCommandImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Command[T]] = { + def commandImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Command[T]] = { import c.universe._ c.Expr[Command[T]]( q"new ${weakTypeOf[Command[T]]}(${Applicative.impl[Task, T, Args](c)(t).tree})" ) } - def targetCachedImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Target[T]] = { + + def targetImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Target[T]] = { c.Expr[Target[T]]( mill.define.Cacher.wrapCached(c)( Applicative.impl[Task, T, Args](c)(t).tree ) ) } - def targetCachedImpl2[T: c.WeakTypeTag, V: c.WeakTypeTag] - (c: Context) - (t: c.Expr[T]) - (f: c.Expr[T => V]): c.Expr[Target[V]] = { - import c.universe._ - c.Expr[Target[V]]( - mill.define.Cacher.wrapCached(c)( - q"""${Applicative.impl[Task, T, Args](c)(t).tree}.map($f)""" - ) - ) - } abstract class Ops[+T]{ this: Task[T] => def map[V](f: T => V) = new Task.Mapped(this, f) @@ -118,13 +120,7 @@ object Task extends Applicative.Applyer[Task, Task, Args]{ val inputs = List(source1, source2) } - def path(path: ammonite.ops.Path) = new Path(path) - class Path(path: ammonite.ops.Path) extends Task[PathRef]{ - def handle = PathRef(path) - def evaluate(args: Args) = handle - override def sideHash = handle.hashCode() - val inputs = Nil - } + def mapCtx[A, B](t: Task[A])(f: (A, Args) => B) = t.mapDest(f) diff --git a/core/src/main/scala/mill/eval/Evaluator.scala b/core/src/main/scala/mill/eval/Evaluator.scala index 54339591..e0c13044 100644 --- a/core/src/main/scala/mill/eval/Evaluator.scala +++ b/core/src/main/scala/mill/eval/Evaluator.scala @@ -13,7 +13,8 @@ class Evaluator(workspacePath: Path, def evaluate(targets: OSet[Task[_]]): Evaluator.Results = { mkdir(workspacePath) - val topoSorted = Evaluator.topoSortedTransitiveTargets(targets) + val transitive = Evaluator.transitiveTargets(targets) + val topoSorted = Evaluator.topoSorted(transitive) val sortedGroups = Evaluator.groupAroundNamedTargets(topoSorted, labeling) val evaluated = new OSet.Mutable[Task[_]] @@ -32,7 +33,7 @@ class Evaluator(workspacePath: Path, } - Evaluator.Results(targets.items.map(results), evaluated) + Evaluator.Results(targets.items.map(results), evaluated, transitive) } def evaluateGroupCached(group: OSet[Task[_]], @@ -128,7 +129,7 @@ class Evaluator(workspacePath: Path, object Evaluator{ class TopoSorted private[Evaluator](val values: OSet[Task[_]]) - case class Results(values: Seq[Any], targets: OSet[Task[_]]) + case class Results(values: Seq[Any], evaluated: OSet[Task[_]], transitive: OSet[Task[_]]) def groupAroundNamedTargets(topoSortedTargets: TopoSorted, labeling: Map[Task[_], Labelled[_]]): MultiBiMap[Int, Task[_]] = { @@ -179,11 +180,7 @@ object Evaluator{ output } - /** - * Takes the given targets, finds all the targets they transitively depend - * on, and sort them topologically. Fails if there are dependency cycles - */ - def topoSortedTransitiveTargets(sourceTargets: OSet[Task[_]]): TopoSorted = { + def transitiveTargets(sourceTargets: OSet[Task[_]]): OSet[Task[_]] = { val transitiveTargets = new OSet.Mutable[Task[_]] def rec(t: Task[_]): Unit = { if (transitiveTargets.contains(t)) () // do nothing @@ -194,6 +191,14 @@ object Evaluator{ } sourceTargets.items.foreach(rec) + transitiveTargets + } + /** + * Takes the given targets, finds all the targets they transitively depend + * on, and sort them topologically. Fails if there are dependency cycles + */ + def topoSorted(transitiveTargets: OSet[Task[_]]): TopoSorted = { + val targetIndices = transitiveTargets.items.zipWithIndex.toMap val numberedEdges = diff --git a/core/src/main/scala/mill/modules/Jvm.scala b/core/src/main/scala/mill/modules/Jvm.scala index bfb7defe..f18e2d96 100644 --- a/core/src/main/scala/mill/modules/Jvm.scala +++ b/core/src/main/scala/mill/modules/Jvm.scala @@ -59,7 +59,7 @@ object Jvm { Some(outputPath) } } - case class jarUp(roots: Task[PathRef]*) extends Task[PathRef]{ + def jarUp(roots: Task[PathRef]*) = new Task[PathRef]{ val inputs = roots def evaluate(args: Args): PathRef = { diff --git a/core/src/test/scala/mill/EvaluationTests.scala b/core/src/test/scala/mill/EvaluationTests.scala index 0face75e..1cfc93fa 100644 --- a/core/src/test/scala/mill/EvaluationTests.scala +++ b/core/src/test/scala/mill/EvaluationTests.scala @@ -27,7 +27,7 @@ object EvaluationTests extends TestSuite{ // are directly evaluating tasks which need to re-evaluate every time secondRunNoOp: Boolean = true) = { - val Evaluator.Results(returnedValues, returnedEvaluated) = evaluator.evaluate(OSet(target)) + val Evaluator.Results(returnedValues, returnedEvaluated, _) = evaluator.evaluate(OSet(target)) val (matchingReturnedEvaled, extra) = returnedEvaluated.items.partition(expEvaled.contains) @@ -39,7 +39,7 @@ object EvaluationTests extends TestSuite{ // Second time the value is already cached, so no evaluation needed if (secondRunNoOp){ - val Evaluator.Results(returnedValues2, returnedEvaluated2) = evaluator.evaluate(OSet(target)) + val Evaluator.Results(returnedValues2, returnedEvaluated2, _) = evaluator.evaluate(OSet(target)) val expecteSecondRunEvaluated = OSet() assert( returnedValues2 == returnedValues, @@ -50,7 +50,9 @@ object EvaluationTests extends TestSuite{ } def countGroups[T: Discovered](t: T, terminals: Task[_]*) = { val labeling = Discovered.mapping(t) - val topoSorted = Evaluator.topoSortedTransitiveTargets(OSet.from(terminals)) + val topoSorted = Evaluator.topoSorted( + Evaluator.transitiveTargets(OSet.from(terminals)) + ) val grouped = Evaluator.groupAroundNamedTargets(topoSorted, labeling) grouped.keyCount } diff --git a/core/src/test/scala/mill/GraphTests.scala b/core/src/test/scala/mill/GraphTests.scala index 575b4a89..60895c71 100644 --- a/core/src/test/scala/mill/GraphTests.scala +++ b/core/src/test/scala/mill/GraphTests.scala @@ -84,7 +84,7 @@ object GraphTests extends TestSuite{ 'topoSortedTransitiveTargets - { def check(targets: OSet[Task[_]], expected: OSet[Task[_]]) = { - val result = Evaluator.topoSortedTransitiveTargets(targets).values + val result = Evaluator.topoSorted(Evaluator.transitiveTargets(targets)).values TestUtil.checkTopological(result) assert(result == expected) } @@ -128,7 +128,7 @@ object GraphTests extends TestSuite{ ) ) 'bigSingleTerminal - { - val result = Evaluator.topoSortedTransitiveTargets(OSet(bigSingleTerminal.j)).values + val result = Evaluator.topoSorted(Evaluator.transitiveTargets(OSet(bigSingleTerminal.j))).values TestUtil.checkTopological(result) assert(result.size == 28) } @@ -140,7 +140,7 @@ object GraphTests extends TestSuite{ expected: OSet[(OSet[R], Int)]) = { val mapping = Discovered.mapping(base) - val topoSortedTransitive = Evaluator.topoSortedTransitiveTargets(OSet(target)) + val topoSortedTransitive = Evaluator.topoSorted(Evaluator.transitiveTargets(OSet(target))) val grouped = Evaluator.groupAroundNamedTargets(topoSortedTransitive, mapping) val flattened = OSet.from(grouped.values().flatMap(_.items)) diff --git a/core/src/test/scala/mill/JavaCompileJarTests.scala b/core/src/test/scala/mill/JavaCompileJarTests.scala index 6e398540..8272ed71 100644 --- a/core/src/test/scala/mill/JavaCompileJarTests.scala +++ b/core/src/test/scala/mill/JavaCompileJarTests.scala @@ -5,7 +5,7 @@ import ammonite.ops._ import ImplicitWd._ import mill.define.Task import mill.discover.Discovered -import mill.eval.{Evaluator, PathRef} +import mill.eval.Evaluator import mill.modules.Jvm.jarUp import mill.util.OSet import utest._ @@ -38,8 +38,8 @@ object JavaCompileJarTests extends TestSuite{ // | // v // resourceRoot ----> jar - def sourceRoot = T{ Task.path(sourceRootPath) } - def resourceRoot = T{ Task.path(resourceRootPath) } + def sourceRoot = T.source{ sourceRootPath } + def resourceRoot = T.source{ resourceRootPath } def allSources = T{ ls.rec(sourceRoot().path).map(PathRef(_)) } def classFiles = T{ compileAll(Task.ctx().dest, allSources()) } def jar = T{ jarUp(resourceRoot, classFiles) } @@ -56,13 +56,13 @@ object JavaCompileJarTests extends TestSuite{ val evaluated = evaluator.evaluate(OSet(t)) Tuple2( evaluated.values(0).asInstanceOf[T], - evaluated.targets.filter(x => mapping.contains(x) || x.isInstanceOf[mill.define.Command[_]]).size + evaluated.evaluated.filter(x => mapping.contains(x) || x.isInstanceOf[mill.define.Command[_]]).size ) } def check(targets: OSet[Task[_]], expected: OSet[Task[_]]) = { val evaluator = new Evaluator(workspacePath, mapping) - val evaluated = evaluator.evaluate(targets).targets.filter(mapping.contains) + val evaluated = evaluator.evaluate(targets).evaluated.filter(mapping.contains) assert(evaluated == expected) } @@ -71,7 +71,7 @@ object JavaCompileJarTests extends TestSuite{ check( targets = OSet(jar), - expected = OSet(resourceRoot, sourceRoot, allSources, classFiles, jar) + expected = OSet(allSources, classFiles, jar) ) // Re-running with no changes results in nothing being evaluated @@ -84,30 +84,32 @@ object JavaCompileJarTests extends TestSuite{ // Appending whitespace forces a recompile, but the classfilesend up // exactly the same so no re-jarring. append(sourceRootPath / "Foo.java", " ") - check(targets = OSet(jar), expected = OSet(sourceRoot, allSources, classFiles)) + // Note that `sourceRoot` and `resourceRoot` never turn up in the `expected` + // list, because they are `Source`s not `Target`s + check(targets = OSet(jar), expected = OSet(/*sourceRoot, */allSources, classFiles)) // Appending a new class changes the classfiles, which forces us to // re-create the final jar append(sourceRootPath / "Foo.java", "\nclass FooTwo{}") - check(targets = OSet(jar), expected = OSet(sourceRoot, allSources, classFiles, jar)) + check(targets = OSet(jar), expected = OSet(allSources, classFiles, jar)) // Tweaking the resources forces rebuild of the final jar, without // recompiling classfiles append(resourceRootPath / "hello.txt", " ") - check(targets = OSet(jar), expected = OSet(resourceRoot, jar)) + check(targets = OSet(jar), expected = OSet(jar)) // Asking for an intermediate target forces things to be build up to that // target only; these are re-used for any downstream targets requested append(sourceRootPath / "Bar.java", "\nclass BarTwo{}") append(resourceRootPath / "hello.txt", " ") - check(targets = OSet(classFiles), expected = OSet(sourceRoot, allSources, classFiles)) - check(targets = OSet(jar), expected = OSet(resourceRoot, jar)) + check(targets = OSet(classFiles), expected = OSet(allSources, classFiles)) + check(targets = OSet(jar), expected = OSet(jar)) check(targets = OSet(allSources), expected = OSet()) append(sourceRootPath / "Bar.java", "\nclass BarThree{}") append(resourceRootPath / "hello.txt", " ") - check(targets = OSet(resourceRoot), expected = OSet(resourceRoot)) - check(targets = OSet(allSources), expected = OSet(sourceRoot, allSources)) + check(targets = OSet(resourceRoot), expected = OSet()) + check(targets = OSet(allSources), expected = OSet(allSources)) check(targets = OSet(jar), expected = OSet(classFiles, jar)) val jarContents = %%('jar, "-tf", workspacePath/'jar)(workspacePath).out.string @@ -154,7 +156,7 @@ object JavaCompileJarTests extends TestSuite{ val (runOutput2, evalCount2) = eval(Build.run("test.BarFour")) assert( runOutput2.out.string == "New Cls!\n", - evalCount2 == 4 + evalCount2 == 3 ) val (runOutput3, evalCount3) = eval(Build.run("test.BarFour")) assert( -- cgit v1.2.3