From 3a87314737bea39b5d194ad2a9e7a88085011755 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Wed, 22 Nov 2017 20:41:35 -0800 Subject: Targets living in cross modules can now be properly discovered, run and cached. Try out the following commands on our Mill `build.sc` file to exercise this functionality - `scalaplugin/target/mill run Core.cross[jarA].jar` - `scalaplugin/target/mill run Core.cross[jarB].jar` - `scalaplugin/target/mill run Core.cross[jarC].jar` --- core/src/main/scala/mill/discover/Discovered.scala | 19 ++++---- core/src/main/scala/mill/discover/Mirror.scala | 34 +++++++++---- core/src/main/scala/mill/eval/Evaluator.scala | 16 ++++++- .../scala/mill/discover/ConsistencyTests.scala | 8 +++- .../scala/mill/discover/CrossModuleTests.scala | 55 ++++++++++++++++++---- .../test/scala/mill/discover/DiscoveredTests.scala | 9 ++-- .../test/scala/mill/discover/LabelingTests.scala | 6 +-- 7 files changed, 109 insertions(+), 38 deletions(-) (limited to 'core') diff --git a/core/src/main/scala/mill/discover/Discovered.scala b/core/src/main/scala/mill/discover/Discovered.scala index 21669fbb..4b4fab50 100644 --- a/core/src/main/scala/mill/discover/Discovered.scala +++ b/core/src/main/scala/mill/discover/Discovered.scala @@ -12,17 +12,15 @@ import scala.reflect.macros.blackbox.Context * Allows you to implicitly summon up a build [[Mirror]] for arbitrary types */ class Discovered[T](val mirror: Mirror[T, T]){ - - def targets(obj: T, crosses: List[List[Any]]) = Mirror.traverse(mirror) { (h, p) => - h.labelled(obj, p, crosses) + def targets(obj: T) = Mirror.traverse(obj, mirror) { (h, p) => + h.labelled(obj, p) } - } object Discovered { def consistencyCheck[T](base: T, d: Discovered[T]) = { val inconsistent = for{ - (t1, t2) <- d.targets(base, Nil).zip(d.targets(base, Nil)) + (t1, t2) <- d.targets(base).zip(d.targets(base)) if t1.target ne t2.target } yield t1.segments inconsistent @@ -30,7 +28,7 @@ object Discovered { def mapping[T: Discovered](t: T): Map[Target[_], LabelledTarget[_]] = { - implicitly[Discovered[T]].targets(t, Nil).map(x => x.target -> x).toMap + implicitly[Discovered[T]].targets(t).map(x => x.target -> x).toMap } implicit def apply[T]: Discovered[T] = macro applyImpl[T] @@ -79,13 +77,14 @@ object Discovered { q"($name, ${rec(Some(name) :: segments, m.typeSignature)})" } + val crossName = q"${TermName(c.freshName())}" val hierarchySelector = { val base = q"${TermName(c.freshName())}" - val ident = segments.reverse.foldLeft[Tree](base) { - case (prefix, Some(name)) => q"$prefix.${TermName(name)}" - case (prefix, None) => q"$prefix.apply(crosses.head)" + val ident = segments.zipWithIndex.reverse.foldLeft[Tree](base) { + case (prefix, (Some(name), i)) => q"$prefix.${TermName(name)}" + case (prefix, (None, i)) => q"$prefix.apply($crossName($i))" } - q"($base: $tpe, crosses: List[List[Any]]) => $ident" + q"($base: $tpe, $crossName: List[List[Any]]) => $ident" } val commands = diff --git a/core/src/main/scala/mill/discover/Mirror.scala b/core/src/main/scala/mill/discover/Mirror.scala index bea2d8e3..962c156b 100644 --- a/core/src/main/scala/mill/discover/Mirror.scala +++ b/core/src/main/scala/mill/discover/Mirror.scala @@ -18,19 +18,31 @@ case class Mirror[-T, V](node: (T, List[List[Any]]) => V, targets: Seq[Mirror.TargetPoint[V, _]], children: List[(String, Mirror[T, _])], crossChildren: Option[(V => List[List[Any]], Mirror[T, _])]){ - def labelled(obj: T, - p: Seq[String], - crosses: List[List[Any]]) = { - targets.map(t => t.labelled(node(obj, crosses), p)) + def labelled(obj: T, p: Seq[Mirror.Segment]) = { + val crossValues = p.map{case Mirror.Segment.Cross(vs) => vs case _ => Nil}.toList + targets.map(t => t.labelled(node(obj, crossValues), p.reverse)) } } object Mirror{ - def traverse[T, V, R](hierarchy: Mirror[T, V]) - (f: (Mirror[T, _], => Seq[String]) => Seq[R]): Seq[R] = { - def rec[C](segmentsRev: List[String], h: Mirror[T, C]): Seq[R]= { + sealed trait Segment + object Segment{ + case class Label(value: String) extends Segment + case class Cross(value: List[Any]) extends Segment + } + def traverse[T, V, R](t: T, hierarchy: Mirror[T, V]) + (f: (Mirror[T, _], => Seq[Segment]) => Seq[R]): Seq[R] = { + def rec[C](segmentsRev: List[Segment], h: Mirror[T, C]): Seq[R]= { + val crossValues = segmentsRev.map{case Segment.Cross(vs) => vs case _ => Nil} val self = f(h, segmentsRev) - self ++ h.children.flatMap{case (label, c) => rec(label :: segmentsRev, c)} + self ++ + h.children.flatMap{case (label, c) => rec(Segment.Label(label) :: segmentsRev, c)} ++ + h.crossChildren.toSeq.flatMap{ + case (crossGen, c) => + crossGen(h.node(t, crossValues)).flatMap(cross => + rec(Segment.Cross(cross) :: segmentsRev, c) + ) + } } rec(Nil, hierarchy) } @@ -40,7 +52,7 @@ object Mirror{ */ case class LabelledTarget[V](target: Target[V], format: upickle.default.ReadWriter[V], - segments: Seq[String]) + segments: Seq[Segment]) /** * Represents metadata about a particular target, before the target is @@ -49,7 +61,9 @@ object Mirror{ case class TargetPoint[T, V](label: String, format: upickle.default.ReadWriter[V], run: T => Target[V]) { - def labelled(t: T, segments: Seq[String]) = LabelledTarget(run(t), format, segments :+ label) + def labelled(t: T, segments: Seq[Segment]) = { + LabelledTarget(run(t), format, segments :+ Segment.Label(label)) + } } def makeTargetPoint[T, V](label: String, func: T => Target[V]) diff --git a/core/src/main/scala/mill/eval/Evaluator.scala b/core/src/main/scala/mill/eval/Evaluator.scala index a36129a5..f91a1e8c 100644 --- a/core/src/main/scala/mill/eval/Evaluator.scala +++ b/core/src/main/scala/mill/eval/Evaluator.scala @@ -2,6 +2,7 @@ package mill.eval import ammonite.ops._ import mill.define.{Graph, Target, Task} +import mill.discover.Mirror import mill.discover.Mirror.LabelledTarget import mill.util import mill.util.{Args, MultiBiMap, OSet} @@ -45,7 +46,11 @@ class Evaluator(workspacePath: Path, } def resolveDestPaths(t: LabelledTarget[_]): (Path, Path) = { - val targetDestPath = workspacePath / t.segments + val segmentStrings = t.segments.flatMap{ + case Mirror.Segment.Label(s) => Seq(s) + case Mirror.Segment.Cross(values) => values.map(_.toString) + } + val targetDestPath = workspacePath / segmentStrings val metadataPath = targetDestPath / up / (targetDestPath.last + ".mill.json") (targetDestPath, metadataPath) } @@ -79,7 +84,14 @@ class Evaluator(workspacePath: Path, case _ => - log("Running " + labelledTarget.segments.mkString(".")) + pprint.log(labelledTarget.segments) + val Seq(first, rest @_*) = labelledTarget.segments + val msgParts = Seq(first.asInstanceOf[Mirror.Segment.Label].value) ++ rest.map{ + case Mirror.Segment.Label(s) => "." + s + case Mirror.Segment.Cross(s) => "[" + s.mkString(",") + "]" + } + + log("Running " + msgParts.mkString) if (labelledTarget.target.flushDest) rm(destPath) val (newResults, newEvaluated) = evaluateGroup(group, results, Some(destPath)) diff --git a/core/src/test/scala/mill/discover/ConsistencyTests.scala b/core/src/test/scala/mill/discover/ConsistencyTests.scala index 67e22595..0591d5fc 100644 --- a/core/src/test/scala/mill/discover/ConsistencyTests.scala +++ b/core/src/test/scala/mill/discover/ConsistencyTests.scala @@ -1,6 +1,7 @@ package mill.discover +import mill.discover.Mirror.Segment.Label import mill.util.TestGraphs import utest._ @@ -15,7 +16,12 @@ object ConsistencyTests extends TestSuite{ // // Maybe later we can convert them into compile errors somehow - val expected = List(List("down"), List("right"), List("left"), List("up")) + val expected = List( + List(Label("down")), + List(Label("right")), + List(Label("left")), + List(Label("up")) + ) 'diamond - { val inconsistent = Discovered.consistencyCheck( diff --git a/core/src/test/scala/mill/discover/CrossModuleTests.scala b/core/src/test/scala/mill/discover/CrossModuleTests.scala index c4e075da..8d8a86f6 100644 --- a/core/src/test/scala/mill/discover/CrossModuleTests.scala +++ b/core/src/test/scala/mill/discover/CrossModuleTests.scala @@ -2,6 +2,8 @@ package mill.discover import mill.{Module, T} import mill.define.Cross +import mill.discover.Mirror.{LabelledTarget, Segment} +import mill.discover.Mirror.Segment.Label import utest._ object CrossModuleTests extends TestSuite{ @@ -17,7 +19,9 @@ object CrossModuleTests extends TestSuite{ } } - val Some((gen, innerMirror)) = Discovered[outer.type] + val discovered = Discovered[outer.type] + + val Some((gen, innerMirror)) = discovered .mirror .children .head._2 @@ -68,14 +72,49 @@ object CrossModuleTests extends TestSuite{ assert(suffix == expected) } } - 'crossCommands - { - object outer { - val cross = for (c <- mill.define.Cross("a", "b", "c")) yield new mill.Module { - def printIt() = T.command { - println("PRINTING IT: " + c) - } - } + 'crossTargetDiscovery - { + object outer{ + val crossed = + for(n <- Cross("2.10.6", "2.11.8", "2.12.4")) + yield new Module{ def scalaVersion = T{ n } } } + val discovered = Discovered[outer.type] + + val segments = discovered.targets(outer).map(_.segments) + val expectedSegments = List( + List(Label("crossed"), Segment.Cross(List("2.10.6")), Label("scalaVersion")), + List(Label("crossed"), Segment.Cross(List("2.11.8")), Label("scalaVersion")), + List(Label("crossed"), Segment.Cross(List("2.12.4")), Label("scalaVersion")) + ) + assert(segments == expectedSegments) + val targets = discovered.targets(outer).map(_.target) + val expected = List( + outer.crossed(List("2.10.6")).scalaVersion, + outer.crossed(List("2.11.8")).scalaVersion, + outer.crossed(List("2.12.4")).scalaVersion + ) + assert(targets == expected) + + } + 'doubleCrossTargetDiscovery - { + object outer{ + val crossed = + for{ + n <- Cross("2.11.8", "2.12.4") + platform <- Cross("sjs0.6", "native0.3") + } yield new Module{ def suffix = T{ n + "_" + platform } } + } + val discovered = Discovered[outer.type] + val targets = discovered.targets(outer).map(_.target) + + val expected = List( + outer.crossed(List("sjs0.6", "2.11.8")).suffix, + outer.crossed(List("native0.3", "2.11.8")).suffix, + outer.crossed(List("sjs0.6", "2.12.4")).suffix, + outer.crossed(List("native0.3", "2.12.4")).suffix + ) + assert(targets == expected) + } } } diff --git a/core/src/test/scala/mill/discover/DiscoveredTests.scala b/core/src/test/scala/mill/discover/DiscoveredTests.scala index b09ff2de..7fa030eb 100644 --- a/core/src/test/scala/mill/discover/DiscoveredTests.scala +++ b/core/src/test/scala/mill/discover/DiscoveredTests.scala @@ -6,6 +6,7 @@ import mill.discover.Router.{ArgSig, EntryPoint} import utest._ import mill.{Module, T} import mill.util.TestUtil.test +import mill.discover.Mirror.Segment.Label object DiscoveredTests extends TestSuite{ val tests = Tests{ @@ -44,12 +45,12 @@ object DiscoveredTests extends TestSuite{ ) assert(flattenedHierarchy == expectedHierarchy) - val mapped = discovered.targets(outer, Nil).map(x => x.segments -> x.target) + val mapped = discovered.targets(outer).map(x => x.segments -> x.target) val expected = Seq( - (List("classInstance", "single"), outer.classInstance.single), - (List("nested", "single"), outer.nested.single), - (List("single"), outer.single) + (List(Label("classInstance"), Label("single")), outer.classInstance.single), + (List(Label("nested"), Label("single")), outer.nested.single), + (List(Label("single")), outer.single) ) assert(mapped.toSet == expected.toSet) } diff --git a/core/src/test/scala/mill/discover/LabelingTests.scala b/core/src/test/scala/mill/discover/LabelingTests.scala index a37c090c..94d91cda 100644 --- a/core/src/test/scala/mill/discover/LabelingTests.scala +++ b/core/src/test/scala/mill/discover/LabelingTests.scala @@ -3,7 +3,7 @@ package mill.discover import mill.define.Task import mill.util.TestGraphs import utest._ - +import mill.discover.Mirror.Segment.Label object LabelingTests extends TestSuite{ val tests = Tests{ @@ -15,10 +15,10 @@ object LabelingTests extends TestSuite{ def check[T: Discovered](base: T, t: Task[_], relPath: Option[String]) = { - val names: Seq[(Task[_], Seq[String])] = Discovered.mapping(base).mapValues(_.segments).toSeq + val names: Seq[(Task[_], Seq[Mirror.Segment])] = Discovered.mapping(base).mapValues(_.segments).toSeq val nameMap = names.toMap - val targetLabel = nameMap.get(t).map(_.mkString(".")) + val targetLabel = nameMap.get(t).map(_.map{case Label(v) => v}.mkString(".")) assert(targetLabel == relPath) } 'singleton - check(singleton, singleton.single, Some("single")) -- cgit v1.2.3