diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2017-11-18 19:42:38 -0800 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2017-11-18 19:42:38 -0800 |
commit | c4ee67d3ea4e2e96fe09d5fcb21734902e6bfa56 (patch) | |
tree | f6fd01818e955452c0adf23bc7f5c57d5b9999de /core/src | |
parent | 2412bf2775676a9c09eb3d370a2bd9064c51822b (diff) | |
download | mill-c4ee67d3ea4e2e96fe09d5fcb21734902e6bfa56.tar.gz mill-c4ee67d3ea4e2e96fe09d5fcb21734902e6bfa56.tar.bz2 mill-c4ee67d3ea4e2e96fe09d5fcb21734902e6bfa56.zip |
First hack pass at cross-build discovery and command-running now works.
Can be tested out on our `build.sc` via `scalaplugin/target/mill run Core.cross[a].printIt`
Needs tons of cleanup...
Diffstat (limited to 'core/src')
-rw-r--r-- | core/src/main/scala/mill/Main.scala | 142 | ||||
-rw-r--r-- | core/src/main/scala/mill/define/Cross.scala | 3 | ||||
-rw-r--r-- | core/src/main/scala/mill/discover/Discovered.scala | 43 | ||||
-rw-r--r-- | core/src/main/scala/mill/discover/Mirror.scala | 11 | ||||
-rw-r--r-- | core/src/test/scala/mill/discover/CrossModuleTests.scala | 82 | ||||
-rw-r--r-- | core/src/test/scala/mill/discover/DiscoveredTests.scala | 4 |
6 files changed, 202 insertions, 83 deletions
diff --git a/core/src/main/scala/mill/Main.scala b/core/src/main/scala/mill/Main.scala index bff8cb22..ef4a78a6 100644 --- a/core/src/main/scala/mill/Main.scala +++ b/core/src/main/scala/mill/Main.scala @@ -13,6 +13,15 @@ import ammonite.repl.Repl object Main { + def parseSelector(input: String) = { + import fastparse.all._ + val segment = P( CharsWhileIn(('a' to 'z') ++ ('A' to 'Z') ++ ('0' to '9')).! ) + val crossSegment = P( "[" ~ CharsWhile(c => c != ',' && c != ']').!.rep(1, sep=",") ~ "]" ) + val query = P( segment ~ ("." ~ segment.map(Left(_)) | crossSegment.map(Right(_))).rep ~ End ).map{ + case (h, rest) => Left(h) :: rest.toList + } + query.parse(input) + } def apply[T: Discovered](args: Seq[String], obj: T, watch: Path => Unit, @@ -21,70 +30,85 @@ object Main { val log = new Logger(coloredOutput) val Seq(selectorString, rest @_*) = args - val selector = selectorString.split('.') - val discovered = implicitly[Discovered[T]] - val consistencyErrors = Discovered.consistencyCheck(obj, discovered) - if (consistencyErrors.nonEmpty) { - log.error("Failed Discovered.consistencyCheck: " + consistencyErrors) - 1 - } else { - val mapping = Discovered.mapping(obj)(discovered) - val workspacePath = pwd / 'out - - def resolve[V](selector: List[String], hierarchy: Mirror[T, V]): Option[Task[Any]] = { - selector match{ - case last :: Nil => - - def target: Option[Task[Any]] = - hierarchy.targets.find(_.label == last).map(_.run(hierarchy.node(obj))) - def command: Option[Task[Any]] = hierarchy.commands.find(_.name == last).flatMap( - _.invoke(hierarchy.node(obj), ammonite.main.Scripts.groupArgs(rest.toList)) match{ - case Router.Result.Success(v) => Some(v) - case _ => None - } - ) - target orElse command - case head :: tail => - hierarchy.children - .collectFirst{ case (label, child) if label == head => resolve(tail, child) } - .flatten - case Nil => ??? - } - } - resolve(selector.toList, discovered.mirror) match{ - case Some(target) => - val evaluator = new Evaluator(workspacePath, mapping, log.info) - val evaluated = evaluator.evaluate(OSet(target)) - evaluated.transitive.foreach{ - case t: define.Source => watch(t.handle.path) - case _ => // do nothing + parseSelector(selectorString) match{ + case f: fastparse.all.Parsed.Failure => + log.error(f.msg) + 1 + case fastparse.all.Parsed.Success(selector, idx) => + val discovered = implicitly[Discovered[T]] + val consistencyErrors = Discovered.consistencyCheck(obj, discovered) + if (consistencyErrors.nonEmpty) { + log.error("Failed Discovered.consistencyCheck: " + consistencyErrors) + 1 + } else { + val mapping = Discovered.mapping(obj)(discovered) + val workspacePath = pwd / 'out + + val crossSelectors = selector.collect{case Right(x) => x.toList} + def resolve[V](selector: List[Either[String, Seq[String]]], + hierarchy: Mirror[T, V]): Option[Task[Any]] = { + selector match{ + case Right(_) :: Nil => ??? + case Left(last) :: Nil => + def target: Option[Task[Any]] = + hierarchy.targets.find(_.label == last).map(_.run(hierarchy.node(obj, crossSelectors))) + def command: Option[Task[Any]] = hierarchy.commands.find(_.name == last).flatMap( + _.invoke(hierarchy.node(obj, crossSelectors), ammonite.main.Scripts.groupArgs(rest.toList)) match{ + case Router.Result.Success(v) => Some(v) + case _ => None + } + ) + target orElse command + case head :: tail => + head match{ + case Left(singleLabel) => + hierarchy.children + .collectFirst{ case (label, child) if label == singleLabel => resolve(tail, child) } + .flatten + case Right(cross) => + resolve(tail, hierarchy.crossChildren.get._2) + } + + case Nil => ??? + } } + resolve(selector, discovered.mirror) match{ + case Some(target) => + val evaluator = new Evaluator(workspacePath, mapping, log.info) + val evaluated = evaluator.evaluate(OSet(target)) + evaluated.transitive.foreach{ + case t: define.Source => watch(t.handle.path) + case _ => // do nothing + } - val failing = evaluated.failing.items - evaluated.failing.keyCount match{ - case 0 => // do nothing - case n => log.error(n + " targets failed") - } + val failing = evaluated.failing.items + evaluated.failing.keyCount match{ + case 0 => // do nothing + case n => log.error(n + " targets failed") + } - for((k, fs) <- failing){ - val ks = k match{ - case Left(t) => t.toString - case Right(t) => t.segments.mkString(".") - } - val fss = fs.map{ - case Result.Exception(t) => t.toString - case Result.Failure(t) => t - } - log.error(ks + " " + fss.mkString(", ")) - } + for((k, fs) <- failing){ + val ks = k match{ + case Left(t) => t.toString + case Right(t) => t.segments.mkString(".") + } + val fss = fs.map{ + case Result.Exception(t) => t.toString + case Result.Failure(t) => t + } + log.error(ks + " " + fss.mkString(", ")) + } - if (evaluated.failing.keyCount == 0) 0 else 1 - case None => - log.error("Unknown selector: " + selector.mkString(".")) - 1 - } + if (evaluated.failing.keyCount == 0) 0 else 1 + case None => + log.error("Unknown selector: " + selector.mkString(".")) + 1 + } + } } + + } case class Config(home: ammonite.ops.Path = pwd/'out/'ammonite, @@ -230,7 +254,7 @@ class Main(config: Main.Config){ val delta = System.currentTimeMillis() - startTime log.info("Finished in " + delta/1000.0 + "s") - watchAndWait(watchedFiles) + if (loop) watchAndWait(watchedFiles) startTime = System.currentTimeMillis() } while(loop) exitCode diff --git a/core/src/main/scala/mill/define/Cross.scala b/core/src/main/scala/mill/define/Cross.scala index 8f8c01e8..0ab87802 100644 --- a/core/src/main/scala/mill/define/Cross.scala +++ b/core/src/main/scala/mill/define/Cross.scala @@ -1,6 +1,6 @@ package mill.define -case class Cross[T](items: List[(List[Any], T)]){ +case class Cross[+T](items: List[(List[Any], T)]){ def flatMap[V](f: T => Cross[V]): Cross[V] = new Cross( items.flatMap{ case (l, v) => f(v).items.map{case (l2, v2) => (l2 ::: l, v2)} @@ -9,6 +9,7 @@ case class Cross[T](items: List[(List[Any], T)]){ def map[V](f: T => V): Cross[V] = new Cross(items.map{case (l, v) => (l, f(v))}) def withFilter(f: T => Boolean): Cross[T] = new Cross(items.filter(t => f(t._2))) + def apply(input: List[Any]): T = items.find(_._1 == input).get._2 } object Cross{ def apply[T](t: T*) = new Cross(t.map(i => List(i) -> i).toList) diff --git a/core/src/main/scala/mill/discover/Discovered.scala b/core/src/main/scala/mill/discover/Discovered.scala index 0f3ced4e..21669fbb 100644 --- a/core/src/main/scala/mill/discover/Discovered.scala +++ b/core/src/main/scala/mill/discover/Discovered.scala @@ -1,7 +1,7 @@ package mill.discover import mill.define.Task.Module -import mill.define.{Target, Task} +import mill.define.{Cross, Target, Task} import mill.discover.Mirror.LabelledTarget import mill.discover.Router.{EntryPoint, Result} @@ -13,8 +13,8 @@ import scala.reflect.macros.blackbox.Context */ class Discovered[T](val mirror: Mirror[T, T]){ - def targets(obj: T) = Mirror.traverse(mirror) { (h, p) => - h.labelled(obj, p) + def targets(obj: T, crosses: List[List[Any]]) = Mirror.traverse(mirror) { (h, p) => + h.labelled(obj, p, crosses) } } @@ -22,7 +22,7 @@ class Discovered[T](val mirror: Mirror[T, T]){ object Discovered { def consistencyCheck[T](base: T, d: Discovered[T]) = { val inconsistent = for{ - (t1, t2) <- d.targets(base).zip(d.targets(base)) + (t1, t2) <- d.targets(base, Nil).zip(d.targets(base, Nil)) if t1.target ne t2.target } yield t1.segments inconsistent @@ -30,15 +30,15 @@ object Discovered { def mapping[T: Discovered](t: T): Map[Target[_], LabelledTarget[_]] = { - implicitly[Discovered[T]].targets(t).map(x => x.target -> x).toMap + implicitly[Discovered[T]].targets(t, Nil).map(x => x.target -> x).toMap } implicit def apply[T]: Discovered[T] = macro applyImpl[T] - + def tupleLeft[T, V](items: List[(T, V)]) = items.map(_._1) def applyImpl[T: c.WeakTypeTag](c: Context): c.Expr[Discovered[T]] = { import c.universe._ val tpe = c.weakTypeTag[T].tpe - def rec(segments: List[String], + def rec(segments: List[Option[String]], t: c.Type): Tree = { val r = new Router(c) @@ -62,22 +62,30 @@ object Discovered { t } + val crossChildren = + if (!(t <:< c.weakTypeOf[Cross[Module]])) q"None" + else { - + val TypeRef(_, _, Seq(arg)) = t + val innerMirror = rec(None :: segments, arg) + q"Some(((c: mill.define.Cross[_]) => mill.discover.Discovered.tupleLeft(c.items), $innerMirror))" + } val childHierarchies = for{ m <- t.members.toList - if m.typeSignature <:< c.weakTypeOf[Module] + if (m.typeSignature <:< c.weakTypeOf[Module]) || + (m.typeSignature <:< c.weakTypeOf[Cross[Module]]) } yield { val name = m.name.toString.trim - q"($name, ${rec(name :: segments, m.typeSignature)})" + q"($name, ${rec(Some(name) :: segments, m.typeSignature)})" } val hierarchySelector = { val base = q"${TermName(c.freshName())}" - val ident = segments.reverse.foldLeft[Tree](base) { (prefix, name) => - q"$prefix.${TermName(name)}" + val ident = segments.reverse.foldLeft[Tree](base) { + case (prefix, Some(name)) => q"$prefix.${TermName(name)}" + case (prefix, None) => q"$prefix.apply(crosses.head)" } - q"($base: $tpe) => $ident" + q"($base: $tpe, crosses: List[List[Any]]) => $ident" } val commands = @@ -89,12 +97,13 @@ object Discovered { $hierarchySelector, $commands, $targets, - $childHierarchies + $childHierarchies, + $crossChildren )""" } - - - c.Expr[Discovered[T]](q"new _root_.mill.discover.Discovered(${rec(Nil, tpe)})") + val res = q"new _root_.mill.discover.Discovered(${rec(Nil, tpe)})" +// println(res) + c.Expr[Discovered[T]](res) } } diff --git a/core/src/main/scala/mill/discover/Mirror.scala b/core/src/main/scala/mill/discover/Mirror.scala index ca89bdcb..bea2d8e3 100644 --- a/core/src/main/scala/mill/discover/Mirror.scala +++ b/core/src/main/scala/mill/discover/Mirror.scala @@ -13,12 +13,15 @@ import scala.language.experimental.macros * Note that [[Mirror]] allows you to store and inspect metadata of a type * [[T]] even without a concrete instance of [[T]] itself. */ -case class Mirror[-T, V](node: T => V, +case class Mirror[-T, V](node: (T, List[List[Any]]) => V, commands: Seq[EntryPoint[V]], targets: Seq[Mirror.TargetPoint[V, _]], - children: List[(String, Mirror[T, _])]){ - def labelled(obj: T, p: Seq[String]) = { - targets.map(t => t.labelled(node(obj), p)) + 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)) } } diff --git a/core/src/test/scala/mill/discover/CrossModuleTests.scala b/core/src/test/scala/mill/discover/CrossModuleTests.scala new file mode 100644 index 00000000..c4e075da --- /dev/null +++ b/core/src/test/scala/mill/discover/CrossModuleTests.scala @@ -0,0 +1,82 @@ +package mill.discover + +import mill.{Module, T} +import mill.define.Cross +import utest._ + +object CrossModuleTests extends TestSuite{ + + val tests = Tests{ + + 'cross - { + object outer{ + val crossed = + for(n <- Cross("2.10.6", "2.11.8", "2.12.4")) + yield new Module{ + def scalaVersion = n + } + } + + val Some((gen, innerMirror)) = Discovered[outer.type] + .mirror + .children + .head._2 + .asInstanceOf[Mirror[outer.type, outer.crossed.type]] + .crossChildren + + val keys = gen(outer.crossed) + assert(keys == List(List("2.10.6"), List("2.11.8"), List("2.12.4"))) + for(k <- keys){ + assert(outer.crossed(k).scalaVersion == k.head) + } + } + 'doubleCross - { + object outer{ + val crossed = + for{ + platform <- Cross("", "sjs0.6", "native0.3") + scalaVersion <- Cross("2.10.6", "2.11.8", "2.12.4") + if !(platform == "native0.3" && scalaVersion == "2.10.6") + } yield new Module{ + def suffix = Seq(scalaVersion, platform).filter(_.nonEmpty).map("_"+_).mkString + } + } + + val Some((gen, innerMirror)) = Discovered[outer.type] + .mirror + .children + .head._2 + .asInstanceOf[Mirror[outer.type, outer.crossed.type]] + .crossChildren + + val keys = gen(outer.crossed) + val expectedKeys = List( + List("2.10.6", ""), + List("2.11.8", ""), + List("2.12.4", ""), + List("2.10.6", "sjs0.6"), + List("2.11.8", "sjs0.6"), + List("2.12.4", "sjs0.6"), + List("2.11.8", "native0.3"), + List("2.12.4", "native0.3"), + ) + + assert(keys == expectedKeys) + for(k <- keys){ + val suffix = outer.crossed(k).suffix + val expected = k.map(_.toString).filter(_.nonEmpty).map("_"+_).mkString + 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) + } + } + } + } + } +} + diff --git a/core/src/test/scala/mill/discover/DiscoveredTests.scala b/core/src/test/scala/mill/discover/DiscoveredTests.scala index e9170967..b09ff2de 100644 --- a/core/src/test/scala/mill/discover/DiscoveredTests.scala +++ b/core/src/test/scala/mill/discover/DiscoveredTests.scala @@ -33,7 +33,7 @@ object DiscoveredTests extends TestSuite{ def flatten(h: Mirror[outer.type, _]): Seq[Any] = { - h.node(outer) :: h.children.flatMap{case (label, c) => flatten(c)} + h.node(outer, Nil) :: h.children.flatMap{case (label, c) => flatten(c)} } val flattenedHierarchy = flatten(discovered.mirror) @@ -44,7 +44,7 @@ object DiscoveredTests extends TestSuite{ ) assert(flattenedHierarchy == expectedHierarchy) - val mapped = discovered.targets(outer).map(x => x.segments -> x.target) + val mapped = discovered.targets(outer, Nil).map(x => x.segments -> x.target) val expected = Seq( (List("classInstance", "single"), outer.classInstance.single), |