summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.sbt15
-rwxr-xr-xbuild.sc2
-rw-r--r--core/src/main/scala/mill/Main.scala105
-rw-r--r--core/src/main/scala/mill/discover/Discovered.scala8
-rw-r--r--core/src/main/scala/mill/discover/Mirror.scala2
-rw-r--r--core/src/main/scala/mill/discover/Router.scala410
-rw-r--r--core/src/main/scala/mill/main/Resolve.scala7
-rw-r--r--core/src/test/scala/mill/discover/DiscoveredTests.scala6
-rw-r--r--plugin/src/main/scala/mill/plugin/Cacher.scala2
9 files changed, 98 insertions, 459 deletions
diff --git a/build.sbt b/build.sbt
index d94e5d49..bc393aa3 100644
--- a/build.sbt
+++ b/build.sbt
@@ -13,7 +13,7 @@ val sharedSettings = Seq(
scalacOptions += "-P:acyclic:force",
autoCompilerPlugins := true,
addCompilerPlugin("com.lihaoyi" %% "acyclic" % "0.1.7"),
- libraryDependencies += "com.lihaoyi" % "ammonite" % "1.0.3-10-4311ac9" % "test" cross CrossVersion.full,
+ libraryDependencies += "com.lihaoyi" % "ammonite" % "1.0.3-20-75e58ac" % "test" cross CrossVersion.full,
sourceGenerators in Test += Def.task {
val file = (sourceManaged in Test).value / "amm.scala"
@@ -53,12 +53,13 @@ def bridge(bridgeVersion: String) = Project(
s"http://repo1.maven.org/maven2/org/scala-sbt/compiler-bridge_$v/1.0.5/compiler-bridge_$v-1.0.5-sources.jar"
val curlDest = java.nio.file.Paths.get(target.value.toString, "sources")
- Seq("rm", "-rf", curlDest.toString).!
- java.nio.file.Files.createDirectories(curlDest)
-
- Seq("curl", "-L", "-o", curlDest.resolve("bridge.jar").toString, url).!
- Seq("unzip", curlDest.resolve("bridge.jar").toString, "-d", curlDest.toString).!
+ if (!java.nio.file.Files.exists(curlDest)) {
+ Seq("rm", "-rf", curlDest.toString).!
+ java.nio.file.Files.createDirectories(curlDest)
+ Seq("curl", "-L", "-o", curlDest.resolve("bridge.jar").toString, url).!
+ Seq("unzip", curlDest.resolve("bridge.jar").toString, "-d", curlDest.toString).!
+ }
val sources = java.nio.file.Files.walk(curlDest)
.iterator
.asScala
@@ -91,7 +92,7 @@ lazy val core = project
"org.scala-lang" % "scala-reflect" % scalaVersion.value % "provided",
"com.lihaoyi" %% "sourcecode" % "0.1.4",
"com.lihaoyi" %% "pprint" % "0.5.3",
- "com.lihaoyi" % "ammonite" % "1.0.3-10-4311ac9" cross CrossVersion.full,
+ "com.lihaoyi" % "ammonite" % "1.0.3-20-75e58ac" cross CrossVersion.full,
"org.scala-sbt" %% "zinc" % "1.0.5",
"org.scala-sbt" % "test-interface" % "1.0"
)
diff --git a/build.sc b/build.sc
index 922db522..9ed794ac 100755
--- a/build.sc
+++ b/build.sc
@@ -66,7 +66,7 @@ object Core extends MillModule with MillPublishModule {
def ivyDeps = Seq(
Dep("com.lihaoyi", "sourcecode", "0.1.4"),
Dep("com.lihaoyi", "pprint", "0.5.3"),
- Dep.Point("com.lihaoyi", "ammonite", "1.0.3-10-4311ac9"),
+ Dep.Point("com.lihaoyi", "ammonite", "1.0.3-20-75e58ac"),
Dep("com.typesafe.play", "play-json", "2.6.6"),
Dep("org.scala-sbt", "zinc", "1.0.5"),
Dep.Java("org.scala-sbt", "test-interface", "1.0")
diff --git a/core/src/main/scala/mill/Main.scala b/core/src/main/scala/mill/Main.scala
index 86ee8649..761430e4 100644
--- a/core/src/main/scala/mill/Main.scala
+++ b/core/src/main/scala/mill/Main.scala
@@ -112,28 +112,6 @@ object Main {
watch: Boolean = false)
def main(args: Array[String]): Unit = {
- val syntheticPath = pwd / 'out / "run.sc"
- write.over(
- syntheticPath,
- s"""import $$file.^.build
- |import mill._
- |
- |val mapping = mill.discover.Discovered.mapping(build)
- |
- |mill.Main.consistencyCheck(mapping).left.foreach(msg => throw new Exception(msg))
- |
- |@main def run(args: String*) = mill.Main(args, mapping, interp.watch, true)
- |
- |@main def idea() = mill.scalaplugin.GenIdea(mapping)
- |
- |val evaluator = new mill.eval.Evaluator(
- | ammonite.ops.pwd / 'out,
- | mapping.value,
- | new mill.util.PrintLogger(true)
- |)
- |
- |implicit val replApplyHandler = new mill.main.ReplApplyHandler(evaluator)""".stripMargin
- )
import ammonite.main.Cli
var repl = false
@@ -158,8 +136,8 @@ object Main {
val config =
if(!repl) cliConfig
else cliConfig.copy(
- predefFile = Some(pwd / 'out / "run.sc"),
- predefCode = "import build._",
+ defaultPredef = false,
+ predefFile = Some(pwd/"build.sc"),
welcomeBanner = None
)
@@ -169,7 +147,7 @@ object Main {
System.in, System.out, System.err
){
override def initMain(isRepl: Boolean) = {
- super.initMain(isRepl).copy(codeWrapper = customCodeWrapper)
+ super.initMain(isRepl).copy(scriptCodeWrapper = customCodeWrapper)
}
}
@@ -177,23 +155,86 @@ object Main {
runner.printInfo("Loading...")
runner.runRepl()
} else {
- runner.runScript(syntheticPath, leftoverArgs)
+ runner.runScript(pwd / "build.sc", leftoverArgs)
}
}
}
val customCodeWrapper = new Preprocessor.CodeWrapper {
def top(pkgName: Seq[Name], imports: Imports, indexedWrapperName: Name) = {
- normalizeNewlines(s"""
-package ${pkgName.head.encoded}
-package ${Util.encodeScalaSourcePath(pkgName.tail)}
-$imports
-
-object ${indexedWrapperName.backticked} extends mill.Module{\n""")
+ s"""
+ |package ${pkgName.head.encoded}
+ |package ${Util.encodeScalaSourcePath(pkgName.tail)}
+ |$imports
+ |import mill._
+ |sealed abstract class ${indexedWrapperName.backticked} extends mill.Module{\n
+ |""".stripMargin
}
def bottom(printCode: String, indexedWrapperName: Name, extraCode: String) = {
+ val wrapName = indexedWrapperName.backticked
+ val tmpName = ammonite.util.Name(indexedWrapperName.raw + "-Temp").backticked
+
+ // Define `discovered` in the `tmpName` trait, before mixing in `MainWrapper`,
+ // to ensure that `$tempName#discovered` is initialized before `MainWrapper` is.
+ //
+ // `import $wrapName._` is necessary too let Ammonite pick up all the
+ // members of class wrapper, which are inherited but otherwise not visible
+ // in the AST of the `$wrapName` object
+ //
+ // We need to duplicate the Ammonite predef as part of the wrapper because
+ // imports within the body of the class wrapper are not brought into scope
+ // by the `import $wrapName._`. Other non-Ammonite-predef imports are not
+ // made available, and that's just too bad
+ s"""
+ |}
+ |trait $tmpName{
+ | val discovered = mill.discover.Discovered.make[$wrapName]
+ | val interpApi = ammonite.interp.InterpBridge.value
+ |}
+ |
+ |object $wrapName
+ |extends $wrapName
+ |with $tmpName
+ |with mill.MainWrapper[$wrapName] {
+ | @ammonite.main.Router.main
+ | def idea() = mill.scalaplugin.GenIdea(mapping)
+ | ${ammonite.main.Defaults.replPredef}
+ | ${ammonite.main.Defaults.predefString}
+ | ${ammonite.Main.extraPredefString}
+ | import ammonite.repl.ReplBridge.{value => repl}
+ | import ammonite.interp.InterpBridge.{value => interp}
+ | import $wrapName._
+ """.stripMargin +
Preprocessor.CodeWrapper.bottom(printCode, indexedWrapperName, extraCode)
}
}
}
+/**
+ * Class that wraps each Mill build file.
+ */
+trait MainWrapper[T]{
+ val discovered: mill.discover.Discovered[T]
+ val interpApi: ammonite.interp.InterpAPI
+ val mapping = discovered.mapping(this.asInstanceOf[T])
+
+ mill.Main.consistencyCheck(mapping).left.foreach(msg => throw new Exception(msg))
+
+ @ammonite.main.Router.main
+ def run(args: String*) = mill.Main(
+ args,
+ mapping,
+ interpApi.watch,
+ true
+ )
+
+
+ val evaluator = new mill.eval.Evaluator(
+ ammonite.ops.pwd / 'out,
+ mapping.value,
+ new mill.util.PrintLogger(true)
+ )
+
+ implicit val replApplyHandler: mill.main.ReplApplyHandler =
+ new mill.main.ReplApplyHandler(evaluator)
+} \ No newline at end of file
diff --git a/core/src/main/scala/mill/discover/Discovered.scala b/core/src/main/scala/mill/discover/Discovered.scala
index 0034a877..1f27c771 100644
--- a/core/src/main/scala/mill/discover/Discovered.scala
+++ b/core/src/main/scala/mill/discover/Discovered.scala
@@ -3,7 +3,8 @@ package mill.discover
import mill.define.Task.Module
import mill.define.{Cross, Target, Task}
import mill.discover.Mirror.LabelledTarget
-import mill.discover.Router.{EntryPoint, Result}
+import ammonite.main.Router
+import ammonite.main.Router.{EntryPoint, Result}
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
@@ -103,7 +104,10 @@ object Discovered {
}
val commands =
- r.getAllRoutesForClass(t.asInstanceOf[r.c.Type])
+ r.getAllRoutesForClass(
+ t.asInstanceOf[r.c.Type],
+ _.returnType <:< weakTypeOf[mill.define.Command[_]].asInstanceOf[r.c.Type]
+ )
.asInstanceOf[Seq[c.Tree]]
.toList
diff --git a/core/src/main/scala/mill/discover/Mirror.scala b/core/src/main/scala/mill/discover/Mirror.scala
index 41b3a0fe..926d9669 100644
--- a/core/src/main/scala/mill/discover/Mirror.scala
+++ b/core/src/main/scala/mill/discover/Mirror.scala
@@ -1,7 +1,7 @@
package mill.discover
import mill.define.{Target, Task}
-import mill.discover.Router.EntryPoint
+import ammonite.main.Router.EntryPoint
import scala.language.experimental.macros
diff --git a/core/src/main/scala/mill/discover/Router.scala b/core/src/main/scala/mill/discover/Router.scala
deleted file mode 100644
index b2d222b9..00000000
--- a/core/src/main/scala/mill/discover/Router.scala
+++ /dev/null
@@ -1,410 +0,0 @@
-package mill.discover
-
-import ammonite.main.Compat
-import mill.define.Task
-import mill.discover.Router.ArgSig
-import sourcecode.Compat.Context
-
-import scala.annotation.StaticAnnotation
-import scala.collection.mutable
-import scala.language.experimental.macros
-/**
- * More or less a minimal version of Autowire's Server that lets you generate
- * a set of "routes" from the methods defined in an object, and call them
- * using passing in name/args/kwargs via Java reflection, without having to
- * generate/compile code or use Scala reflection. This saves us spinning up
- * the Scala compiler and greatly reduces the startup time of cached scripts.
- */
-object Router{
- class doc(s: String) extends StaticAnnotation
- def generateRoutes[T]: Seq[Router.EntryPoint[T]] = macro generateRoutesImpl[T]
- def generateRoutesImpl[T: c.WeakTypeTag](c: Context): c.Expr[Seq[EntryPoint[T]]] = {
- import c.universe._
- val r = new Router(c)
- val allRoutes = r.getAllRoutesForClass(
- weakTypeOf[T].asInstanceOf[r.c.Type],
- ).asInstanceOf[Iterable[c.Tree]]
-
- c.Expr[Seq[EntryPoint[T]]](q"_root_.scala.Seq(..$allRoutes)")
- }
-
- /**
- * Models what is known by the router about a single argument: that it has
- * a [[name]], a human-readable [[typeString]] describing what the type is
- * (just for logging and reading, not a replacement for a `TypeTag`) and
- * possible a function that can compute its default value
- */
- case class ArgSig[T](name: String,
- typeString: String,
- doc: Option[String],
- default: Option[T => Any])
-
- def stripDashes(s: String) = {
- if (s.startsWith("--")) s.drop(2)
- else if (s.startsWith("-")) s.drop(1)
- else s
- }
- /**
- * What is known about a single endpoint for our routes. It has a [[name]],
- * [[argSignatures]] for each argument, and a macro-generated [[invoke0]]
- * that performs all the necessary argument parsing and de-serialization.
- *
- * Realistically, you will probably spend most of your time calling [[invoke]]
- * instead, which provides a nicer API to call it that mimmicks the API of
- * calling a Scala method.
- */
- case class EntryPoint[T](name: String,
- argSignatures: Seq[ArgSig[T]],
- doc: Option[String],
- varargs: Boolean,
- invoke0: (T, Map[String, String], Seq[String]) => Result[Task[Any]]){
- def invoke(target: T, groupedArgs: Seq[(String, Option[String])]): Result[Task[Any]] = {
- var remainingArgSignatures = argSignatures.toList
-
-
- val accumulatedKeywords = mutable.Map.empty[ArgSig[T], mutable.Buffer[String]]
- val keywordableArgs = if (varargs) argSignatures.dropRight(1) else argSignatures
-
- for(arg <- keywordableArgs) accumulatedKeywords(arg) = mutable.Buffer.empty
-
- val leftoverArgs = mutable.Buffer.empty[String]
-
- val lookupArgSig = argSignatures.map(x => (x.name, x)).toMap
-
- var incomplete: Option[ArgSig[T]] = None
-
- for(group <- groupedArgs){
-
- group match{
- case (value, None) =>
- if (value(0) == '-' && !varargs){
- lookupArgSig.get(stripDashes(value)) match{
- case None => leftoverArgs.append(value)
- case Some(sig) => incomplete = Some(sig)
- }
-
- } else remainingArgSignatures match {
- case Nil => leftoverArgs.append(value)
- case last :: Nil if varargs => leftoverArgs.append(value)
- case next :: rest =>
- accumulatedKeywords(next).append(value)
- remainingArgSignatures = rest
- }
- case (rawKey, Some(value)) =>
- val key = stripDashes(rawKey)
- lookupArgSig.get(key) match{
- case Some(x) if accumulatedKeywords.contains(x) =>
- if (accumulatedKeywords(x).nonEmpty && varargs){
- leftoverArgs.append(rawKey, value)
- }else{
- accumulatedKeywords(x).append(value)
- remainingArgSignatures = remainingArgSignatures.filter(_.name != key)
- }
- case _ =>
- leftoverArgs.append(rawKey, value)
- }
- }
- }
-
- val missing0 = remainingArgSignatures.filter(_.default.isEmpty)
- val missing = if(varargs) {
- missing0.filter(_ != argSignatures.last)
- } else {
- missing0.filter(x => incomplete != Some(x))
- }
- val duplicates = accumulatedKeywords.toSeq.filter(_._2.length > 1)
-
- if (
- incomplete.nonEmpty ||
- missing.nonEmpty ||
- duplicates.nonEmpty ||
- (leftoverArgs.nonEmpty && !varargs)
- ){
- Result.Error.MismatchedArguments(
- missing = missing,
- unknown = leftoverArgs,
- duplicate = duplicates,
- incomplete = incomplete
-
- )
- } else {
- val mapping = accumulatedKeywords
- .iterator
- .collect{case (k, Seq(single)) => (k.name, single)}
- .toMap
-
- try invoke0(target, mapping, leftoverArgs)
- catch{case e: Throwable =>
- Result.Error.Exception(e)
- }
- }
- }
- }
-
- def tryEither[T](t: => T, error: Throwable => Result.ParamError) = {
- try Right(t)
- catch{ case e: Throwable => Left(error(e))}
- }
- def readVarargs[T](arg: ArgSig[_],
- values: Seq[String],
- thunk: String => T) = {
- val attempts =
- for(item <- values)
- yield tryEither(thunk(item), Result.ParamError.Invalid(arg, item, _))
-
-
- val bad = attempts.collect{ case Left(x) => x}
- if (bad.nonEmpty) Left(bad)
- else Right(attempts.collect{case Right(x) => x})
- }
- def read[T](dict: Map[String, String],
- default: => Option[Any],
- arg: ArgSig[_],
- thunk: String => T): FailMaybe = {
- dict.get(arg.name) match{
- case None =>
- tryEither(default.get, Result.ParamError.DefaultFailed(arg, _)).left.map(Seq(_))
-
- case Some(x) =>
- tryEither(thunk(x), Result.ParamError.Invalid(arg, x, _)).left.map(Seq(_))
- }
- }
-
- /**
- * Represents what comes out of an attempt to invoke an [[EntryPoint]].
- * Could succeed with a value, but could fail in many different ways.
- */
- sealed trait Result[+T]
- object Result{
-
- /**
- * Invoking the [[EntryPoint]] was totally successful, and returned a
- * result
- */
- case class Success[T](value: T) extends Result[T]
-
- /**
- * Invoking the [[EntryPoint]] was not successful
- */
- sealed trait Error extends Result[Nothing]
- object Error{
-
- /**
- * Invoking the [[EntryPoint]] failed with an exception while executing
- * code within it.
- */
- case class Exception(t: Throwable) extends Error
-
- /**
- * Invoking the [[EntryPoint]] failed because the arguments provided
- * did not line up with the arguments expected
- */
- case class MismatchedArguments(missing: Seq[ArgSig[_]],
- unknown: Seq[String],
- duplicate: Seq[(ArgSig[_], Seq[String])],
- incomplete: Option[ArgSig[_]]) extends Error
- /**
- * Invoking the [[EntryPoint]] failed because there were problems
- * deserializing/parsing individual arguments
- */
- case class InvalidArguments(values: Seq[ParamError]) extends Error
- }
-
- sealed trait ParamError
- object ParamError{
- /**
- * Something went wrong trying to de-serialize the input parameter;
- * the thrown exception is stored in [[ex]]
- */
- case class Invalid(arg: ArgSig[_], value: String, ex: Throwable) extends ParamError
- /**
- * Something went wrong trying to evaluate the default value
- * for this input parameter
- */
- case class DefaultFailed(arg: ArgSig[_], ex: Throwable) extends ParamError
- }
- }
-
-
- type FailMaybe = Either[Seq[Result.ParamError], Any]
- type FailAll = Either[Seq[Result.ParamError], Seq[Any]]
-
- def validate(args: Seq[FailMaybe]): Result[Seq[Any]] = {
- val lefts = args.collect{case Left(x) => x}.flatten
-
- if (lefts.nonEmpty) Result.Error.InvalidArguments(lefts)
- else {
- val rights = args.collect{case Right(x) => x}
- Result.Success(rights)
- }
- }
-
- def makeReadCall[T: scopt.Read](dict: Map[String, String],
- default: => Option[Any],
- arg: ArgSig[_]) = {
- read[T](dict, default, arg, implicitly[scopt.Read[T]].reads(_))
- }
- def makeReadVarargsCall[T: scopt.Read](arg: ArgSig[_],
- values: Seq[String]) = {
- readVarargs[T](arg, values, implicitly[scopt.Read[T]].reads(_))
- }
-}
-
-class Router [C <: Context](val c: C) {
- import c.universe._
- def getValsOrMeths(curCls: Type): Iterable[MethodSymbol] = {
- def isAMemberOfAnyRef(member: Symbol) = {
- // AnyRef is an alias symbol, we go to the real "owner" of these methods
- val anyRefSym = c.mirror.universe.definitions.ObjectClass
- member.owner == anyRefSym
- }
- val extractableMembers = for {
- member <- curCls.members
- if !isAMemberOfAnyRef(member)
- if !member.isSynthetic
- if member.isPublic
- if member.isTerm
- memTerm = member.asTerm
- if memTerm.isMethod
- if !memTerm.isModule
- } yield memTerm.asMethod
-
- extractableMembers flatMap { case memTerm =>
- if (memTerm.isSetter || memTerm.isConstructor || memTerm.isGetter) Nil
- else Seq(memTerm)
-
- }
- }
-
-
-
- def extractMethod(meth: MethodSymbol, curCls: c.universe.Type): c.universe.Tree = {
- val flattenedArgLists = meth.paramss.flatten
- def hasDefault(i: Int) = {
- val defaultName = s"${meth.name}$$default$$${i + 1}"
- if (curCls.members.exists(_.name.toString == defaultName)) Some(defaultName)
- else None
- }
- val argListSymbol = q"${c.fresh[TermName]("argsList")}"
- val extrasSymbol = q"${c.fresh[TermName]("extras")}"
- val defaults = for ((arg, i) <- flattenedArgLists.zipWithIndex) yield {
- val arg = TermName(c.freshName())
- hasDefault(i).map(defaultName => q"($arg: $curCls) => $arg.${newTermName(defaultName)}")
- }
-
- def getDocAnnotation(annotations: List[Annotation]) = {
- val (docTrees, remaining) = annotations.partition(_.tpe =:= typeOf[Router.doc])
- val docValues = for {
- doc <- docTrees
- if doc.scalaArgs.head.isInstanceOf[Literal]
- l = doc.scalaArgs.head.asInstanceOf[Literal]
- if l.value.value.isInstanceOf[String]
- } yield l.value.value.asInstanceOf[String]
- (remaining, docValues.headOption)
- }
-
- def unwrapVarargType(arg: Symbol) = {
- val vararg = arg.typeSignature.typeSymbol == definitions.RepeatedParamClass
- val unwrappedType =
- if (!vararg) arg.typeSignature
- else arg.typeSignature.asInstanceOf[TypeRef].args(0)
-
- (vararg, unwrappedType)
- }
-
-
- val (_, methodDoc) = getDocAnnotation(meth.annotations)
- val readArgSigs = for(
- ((arg, defaultOpt), i) <- flattenedArgLists.zip(defaults).zipWithIndex
- ) yield {
-
- val (vararg, varargUnwrappedType) = unwrapVarargType(arg)
-
- val default =
- if (vararg) q"scala.Some(scala.Nil)"
- else defaultOpt match {
- case Some(defaultExpr) => q"scala.Some($defaultExpr())"
- case None => q"scala.None"
- }
-
- val (docUnwrappedType, docOpt) = varargUnwrappedType match{
- case t: AnnotatedType =>
-
- val (remaining, docValue) = getDocAnnotation(t.annotations)
- if (remaining.isEmpty) (t.underlying, docValue)
- else (Compat.copyAnnotatedType(c)(t, remaining), docValue)
-
- case t => (t, None)
- }
-
- val docTree = docOpt match{
- case None => q"scala.None"
- case Some(s) => q"scala.Some($s)"
- }
- val argSig = q"""
- mill.discover.Router.ArgSig(
- ${arg.name.toString},
- ${docUnwrappedType.toString + (if(vararg) "*" else "")},
- $docTree,
- $defaultOpt
- )
- """
-
- val reader =
- if(vararg) q"""
- mill.discover.Router.makeReadVarargsCall[$docUnwrappedType](
- $argSig,
- $extrasSymbol
- )
- """ else q"""
- mill.discover.Router.makeReadCall[$docUnwrappedType](
- $argListSymbol,
- $default,
- $argSig
- )
- """
- c.internal.setPos(reader, meth.pos)
- (reader, argSig, vararg)
- }
-
- val (readArgs, argSigs, varargs) = readArgSigs.unzip3
- val (argNames, argNameCasts) = flattenedArgLists.map { arg =>
- val (vararg, unwrappedType) = unwrapVarargType(arg)
- (
- pq"${arg.name.toTermName}",
- if (!vararg) q"${arg.name.toTermName}.asInstanceOf[$unwrappedType]"
- else q"${arg.name.toTermName}.asInstanceOf[Seq[$unwrappedType]]: _*"
-
- )
- }.unzip
-
- val arg = TermName(c.freshName())
- q"""
- mill.discover.Router.EntryPoint(
- ${meth.name.toString},
- scala.Seq(..$argSigs),
- ${methodDoc match{
- case None => q"scala.None"
- case Some(s) => q"scala.Some($s)"
- }},
- ${varargs.contains(true)},
- ($arg: $curCls, $argListSymbol: Map[String, String], $extrasSymbol: Seq[String]) =>
- mill.discover.Router.validate(Seq(..$readArgs)) match{
- case mill.discover.Router.Result.Success(List(..$argNames)) =>
- mill.discover.Router.Result.Success($arg.${meth.name.toTermName}(..$argNameCasts))
- case x: mill.discover.Router.Result.Error => x
- }
- )
- """
- }
-
- def getAllRoutesForClass(curCls: Type): Iterable[c.universe.Tree] = {
- for{
- t <- getValsOrMeths(curCls)
- if t.returnType <:< typeOf[mill.define.Command[_]]
- } yield {
- extractMethod(t, curCls)
- }
- }
-}
-
diff --git a/core/src/main/scala/mill/main/Resolve.scala b/core/src/main/scala/mill/main/Resolve.scala
index 9cfc33b5..6196cdc6 100644
--- a/core/src/main/scala/mill/main/Resolve.scala
+++ b/core/src/main/scala/mill/main/Resolve.scala
@@ -2,7 +2,8 @@ package mill.main
import mill.define.Task
import mill.define.Task.TaskModule
-import mill.discover.{Mirror, Router}
+import mill.discover.{Mirror}
+import ammonite.main.Router
object Resolve {
def resolve[T, V](remainingSelector: List[Mirror.Segment],
@@ -45,7 +46,9 @@ object Resolve {
case None => Left("Cannot resolve task " + Mirror.renderSelector(
(Mirror.Segment.Label(last) :: revSelectorsSoFar).reverse)
)
- case Some(either) => either
+ // Contents of `either` *must* be a `Task`, because we only select
+ // methods returning `Task` in the discovery process
+ case Some(either) => either.right.map{ case x: Task[Any] => x }
}
diff --git a/core/src/test/scala/mill/discover/DiscoveredTests.scala b/core/src/test/scala/mill/discover/DiscoveredTests.scala
index 33196ff9..0f73074f 100644
--- a/core/src/test/scala/mill/discover/DiscoveredTests.scala
+++ b/core/src/test/scala/mill/discover/DiscoveredTests.scala
@@ -2,7 +2,7 @@ package mill.discover
import java.io.InputStreamReader
-import mill.discover.Router.{ArgSig, EntryPoint}
+import ammonite.main.Router.{ArgSig, EntryPoint}
import utest._
import mill.{Module, T}
import mill.discover.Mirror.Segment.Label
@@ -68,13 +68,13 @@ object DiscoveredTests extends TestSuite{
val outerCommands = discovered.mirror.commands
assertMatch(outerCommands){case Seq(
+ EntryPoint("hello", Nil, None, false, _),
EntryPoint("echoPair",
List(ArgSig("prefix", "String", None, None), ArgSig("suffix", "String", None, None)),
None,
false,
_
- ),
- EntryPoint("hello", Nil, None, false, _)
+ )
) =>}
val innerCommands = discovered.mirror
diff --git a/plugin/src/main/scala/mill/plugin/Cacher.scala b/plugin/src/main/scala/mill/plugin/Cacher.scala
index 4c854df7..17f0a8d0 100644
--- a/plugin/src/main/scala/mill/plugin/Cacher.scala
+++ b/plugin/src/main/scala/mill/plugin/Cacher.scala
@@ -5,7 +5,7 @@ import scala.reflect.macros.blackbox.Context
trait Cacher[C[_]]{
- private[this] val cacherLazyMap = mutable.Map.empty[sourcecode.Enclosing, C[_]]
+ private[this] lazy val cacherLazyMap = mutable.Map.empty[sourcecode.Enclosing, C[_]]
def wrapCached[T](in: C[T], enclosing: String): C[T]
protected[this] def cachedTarget[T](t: => C[T])
(implicit c: sourcecode.Enclosing): C[T] = synchronized{