summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbuild.sc6
-rw-r--r--core/src/main/scala/mill/Main.scala142
-rw-r--r--core/src/main/scala/mill/define/Cross.scala3
-rw-r--r--core/src/main/scala/mill/discover/Discovered.scala43
-rw-r--r--core/src/main/scala/mill/discover/Mirror.scala11
-rw-r--r--core/src/test/scala/mill/discover/CrossModuleTests.scala82
-rw-r--r--core/src/test/scala/mill/discover/DiscoveredTests.scala4
-rw-r--r--scalaplugin/src/main/scala/mill/scalaplugin/GenIdea.scala2
8 files changed, 209 insertions, 84 deletions
diff --git a/build.sc b/build.sc
index 821780b6..ca57bb99 100755
--- a/build.sc
+++ b/build.sc
@@ -20,6 +20,12 @@ object Core extends ScalaModule {
def basePath = pwd / 'core
override def sources = pwd/'core/'src/'main/'scala
+
+ val cross = for(c <- mill.define.Cross("a", "b", "c")) yield new mill.Module{
+ def printIt() = T.command{
+ println("PRINTING IT: " + c)
+ }
+ }
}
object CoreTests extends ScalaModule {
def scalaVersion = "2.12.4"
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),
diff --git a/scalaplugin/src/main/scala/mill/scalaplugin/GenIdea.scala b/scalaplugin/src/main/scala/mill/scalaplugin/GenIdea.scala
index 5e08a394..f51561d0 100644
--- a/scalaplugin/src/main/scala/mill/scalaplugin/GenIdea.scala
+++ b/scalaplugin/src/main/scala/mill/scalaplugin/GenIdea.scala
@@ -24,7 +24,7 @@ object GenIdea {
val evaluator = new Evaluator(workspacePath, mapping, _ => ())
val modules = Mirror.traverse(discovered.mirror){ (h, p) =>
- h.node(obj) match {
+ h.node(obj, Nil) match {
case m: ScalaModule => Seq(p -> m)
case _ => Nil
}