summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2018-02-07 22:29:40 -0800
committerLi Haoyi <haoyi.sg@gmail.com>2018-02-07 22:29:40 -0800
commit54a2419b0e66eaf52211870bf04d84af87deaa80 (patch)
treef8229f8dd3f68ca571be6ade115ebf6cd3f2c746
parente381552b992a5ba4743fa54f5111db9a2e9d98e2 (diff)
downloadmill-54a2419b0e66eaf52211870bf04d84af87deaa80.tar.gz
mill-54a2419b0e66eaf52211870bf04d84af87deaa80.tar.bz2
mill-54a2419b0e66eaf52211870bf04d84af87deaa80.zip
wip
-rwxr-xr-xbuild.sc4
-rw-r--r--core/src/mill/define/BaseModule.scala9
-rw-r--r--core/src/mill/define/Discover.scala4
-rw-r--r--core/src/mill/define/Router.scala434
-rw-r--r--core/src/mill/eval/Evaluator.scala13
-rw-r--r--core/src/mill/main/MainRunner.scala33
-rw-r--r--core/src/mill/main/RunScript.scala5
-rw-r--r--core/test/src/mill/define/CacherTests.scala42
-rw-r--r--core/test/src/mill/eval/CrossTests.scala5
-rw-r--r--core/test/src/mill/eval/EvaluationTests.scala3
-rw-r--r--core/test/src/mill/eval/FailureTests.scala1
-rw-r--r--core/test/src/mill/eval/ModuleTests.scala2
-rw-r--r--core/test/src/mill/eval/TaskTests.scala1
-rw-r--r--core/test/src/mill/main/MainTests.scala1
-rw-r--r--scalalib/src/mill/scalalib/PublishModule.scala8
15 files changed, 521 insertions, 44 deletions
diff --git a/build.sc b/build.sc
index d31b66d8..d8523013 100755
--- a/build.sc
+++ b/build.sc
@@ -243,3 +243,7 @@ def uploadToGithub(assembly: Path, authKey: String, release: String, label: Stri
upload.apply(assembly, release, label, authKey)
}
+
+//def resolve(targets: mill.define.TargetScopt.Targets[Any]) = mill.T.command{
+// targets.items.foreach(println)
+//} \ No newline at end of file
diff --git a/core/src/mill/define/BaseModule.scala b/core/src/mill/define/BaseModule.scala
index 12a01e89..cedfcef7 100644
--- a/core/src/mill/define/BaseModule.scala
+++ b/core/src/mill/define/BaseModule.scala
@@ -51,6 +51,8 @@ abstract class ExternalModule(implicit millModuleEnclosing0: sourcecode.Enclosin
}
object TargetScopt{
+ case class Targets[T](items: Seq[mill.define.Target[T]])
+ implicit def millScoptTargetReads[T] = new TargetScopt[T]()
// This needs to be a ThreadLocal because we need to pass it into the body of
// the TargetScopt#read call, which does not accept additional parameters.
// Until we migrate our CLI parsing off of Scopt (so we can pass the BaseModule
@@ -58,12 +60,13 @@ object TargetScopt{
val currentRootModule = new ThreadLocal[BaseModule]
}
class TargetScopt[T]()
- extends scopt.Read[Seq[mill.define.Target[T]]]{
+ extends scopt.Read[TargetScopt.Targets[T]]{
def arity = 1
def reads = s => try{
val rootModule = TargetScopt.currentRootModule.get
val d = rootModule.millDiscover
- val (expanded, Nil) = ParseArgs(Seq(s)).fold(e => throw new Exception(e), identity)
+ val (expanded, Nil) = ParseArgs(Seq("--all", s)).fold(e => throw new Exception(e), identity)
+
val resolved = expanded.map{
case (Some(scoping), segments) =>
val moduleCls = rootModule.getClass.getClassLoader.loadClass(scoping.render + "$")
@@ -82,7 +85,7 @@ class TargetScopt[T]()
}
mill.util.EitherOps.sequence(resolved) match{
case Left(s) => throw new Exception(s)
- case Right(ts) => ts.flatten.collect{case t: mill.define.Target[T] => t}
+ case Right(ts) => TargetScopt.Targets(ts.flatten.collect{case t: mill.define.Target[T] => t})
}
}catch{case e => e.printStackTrace(); throw e}
}
diff --git a/core/src/mill/define/Discover.scala b/core/src/mill/define/Discover.scala
index 98e29a76..b213d9f3 100644
--- a/core/src/mill/define/Discover.scala
+++ b/core/src/mill/define/Discover.scala
@@ -41,7 +41,7 @@ object Discover {
}
rec(weakTypeOf[T])
- val router = new ammonite.main.Router(c)
+ val router = new mill.define.Router(c)
val mapping = for{
discoveredModuleType <- seen
val curCls = discoveredModuleType.asInstanceOf[router.c.Type]
@@ -57,7 +57,7 @@ object Discover {
val (overrides, routes) = overridesRoutes.unzip
val lhs = q"classOf[${discoveredModuleType.typeSymbol.asClass}]"
val clsType = discoveredModuleType.typeSymbol.asClass
- val rhs = q"scala.Seq[(Int, ammonite.main.Router.EntryPoint[$clsType])](..$overridesRoutes)"
+ val rhs = q"scala.Seq[(Int, ammonite.main.Router.EntryPoint[_])](..$overridesRoutes)"
q"$lhs -> $rhs"
}
diff --git a/core/src/mill/define/Router.scala b/core/src/mill/define/Router.scala
new file mode 100644
index 00000000..4a9c3ffb
--- /dev/null
+++ b/core/src/mill/define/Router.scala
@@ -0,0 +1,434 @@
+package mill.define
+
+
+import ammonite.main.Compat
+import sourcecode.Compat.Context
+
+import scala.annotation.StaticAnnotation
+import scala.collection.mutable
+import scala.language.experimental.macros
+import scala.reflect.macros.blackbox.Context
+/**
+ * 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{
+ /**
+ * Allows you to query how many things are overriden by the enclosing owner.
+ */
+ case class Overrides(value: Int)
+ object Overrides{
+ def apply()(implicit c: Overrides) = c.value
+ implicit def generate: Overrides = macro impl
+ def impl(c: Context): c.Tree = {
+ import c.universe._
+ q"new _root_.ammonite.main.Router.Overrides(${c.internal.enclosingOwner.overrides.length})"
+ }
+ }
+
+ class doc(s: String) extends StaticAnnotation
+ class main 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[Any],
+ overrides: Int){
+ def invoke(target: T, groupedArgs: Seq[(String, Option[String])]): Result[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.toList.reverse
+ 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 baseArgSym = TermName(c.freshName())
+ 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($baseArgSym))"
+ 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"""
+ ammonite.main.Router.ArgSig(
+ ${arg.name.toString},
+ ${docUnwrappedType.toString + (if(vararg) "*" else "")},
+ $docTree,
+ $defaultOpt
+ )
+ """
+
+ val reader =
+ if(vararg) q"""
+ ammonite.main.Router.makeReadVarargsCall[$docUnwrappedType](
+ $argSig,
+ $extrasSymbol
+ )
+ """ else q"""
+ ammonite.main.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
+
+
+ q"""
+ ammonite.main.Router.EntryPoint[$curCls](
+ ${meth.name.toString},
+ scala.Seq(..$argSigs),
+ ${methodDoc match{
+ case None => q"scala.None"
+ case Some(s) => q"scala.Some($s)"
+ }},
+ ${varargs.contains(true)},
+ ($baseArgSym: $curCls, $argListSymbol: Map[String, String], $extrasSymbol: Seq[String]) =>
+ ammonite.main.Router.validate(Seq(..$readArgs)) match{
+ case ammonite.main.Router.Result.Success(List(..$argNames)) =>
+ ammonite.main.Router.Result.Success(
+ $baseArgSym.${meth.name.toTermName}(..$argNameCasts)
+ )
+ case x: ammonite.main.Router.Result.Error => x
+ },
+ ammonite.main.Router.Overrides()
+ )
+ """
+ }
+
+ def hasMainAnnotation(t: MethodSymbol) = {
+ t.annotations.exists(_.tpe =:= typeOf[Router.main])
+ }
+ def getAllRoutesForClass(curCls: Type,
+ pred: MethodSymbol => Boolean = hasMainAnnotation)
+ : Iterable[c.universe.Tree] = {
+ for{
+ t <- getValsOrMeths(curCls)
+ if pred(t)
+ } yield {
+ extractMethod(t, curCls)
+ }
+ }
+}
+
diff --git a/core/src/mill/eval/Evaluator.scala b/core/src/mill/eval/Evaluator.scala
index fe43d0a3..3d8e82b8 100644
--- a/core/src/mill/eval/Evaluator.scala
+++ b/core/src/mill/eval/Evaluator.scala
@@ -58,9 +58,14 @@ class Evaluator[T](val outPath: Path,
}
}
}
+ pprint.log(discover.value.keySet)
+ pprint.log(c.cls)
findMatching(c.cls) match{
- case Some(v) => v.find(_._2.name == c.ctx.segment.pathSegments.head).get._1
+ case Some(v) =>
+ pprint.log(v)
+ pprint.log(c.ctx.segment.pathSegments)
+ v.find(_._2.name == c.ctx.segment.pathSegments.head).get._1
// For now we don't properly support overrides for external modules
// that do not appear in the Evaluator's main Discovered listing
case None => 0
@@ -329,6 +334,12 @@ class Evaluator[T](val outPath: Path,
object Evaluator{
+ class Scopt extends scopt.Read[Evaluator[_]] {
+ def arity = 0
+ def reads = _ => dynamicScopt.get
+ }
+ val dynamicScopt = new ThreadLocal[Evaluator[_]]
+ implicit def evaluatorScopt = new Scopt
case class Paths(out: Path,
dest: Path,
meta: Path,
diff --git a/core/src/mill/main/MainRunner.scala b/core/src/mill/main/MainRunner.scala
index e3820e3d..b61ab450 100644
--- a/core/src/mill/main/MainRunner.scala
+++ b/core/src/mill/main/MainRunner.scala
@@ -62,11 +62,11 @@ class MainRunner(config: ammonite.main.Cli.Config,
override def handleWatchRes[T](res: Res[T], printing: Boolean) = {
res match{
case Res.Success(value) =>
- if (show){
- for(json <- value.asInstanceOf[Seq[Js.Value]]){
- outprintStream.println(json)
- }
- }
+// if (show){
+// for(json <- value.asInstanceOf[Seq[Js.Value]]){
+// outprintStream.println(json)
+// }
+// }
true
@@ -91,7 +91,7 @@ class MainRunner(config: ammonite.main.Cli.Config,
|package ${Util.encodeScalaSourcePath(pkgName.tail)}
|$imports
|import mill._
- |
+ |import mill.eval.Evaluator.evaluatorScopt
|object $wrapName
|extends mill.define.BaseModule(ammonite.ops.Path($literalPath))
|with $wrapName{
@@ -106,7 +106,26 @@ class MainRunner(config: ammonite.main.Cli.Config,
|}
|
|sealed trait $wrapName extends mill.Module{this: mill.define.BaseModule =>
- |
+ | def resolve(targets: mill.define.TargetScopt.Targets[Any]*) = mill.T.command{
+ | targets.flatMap(_.items).foreach(println)
+ | }
+ | def all(evaluator: mill.eval.Evaluator[_],
+ | targets: mill.define.TargetScopt.Targets[Any]*) = mill.T.command{
+ | val (watched, res) = mill.main.RunScript.evaluate(
+ | evaluator,
+ | mill.util.Strict.Agg.from(targets.flatMap(_.items))
+ | )
+ | }
+ | def show(evaluator: mill.eval.Evaluator[_],
+ | targets: mill.define.TargetScopt.Targets[Any]*) = mill.T.command{
+ | val (watched, res) = mill.main.RunScript.evaluate(
+ | evaluator,
+ | mill.util.Strict.Agg.from(targets.flatMap(_.items))
+ | )
+ | for(json <- res.right.get.flatMap(_._2)){
+ | println(json)
+ | }
+ | }
| implicit def millDiscover: mill.define.Discover[_]
|""".stripMargin
}
diff --git a/core/src/mill/main/RunScript.scala b/core/src/mill/main/RunScript.scala
index 613d6441..3aaeb44f 100644
--- a/core/src/mill/main/RunScript.scala
+++ b/core/src/mill/main/RunScript.scala
@@ -129,8 +129,7 @@ object RunScript{
} yield (module, discover)
}
- def evaluateTarget[T](evaluator: Evaluator[T],
- scriptArgs: Seq[String]) = {
+ def evaluateTarget[T](evaluator: Evaluator[T], scriptArgs: Seq[String]) = {
for {
parsed <- ParseArgs(scriptArgs)
(selectors, args) = parsed
@@ -157,12 +156,14 @@ object RunScript{
// main build. Resolving targets from external builds as CLI arguments
// is not currently supported
mill.define.TargetScopt.currentRootModule.set(evaluator.rootModule)
+ mill.eval.Evaluator.dynamicScopt.set(evaluator)
mill.main.Resolve.resolve(
sel.value.toList, rootModule,
discover,
args, crossSelectors.toList, Nil
)
} finally{
+ mill.eval.Evaluator.dynamicScopt.set(null)
mill.define.TargetScopt.currentRootModule.set(null)
}
}
diff --git a/core/test/src/mill/define/CacherTests.scala b/core/test/src/mill/define/CacherTests.scala
index 611db8c9..077fea8d 100644
--- a/core/test/src/mill/define/CacherTests.scala
+++ b/core/test/src/mill/define/CacherTests.scala
@@ -9,6 +9,7 @@ import utest._
import utest.framework.TestPath
import mill.util.TestEvaluator.implicitDisover
+import TargetScopt.millScoptTargetReads
object CacherTests extends TestSuite{
object Base extends Base
trait Base extends TestUtil.BaseModule{
@@ -27,36 +28,37 @@ object CacherTests extends TestSuite{
val tests = Tests{
def eval[T <: TestUtil.BaseModule, V](mapping: T, v: Task[V])
- (implicit discover: Discover[T], tp: TestPath) = {
+ (implicit discover: Discover[T], tp: TestPath) = {
val evaluator = new TestEvaluator(mapping)
evaluator(v).right.get._1
}
+ def check(x: Any, y: Any) = assert(x == y)
- 'simpleDefIsCached - assert(
- Base.value eq Base.value,
- eval(Base, Base.value) == 1
- )
+ 'simpleDefIsCached - {
+ Predef.assert(Base.value eq Base.value)
+ Predef.assert(eval(Base, Base.value) == 1)
+ }
- 'resultDefIsCached - assert(
- Base.result eq Base.result,
- eval(Base, Base.result) == 1
- )
+ 'resultDefIsCached - {
+ Predef.assert(Base.result eq Base.result)
+ Predef.assert(eval(Base, Base.result) == 1)
+ }
- 'overridingDefIsAlsoCached - assert(
- eval(Middle, Middle.value) == 3,
- Middle.value eq Middle.value
- )
+ 'overridingDefIsAlsoCached - {
+ Predef.assert(eval(Middle, Middle.value) == 3)
+ Predef.assert(Middle.value eq Middle.value)
+ }
- 'overridenDefRemainsAvailable - assert(
- eval(Middle, Middle.overriden) == 1
- )
+ 'overridenDefRemainsAvailable - {
+ Predef.assert(eval(Middle, Middle.overriden) == 1)
+ }
- 'multipleOverridesWork- assert(
- eval(Terminal, Terminal.value) == 7,
- eval(Terminal, Terminal.overriden) == 1
- )
+ 'multipleOverridesWork- {
+ Predef.assert(eval(Terminal, Terminal.value) == 7)
+ Predef.assert(eval(Terminal, Terminal.overriden) == 1)
+ }
// Doesn't fail, presumably compileError doesn't go far enough in the
// compilation pipeline to hit the override checks
//
diff --git a/core/test/src/mill/eval/CrossTests.scala b/core/test/src/mill/eval/CrossTests.scala
index c9f7cb70..4e772a40 100644
--- a/core/test/src/mill/eval/CrossTests.scala
+++ b/core/test/src/mill/eval/CrossTests.scala
@@ -1,13 +1,12 @@
package mill.eval
import ammonite.ops._
-
-import mill.define.Discover
+import mill.define.{Discover, TargetScopt}
import mill.util.TestEvaluator
import mill.util.TestEvaluator.implicitDisover
import mill.util.TestGraphs.{crossResolved, doubleCross, nestedCrosses, singleCross}
import utest._
-
+import TargetScopt.millScoptTargetReads
object CrossTests extends TestSuite{
val tests = Tests{
'singleCross - {
diff --git a/core/test/src/mill/eval/EvaluationTests.scala b/core/test/src/mill/eval/EvaluationTests.scala
index 9c4ace41..e5f0e57d 100644
--- a/core/test/src/mill/eval/EvaluationTests.scala
+++ b/core/test/src/mill/eval/EvaluationTests.scala
@@ -10,6 +10,7 @@ import utest._
import utest.framework.TestPath
import mill.util.TestEvaluator.implicitDisover
import ammonite.ops._
+
object EvaluationTests extends TestSuite{
class Checker[T <: TestUtil.BaseModule](module: T)
(implicit tp: TestPath, discover: Discover[T]) {
@@ -52,7 +53,7 @@ object EvaluationTests extends TestSuite{
val tests = Tests{
- val graphs = new TestGraphs()
+ object graphs extends TestGraphs()
import graphs._
import TestGraphs._
'evaluateSingle - {
diff --git a/core/test/src/mill/eval/FailureTests.scala b/core/test/src/mill/eval/FailureTests.scala
index 91b1851d..6bf53f7c 100644
--- a/core/test/src/mill/eval/FailureTests.scala
+++ b/core/test/src/mill/eval/FailureTests.scala
@@ -6,6 +6,7 @@ import mill.eval.Result.OuterStack
import utest._
import utest.framework.TestPath
import mill.util.TestEvaluator.implicitDisover
+import mill.define.TargetScopt.millScoptTargetReads
object FailureTests extends TestSuite{
val tests = Tests{
diff --git a/core/test/src/mill/eval/ModuleTests.scala b/core/test/src/mill/eval/ModuleTests.scala
index c6061abb..278b46cc 100644
--- a/core/test/src/mill/eval/ModuleTests.scala
+++ b/core/test/src/mill/eval/ModuleTests.scala
@@ -6,7 +6,7 @@ import mill.T
import mill.define.Discover
import mill.util.TestEvaluator.implicitDisover
import utest._
-
+import mill.define.TargetScopt.millScoptTargetReads
object ModuleTests extends TestSuite{
object ExternalModule extends mill.define.ExternalModule {
diff --git a/core/test/src/mill/eval/TaskTests.scala b/core/test/src/mill/eval/TaskTests.scala
index 114a2910..ea82677d 100644
--- a/core/test/src/mill/eval/TaskTests.scala
+++ b/core/test/src/mill/eval/TaskTests.scala
@@ -5,6 +5,7 @@ import ammonite.ops._
import mill.T
import mill.util.TestEvaluator.implicitDisover
import mill.util.TestEvaluator
+import mill.define.TargetScopt.millScoptTargetReads
object TaskTests extends TestSuite{
val tests = Tests{
object build extends mill.util.TestUtil.BaseModule{
diff --git a/core/test/src/mill/main/MainTests.scala b/core/test/src/mill/main/MainTests.scala
index 22f93ae0..e1a419cb 100644
--- a/core/test/src/mill/main/MainTests.scala
+++ b/core/test/src/mill/main/MainTests.scala
@@ -4,6 +4,7 @@ import mill.define.{Discover, Segment, Task}
import mill.util.TestGraphs._
import mill.util.TestEvaluator.implicitDisover
import utest._
+import mill.define.TargetScopt.millScoptTargetReads
object MainTests extends TestSuite{
def check[T <: mill.Module](module: T)(
diff --git a/scalalib/src/mill/scalalib/PublishModule.scala b/scalalib/src/mill/scalalib/PublishModule.scala
index 87a2f84f..993fe7e6 100644
--- a/scalalib/src/mill/scalalib/PublishModule.scala
+++ b/scalalib/src/mill/scalalib/PublishModule.scala
@@ -2,7 +2,7 @@ package mill
package scalalib
import ammonite.ops._
-import mill.define.{ExternalModule, Task}
+import mill.define.{ExternalModule, TargetScopt, Task}
import mill.eval.{PathRef, Result}
import mill.scalalib.publish.{Artifact, SonatypePublisher}
import mill.util.Loose.Agg
@@ -88,10 +88,10 @@ trait PublishModule extends ScalaModule { outer =>
object PublishModule extends ExternalModule{
def publishAll(sonatypeCreds: String,
gpgPassphrase: String,
- publishArtifacts: Seq[mill.T[(mill.scalalib.publish.Artifact, Seq[(PathRef, String)])]],
+ publishArtifacts: TargetScopt.Targets[(mill.scalalib.publish.Artifact, Seq[(PathRef, String)])],
sonatypeUri: String = "https://oss.sonatype.org/service/local",
sonatypeSnapshotUri: String = "https://oss.sonatype.org/content/repositories/snapshots") = T.command{
- val x: Seq[(Seq[(Path, String)], Artifact)] = Task.sequence(publishArtifacts)().map{
+ val x: Seq[(Seq[(Path, String)], Artifact)] = Task.sequence(publishArtifacts.items)().map{
case (a, s) => (s.map{case (p, f) => (p.path, f)}, a)
}
new SonatypePublisher(
@@ -104,6 +104,6 @@ object PublishModule extends ExternalModule{
x:_*
)
}
- millScoptTargetReads
+
def millDiscover: mill.define.Discover[this.type] = mill.define.Discover[this.type]
} \ No newline at end of file