diff options
author | Li Haoyi <haoyi.sg@gmail.com> | 2018-02-07 22:29:40 -0800 |
---|---|---|
committer | Li Haoyi <haoyi.sg@gmail.com> | 2018-02-07 22:29:40 -0800 |
commit | 54a2419b0e66eaf52211870bf04d84af87deaa80 (patch) | |
tree | f8229f8dd3f68ca571be6ade115ebf6cd3f2c746 /core | |
parent | e381552b992a5ba4743fa54f5111db9a2e9d98e2 (diff) | |
download | mill-54a2419b0e66eaf52211870bf04d84af87deaa80.tar.gz mill-54a2419b0e66eaf52211870bf04d84af87deaa80.tar.bz2 mill-54a2419b0e66eaf52211870bf04d84af87deaa80.zip |
wip
Diffstat (limited to 'core')
-rw-r--r-- | core/src/mill/define/BaseModule.scala | 9 | ||||
-rw-r--r-- | core/src/mill/define/Discover.scala | 4 | ||||
-rw-r--r-- | core/src/mill/define/Router.scala | 434 | ||||
-rw-r--r-- | core/src/mill/eval/Evaluator.scala | 13 | ||||
-rw-r--r-- | core/src/mill/main/MainRunner.scala | 33 | ||||
-rw-r--r-- | core/src/mill/main/RunScript.scala | 5 | ||||
-rw-r--r-- | core/test/src/mill/define/CacherTests.scala | 42 | ||||
-rw-r--r-- | core/test/src/mill/eval/CrossTests.scala | 5 | ||||
-rw-r--r-- | core/test/src/mill/eval/EvaluationTests.scala | 3 | ||||
-rw-r--r-- | core/test/src/mill/eval/FailureTests.scala | 1 | ||||
-rw-r--r-- | core/test/src/mill/eval/ModuleTests.scala | 2 | ||||
-rw-r--r-- | core/test/src/mill/eval/TaskTests.scala | 1 | ||||
-rw-r--r-- | core/test/src/mill/main/MainTests.scala | 1 |
13 files changed, 513 insertions, 40 deletions
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)( |