diff options
Diffstat (limited to 'core')
45 files changed, 51 insertions, 3646 deletions
diff --git a/core/src/mill/Main.scala b/core/src/mill/Main.scala deleted file mode 100644 index 3025994c..00000000 --- a/core/src/mill/Main.scala +++ /dev/null @@ -1,83 +0,0 @@ -package mill - -import ammonite.main.Cli.{formatBlock, genericSignature, replSignature} -import ammonite.ops._ -import ammonite.util.Util - -object Main { - case class Config(home: ammonite.ops.Path = pwd/'out/'ammonite, - colored: Option[Boolean] = None, - help: Boolean = false, - repl: Boolean = false, - watch: Boolean = false) - - def main(args: Array[String]): Unit = { - - import ammonite.main.Cli - - var show = false - val showCliArg = Cli.Arg[Cli.Config, Unit]( - "show", - None, - "Display the json-formatted value of the given target, if any", - (x, _) => { - show = true - x - } - ) - val removed = Set("predef-code", "home", "no-home-predef") - val millArgSignature = (Cli.genericSignature :+ showCliArg).filter(a => !removed(a.name)) - Cli.groupArgs( - args.toList, - millArgSignature, - Cli.Config(remoteLogging = false) - ) match{ - case Left(msg) => - System.err.println(msg) - System.exit(1) - case Right((cliConfig, _)) if cliConfig.help => - val leftMargin = millArgSignature.map(ammonite.main.Cli.showArg(_).length).max + 2 - System.out.println( - s"""Mill Build Tool - |usage: mill [mill-options] [target [target-options]] - | - |${formatBlock(millArgSignature, leftMargin).mkString(Util.newLine)}""".stripMargin - ) - System.exit(0) - case Right((cliConfig, leftoverArgs)) => - - val repl = leftoverArgs.isEmpty - val config = - if(!repl) cliConfig - else cliConfig.copy( - predefCode = - """import $file.build, build._ - |implicit val replApplyHandler = mill.main.ReplApplyHandler( - | interp.colors(), - | repl.pprinter(), - | build.millSelf.get, - | build.millDiscover - |) - |repl.pprinter() = replApplyHandler.pprinter - |import replApplyHandler.generatedEval._ - | - """.stripMargin, - welcomeBanner = None - ) - - val runner = new mill.main.MainRunner( - config, show, - System.out, System.err, System.in - ) - if (repl){ - runner.printInfo("Loading...") - runner.runRepl() - } else { - val result = runner.runScript(pwd / "build.sc", leftoverArgs) - System.exit(if(result) 0 else 1) - } - } - } -} - - diff --git a/core/src/mill/define/BaseModule.scala b/core/src/mill/define/BaseModule.scala index 5253e691..fccb19ae 100644 --- a/core/src/mill/define/BaseModule.scala +++ b/core/src/mill/define/BaseModule.scala @@ -1,8 +1,6 @@ package mill.define -import mill.main.Router.Overrides import ammonite.ops.Path -import mill.main.ParseArgs object BaseModule{ case class Implicit(value: BaseModule) @@ -11,7 +9,8 @@ object BaseModule{ abstract class BaseModule(millSourcePath0: Path, external0: Boolean = false) (implicit millModuleEnclosing0: sourcecode.Enclosing, millModuleLine0: sourcecode.Line, - millName0: sourcecode.Name) + millName0: sourcecode.Name, + millFile0: sourcecode.File) extends Module()( mill.define.Ctx.make( implicitly, @@ -19,8 +18,9 @@ abstract class BaseModule(millSourcePath0: Path, external0: Boolean = false) implicitly, BasePath(millSourcePath0), Segments(), - Overrides(0), - Ctx.External(external0) + mill.util.Router.Overrides(0), + Ctx.External(external0), + millFile0 ) ){ // A BaseModule should provide an empty Segments list to it's children, since diff --git a/core/src/mill/define/Ctx.scala b/core/src/mill/define/Ctx.scala index 11e9e1f5..6075b804 100644 --- a/core/src/mill/define/Ctx.scala +++ b/core/src/mill/define/Ctx.scala @@ -1,6 +1,6 @@ package mill.define -import mill.main.Router.Overrides + import ammonite.ops.{Path, RelPath} import scala.annotation.implicitNotFound @@ -57,7 +57,8 @@ case class Ctx(enclosing: String, millSourcePath: Path, segments: Segments, overrides: Int, - external: Boolean){ + external: Boolean, + fileName: String){ } object Ctx{ @@ -67,8 +68,9 @@ object Ctx{ millName0: sourcecode.Name, millModuleBasePath0: BasePath, segments0: Segments, - overrides0: mill.main.Router.Overrides, - external0: External): Ctx = { + overrides0: mill.util.Router.Overrides, + external0: External, + fileName: sourcecode.File): Ctx = { Ctx( millModuleEnclosing0.value, millModuleLine0.value, @@ -76,7 +78,8 @@ object Ctx{ millModuleBasePath0.value, segments0, overrides0.value, - external0.value + external0.value, + fileName.value ) } }
\ No newline at end of file diff --git a/core/src/mill/define/Discover.scala b/core/src/mill/define/Discover.scala index fd5bd449..1b6b002a 100644 --- a/core/src/mill/define/Discover.scala +++ b/core/src/mill/define/Discover.scala @@ -1,6 +1,7 @@ package mill.define +import mill.util.Router.EntryPoint + import language.experimental.macros -import mill.main.Router.{EntryPoint, Overrides} import sourcecode.Compat.Context import scala.collection.mutable @@ -41,7 +42,7 @@ object Discover { } rec(weakTypeOf[T]) - val router = new mill.main.Router(c) + val router = new mill.util.Router(c) val mapping = for{ discoveredModuleType <- seen val curCls = discoveredModuleType.asInstanceOf[router.c.Type] @@ -57,7 +58,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, mill.main.Router.EntryPoint[_])](..$overridesRoutes)" + val rhs = q"scala.Seq[(Int, mill.util.Router.EntryPoint[_])](..$overridesRoutes)" q"$lhs -> $rhs" } diff --git a/core/src/mill/define/Module.scala b/core/src/mill/define/Module.scala index a53ed345..bfc15191 100644 --- a/core/src/mill/define/Module.scala +++ b/core/src/mill/define/Module.scala @@ -2,12 +2,10 @@ package mill.define import java.lang.reflect.Modifier -import mill.main.Router.{EntryPoint, Overrides} import ammonite.ops.Path import scala.language.experimental.macros import scala.reflect.ClassTag -import scala.reflect.macros.blackbox /** * `Module` is a class meant to be extended by `trait`s *only*, in order to * propagate the implicit parameters forward to the final concrete diff --git a/core/src/mill/eval/Evaluator.scala b/core/src/mill/eval/Evaluator.scala index 347ad321..70fab152 100644 --- a/core/src/mill/eval/Evaluator.scala +++ b/core/src/mill/eval/Evaluator.scala @@ -2,7 +2,7 @@ package mill.eval import java.net.URLClassLoader -import mill.main.Router.EntryPoint +import mill.util.Router.EntryPoint import ammonite.ops._ import ammonite.runtime.SpecialClassLoader import mill.define.{Ctx => _, _} @@ -330,6 +330,12 @@ class Evaluator[T](val outPath: Path, object Evaluator{ + // 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 + // in directly) we are forced to pass it in via a ThreadLocal + val currentEvaluator = new ThreadLocal[mill.eval.Evaluator[_]] + case class Paths(out: Path, dest: Path, meta: Path, diff --git a/core/src/mill/main/MagicScopt.scala b/core/src/mill/main/MagicScopt.scala deleted file mode 100644 index acba57cb..00000000 --- a/core/src/mill/main/MagicScopt.scala +++ /dev/null @@ -1,49 +0,0 @@ -package mill.main -import mill.define.ExternalModule -import mill.main.ParseArgs - -object MagicScopt{ - // 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 - // in directly) we are forced to pass it in via a ThreadLocal - val currentEvaluator = new ThreadLocal[mill.eval.Evaluator[_]] - - case class Tasks[T](items: Seq[mill.define.NamedTask[T]]) -} -class EvaluatorScopt[T]() - extends scopt.Read[mill.eval.Evaluator[T]]{ - def arity = 0 - def reads = s => try{ - MagicScopt.currentEvaluator.get.asInstanceOf[mill.eval.Evaluator[T]] - } -} -class TargetScopt[T]() - extends scopt.Read[MagicScopt.Tasks[T]]{ - def arity = 0 - def reads = s => { - val rootModule = MagicScopt.currentEvaluator.get.rootModule - val d = rootModule.millDiscover - val (expanded, leftover) = ParseArgs(Seq(s)).fold(e => throw new Exception(e), identity) - val resolved = expanded.map{ - case (Some(scoping), segments) => - val moduleCls = rootModule.getClass.getClassLoader.loadClass(scoping.render + "$") - val externalRootModule = moduleCls.getField("MODULE$").get(moduleCls).asInstanceOf[ExternalModule] - val crossSelectors = segments.value.map { - case mill.define.Segment.Cross(x) => x.toList.map(_.toString) - case _ => Nil - } - mill.main.Resolve.resolve(segments.value.toList, externalRootModule, d, leftover, crossSelectors.toList, Nil) - case (None, segments) => - val crossSelectors = segments.value.map { - case mill.define.Segment.Cross(x) => x.toList.map(_.toString) - case _ => Nil - } - mill.main.Resolve.resolve(segments.value.toList, rootModule, d, leftover, crossSelectors.toList, Nil) - } - mill.util.EitherOps.sequence(resolved) match{ - case Left(s) => throw new Exception(s) - case Right(ts) => MagicScopt.Tasks(ts.flatten).asInstanceOf[MagicScopt.Tasks[T]] - } - } -}
\ No newline at end of file diff --git a/core/src/mill/main/MainModule.scala b/core/src/mill/main/MainModule.scala deleted file mode 100644 index fd46fb77..00000000 --- a/core/src/mill/main/MainModule.scala +++ /dev/null @@ -1,27 +0,0 @@ -package mill.main - -trait MainModule extends mill.Module{ - implicit def millDiscover: mill.define.Discover[_] - implicit def millScoptTargetReads[T] = new mill.main.TargetScopt[T]() - implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]() - def resolve(targets: mill.main.MagicScopt.Tasks[Any]*) = mill.T.command{ - targets.flatMap(_.items).foreach(println) - } - def all(evaluator: mill.eval.Evaluator[Any], - targets: mill.main.MagicScopt.Tasks[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[Any], - targets: mill.main.MagicScopt.Tasks[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) - } - } -} diff --git a/core/src/mill/main/MainRunner.scala b/core/src/mill/main/MainRunner.scala deleted file mode 100644 index 9004de39..00000000 --- a/core/src/mill/main/MainRunner.scala +++ /dev/null @@ -1,119 +0,0 @@ -package mill.main -import java.io.{InputStream, OutputStream, PrintStream} - -import ammonite.Main -import ammonite.interp.{Interpreter, Preprocessor} -import ammonite.ops.Path -import ammonite.util._ -import mill.define.Discover -import mill.eval.{Evaluator, PathRef} -import mill.util.PrintLogger -import upickle.Js - -/** - * Customized version of [[ammonite.MainRunner]], allowing us to run Mill - * `build.sc` scripts with mill-specific tweaks such as a custom - * `scriptCodeWrapper` or with a persistent evaluator between runs. - */ -class MainRunner(config: ammonite.main.Cli.Config, - show: Boolean, - outprintStream: PrintStream, - errPrintStream: PrintStream, - stdIn: InputStream) - extends ammonite.MainRunner( - config, outprintStream, errPrintStream, - stdIn, outprintStream, errPrintStream - ){ - - var lastEvaluator: Option[(Seq[(Path, Long)], Evaluator[Any])] = None - - override def runScript(scriptPath: Path, scriptArgs: List[String]) = - watchLoop( - isRepl = false, - printing = true, - mainCfg => { - val (result, interpWatched) = RunScript.runScript( - mainCfg.wd, - scriptPath, - mainCfg.instantiateInterpreter(), - scriptArgs, - lastEvaluator, - new PrintLogger( - colors != ammonite.util.Colors.BlackWhite, - colors, - if (show) errPrintStream else outprintStream, - errPrintStream, - errPrintStream - ) - ) - - result match{ - case Res.Success(data) => - val (eval, evaluationWatches, res) = data - - lastEvaluator = Some((interpWatched, eval)) - - (Res(res), interpWatched ++ evaluationWatches) - case _ => (result, interpWatched) - } - } - ) - - 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) -// } -// } - - true - - case _ => super.handleWatchRes(res, printing) - } - - } - override def initMain(isRepl: Boolean) = { - super.initMain(isRepl).copy( - scriptCodeWrapper = CustomCodeWrapper, - // Ammonite does not properly forward the wd from CliConfig to Main, so - // force forward it outselves - wd = config.wd - ) - } - object CustomCodeWrapper extends Preprocessor.CodeWrapper { - def top(pkgName: Seq[Name], imports: Imports, indexedWrapperName: Name) = { - val wrapName = indexedWrapperName.backticked - val literalPath = pprint.Util.literalize(config.wd.toString) - s""" - |package ${pkgName.head.encoded} - |package ${Util.encodeScalaSourcePath(pkgName.tail)} - |$imports - |import mill._ - |object $wrapName - |extends mill.define.BaseModule(ammonite.ops.Path($literalPath)) - |with $wrapName{ - | // Stub to make sure Ammonite has something to call after it evaluates a script, - | // even if it does nothing... - | def $$main() = Iterator[String]() - | - | implicit def millDiscover: mill.define.Discover[this.type] = mill.define.Discover[this.type] - | // Need to wrap the returned Module in Some(...) to make sure it - | // doesn't get picked up during reflective child-module discovery - | val millSelf = Some(this) - |} - | - |sealed trait $wrapName extends mill.main.MainModule{ - |""".stripMargin - } - - - def bottom(printCode: String, indexedWrapperName: Name, extraCode: String) = { - // We need to disable the `$main` method definition inside the wrapper class, - // because otherwise it might get picked up by Ammonite and run as a static - // class method, which blows up since it's defined as an instance method - "\n}" - } - } -} diff --git a/core/src/mill/main/ReplApplyHandler.scala b/core/src/mill/main/ReplApplyHandler.scala deleted file mode 100644 index 0849f2c8..00000000 --- a/core/src/mill/main/ReplApplyHandler.scala +++ /dev/null @@ -1,125 +0,0 @@ -package mill.main - - -import mill.define.Applicative.ApplyHandler -import mill.define.Segment.Label -import mill.define._ -import mill.eval.{Evaluator, Result} -import mill.util.Strict.Agg - -import scala.collection.mutable -object ReplApplyHandler{ - def apply[T](colors: ammonite.util.Colors, - pprinter0: pprint.PPrinter, - rootModule: mill.define.BaseModule, - discover: Discover[_]) = { - new ReplApplyHandler( - pprinter0, - new Evaluator( - ammonite.ops.pwd / 'out, - ammonite.ops.pwd / 'out, - rootModule, - discover, - new mill.util.PrintLogger( - colors != ammonite.util.Colors.BlackWhite, - colors, - System.out, - System.err, - System.err - ) - ) - ) - } -} -class ReplApplyHandler(pprinter0: pprint.PPrinter, - evaluator: Evaluator[_]) extends ApplyHandler[Task] { - // Evaluate classLoaderSig only once in the REPL to avoid busting caches - // as the user enters more REPL commands and changes the classpath - val classLoaderSig = Evaluator.classLoaderSig - override def apply[V](t: Task[V]) = { - val res = evaluator.evaluate(Agg(t)) - res.values match{ - case Seq(head: V) => head - case Nil => - val msg = new mutable.StringBuilder() - msg.append(res.failing.keyCount + " targets failed\n") - for((k, vs) <- res.failing.items){ - msg.append(k match{ - case Left(t) => "Anonymous Task\n" - case Right(k) => k.segments.render + "\n" - }) - - for(v <- vs){ - v match{ - case Result.Failure(m, _) => msg.append(m + "\n") - case Result.Exception(t, outerStack) => - msg.append( - t.toString + - t.getStackTrace.dropRight(outerStack.value.length).map("\n " + _).mkString + - "\n" - ) - - } - } - } - throw new Exception(msg.toString) - } - } - - val generatedEval = new EvalGenerated(evaluator) - - val millHandlers: PartialFunction[Any, pprint.Tree] = { - case c: Cross[_] => - pprint.Tree.Lazy( ctx => - Iterator(c.millOuterCtx.enclosing , ":", c.millOuterCtx.lineNum.toString, ctx.applyPrefixColor("\nChildren:").toString) ++ - c.items.iterator.map(x => - "\n (" + x._1.map(pprint.PPrinter.BlackWhite.apply(_)).mkString(", ") + ")" - ) - ) - case m: mill.Module if evaluator.rootModule.millInternal.modules.contains(m) => - pprint.Tree.Lazy( ctx => - Iterator(m.millInternal.millModuleEnclosing, ":", m.millInternal.millModuleLine.toString) ++ - (if (m.millInternal.reflect[mill.Module].isEmpty) Nil - else - ctx.applyPrefixColor("\nChildren:").toString +: - m.millInternal.reflect[mill.Module].map("\n ." + _.millOuterCtx.segment.pathSegments.mkString("."))) ++ - (evaluator.discover.value.get(m.getClass) match{ - case None => Nil - case Some(commands) => - ctx.applyPrefixColor("\nCommands:").toString +: commands.map{c => - "\n ." + c._2.name + "(" + - c._2.argSignatures.map(s => s.name + ": " + s.typeString).mkString(", ") + - ")()" - } - }) ++ - (if (m.millInternal.reflect[Target[_]].isEmpty) Nil - else { - Seq(ctx.applyPrefixColor("\nTargets:").toString) ++ - m.millInternal.reflect[Target[_]].sortBy(_.label).map(t => - "\n ." + t.label + "()" - ) - }) - - ) - case t: mill.define.Target[_] if evaluator.rootModule.millInternal.targets.contains(t) => - val seen = mutable.Set.empty[Task[_]] - def rec(t: Task[_]): Seq[Segments] = { - if (seen(t)) Nil // do nothing - else t match { - case t: Target[_] if evaluator.rootModule.millInternal.targets.contains(t) => - Seq(t.ctx.segments) - case _ => - seen.add(t) - t.inputs.flatMap(rec) - } - } - pprint.Tree.Lazy(ctx => - Iterator(t.ctx.enclosing, ":", t.ctx.lineNum.toString, "\n", ctx.applyPrefixColor("Inputs:").toString) ++ - t.inputs.iterator.flatMap(rec).map("\n " + _.render) - ) - - } - val pprinter = pprinter0.copy( - additionalHandlers = millHandlers orElse pprinter0.additionalHandlers - ) -} diff --git a/core/src/mill/main/Resolve.scala b/core/src/mill/main/Resolve.scala deleted file mode 100644 index 1932c241..00000000 --- a/core/src/mill/main/Resolve.scala +++ /dev/null @@ -1,143 +0,0 @@ -package mill.main - -import mill.define._ -import mill.define.TaskModule -import mill.main.Router.EntryPoint -import ammonite.util.{Res} - -object Resolve { - def resolve[T, V](remainingSelector: List[Segment], - obj: mill.Module, - discover: Discover[_], - rest: Seq[String], - remainingCrossSelectors: List[List[String]], - revSelectorsSoFar: List[Segment]): Either[String, Seq[NamedTask[Any]]] = { - - remainingSelector match{ - case Segment.Cross(_) :: Nil => Left("Selector cannot start with a [cross] segment") - case Segment.Label(last) :: Nil => - val target = - obj - .millInternal - .reflect[Target[_]] - .find(_.label == last) - .map(Right(_)) - - def shimArgsig[T](a: mill.main.Router.ArgSig[T, _]) = { - ammonite.main.Router.ArgSig[T]( - a.name, - a.typeString, - a.doc, - a.default - ) - } - def invokeCommand(target: mill.Module, name: String) = for{ - (cls, entryPoints) <- discover.value - if cls.isAssignableFrom(target.getClass) - ep <- entryPoints - if ep._2.name == name - } yield mill.main.Scripts.runMainMethod( - target, - ep._2.asInstanceOf[EntryPoint[mill.Module]], - ammonite.main.Scripts.groupArgs(rest.toList) - ) match{ - case Res.Success(v: Command[_]) => Right(v) - case Res.Failure(msg) => Left(msg) - case Res.Exception(ex, msg) => - val sw = new java.io.StringWriter() - ex.printStackTrace(new java.io.PrintWriter(sw)) - val prefix = if (msg.nonEmpty) msg + "\n" else msg - Left(prefix + sw.toString) - - } - - val runDefault = for{ - child <- obj.millInternal.reflectNestedObjects[mill.Module] - if child.millOuterCtx.segment == Segment.Label(last) - res <- child match{ - case taskMod: TaskModule => Some(invokeCommand(child, taskMod.defaultCommandName()).headOption) - case _ => None - } - } yield res - - val command = invokeCommand(obj, last).headOption - - command orElse target orElse runDefault.flatten.headOption match{ - case None => Left("Cannot resolve task " + - Segments((Segment.Label(last) :: revSelectorsSoFar).reverse:_*).render - ) - // Contents of `either` *must* be a `Task`, because we only select - // methods returning `Task` in the discovery process - case Some(either) => either.right.map(Seq(_)) - } - - - case head :: tail => - val newRevSelectorsSoFar = head :: revSelectorsSoFar - head match{ - case Segment.Label(singleLabel) => - if (singleLabel == "__") { - - val matching = - obj.millInternal.modules - .map(resolve(tail, _, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar)) - .collect{case Right(vs) => vs}.flatten - - if (matching.nonEmpty)Right(matching) - else Left("Cannot resolve module " + Segments(newRevSelectorsSoFar.reverse:_*).render) - } else if (singleLabel == "_") { - - val matching = - obj.millModuleDirectChildren - .map(resolve(tail, _, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar)) - .collect{case Right(vs) => vs}.flatten - - if (matching.nonEmpty)Right(matching) - else Left("Cannot resolve module " + Segments(newRevSelectorsSoFar.reverse:_*).render) - }else{ - - obj.millInternal.reflectNestedObjects[mill.Module].find{ - _.millOuterCtx.segment == Segment.Label(singleLabel) - } match{ - case Some(child: mill.Module) => resolve(tail, child, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar) - case None => Left("Cannot resolve module " + Segments(newRevSelectorsSoFar.reverse:_*).render) - } - } - - case Segment.Cross(cross) => - obj match{ - case c: Cross[_] => - if(cross == Seq("__")){ - val matching = - for ((k, v) <- c.items) - yield resolve(tail, v.asInstanceOf[mill.Module], discover, rest, remainingCrossSelectors, newRevSelectorsSoFar) - - val results = matching.collect{case Right(res) => res}.flatten - - if (results.isEmpty) Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render) - else Right(results) - } else if (cross.contains("_")){ - val matching = for { - (k, v) <- c.items - if k.length == cross.length - if k.zip(cross).forall { case (l, r) => l == r || r == "_" } - } yield resolve(tail, v.asInstanceOf[mill.Module], discover, rest, remainingCrossSelectors, newRevSelectorsSoFar) - - val results = matching.collect{case Right(res) => res}.flatten - - if (results.isEmpty) Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render) - else Right(results) - }else{ - c.itemMap.get(cross.toList) match{ - case Some(m: mill.Module) => resolve(tail, m, discover, rest, remainingCrossSelectors, newRevSelectorsSoFar) - case None => Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render) - } - } - case _ => Left("Cannot resolve cross " + Segments(newRevSelectorsSoFar.reverse:_*).render) - } - } - - case Nil => Left("Selector cannot be empty") - } - } -} diff --git a/core/src/mill/main/RunScript.scala b/core/src/mill/main/RunScript.scala deleted file mode 100644 index 17d520e7..00000000 --- a/core/src/mill/main/RunScript.scala +++ /dev/null @@ -1,231 +0,0 @@ -package mill.main - -import java.nio.file.NoSuchFileException - -import ammonite.interp.Interpreter -import ammonite.ops.{Path, read} -import ammonite.runtime.SpecialClassLoader -import ammonite.util.Util.CodeSource -import ammonite.util.{Name, Res, Util} -import mill.{PathRef, define} -import mill.define.{Discover, ExternalModule, Segment, Task} -import mill.eval.{Evaluator, Result} -import mill.util.{EitherOps, Logger} -import mill.util.Strict.Agg -import upickle.Js - -/** - * Custom version of ammonite.main.Scripts, letting us run the build.sc script - * directly without going through Ammonite's main-method/argument-parsing - * subsystem - */ -object RunScript{ - def runScript(wd: Path, - path: Path, - instantiateInterpreter: => Either[(Res.Failing, Seq[(Path, Long)]), ammonite.interp.Interpreter], - scriptArgs: Seq[String], - lastEvaluator: Option[(Seq[(Path, Long)], Evaluator[Any])], - log: Logger) - : (Res[(Evaluator[Any], Seq[(Path, Long)], Either[String, Seq[Js.Value]])], Seq[(Path, Long)]) = { - - val (evalRes, interpWatched) = lastEvaluator match{ - case Some((prevInterpWatchedSig, prevEvaluator)) - if watchedSigUnchanged(prevInterpWatchedSig) => - - (Res.Success(prevEvaluator), prevInterpWatchedSig) - - case _ => - instantiateInterpreter match{ - case Left((res, watched)) => (res, watched) - case Right(interp) => - interp.watch(path) - val eval = - for((mapping, discover) <- evaluateMapping(wd, path, interp)) - yield new Evaluator[Any]( - wd / 'out, wd / 'out, mapping, discover, log, - mapping.getClass.getClassLoader.asInstanceOf[SpecialClassLoader].classpathSignature - ) - - (eval, interp.watchedFiles) - } - } - - val evaluated = for{ - evaluator <- evalRes - (evalWatches, res) <- Res(evaluateTarget(evaluator, scriptArgs)) - } yield { - val alreadyStale = evalWatches.exists(p => p.sig != new PathRef(p.path, p.quick).sig) - // If the file changed between the creation of the original - // `PathRef` and the current moment, use random junk .sig values - // to force an immediate re-run. Otherwise calculate the - // pathSignatures the same way Ammonite would and hand over the - // values, so Ammonite can watch them and only re-run if they - // subsequently change - val evaluationWatches = - if (alreadyStale) evalWatches.map(_.path -> util.Random.nextLong()) - else evalWatches.map(p => p.path -> Interpreter.pathSignature(p.path)) - - (evaluator, evaluationWatches, res.map(_.flatMap(_._2))) - } - (evaluated, interpWatched) - } - - def watchedSigUnchanged(sig: Seq[(Path, Long)]) = { - sig.forall{case (p, l) => Interpreter.pathSignature(p) == l} - } - - def evaluateMapping(wd: Path, - path: Path, - interp: ammonite.interp.Interpreter): Res[(mill.define.BaseModule, Discover[Any])] = { - - val (pkg, wrapper) = Util.pathToPackageWrapper(Seq(), path relativeTo wd) - - for { - scriptTxt <- - try Res.Success(Util.normalizeNewlines(read(path))) - catch { case e: NoSuchFileException => Res.Failure("Script file not found: " + path) } - - processed <- interp.processModule( - scriptTxt, - CodeSource(wrapper, pkg, Seq(Name("ammonite"), Name("$file")), Some(path)), - autoImport = true, - extraCode = "", - hardcoded = true - ) - - buildClsName <- processed.blockInfo.lastOption match { - case Some(meta) => Res.Success(meta.id.wrapperPath) - case None => Res.Skip - } - - buildCls = interp - .evalClassloader - .loadClass(buildClsName) - - module <- try { - Util.withContextClassloader(interp.evalClassloader) { - Res.Success( - buildCls.getMethod("millSelf") - .invoke(null) - .asInstanceOf[Some[mill.define.BaseModule]] - .get - ) - } - } catch { - case e: Throwable => Res.Exception(e, "") - } - discover <- try { - Util.withContextClassloader(interp.evalClassloader) { - Res.Success( - buildCls.getMethod("millDiscover") - .invoke(module) - .asInstanceOf[Discover[Any]] - ) - } - } catch { - case e: Throwable => Res.Exception(e, "") - } -// _ <- Res(consistencyCheck(mapping)) - } yield (module, discover) - } - - def evaluateTarget[T](evaluator: Evaluator[T], scriptArgs: Seq[String]) = { - for { - parsed <- ParseArgs(scriptArgs) - (selectors, args) = parsed - targets <- { - val selected = selectors.map { case (scopedSel, sel) => - val (rootModule, discover) = scopedSel match{ - case None => (evaluator.rootModule, evaluator.discover) - case Some(scoping) => - val moduleCls = - evaluator.rootModule.getClass.getClassLoader.loadClass(scoping.render + "$") - - val rootModule = moduleCls.getField("MODULE$").get(moduleCls).asInstanceOf[ExternalModule] - (rootModule, rootModule.millDiscover) - } - val crossSelectors = sel.value.map { - case Segment.Cross(x) => x.toList.map(_.toString) - case _ => Nil - } - - try { - // We inject the `evaluator.rootModule` into the TargetScopt, rather - // than the `rootModule`, because even if you are running an external - // module we still want you to be able to resolve targets from your - // main build. Resolving targets from external builds as CLI arguments - // is not currently supported - mill.main.MagicScopt.currentEvaluator.set(evaluator) - mill.main.Resolve.resolve( - sel.value.toList, rootModule, - discover, - args, crossSelectors.toList, Nil - ) - } finally{ - mill.main.MagicScopt.currentEvaluator.set(null) - } - } - EitherOps.sequence(selected) - } - (watched, res) = evaluate( - evaluator, - Agg.from(targets.flatten.distinct) - ) - } yield (watched, res) - } - - def evaluate(evaluator: Evaluator[_], - targets: Agg[Task[Any]]): (Seq[PathRef], Either[String, Seq[(Any, Option[upickle.Js.Value])]]) = { - val evaluated = evaluator.evaluate(targets) - val watched = evaluated.results - .iterator - .collect { - case (t: define.Sources, Result.Success(p: Seq[PathRef])) => p - } - .flatten - .toSeq - - val errorStr = - (for((k, fs) <- evaluated.failing.items()) yield { - val ks = k match{ - case Left(t) => t.toString - case Right(t) => t.segments.render - } - val fss = fs.map{ - case Result.Exception(t, outerStack) => - t.toString + - t.getStackTrace.dropRight(outerStack.value.length).map("\n " + _).mkString - case Result.Failure(t, _) => t - } - s"$ks ${fss.mkString(", ")}" - }).mkString("\n") - - evaluated.failing.keyCount match { - case 0 => - val json = for(t <- targets.toSeq) yield { - t match { - case t: mill.define.NamedTask[_] => - val jsonFile = Evaluator - .resolveDestPaths(evaluator.outPath, t.ctx.segments) - .meta - val metadata = upickle.json.read(jsonFile.toIO) - Some(metadata(1)) - - case _ => None - } - } - - watched -> Right(evaluated.values.zip(json)) - case n => watched -> Left(s"$n targets failed\n$errorStr") - } - } - -// def consistencyCheck[T](mapping: Discovered.Mapping[T]): Either[String, Unit] = { -// val consistencyErrors = Discovered.consistencyCheck(mapping) -// if (consistencyErrors.nonEmpty) { -// Left(s"Failed Discovered.consistencyCheck: ${consistencyErrors.map(_.render)}") -// } else { -// Right(()) -// } -// } -} diff --git a/core/src/mill/modules/Jvm.scala b/core/src/mill/modules/Jvm.scala deleted file mode 100644 index 297dcf1f..00000000 --- a/core/src/mill/modules/Jvm.scala +++ /dev/null @@ -1,257 +0,0 @@ -package mill.modules - -import java.io.FileOutputStream -import java.lang.reflect.Modifier -import java.net.URLClassLoader -import java.nio.file.attribute.PosixFilePermission -import java.util.jar.{JarEntry, JarFile, JarOutputStream} - -import ammonite.ops._ -import mill.define.Task -import mill.eval.PathRef -import mill.util.{Ctx, Loose} -import mill.util.Ctx.Log -import mill.util.Loose.Agg -import upickle.default.{Reader, Writer} - -import scala.annotation.tailrec -import scala.collection.mutable -import scala.reflect.ClassTag - - -object Jvm { - - def interactiveSubprocess(mainClass: String, - classPath: Agg[Path], - jvmArgs: Seq[String] = Seq.empty, - envArgs: Map[String, String] = Map.empty, - mainArgs: Seq[String] = Seq.empty, - workingDir: Path = null): Unit = { - import ammonite.ops.ImplicitWd._ - val commandArgs = - Vector("java") ++ - jvmArgs ++ - Vector("-cp", classPath.mkString(":"), mainClass) ++ - mainArgs - - %.copy(envArgs = envArgs)(commandArgs)(workingDir) - } - - def runLocal(mainClass: String, - classPath: Agg[Path], - mainArgs: Seq[String] = Seq.empty) - (implicit ctx: Ctx): Unit = { - inprocess(classPath, classLoaderOverrideSbtTesting = false, cl => { - getMainMethod(mainClass, cl).invoke(null, mainArgs.toArray) - }) - } - - private def getMainMethod(mainClassName: String, cl: ClassLoader) = { - val mainClass = cl.loadClass(mainClassName) - val method = mainClass.getMethod("main", classOf[Array[String]]) - // jvm allows the actual main class to be non-public and to run a method in the non-public class, - // we need to make it accessible - method.setAccessible(true) - val modifiers = method.getModifiers - if (!Modifier.isPublic(modifiers)) - throw new NoSuchMethodException(mainClassName + ".main is not public") - if (!Modifier.isStatic(modifiers)) - throw new NoSuchMethodException(mainClassName + ".main is not static") - method - } - - - - def inprocess[T](classPath: Agg[Path], - classLoaderOverrideSbtTesting: Boolean, - body: ClassLoader => T): T = { - val cl = if (classLoaderOverrideSbtTesting) { - val outerClassLoader = getClass.getClassLoader - new URLClassLoader(classPath.map(_.toIO.toURI.toURL).toArray, null){ - override def findClass(name: String) = { - if (name.startsWith("sbt.testing.")){ - outerClassLoader.loadClass(name) - }else{ - super.findClass(name) - } - } - } - } else { - new URLClassLoader(classPath.map(_.toIO.toURI.toURL).toArray, null) - } - val oldCl = Thread.currentThread().getContextClassLoader - Thread.currentThread().setContextClassLoader(cl) - try { - body(cl) - }finally{ - Thread.currentThread().setContextClassLoader(oldCl) - cl.close() - } - } - - def subprocess(mainClass: String, - classPath: Agg[Path], - jvmArgs: Seq[String] = Seq.empty, - envArgs: Map[String, String] = Map.empty, - mainArgs: Seq[String] = Seq.empty, - workingDir: Path = null) - (implicit ctx: Ctx) = { - - val commandArgs = - Vector("java") ++ - jvmArgs ++ - Vector("-cp", classPath.mkString(":"), mainClass) ++ - mainArgs - - val workingDir1 = Option(workingDir).getOrElse(ctx.dest) - mkdir(workingDir1) - val builder = - new java.lang.ProcessBuilder() - .directory(workingDir1.toIO) - .command(commandArgs:_*) - .redirectOutput(ProcessBuilder.Redirect.PIPE) - .redirectError(ProcessBuilder.Redirect.PIPE) - - for((k, v) <- envArgs) builder.environment().put(k, v) - val proc = builder.start() - val stdout = proc.getInputStream - val stderr = proc.getErrorStream - val sources = Seq( - (stdout, Left(_: Bytes), ctx.log.outputStream), - (stderr, Right(_: Bytes),ctx.log.errorStream ) - ) - val chunks = mutable.Buffer.empty[Either[Bytes, Bytes]] - while( - // Process.isAlive doesn't exist on JDK 7 =/ - util.Try(proc.exitValue).isFailure || - stdout.available() > 0 || - stderr.available() > 0 - ){ - var readSomething = false - for ((subStream, wrapper, parentStream) <- sources){ - while (subStream.available() > 0){ - readSomething = true - val array = new Array[Byte](subStream.available()) - val actuallyRead = subStream.read(array) - chunks.append(wrapper(new ammonite.ops.Bytes(array))) - parentStream.write(array, 0, actuallyRead) - } - } - // if we did not read anything sleep briefly to avoid spinning - if(!readSomething) - Thread.sleep(2) - } - - if (proc.exitValue() != 0) throw new InteractiveShelloutException() - else ammonite.ops.CommandResult(proc.exitValue(), chunks) - } - - private def createManifest(mainClass: Option[String]) = { - val m = new java.util.jar.Manifest() - m.getMainAttributes.put(java.util.jar.Attributes.Name.MANIFEST_VERSION, "1.0") - m.getMainAttributes.putValue( "Created-By", "Scala mill" ) - mainClass.foreach( - m.getMainAttributes.put(java.util.jar.Attributes.Name.MAIN_CLASS, _) - ) - m - } - - def createJar(inputPaths: Agg[Path], mainClass: Option[String] = None) - (implicit ctx: Ctx.Dest): PathRef = { - val outputPath = ctx.dest / "out.jar" - rm(outputPath) - - val seen = mutable.Set.empty[RelPath] - seen.add("META-INF" / "MANIFEST.MF") - val jar = new JarOutputStream( - new FileOutputStream(outputPath.toIO), - createManifest(mainClass) - ) - - try{ - assert(inputPaths.forall(exists(_))) - for{ - p <- inputPaths - (file, mapping) <- - if (p.isFile) Iterator(p -> empty/p.last) - else ls.rec(p).filter(_.isFile).map(sub => sub -> sub.relativeTo(p)) - if !seen(mapping) - } { - seen.add(mapping) - val entry = new JarEntry(mapping.toString) - entry.setTime(file.mtime.toMillis) - jar.putNextEntry(entry) - jar.write(read.bytes(file)) - jar.closeEntry() - } - } finally { - jar.close() - } - - PathRef(outputPath) - } - - def createAssembly(inputPaths: Agg[Path], - mainClass: Option[String] = None, - prependShellScript: String = "") - (implicit ctx: Ctx.Dest): PathRef = { - val outputPath = ctx.dest / "out.jar" - rm(outputPath) - - if(inputPaths.nonEmpty) { - - val output = new FileOutputStream(outputPath.toIO) - - // Prepend shell script and make it executable - if (prependShellScript.nonEmpty) { - output.write((prependShellScript + "\n").getBytes) - val perms = java.nio.file.Files.getPosixFilePermissions(outputPath.toNIO) - perms.add(PosixFilePermission.GROUP_EXECUTE) - perms.add(PosixFilePermission.OWNER_EXECUTE) - perms.add(PosixFilePermission.OTHERS_EXECUTE) - java.nio.file.Files.setPosixFilePermissions(outputPath.toNIO, perms) - } - - val jar = new JarOutputStream( - output, - createManifest(mainClass) - ) - - val seen = mutable.Set("META-INF/MANIFEST.MF") - try{ - - - for{ - p <- inputPaths - if exists(p) - (file, mapping) <- - if (p.isFile) { - val jf = new JarFile(p.toIO) - import collection.JavaConverters._ - for(entry <- jf.entries().asScala if !entry.isDirectory) yield { - read.bytes(jf.getInputStream(entry)) -> entry.getName - } - } - else { - ls.rec(p).iterator - .filter(_.isFile) - .map(sub => read.bytes(sub) -> sub.relativeTo(p).toString) - } - if !seen(mapping) - } { - seen.add(mapping) - val entry = new JarEntry(mapping.toString) - jar.putNextEntry(entry) - jar.write(file) - jar.closeEntry() - } - } finally { - jar.close() - output.close() - } - - } - PathRef(outputPath) - } - -} diff --git a/core/src/mill/modules/Util.scala b/core/src/mill/modules/Util.scala deleted file mode 100644 index cef11859..00000000 --- a/core/src/mill/modules/Util.scala +++ /dev/null @@ -1,65 +0,0 @@ -package mill.modules - - -import ammonite.ops.{Path, RelPath, empty, mkdir, read} -import mill.eval.PathRef -import mill.util.Ctx - -object Util { - def download(url: String, dest: RelPath = "download")(implicit ctx: Ctx.Dest) = { - val out = ctx.dest / dest - - val website = new java.net.URI(url).toURL - val rbc = java.nio.channels.Channels.newChannel(website.openStream) - try{ - val fos = new java.io.FileOutputStream(out.toIO) - try{ - fos.getChannel.transferFrom(rbc, 0, java.lang.Long.MAX_VALUE) - PathRef(out) - } finally{ - fos.close() - } - } finally{ - rbc.close() - } - } - - def downloadUnpackZip(url: String, dest: RelPath = "unpacked") - (implicit ctx: Ctx.Dest) = { - - val tmpName = if (dest == empty / "tmp.zip") "tmp2.zip" else "tmp.zip" - val downloaded = download(url, tmpName) - unpackZip(downloaded.path, dest) - } - - def unpackZip(src: Path, dest: RelPath = "unpacked") - (implicit ctx: Ctx.Dest) = { - - val byteStream = read.getInputStream(src) - val zipStream = new java.util.zip.ZipInputStream(byteStream) - while({ - zipStream.getNextEntry match{ - case null => false - case entry => - if (!entry.isDirectory) { - val entryDest = ctx.dest / dest / RelPath(entry.getName) - mkdir(entryDest / ammonite.ops.up) - val fileOut = new java.io.FileOutputStream(entryDest.toString) - val buffer = new Array[Byte](4096) - while ( { - zipStream.read(buffer) match { - case -1 => false - case n => - fileOut.write(buffer, 0, n) - true - } - }) () - fileOut.close() - } - zipStream.closeEntry() - true - } - })() - PathRef(ctx.dest / dest) - } -} diff --git a/core/src/mill/package.scala b/core/src/mill/package.scala deleted file mode 100644 index 93916c8b..00000000 --- a/core/src/mill/package.scala +++ /dev/null @@ -1,12 +0,0 @@ -import mill.util.JsonFormatters - -package object mill extends JsonFormatters{ - val T = define.Target - type T[T] = define.Target[T] - val PathRef = mill.eval.PathRef - type PathRef = mill.eval.PathRef - type Module = define.Module - type Cross[T] = define.Cross[T] - type Agg[T] = util.Loose.Agg[T] - val Agg = util.Loose.Agg -} diff --git a/core/src/mill/main/ParseArgs.scala b/core/src/mill/util/ParseArgs.scala index bafcd907..315edabc 100644 --- a/core/src/mill/main/ParseArgs.scala +++ b/core/src/mill/util/ParseArgs.scala @@ -1,6 +1,5 @@ -package mill.main +package mill.util -import mill.util.EitherOps import fastparse.all._ import mill.define.{Segment, Segments} diff --git a/core/src/mill/main/Router.scala b/core/src/mill/util/Router.scala index 935ffc72..f628730b 100644 --- a/core/src/mill/main/Router.scala +++ b/core/src/mill/util/Router.scala @@ -1,12 +1,12 @@ -package mill.main - +package mill.util import ammonite.main.Compat +import language.experimental.macros 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 @@ -24,7 +24,7 @@ object Router{ implicit def generate: Overrides = macro impl def impl(c: Context): c.Tree = { import c.universe._ - q"new _root_.mill.main.Router.Overrides(${c.internal.enclosingOwner.overrides.length})" + q"new _root_.mill.util.Router.Overrides(${c.internal.enclosingOwner.overrides.length})" } } @@ -262,8 +262,8 @@ object Router{ } def makeReadCall(dict: Map[String, String], - default: => Option[Any], - arg: ArgSig[_, _]) = { + default: => Option[Any], + arg: ArgSig[_, _]) = { read(dict, default, arg, arg.reads.reads(_)) } def makeReadVarargsCall(arg: ArgSig[_, _], values: Seq[String]) = { @@ -271,6 +271,7 @@ object Router{ } } + class Router [C <: Context](val c: C) { import c.universe._ def getValsOrMeths(curCls: Type): Iterable[MethodSymbol] = { @@ -365,7 +366,7 @@ class Router [C <: Context](val c: C) { } val argSig = q""" - mill.main.Router.ArgSig[$curCls, $docUnwrappedType]( + mill.util.Router.ArgSig[$curCls, $docUnwrappedType]( ${arg.name.toString}, ${docUnwrappedType.toString + (if(vararg) "*" else "")}, $docTree, @@ -375,12 +376,12 @@ class Router [C <: Context](val c: C) { val reader = if(vararg) q""" - mill.main.Router.makeReadVarargsCall( + mill.util.Router.makeReadVarargsCall( $argSig, $extrasSymbol ) """ else q""" - mill.main.Router.makeReadCall( + mill.util.Router.makeReadCall( $argListSymbol, $default, $argSig @@ -403,7 +404,7 @@ class Router [C <: Context](val c: C) { val res = q""" - mill.main.Router.EntryPoint[$curCls]( + mill.util.Router.EntryPoint[$curCls]( ${meth.name.toString}, scala.Seq(..$argSigs), ${methodDoc match{ @@ -412,12 +413,12 @@ class Router [C <: Context](val c: C) { }}, ${varargs.contains(true)}, ($baseArgSym: $curCls, $argListSymbol: Map[String, String], $extrasSymbol: Seq[String]) => - mill.main.Router.validate(Seq(..$readArgs)) match{ - case mill.main.Router.Result.Success(List(..$argNames)) => - mill.main.Router.Result.Success( + mill.util.Router.validate(Seq(..$readArgs)) match{ + case mill.util.Router.Result.Success(List(..$argNames)) => + mill.util.Router.Result.Success( $baseArgSym.${meth.name.toTermName}(..$argNameCasts) ) - case x: mill.main.Router.Result.Error => x + case x: mill.util.Router.Result.Error => x }, ammonite.main.Router.Overrides() ) @@ -439,4 +440,3 @@ class Router [C <: Context](val c: C) { } } } - diff --git a/core/src/mill/main/Scripts.scala b/core/src/mill/util/Scripts.scala index 334c610f..7dde8252 100644 --- a/core/src/mill/main/Scripts.scala +++ b/core/src/mill/util/Scripts.scala @@ -1,14 +1,14 @@ -package mill.main -import java.nio.file.NoSuchFileException +package mill.util +import java.nio.file.NoSuchFileException -import mill.main.Router.{ArgSig, EntryPoint} import ammonite.ops._ import ammonite.runtime.Evaluator.AmmoniteExit import ammonite.util.Name.backtickWrap import ammonite.util.Util.CodeSource import ammonite.util.{Name, Res, Util} import fastparse.utils.Utils._ +import mill.util.Router.{ArgSig, EntryPoint} /** * Logic around using Ammonite as a script-runner; invoking scripts via the diff --git a/core/src/mill/util/Watched.scala b/core/src/mill/util/Watched.scala new file mode 100644 index 00000000..f1ef4fee --- /dev/null +++ b/core/src/mill/util/Watched.scala @@ -0,0 +1,8 @@ +package mill.util + +import mill.eval.PathRef + +case class Watched[T](value: T, watched: Seq[PathRef]) +object Watched{ + implicit def readWrite[T: upickle.default.ReadWriter] = upickle.default.macroRW[Watched[T]] +} diff --git a/core/test/resources/examples/javac/build.sc b/core/test/resources/examples/javac/build.sc deleted file mode 100644 index 0783ac17..00000000 --- a/core/test/resources/examples/javac/build.sc +++ /dev/null @@ -1,23 +0,0 @@ -import ammonite.ops._ -import mill.T -import mill.eval.JavaCompileJarTests.compileAll -import mill.eval.PathRef -import mill.modules.Jvm -import mill.util.Loose - -def sourceRootPath = millSourcePath / 'src -def resourceRootPath = millSourcePath / 'resources - -// sourceRoot -> allSources -> classFiles -// | -// v -// resourceRoot ----> jar -def sourceRoot = T.sources{ sourceRootPath } -def resourceRoot = T.sources{ resourceRootPath } -def allSources = T{ sourceRoot().flatMap(p => ls.rec(p.path)).map(PathRef(_)) } -def classFiles = T{ compileAll(allSources()) } -def jar = T{ Jvm.createJar(Loose.Agg(classFiles().path) ++ resourceRoot().map(_.path)) } - -def run(mainClsName: String) = T.command{ - %%('java, "-cp", classFiles().path, mainClsName)(T.ctx().dest) -} diff --git a/core/test/resources/examples/javac/resources/hello.txt b/core/test/resources/examples/javac/resources/hello.txt deleted file mode 100644 index 5e1c309d..00000000 --- a/core/test/resources/examples/javac/resources/hello.txt +++ /dev/null @@ -1 +0,0 @@ -Hello World
\ No newline at end of file diff --git a/core/test/resources/examples/javac/src/Bar.java b/core/test/resources/examples/javac/src/Bar.java deleted file mode 100644 index 4e30c89b..00000000 --- a/core/test/resources/examples/javac/src/Bar.java +++ /dev/null @@ -1,4 +0,0 @@ -package test; -public class Bar{ - static int value = 271828; -}
\ No newline at end of file diff --git a/core/test/resources/examples/javac/src/Foo.java b/core/test/resources/examples/javac/src/Foo.java deleted file mode 100644 index e694f9fa..00000000 --- a/core/test/resources/examples/javac/src/Foo.java +++ /dev/null @@ -1,7 +0,0 @@ -package test; -public class Foo{ - static int value = 31337; - public static void main(String[] args){ - System.out.println(value + Bar.value); - } -}
\ No newline at end of file diff --git a/core/test/src/mill/TestMain.scala b/core/test/src/mill/TestMain.scala deleted file mode 100644 index 80e7e627..00000000 --- a/core/test/src/mill/TestMain.scala +++ /dev/null @@ -1,6 +0,0 @@ -package mill - -object TestMain { - def main(args: Array[String]): Unit = { - } -} diff --git a/core/test/src/mill/UTestFramework.scala b/core/test/src/mill/UTestFramework.scala deleted file mode 100644 index 6c0d5191..00000000 --- a/core/test/src/mill/UTestFramework.scala +++ /dev/null @@ -1,11 +0,0 @@ -package mill - -class UTestFramework extends utest.runner.Framework { - override def exceptionStackFrameHighlighter(s: StackTraceElement) = { - s.getClassName.startsWith("mill.") - } - override def setup() = { - import ammonite.ops._ - rm(pwd / 'target / 'workspace) - } -} diff --git a/core/test/src/mill/define/ApplicativeTests.scala b/core/test/src/mill/define/ApplicativeTests.scala deleted file mode 100644 index 72b715bb..00000000 --- a/core/test/src/mill/define/ApplicativeTests.scala +++ /dev/null @@ -1,125 +0,0 @@ -package mill.define - -import mill.define.Applicative.ImplicitStub -import utest._ - -import scala.annotation.compileTimeOnly -import scala.language.experimental.macros - - -object ApplicativeTests extends TestSuite { - implicit def optionToOpt[T](o: Option[T]): Opt[T] = new Opt(o) - class Opt[T](val self: Option[T]) extends Applicative.Applyable[Option, T] - object Opt extends OptGenerated with Applicative.Applyer[Opt, Option, Applicative.Id, String]{ - - val injectedCtx = "helloooo" - def underlying[A](v: Opt[A]) = v.self - def apply[T](t: T): Option[T] = macro Applicative.impl[Option, T, String] - - def mapCtx[A, B](a: Option[A])(f: (A, String) => B): Option[B] = a.map(f(_, injectedCtx)) - def zip() = Some(()) - def zip[A](a: Option[A]) = a.map(Tuple1(_)) - } - class Counter{ - var value = 0 - def apply() = { - value += 1 - value - } - } - @compileTimeOnly("Target.ctx() can only be used with a T{...} block") - @ImplicitStub - implicit def taskCtx: String = ??? - - val tests = Tests{ - - 'selfContained - { - - 'simple - assert(Opt("lol " + 1) == Some("lol 1")) - 'singleSome - assert(Opt("lol " + Some("hello")()) == Some("lol hello")) - 'twoSomes - assert(Opt(Some("lol ")() + Some("hello")()) == Some("lol hello")) - 'singleNone - assert(Opt("lol " + None()) == None) - 'twoNones - assert(Opt("lol " + None() + None()) == None) - } - 'context - { - assert(Opt(Opt.ctx() + Some("World")()) == Some("hellooooWorld")) - } - 'capturing - { - val lol = "lol " - def hell(o: String) = "hell" + o - 'simple - assert(Opt(lol + 1) == Some("lol 1")) - 'singleSome - assert(Opt(lol + Some(hell("o"))()) == Some("lol hello")) - 'twoSomes - assert(Opt(Some(lol)() + Some(hell("o"))()) == Some("lol hello")) - 'singleNone - assert(Opt(lol + None()) == None) - 'twoNones - assert(Opt(lol + None() + None()) == None) - } - 'allowedLocalDef - { - // Although x is defined inside the Opt{...} block, it is also defined - // within the LHS of the Applyable#apply call, so it is safe to life it - // out into the `zipMap` arguments list. - val res = Opt{ "lol " + Some("hello").flatMap(x => Some(x)).apply() } - assert(res == Some("lol hello")) - } - 'upstreamAlwaysEvaluated - { - // Whether or not control-flow reaches the Applyable#apply call inside an - // Opt{...} block, we always evaluate the LHS of the Applyable#apply - // because it gets lifted out of any control flow statements - val counter = new Counter() - def up = Opt{ "lol " + counter() } - val down = Opt{ if ("lol".length > 10) up() else "fail" } - assert( - down == Some("fail"), - counter.value == 1 - ) - } - 'upstreamEvaluatedOnlyOnce - { - // Even if control-flow reaches the Applyable#apply call more than once, - // it only gets evaluated once due to its lifting out of the Opt{...} block - val counter = new Counter() - def up = Opt{ "lol " + counter() } - def runTwice[T](t: => T) = (t, t) - val down = Opt{ runTwice(up()) } - assert( - down == Some(("lol 1", "lol 1")), - counter.value == 1 - ) - } - 'evaluationsInsideLambdasWork - { - // This required some fiddling with owner chains inside the macro to get - // working, so ensure it doesn't regress - val counter = new Counter() - def up = Opt{ "hello" + counter() } - val down1 = Opt{ (() => up())() } - val down2 = Opt{ Seq(1, 2, 3).map(n => up() * n) } - assert( - down1 == Some("hello1"), - down2 == Some(Seq("hello2", "hello2hello2", "hello2hello2hello2")) - ) - } - 'appliesEvaluatedOncePerLexicalCallsite - { - // If you have multiple Applyable#apply() lexically in the source code of - // your Opt{...} call, each one gets evaluated once, even if the LHS of each - // apply() call is identical. It's up to the downstream zipMap() - // implementation to decide if it wants to dedup them or do other things. - val counter = new Counter() - def up = Opt{ "hello" + counter() } - val down = Opt{ Seq(1, 2, 3).map(n => n + up() + up()) } - assert(down == Some(Seq("1hello1hello2", "2hello1hello2", "3hello1hello2"))) - } - 'appliesEvaluateBeforehand - { - // Every Applyable#apply() within a Opt{...} block evaluates before any - // other logic within that block, even if they would happen first in the - // normal Scala evaluation order - val counter = new Counter() - def up = Opt{ counter() } - val down = Opt{ - val res = counter() - val one = up() - val two = up() - val three = up() - (res, one, two, three) - } - assert(down == Some((4, 1, 2, 3))) - } - } -} diff --git a/core/test/src/mill/define/BasePathTests.scala b/core/test/src/mill/define/BasePathTests.scala deleted file mode 100644 index 1f5b4037..00000000 --- a/core/test/src/mill/define/BasePathTests.scala +++ /dev/null @@ -1,68 +0,0 @@ -package mill.define - -import mill.util.{TestGraphs, TestUtil} -import utest._ -import ammonite.ops._ -import mill.{Module, T} -object BasePathTests extends TestSuite{ - val testGraphs = new TestGraphs - val tests = Tests{ - def check[T <: Module](m: T)(f: T => Module, segments: String*) = { - val remaining = f(m).millSourcePath.relativeTo(m.millSourcePath).segments - assert(remaining == segments) - } - 'singleton - { - check(testGraphs.singleton)(identity) - } - 'separateGroups - { - check(TestGraphs.triangleTask)(identity) - } - 'TraitWithModuleObject - { - check(TestGraphs.TraitWithModuleObject)( - _.TraitModule, - "TraitModule" - ) - } - 'nestedModuleNested - { - check(TestGraphs.nestedModule)(_.nested, "nested") - } - 'nestedModuleInstance - { - check(TestGraphs.nestedModule)(_.classInstance, "classInstance") - } - 'singleCross - { - check(TestGraphs.singleCross)(_.cross, "cross") - check(TestGraphs.singleCross)(_.cross("210"), "cross", "210") - check(TestGraphs.singleCross)(_.cross("211"), "cross", "211") - } - 'doubleCross - { - check(TestGraphs.doubleCross)(_.cross, "cross") - check(TestGraphs.doubleCross)(_.cross("210", "jvm"), "cross", "210", "jvm") - check(TestGraphs.doubleCross)(_.cross("212", "js"), "cross", "212", "js") - } - 'nestedCrosses - { - check(TestGraphs.nestedCrosses)(_.cross, "cross") - check(TestGraphs.nestedCrosses)( - _.cross("210").cross2("js"), - "cross", "210", "cross2", "js" - ) - } - 'overriden - { - object overridenBasePath extends TestUtil.BaseModule { - override def millSourcePath = pwd / 'overridenBasePathRootValue - object nested extends Module{ - override def millSourcePath = super.millSourcePath / 'overridenBasePathNested - object nested extends Module{ - override def millSourcePath = super.millSourcePath / 'overridenBasePathDoubleNested - } - } - } - assert( - overridenBasePath.millSourcePath == pwd / 'overridenBasePathRootValue, - overridenBasePath.nested.millSourcePath == pwd / 'overridenBasePathRootValue / 'nested / 'overridenBasePathNested, - overridenBasePath.nested.nested.millSourcePath == pwd / 'overridenBasePathRootValue / 'nested / 'overridenBasePathNested / 'nested / 'overridenBasePathDoubleNested - ) - } - - } -} - diff --git a/core/test/src/mill/define/CacherTests.scala b/core/test/src/mill/define/CacherTests.scala deleted file mode 100644 index 606de846..00000000 --- a/core/test/src/mill/define/CacherTests.scala +++ /dev/null @@ -1,76 +0,0 @@ -package mill.define - -import ammonite.ops.pwd -import mill.util.{DummyLogger, TestEvaluator, TestUtil} -import mill.util.Strict.Agg -import mill.T -import mill.eval.Result.Success -import utest._ -import utest.framework.TestPath -import mill.util.TestEvaluator.implicitDisover - -object CacherTests extends TestSuite{ - object Base extends Base - trait Base extends TestUtil.BaseModule{ - def value = T{ 1 } - def result = T{ Success(1) } - } - object Middle extends Middle - trait Middle extends Base{ - def value = T{ super.value() + 2} - def overriden = T{ super.value()} - } - object Terminal extends Terminal - trait Terminal extends Middle{ - override def value = T{ super.value() + 4} - } - - val tests = Tests{ - def eval[T <: TestUtil.BaseModule, V](mapping: T, v: Task[V]) - (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 - { - Predef.assert(Base.value eq Base.value) - Predef.assert(eval(Base, Base.value) == 1) - } - - 'resultDefIsCached - { - Predef.assert(Base.result eq Base.result) - Predef.assert(eval(Base, Base.result) == 1) - } - - - 'overridingDefIsAlsoCached - { - Predef.assert(eval(Middle, Middle.value) == 3) - Predef.assert(Middle.value eq Middle.value) - } - - 'overridenDefRemainsAvailable - { - Predef.assert(eval(Middle, Middle.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 - // - // 'overrideOutsideModuleFails - { - // compileError(""" - // trait Foo{ - // def x = 1 - // } - // object Bar extends Foo{ - // def x = 2 - // } - // """) - // } - } -} - diff --git a/core/test/src/mill/define/DiscoverTests.scala b/core/test/src/mill/define/DiscoverTests.scala deleted file mode 100644 index 7621169a..00000000 --- a/core/test/src/mill/define/DiscoverTests.scala +++ /dev/null @@ -1,60 +0,0 @@ -package mill.define - -import mill.util.TestGraphs -import utest._ - -object DiscoverTests extends TestSuite{ - val testGraphs = new TestGraphs - val tests = Tests{ - def check[T <: Module](m: T)(targets: (T => Target[_])*) = { - val discovered = m.millInternal.targets - val expected = targets.map(_(m)).toSet - assert(discovered == expected) - } - 'singleton - { - check(testGraphs.singleton)(_.single) - } - 'separateGroups - { - check(TestGraphs.triangleTask)(_.left, _.right) - } - 'TraitWithModuleObject - { - check(TestGraphs.TraitWithModuleObject)(_.TraitModule.testFramework) - } - 'nestedModule - { - check(TestGraphs.nestedModule)(_.single, _.nested.single, _.classInstance.single) - } - 'singleCross - { - check(TestGraphs.singleCross)( - _.cross("210").suffix, - _.cross("211").suffix, - _.cross("212").suffix - ) - } - 'doubleCross - { - check(TestGraphs.doubleCross)( - _.cross("210", "jvm").suffix, - _.cross("210", "js").suffix, - _.cross("211", "jvm").suffix, - _.cross("211", "js").suffix, - _.cross("212", "jvm").suffix, - _.cross("212", "js").suffix, - _.cross("212", "native").suffix - ) - } - 'nestedCrosses - { - check(TestGraphs.nestedCrosses)( - _.cross("210").cross2("jvm").suffix, - _.cross("210").cross2("js").suffix, - _.cross("210").cross2("native").suffix, - _.cross("211").cross2("jvm").suffix, - _.cross("211").cross2("js").suffix, - _.cross("211").cross2("native").suffix, - _.cross("212").cross2("jvm").suffix, - _.cross("212").cross2("js").suffix, - _.cross("212").cross2("native").suffix - ) - } - - } -} - diff --git a/core/test/src/mill/define/GraphTests.scala b/core/test/src/mill/define/GraphTests.scala deleted file mode 100644 index 7e6680be..00000000 --- a/core/test/src/mill/define/GraphTests.scala +++ /dev/null @@ -1,199 +0,0 @@ -package mill.define - - -import mill.eval.Evaluator -import mill.util.{TestGraphs, TestUtil} -import utest._ -import mill.util.Strict.Agg -object GraphTests extends TestSuite{ - - val tests = Tests{ - - - val graphs = new TestGraphs() - import graphs._ - import TestGraphs._ - - 'topoSortedTransitiveTargets - { - def check(targets: Agg[Task[_]], expected: Agg[Task[_]]) = { - val result = Graph.topoSorted(Graph.transitiveTargets(targets)).values - TestUtil.checkTopological(result) - assert(result == expected) - } - - 'singleton - check( - targets = Agg(singleton.single), - expected = Agg(singleton.single) - ) - 'pair - check( - targets = Agg(pair.down), - expected = Agg(pair.up, pair.down) - ) - 'anonTriple - check( - targets = Agg(anonTriple.down), - expected = Agg(anonTriple.up, anonTriple.down.inputs(0), anonTriple.down) - ) - 'diamond - check( - targets = Agg(diamond.down), - expected = Agg(diamond.up, diamond.left, diamond.right, diamond.down) - ) - 'anonDiamond - check( - targets = Agg(diamond.down), - expected = Agg( - diamond.up, - diamond.down.inputs(0), - diamond.down.inputs(1), - diamond.down - ) - ) - 'defCachedDiamond - check( - targets = Agg(defCachedDiamond.down), - expected = Agg( - defCachedDiamond.up.inputs(0), - defCachedDiamond.up, - defCachedDiamond.down.inputs(0).inputs(0).inputs(0), - defCachedDiamond.down.inputs(0).inputs(0), - defCachedDiamond.down.inputs(0).inputs(1).inputs(0), - defCachedDiamond.down.inputs(0).inputs(1), - defCachedDiamond.down.inputs(0), - defCachedDiamond.down - ) - ) - 'bigSingleTerminal - { - val result = Graph.topoSorted(Graph.transitiveTargets(Agg(bigSingleTerminal.j))).values - TestUtil.checkTopological(result) - assert(result.size == 28) - } - } - - 'groupAroundNamedTargets - { - def check[T, R <: Target[Int]](base: T) - (target: T => R, - important0: Agg[T => Target[_]], - expected: Agg[(R, Int)]) = { - - val topoSorted = Graph.topoSorted(Graph.transitiveTargets(Agg(target(base)))) - - val important = important0.map(_ (base)) - val grouped = Graph.groupAroundImportantTargets(topoSorted) { - case t: Target[_] if important.contains(t) => t - } - val flattened = Agg.from(grouped.values().flatMap(_.items)) - - TestUtil.checkTopological(flattened) - for ((terminal, expectedSize) <- expected) { - val grouping = grouped.lookupKey(terminal) - assert( - grouping.size == expectedSize, - grouping.flatMap(_.asTarget: Option[Target[_]]).filter(important.contains) == Agg(terminal) - ) - } - } - - 'singleton - check(singleton)( - _.single, - Agg(_.single), - Agg(singleton.single -> 1) - ) - 'pair - check(pair)( - _.down, - Agg(_.up, _.down), - Agg(pair.up -> 1, pair.down -> 1) - ) - 'anonTriple - check(anonTriple)( - _.down, - Agg(_.up, _.down), - Agg(anonTriple.up -> 1, anonTriple.down -> 2) - ) - 'diamond - check(diamond)( - _.down, - Agg(_.up, _.left, _.right, _.down), - Agg( - diamond.up -> 1, - diamond.left -> 1, - diamond.right -> 1, - diamond.down -> 1 - ) - ) - - 'defCachedDiamond - check(defCachedDiamond)( - _.down, - Agg(_.up, _.left, _.right, _.down), - Agg( - defCachedDiamond.up -> 2, - defCachedDiamond.left -> 2, - defCachedDiamond.right -> 2, - defCachedDiamond.down -> 2 - ) - ) - - 'anonDiamond - check(anonDiamond)( - _.down, - Agg(_.down, _.up), - Agg( - anonDiamond.up -> 1, - anonDiamond.down -> 3 - ) - ) - 'bigSingleTerminal - check(bigSingleTerminal)( - _.j, - Agg(_.a, _.b, _.e, _.f, _.i, _.j), - Agg( - bigSingleTerminal.a -> 3, - bigSingleTerminal.b -> 2, - bigSingleTerminal.e -> 9, - bigSingleTerminal.i -> 6, - bigSingleTerminal.f -> 4, - bigSingleTerminal.j -> 4 - ) - ) - } - 'multiTerminalGroupCounts - { - def countGroups(goals: Task[_]*) = { - - val topoSorted = Graph.topoSorted( - Graph.transitiveTargets(Agg.from(goals)) - ) - val grouped = Graph.groupAroundImportantTargets(topoSorted) { - case t: NamedTask[Any] => t - case t if goals.contains(t) => t - } - grouped.keyCount - } - - 'separateGroups - { - import separateGroups._ - val groupCount = countGroups(right, left) - assert(groupCount == 3) - } - - 'triangleTask - { - // Make sure the following graph ends up as a single group, since although - // `right` depends on `left`, both of them depend on the un-cached `task` - // which would force them both to re-compute every time `task` changes - import triangleTask._ - val groupCount = countGroups(right, left) - assert(groupCount == 2) - } - - - 'multiTerminalGroup - { - // Make sure the following graph ends up as two groups - import multiTerminalGroup._ - val groupCount = countGroups(right, left) - assert(groupCount == 2) - } - - - 'multiTerminalBoundary - { - // Make sure the following graph ends up as a three groups: one for - // each cached target, and one for the downstream task we are running - import multiTerminalBoundary._ - val groupCount = countGroups(task2) - assert(groupCount == 3) - } - } - - - } -} diff --git a/core/test/src/mill/define/MacroErrorTests.scala b/core/test/src/mill/define/MacroErrorTests.scala deleted file mode 100644 index a389feaa..00000000 --- a/core/test/src/mill/define/MacroErrorTests.scala +++ /dev/null @@ -1,83 +0,0 @@ -package mill.define - -import utest._ -import mill.{T, Module} -import mill.util.TestUtil -object MacroErrorTests extends TestSuite{ - - val tests = Tests{ - - 'errors{ - val expectedMsg = - "T{} members must be defs defined in a Cacher class/trait/object body" - - val err = compileError("object Foo extends TestUtil.BaseModule{ val x = T{1} }") - assert(err.msg == expectedMsg) - } - - 'badTmacro - { - // Make sure we can reference values from outside the T{...} block as part - // of our `Target#apply()` calls, but we cannot reference any values that - // come from inside the T{...} block - 'pos - { - val e = compileError(""" - val a = T{ 1 } - val arr = Array(a) - val b = { - val c = 0 - T{ - arr(c)() - } - } - """) - assert(e.msg.contains( - "Modules, Targets and Commands can only be defined within a mill Module") - ) - } - 'neg - { - - val expectedMsg = - "Target#apply() call cannot use `value n` defined within the T{...} block" - val err = compileError("""new Module{ - def a = T{ 1 } - val arr = Array(a) - def b = { - T{ - val n = 0 - arr(n)() - } - } - }""") - assert(err.msg == expectedMsg) - } - 'neg2 - { - - val expectedMsg = - "Target#apply() call cannot use `value x` defined within the T{...} block" - val err = compileError("""new Module{ - def a = T{ 1 } - val arr = Array(a) - def b = { - T{ - arr.map{x => x()} - } - } - }""") - assert(err.msg == expectedMsg) - } - 'neg3{ - val borkedCachedDiamond1 = utest.compileError(""" - object borkedCachedDiamond1 { - def up = T{ TestUtil.test() } - def left = T{ TestUtil.test(up) } - def right = T{ TestUtil.test(up) } - def down = T{ TestUtil.test(left, right) } - } - """) - assert(borkedCachedDiamond1.msg.contains( - "Modules, Targets and Commands can only be defined within a mill Module") - ) - } - } - } -} diff --git a/core/test/src/mill/eval/CrossTests.scala b/core/test/src/mill/eval/CrossTests.scala deleted file mode 100644 index aa12e180..00000000 --- a/core/test/src/mill/eval/CrossTests.scala +++ /dev/null @@ -1,56 +0,0 @@ -package mill.eval - -import ammonite.ops._ -import mill.define.Discover -import mill.util.TestEvaluator -import mill.util.TestEvaluator.implicitDisover -import mill.util.TestGraphs.{crossResolved, doubleCross, nestedCrosses, singleCross} -import utest._ -object CrossTests extends TestSuite{ - val tests = Tests{ - 'singleCross - { - val check = new TestEvaluator(singleCross) - - val Right(("210", 1)) = check.apply(singleCross.cross("210").suffix) - val Right(("211", 1)) = check.apply(singleCross.cross("211").suffix) - val Right(("212", 1)) = check.apply(singleCross.cross("212").suffix) - } - - 'crossResolved - { - val check = new TestEvaluator(crossResolved) - - val Right(("2.10", 1)) = check.apply(crossResolved.foo("2.10").suffix) - val Right(("2.11", 1)) = check.apply(crossResolved.foo("2.11").suffix) - val Right(("2.12", 1)) = check.apply(crossResolved.foo("2.12").suffix) - - val Right(("_2.10", 1)) = check.apply(crossResolved.bar("2.10").longSuffix) - val Right(("_2.11", 1)) = check.apply(crossResolved.bar("2.11").longSuffix) - val Right(("_2.12", 1)) = check.apply(crossResolved.bar("2.12").longSuffix) - } - - - 'doubleCross - { - val check = new TestEvaluator(doubleCross) - - val Right(("210_jvm", 1)) = check.apply(doubleCross.cross("210", "jvm").suffix) - val Right(("210_js", 1)) = check.apply(doubleCross.cross("210", "js").suffix) - val Right(("211_jvm", 1)) = check.apply(doubleCross.cross("211", "jvm").suffix) - val Right(("211_js", 1)) = check.apply(doubleCross.cross("211", "js").suffix) - val Right(("212_jvm", 1)) = check.apply(doubleCross.cross("212", "jvm").suffix) - val Right(("212_js", 1)) = check.apply(doubleCross.cross("212", "js").suffix) - val Right(("212_native", 1)) = check.apply(doubleCross.cross("212", "native").suffix) - } - - 'nestedCrosses - { - val check = new TestEvaluator(nestedCrosses) - - val Right(("210_jvm", 1)) = check.apply(nestedCrosses.cross("210").cross2("jvm").suffix) - val Right(("210_js", 1)) = check.apply(nestedCrosses.cross("210").cross2("js").suffix) - val Right(("211_jvm", 1)) = check.apply(nestedCrosses.cross("211").cross2("jvm").suffix) - val Right(("211_js", 1)) = check.apply(nestedCrosses.cross("211").cross2("js").suffix) - val Right(("212_jvm", 1)) = check.apply(nestedCrosses.cross("212").cross2("jvm").suffix) - val Right(("212_js", 1)) = check.apply(nestedCrosses.cross("212").cross2("js").suffix) - val Right(("212_native", 1)) = check.apply(nestedCrosses.cross("212").cross2("native").suffix) - } - } -} diff --git a/core/test/src/mill/eval/EvaluationTests.scala b/core/test/src/mill/eval/EvaluationTests.scala deleted file mode 100644 index e5f0e57d..00000000 --- a/core/test/src/mill/eval/EvaluationTests.scala +++ /dev/null @@ -1,317 +0,0 @@ -package mill.eval - - -import mill.util.TestUtil.{Test, test} -import mill.define.{Discover, Graph, Target, Task} -import mill.{Module, T} -import mill.util.{DummyLogger, TestEvaluator, TestGraphs, TestUtil} -import mill.util.Strict.Agg -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]) { - // Make sure data is persisted even if we re-create the evaluator each time - - def evaluator = new TestEvaluator(module).evaluator - - def apply(target: Task[_], expValue: Any, - expEvaled: Agg[Task[_]], - // How many "other" tasks were evaluated other than those listed above. - // Pass in -1 to skip the check entirely - extraEvaled: Int = 0, - // Perform a second evaluation of the same tasks, and make sure the - // outputs are the same but nothing was evaluated. Disable this if you - // are directly evaluating tasks which need to re-evaluate every time - secondRunNoOp: Boolean = true) = { - - val evaled = evaluator.evaluate(Agg(target)) - - val (matchingReturnedEvaled, extra) = - evaled.evaluated.indexed.partition(expEvaled.contains) - - assert( - evaled.values == Seq(expValue), - matchingReturnedEvaled.toSet == expEvaled.toSet, - extraEvaled == -1 || extra.length == extraEvaled - ) - - // Second time the value is already cached, so no evaluation needed - if (secondRunNoOp){ - val evaled2 = evaluator.evaluate(Agg(target)) - val expecteSecondRunEvaluated = Agg() - assert( - evaled2.values == evaled.values, - evaled2.evaluated == expecteSecondRunEvaluated - ) - } - } - } - - - val tests = Tests{ - object graphs extends TestGraphs() - import graphs._ - import TestGraphs._ - 'evaluateSingle - { - - 'singleton - { - import singleton._ - val check = new Checker(singleton) - // First time the target is evaluated - check(single, expValue = 0, expEvaled = Agg(single)) - - single.counter += 1 - // After incrementing the counter, it forces re-evaluation - check(single, expValue = 1, expEvaled = Agg(single)) - } - 'pair - { - import pair._ - val check = new Checker(pair) - check(down, expValue = 0, expEvaled = Agg(up, down)) - - down.counter += 1 - check(down, expValue = 1, expEvaled = Agg(down)) - - up.counter += 1 - check(down, expValue = 2, expEvaled = Agg(up, down)) - } - 'anonTriple - { - import anonTriple._ - val check = new Checker(anonTriple) - val middle = down.inputs(0) - check(down, expValue = 0, expEvaled = Agg(up, middle, down)) - - down.counter += 1 - check(down, expValue = 1, expEvaled = Agg(middle, down)) - - up.counter += 1 - check(down, expValue = 2, expEvaled = Agg(up, middle, down)) - - middle.asInstanceOf[TestUtil.Test].counter += 1 - - check(down, expValue = 3, expEvaled = Agg(middle, down)) - } - 'diamond - { - import diamond._ - val check = new Checker(diamond) - check(down, expValue = 0, expEvaled = Agg(up, left, right, down)) - - down.counter += 1 - check(down, expValue = 1, expEvaled = Agg(down)) - - up.counter += 1 - // Increment by 2 because up is referenced twice: once by left once by right - check(down, expValue = 3, expEvaled = Agg(up, left, right, down)) - - left.counter += 1 - check(down, expValue = 4, expEvaled = Agg(left, down)) - - right.counter += 1 - check(down, expValue = 5, expEvaled = Agg(right, down)) - } - 'anonDiamond - { - import anonDiamond._ - val check = new Checker(anonDiamond) - val left = down.inputs(0).asInstanceOf[TestUtil.Test] - val right = down.inputs(1).asInstanceOf[TestUtil.Test] - check(down, expValue = 0, expEvaled = Agg(up, left, right, down)) - - down.counter += 1 - check(down, expValue = 1, expEvaled = Agg(left, right, down)) - - up.counter += 1 - // Increment by 2 because up is referenced twice: once by left once by right - check(down, expValue = 3, expEvaled = Agg(up, left, right, down)) - - left.counter += 1 - check(down, expValue = 4, expEvaled = Agg(left, right, down)) - - right.counter += 1 - check(down, expValue = 5, expEvaled = Agg(left, right, down)) - } - - 'bigSingleTerminal - { - import bigSingleTerminal._ - val check = new Checker(bigSingleTerminal) - - check(j, expValue = 0, expEvaled = Agg(a, b, e, f, i, j), extraEvaled = 22) - - j.counter += 1 - check(j, expValue = 1, expEvaled = Agg(j), extraEvaled = 3) - - i.counter += 1 - // increment value by 2 because `i` is used twice on the way to `j` - check(j, expValue = 3, expEvaled = Agg(j, i), extraEvaled = 8) - - b.counter += 1 - // increment value by 4 because `b` is used four times on the way to `j` - check(j, expValue = 7, expEvaled = Agg(b, e, f, i, j), extraEvaled = 20) - } - } - - 'evaluateMixed - { - 'separateGroups - { - // Make sure that `left` and `right` are able to recompute separately, - // even though one depends on the other - - import separateGroups._ - val checker = new Checker(separateGroups) - val evaled1 = checker.evaluator.evaluate(Agg(right, left)) - val filtered1 = evaled1.evaluated.filter(_.isInstanceOf[Target[_]]) - assert(filtered1 == Agg(change, left, right)) - val evaled2 = checker.evaluator.evaluate(Agg(right, left)) - val filtered2 = evaled2.evaluated.filter(_.isInstanceOf[Target[_]]) - assert(filtered2 == Agg()) - change.counter += 1 - val evaled3 = checker.evaluator.evaluate(Agg(right, left)) - val filtered3 = evaled3.evaluated.filter(_.isInstanceOf[Target[_]]) - assert(filtered3 == Agg(change, right)) - - - } - 'triangleTask - { - - import triangleTask._ - val checker = new Checker(triangleTask) - checker(right, 3, Agg(left, right), extraEvaled = -1) - checker(left, 1, Agg(), extraEvaled = -1) - - } - 'multiTerminalGroup - { - import multiTerminalGroup._ - - val checker = new Checker(multiTerminalGroup) - checker(right, 1, Agg(right), extraEvaled = -1) - checker(left, 1, Agg(left), extraEvaled = -1) - } - - 'multiTerminalBoundary - { - - import multiTerminalBoundary._ - - val checker = new Checker(multiTerminalBoundary) - checker(task2, 4, Agg(right, left), extraEvaled = -1, secondRunNoOp = false) - checker(task2, 4, Agg(), extraEvaled = -1, secondRunNoOp = false) - } - - 'overrideSuperTask - { - // Make sure you can override targets, call their supers, and have the - // overriden target be allocated a spot within the overriden/ folder of - // the main publically-available target - import canOverrideSuper._ - - val checker = new Checker(canOverrideSuper) - checker(foo, Seq("base", "object"), Agg(foo), extraEvaled = -1) - - - val public = ammonite.ops.read(checker.evaluator.outPath / 'foo / "meta.json") - val overriden = ammonite.ops.read( - checker.evaluator.outPath / 'foo / - 'overriden / "mill.util.TestGraphs.BaseModule#foo" / "meta.json" - ) - assert( - public.contains("base"), - public.contains("object"), - overriden.contains("base"), - !overriden.contains("object") - ) - } - 'overrideSuperCommand - { - // Make sure you can override commands, call their supers, and have the - // overriden command be allocated a spot within the overriden/ folder of - // the main publically-available command - import canOverrideSuper._ - - val checker = new Checker(canOverrideSuper) - val runCmd = cmd(1) - checker( - runCmd, - Seq("base1", "object1"), - Agg(runCmd), - extraEvaled = -1, - secondRunNoOp = false - ) - - val public = ammonite.ops.read(checker.evaluator.outPath / 'cmd / "meta.json") - val overriden = ammonite.ops.read( - checker.evaluator.outPath / 'cmd / - 'overriden / "mill.util.TestGraphs.BaseModule#cmd" / "meta.json" - ) - assert( - public.contains("base1"), - public.contains("object1"), - overriden.contains("base1"), - !overriden.contains("object1") - ) - } - - 'tasksAreUncached - { - // Make sure the tasks `left` and `middle` re-compute every time, while - // the target `right` does not - // - // ___ left ___ - // / \ - // up middle -- down - // / - // right - object build extends TestUtil.BaseModule{ - var leftCount = 0 - var rightCount = 0 - var middleCount = 0 - def up = T{ test.anon() } - def left = T.task{ leftCount += 1; up() + 1 } - def middle = T.task{ middleCount += 1; 100 } - def right = T{ rightCount += 1; 10000 } - def down = T{ left() + middle() + right() } - } - - import build._ - - // Ensure task objects themselves are not cached, and recomputed each time - assert( - up eq up, - left ne left, - middle ne middle, - right eq right, - down eq down - ) - - // During the first evaluation, they get computed normally like any - // cached target - val check = new Checker(build) - assert(leftCount == 0, rightCount == 0) - check(down, expValue = 10101, expEvaled = Agg(up, right, down), extraEvaled = 8) - assert(leftCount == 1, middleCount == 1, rightCount == 1) - - // If the upstream `up` doesn't change, the entire block of tasks - // doesn't need to recompute - check(down, expValue = 10101, expEvaled = Agg()) - assert(leftCount == 1, middleCount == 1, rightCount == 1) - - // But if `up` changes, the entire block of downstream tasks needs to - // recompute together, including `middle` which doesn't depend on `up`, - // because tasks have no cached value that can be used. `right`, which - // is a cached Target, does not recompute - up.inputs(0).asInstanceOf[Test].counter += 1 - check(down, expValue = 10102, expEvaled = Agg(up, down), extraEvaled = 6) - assert(leftCount == 2, middleCount == 2, rightCount == 1) - - // Running the tasks themselves results in them being recomputed every - // single time, even if nothing changes - check(left, expValue = 2, expEvaled = Agg(), extraEvaled = 1, secondRunNoOp = false) - assert(leftCount == 3, middleCount == 2, rightCount == 1) - check(left, expValue = 2, expEvaled = Agg(), extraEvaled = 1, secondRunNoOp = false) - assert(leftCount == 4, middleCount == 2, rightCount == 1) - - check(middle, expValue = 100, expEvaled = Agg(), extraEvaled = 2, secondRunNoOp = false) - assert(leftCount == 4, middleCount == 3, rightCount == 1) - check(middle, expValue = 100, expEvaled = Agg(), extraEvaled = 2, secondRunNoOp = false) - assert(leftCount == 4, middleCount == 4, rightCount == 1) - } - } - } -} diff --git a/core/test/src/mill/eval/FailureTests.scala b/core/test/src/mill/eval/FailureTests.scala deleted file mode 100644 index 90cff686..00000000 --- a/core/test/src/mill/eval/FailureTests.scala +++ /dev/null @@ -1,100 +0,0 @@ -package mill.eval -import mill.T -import mill.util.{TestEvaluator, TestUtil} -import ammonite.ops.{Path, pwd, rm} -import mill.eval.Result.OuterStack -import utest._ -import utest.framework.TestPath -import mill.util.TestEvaluator.implicitDisover - -object FailureTests extends TestSuite{ - - val tests = Tests{ - val graphs = new mill.util.TestGraphs() - import graphs._ - - 'evaluateSingle - { - val check = new TestEvaluator(singleton) - check.fail( - target = singleton.single, - expectedFailCount = 0, - expectedRawValues = Seq(Result.Success(0)) - ) - - singleton.single.failure = Some("lols") - - check.fail( - target = singleton.single, - expectedFailCount = 1, - expectedRawValues = Seq(Result.Failure("lols")) - ) - - singleton.single.failure = None - - check.fail( - target = singleton.single, - expectedFailCount = 0, - expectedRawValues = Seq(Result.Success(0)) - ) - - - val ex = new IndexOutOfBoundsException() - singleton.single.exception = Some(ex) - - - check.fail( - target = singleton.single, - expectedFailCount = 1, - expectedRawValues = Seq(Result.Exception(ex, new OuterStack(Nil))) - ) - } - 'evaluatePair - { - val check = new TestEvaluator(pair) - check.fail( - pair.down, - expectedFailCount = 0, - expectedRawValues = Seq(Result.Success(0)) - ) - - pair.up.failure = Some("lols") - - check.fail( - pair.down, - expectedFailCount = 1, - expectedRawValues = Seq(Result.Skipped) - ) - - pair.up.failure = None - - check.fail( - pair.down, - expectedFailCount = 0, - expectedRawValues = Seq(Result.Success(0)) - ) - - pair.up.exception = Some(new IndexOutOfBoundsException()) - - check.fail( - pair.down, - expectedFailCount = 1, - expectedRawValues = Seq(Result.Skipped) - ) - } - 'multipleUsesOfDest - { - object build extends TestUtil.BaseModule { - // Using `T.ctx( ).dest` twice in a single task is ok - def left = T{ + T.ctx().dest.toString.length + T.ctx().dest.toString.length } - - // Using `T.ctx( ).dest` once in two different tasks is not ok - val task = T.task{ T.ctx().dest.toString.length } - def right = T{ task() + left() + T.ctx().dest.toString().length } - } - - val check = new TestEvaluator(build) - val Right(_) = check(build.left) - val Left(Result.Exception(e, _)) = check(build.right) - assert(e.getMessage.contains("`dest` can only be used in one place")) - } - } -} - diff --git a/core/test/src/mill/eval/JavaCompileJarTests.scala b/core/test/src/mill/eval/JavaCompileJarTests.scala deleted file mode 100644 index 71feebda..00000000 --- a/core/test/src/mill/eval/JavaCompileJarTests.scala +++ /dev/null @@ -1,159 +0,0 @@ -package mill.eval - -import ammonite.ops.ImplicitWd._ -import ammonite.ops._ -import mill.define.{Discover, Input, Target, Task} -import mill.modules.Jvm -import mill.util.Ctx.Dest -import mill.{Module, T} -import mill.util.{DummyLogger, Loose, TestEvaluator, TestUtil} -import mill.util.Strict.Agg -import utest._ -import mill._ -import TestEvaluator.implicitDisover -object JavaCompileJarTests extends TestSuite{ - def compileAll(sources: Seq[PathRef])(implicit ctx: Dest) = { - mkdir(ctx.dest) - import ammonite.ops._ - %("javac", sources.map(_.path.toString()), "-d", ctx.dest)(wd = ctx.dest) - PathRef(ctx.dest) - } - - val tests = Tests{ - 'javac { - val javacSrcPath = pwd / 'core / 'test / 'resources / 'examples / 'javac - val javacDestPath = TestUtil.getOutPath() / 'src - - mkdir(javacDestPath / up) - cp(javacSrcPath, javacDestPath) - - object Build extends TestUtil.BaseModule{ - def sourceRootPath = javacDestPath / 'src - def resourceRootPath = javacDestPath / 'resources - - // sourceRoot -> allSources -> classFiles - // | - // v - // resourceRoot ----> jar - def sourceRoot = T.sources{ sourceRootPath } - def resourceRoot = T.sources{ resourceRootPath } - def allSources = T{ sourceRoot().flatMap(p => ls.rec(p.path)).map(PathRef(_)) } - def classFiles = T{ compileAll(allSources()) } - def jar = T{ Jvm.createJar(Loose.Agg(classFiles().path) ++ resourceRoot().map(_.path)) } - - def run(mainClsName: String) = T.command{ - %%('java, "-cp", classFiles().path, mainClsName) - } - } - - import Build._ - - var evaluator = new TestEvaluator(Build) - def eval[T](t: Task[T]) = { - evaluator.apply(t) - } - def check(targets: Agg[Task[_]], expected: Agg[Task[_]]) = { - evaluator.check(targets, expected) - } - - def append(path: Path, txt: String) = ammonite.ops.write.append(path, txt) - - - check( - targets = Agg(jar), - expected = Agg(allSources, classFiles, jar) - ) - - // Re-running with no changes results in nothing being evaluated - check(targets = Agg(jar), expected = Agg()) - - // Appending an empty string gets ignored due to file-content hashing - append(sourceRootPath / "Foo.java", "") - check(targets = Agg(jar), expected = Agg()) - - // Appending whitespace forces a recompile, but the classfilesend up - // exactly the same so no re-jarring. - append(sourceRootPath / "Foo.java", " ") - // Note that `sourceRoot` and `resourceRoot` never turn up in the `expected` - // list, because they are `Source`s not `Target`s - check(targets = Agg(jar), expected = Agg(/*sourceRoot, */allSources, classFiles)) - - // Appending a new class changes the classfiles, which forces us to - // re-create the final jar - append(sourceRootPath / "Foo.java", "\nclass FooTwo{}") - check(targets = Agg(jar), expected = Agg(allSources, classFiles, jar)) - - // Tweaking the resources forces rebuild of the final jar, without - // recompiling classfiles - append(resourceRootPath / "hello.txt", " ") - check(targets = Agg(jar), expected = Agg(jar)) - - // You can swap evaluators halfway without any ill effects - evaluator = new TestEvaluator(Build) - - // Asking for an intermediate target forces things to be build up to that - // target only; these are re-used for any downstream targets requested - append(sourceRootPath / "Bar.java", "\nclass BarTwo{}") - append(resourceRootPath / "hello.txt", " ") - check(targets = Agg(classFiles), expected = Agg(allSources, classFiles)) - check(targets = Agg(jar), expected = Agg(jar)) - check(targets = Agg(allSources), expected = Agg()) - - append(sourceRootPath / "Bar.java", "\nclass BarThree{}") - append(resourceRootPath / "hello.txt", " ") - check(targets = Agg(resourceRoot), expected = Agg()) - check(targets = Agg(allSources), expected = Agg(allSources)) - check(targets = Agg(jar), expected = Agg(classFiles, jar)) - - val jarContents = %%('jar, "-tf", evaluator.outPath/'jar/'dest/"out.jar")(evaluator.outPath).out.string - val expectedJarContents = - """META-INF/MANIFEST.MF - |test/Bar.class - |test/BarThree.class - |test/BarTwo.class - |test/Foo.class - |test/FooTwo.class - |hello.txt - |""".stripMargin - assert(jarContents == expectedJarContents) - - val executed = %%('java, "-cp", evaluator.outPath/'jar/'dest/"out.jar", "test.Foo")(evaluator.outPath).out.string - assert(executed == (31337 + 271828) + "\n") - - for(i <- 0 until 3){ - // Build.run is not cached, so every time we eval it it has to - // re-evaluate - val Right((runOutput, evalCount)) = eval(Build.run("test.Foo")) - assert( - runOutput.out.string == (31337 + 271828) + "\n", - evalCount == 1 - ) - } - - val Left(Result.Exception(ex, _)) = eval(Build.run("test.BarFour")) - - assert(ex.getMessage.contains("Could not find or load main class")) - - append( - sourceRootPath / "Bar.java", - """ - class BarFour{ - public static void main(String[] args){ - System.out.println("New Cls!"); - } - } - """ - ) - val Right((runOutput2, evalCount2)) = eval(Build.run("test.BarFour")) - assert( - runOutput2.out.string == "New Cls!\n", - evalCount2 == 3 - ) - val Right((runOutput3, evalCount3)) = eval(Build.run("test.BarFour")) - assert( - runOutput3.out.string == "New Cls!\n", - evalCount3 == 1 - ) - } - } -} diff --git a/core/test/src/mill/eval/ModuleTests.scala b/core/test/src/mill/eval/ModuleTests.scala deleted file mode 100644 index c6125b32..00000000 --- a/core/test/src/mill/eval/ModuleTests.scala +++ /dev/null @@ -1,45 +0,0 @@ -package mill.eval - -import ammonite.ops._ -import mill.util.{TestEvaluator, TestUtil} -import mill.T -import mill.define.Discover -import mill.util.TestEvaluator.implicitDisover -import utest._ - -object ModuleTests extends TestSuite{ - object ExternalModule extends mill.define.ExternalModule { - def x = T{13} - object inner extends mill.Module{ - def y = T{17} - } - def millDiscover = Discover[this.type] - } - object Build extends TestUtil.BaseModule{ - def z = T{ ExternalModule.x() + ExternalModule.inner.y() } - } - val tests = Tests { - rm(TestEvaluator.externalOutPath) - 'externalModuleTargetsAreNamespacedByModulePackagePath - { - val check = new TestEvaluator(Build) - - val Right((30, 1)) = check.apply(Build.z) - assert( - read(check.evaluator.outPath / 'z / "meta.json").contains("30"), - read(TestEvaluator.externalOutPath / 'mill / 'eval / 'ModuleTests / 'ExternalModule / 'x / "meta.json").contains("13"), - read(TestEvaluator.externalOutPath / 'mill / 'eval / 'ModuleTests / 'ExternalModule / 'inner / 'y / "meta.json").contains("17") - ) - } - 'externalModuleMustBeGlobalStatic - { - - - object Build extends mill.define.ExternalModule { - - def z = T{ ExternalModule.x() + ExternalModule.inner.y() } - def millDiscover = Discover[this.type] - } - - intercept[java.lang.AssertionError]{ Build } - } - } -} diff --git a/core/test/src/mill/eval/TarjanTests.scala b/core/test/src/mill/eval/TarjanTests.scala deleted file mode 100644 index 2f9d0a4d..00000000 --- a/core/test/src/mill/eval/TarjanTests.scala +++ /dev/null @@ -1,91 +0,0 @@ -package mill.eval - -import utest._ - -object TarjanTests extends TestSuite{ - def check(input: Seq[Seq[Int]], expected: Seq[Seq[Int]]) = { - val result = Tarjans(input).map(_.sorted) - val sortedExpected = expected.map(_.sorted) - assert(result == sortedExpected) - } - val tests = Tests{ - // - 'empty - check(Seq(), Seq()) - - // (0) - 'singleton - check(Seq(Seq()), Seq(Seq(0))) - - - // (0)-. - // ^._/ - 'selfCycle - check(Seq(Seq(0)), Seq(Seq(0))) - - // (0) <-> (1) - 'simpleCycle- check(Seq(Seq(1), Seq(0)), Seq(Seq(1, 0))) - - // (0) (1) (2) - 'multipleSingletons - check( - Seq(Seq(), Seq(), Seq()), - Seq(Seq(0), Seq(1), Seq(2)) - ) - - // (0) -> (1) -> (2) - 'straightLineNoCycles- check( - Seq(Seq(1), Seq(2), Seq()), - Seq(Seq(2), Seq(1), Seq(0)) - ) - - // (0) <- (1) <- (2) - 'straightLineNoCyclesReversed- check( - Seq(Seq(), Seq(0), Seq(1)), - Seq(Seq(0), Seq(1), Seq(2)) - ) - - // (0) <-> (1) (2) -> (3) -> (4) - // ^.____________/ - 'independentSimpleCycles - check( - Seq(Seq(1), Seq(0), Seq(3), Seq(4), Seq(2)), - Seq(Seq(1, 0), Seq(4, 3, 2)) - ) - - // ___________________ - // v \ - // (0) <-> (1) (2) -> (3) -> (4) - // ^.____________/ - 'independentLinkedCycles - check( - Seq(Seq(1), Seq(0), Seq(3), Seq(4), Seq(2, 1)), - Seq(Seq(1, 0), Seq(4, 3, 2)) - ) - // _____________ - // / v - // (0) <-> (1) (2) -> (3) -> (4) - // ^.____________/ - 'independentLinkedCycles2 - check( - Seq(Seq(1, 2), Seq(0), Seq(3), Seq(4), Seq(2)), - Seq(Seq(4, 3, 2), Seq(1, 0)) - ) - - // _____________ - // / v - // (0) <-> (1) (2) -> (3) -> (4) - // ^. ^.____________/ - // \________________/ - 'combinedCycles - check( - Seq(Seq(1, 2), Seq(0), Seq(3), Seq(4), Seq(2, 1)), - Seq(Seq(4, 3, 2, 1, 0)) - ) - // - // (0) <-> (1) <- (2) <- (3) <-> (4) <- (5) - // ^.____________/ / / - // / / - // (6) <- (7) <-/ (8) <-' - // / / - // v / - // (9) <--------' - 'combinedCycles - check( - Seq(Seq(1), Seq(0), Seq(0, 1), Seq(2, 4, 7, 9), Seq(3), Seq(4, 8), Seq(9), Seq(6), Seq(), Seq()), - Seq(Seq(0, 1), Seq(2), Seq(9), Seq(6), Seq(7), Seq(3, 4), Seq(8), Seq(5)) - ) - - } -}
\ No newline at end of file diff --git a/core/test/src/mill/eval/TaskTests.scala b/core/test/src/mill/eval/TaskTests.scala deleted file mode 100644 index 114a2910..00000000 --- a/core/test/src/mill/eval/TaskTests.scala +++ /dev/null @@ -1,95 +0,0 @@ -package mill.eval - -import utest._ -import ammonite.ops._ -import mill.T -import mill.util.TestEvaluator.implicitDisover -import mill.util.TestEvaluator -object TaskTests extends TestSuite{ - val tests = Tests{ - object build extends mill.util.TestUtil.BaseModule{ - var count = 0 - // Explicitly instantiate `Function1` objects to make sure we get - // different instances each time - def staticWorker = T.worker{ - new Function1[Int, Int] { - def apply(v1: Int) = v1 + 1 - } - } - def noisyWorker = T.worker{ - new Function1[Int, Int] { - def apply(v1: Int) = input() + 1 - } - } - def input = T.input{ - count += 1 - count - } - def task = T.task{ - count += 1 - count - } - def taskInput = T{ input() } - def taskNoInput = T{ task() } - - def persistent = T.persistent{ - input() // force re-computation - mkdir(T.ctx().dest) - write.append(T.ctx().dest/'count, "hello\n") - read.lines(T.ctx().dest/'count).length - } - def nonPersistent = T{ - input() // force re-computation - mkdir(T.ctx().dest) - write.append(T.ctx().dest/'count, "hello\n") - read.lines(T.ctx().dest/'count).length - } - - def staticWorkerDownstream = T{ - staticWorker().apply(1) - } - def noisyWorkerDownstream = T{ - noisyWorker().apply(1) - } - } - - 'inputs - { - // Inputs always re-evaluate, including forcing downstream cached Targets - // to re-evaluate, but normal Tasks behind a Target run once then are cached - val check = new TestEvaluator(build) - - val Right((1, 1)) = check.apply(build.taskInput) - val Right((2, 1)) = check.apply(build.taskInput) - val Right((3, 1)) = check.apply(build.taskInput) - - val Right((4, 1)) = check.apply(build.taskNoInput) - val Right((4, 0)) = check.apply(build.taskNoInput) - val Right((4, 0)) = check.apply(build.taskNoInput) - } - - 'persistent - { - // Persistent tasks keep the working dir around between runs - val check = new TestEvaluator(build) - val Right((1, 1)) = check.apply(build.persistent) - val Right((2, 1)) = check.apply(build.persistent) - val Right((3, 1)) = check.apply(build.persistent) - - val Right((1, 1)) = check.apply(build.nonPersistent) - val Right((1, 1)) = check.apply(build.nonPersistent) - val Right((1, 1)) = check.apply(build.nonPersistent) - } - - 'worker - { - // Persistent task - def check = new TestEvaluator(build) - - val Right((2, 1)) = check.apply(build.noisyWorkerDownstream) - val Right((3, 1)) = check.apply(build.noisyWorkerDownstream) - val Right((4, 1)) = check.apply(build.noisyWorkerDownstream) - - val Right((2, 1)) = check.apply(build.staticWorkerDownstream) - val Right((2, 0)) = check.apply(build.staticWorkerDownstream) - val Right((2, 0)) = check.apply(build.staticWorkerDownstream) - } - } -} diff --git a/core/test/src/mill/main/JavaCompileJarTests.scala b/core/test/src/mill/main/JavaCompileJarTests.scala deleted file mode 100644 index a4c9fafe..00000000 --- a/core/test/src/mill/main/JavaCompileJarTests.scala +++ /dev/null @@ -1,66 +0,0 @@ -package mill.main - -import ammonite.ops._ -import mill.util.ScriptTestSuite -import utest._ - -object JavaCompileJarTests extends ScriptTestSuite { - def workspaceSlug = "java-compile-jar" - def scriptSourcePath = pwd / 'core / 'test / 'resources / 'examples / 'javac - val tests = Tests{ - initWorkspace() - 'test - { - // Basic target evaluation works - assert(eval("classFiles")) - assert(eval("jar")) - - val classFiles1 = meta("classFiles") - val jar1 = meta("jar") - - assert(eval("classFiles")) - assert(eval("jar")) - - // Repeated evaluation has the same results - val classFiles2 = meta("classFiles") - val jar2 = meta("jar") - - assert( - jar1 == jar2, - classFiles1 == classFiles2 - ) - - // If we update resources, classFiles are unchanged but jar changes - for(scalaFile <- ls.rec(workspacePath).filter(_.ext == "txt")){ - write.append(scalaFile, "\n") - } - - assert(eval("classFiles")) - assert(eval("jar")) - - val classFiles3 = meta("classFiles") - val jar3 = meta("jar") - - assert( - jar2 != jar3, - classFiles2 == classFiles3 - ) - - // We can intentionally break the code, have the targets break, then - // fix the code and have them recover. - for(scalaFile <- ls.rec(workspacePath).filter(_.ext == "java")){ - write.append(scalaFile, "\n}") - } - - assert(!eval("classFiles")) - assert(!eval("jar")) - - for(scalaFile <- ls.rec(workspacePath).filter(_.ext == "java")){ - write.over(scalaFile, read(scalaFile).dropRight(2)) - } - - assert(eval("classFiles")) - assert(eval("jar")) - } - } -} - diff --git a/core/test/src/mill/main/MainTests.scala b/core/test/src/mill/main/MainTests.scala deleted file mode 100644 index 22f93ae0..00000000 --- a/core/test/src/mill/main/MainTests.scala +++ /dev/null @@ -1,222 +0,0 @@ -package mill.main - -import mill.define.{Discover, Segment, Task} -import mill.util.TestGraphs._ -import mill.util.TestEvaluator.implicitDisover -import utest._ -object MainTests extends TestSuite{ - - def check[T <: mill.Module](module: T)( - selectorString: String, - expected0: Either[String, Seq[T => Task[_]]]) - (implicit discover: Discover[T])= { - - val expected = expected0.map(_.map(_(module))) - val resolved = for{ - selectors <- mill.main.ParseArgs(Seq(selectorString)).map(_._1.head) - val crossSelectors = selectors._2.value.map{case Segment.Cross(x) => x.toList.map(_.toString) case _ => Nil} - task <- mill.main.Resolve.resolve( - selectors._2.value.toList, module, discover, Nil, crossSelectors.toList, Nil - ) - } yield task - assert(resolved == expected) - } - val tests = Tests{ - val graphs = new mill.util.TestGraphs() - import graphs._ - 'single - { - val check = MainTests.check(singleton) _ - 'pos - check("single", Right(Seq(_.single))) - 'neg1 - check("doesntExist", Left("Cannot resolve task doesntExist")) - 'neg2 - check("single.doesntExist", Left("Cannot resolve module single")) - 'neg3 - check("", Left("Selector cannot be empty")) - } - 'nested - { - val check = MainTests.check(nestedModule) _ - 'pos1 - check("single", Right(Seq(_.single))) - 'pos2 - check("nested.single", Right(Seq(_.nested.single))) - 'pos3 - check("classInstance.single", Right(Seq(_.classInstance.single))) - 'neg1 - check("doesntExist", Left("Cannot resolve task doesntExist")) - 'neg2 - check("single.doesntExist", Left("Cannot resolve module single")) - 'neg3 - check("nested.doesntExist", Left("Cannot resolve task nested.doesntExist")) - 'neg4 - check("classInstance.doesntExist", Left("Cannot resolve task classInstance.doesntExist")) - 'wildcard - check( - "_.single", - Right(Seq( - _.classInstance.single, - _.nested.single - )) - ) - 'wildcardNeg - check( - "_._.single", - Left("Cannot resolve module _") - ) - 'wildcardNeg2 - check( - "_._.__", - Left("Cannot resolve module _") - ) - 'wildcard2 - check( - "__.single", - Right(Seq( - _.single, - _.classInstance.single, - _.nested.single - )) - ) - - 'wildcard3 - check( - "_.__.single", - Right(Seq( - _.classInstance.single, - _.nested.single - )) - ) - - } - 'cross - { - 'single - { - val check = MainTests.check(singleCross) _ - 'pos1 - check("cross[210].suffix", Right(Seq(_.cross("210").suffix))) - 'pos2 - check("cross[211].suffix", Right(Seq(_.cross("211").suffix))) - 'neg1 - check("cross[210].doesntExist", Left("Cannot resolve task cross[210].doesntExist")) - 'neg2 - check("cross[doesntExist].doesntExist", Left("Cannot resolve cross cross[doesntExist]")) - 'neg2 - check("cross[doesntExist].suffix", Left("Cannot resolve cross cross[doesntExist]")) - 'wildcard - check( - "cross[_].suffix", - Right(Seq( - _.cross("210").suffix, - _.cross("211").suffix, - _.cross("212").suffix - )) - ) - 'wildcard2 - check( - "cross[__].suffix", - Right(Seq( - _.cross("210").suffix, - _.cross("211").suffix, - _.cross("212").suffix - )) - ) - } - 'double - { - val check = MainTests.check(doubleCross) _ - 'pos1 - check( - "cross[210,jvm].suffix", - Right(Seq(_.cross("210", "jvm").suffix)) - ) - 'pos2 - check( - "cross[211,jvm].suffix", - Right(Seq(_.cross("211", "jvm").suffix)) - ) - 'wildcard - { - 'labelNeg - check( - "_.suffix", - Left("Cannot resolve module _") - ) - 'labelPos - check( - "__.suffix", - Right(Seq( - _.cross("210", "jvm").suffix, - _.cross("210", "js").suffix, - - _.cross("211", "jvm").suffix, - _.cross("211", "js").suffix, - - _.cross("212", "jvm").suffix, - _.cross("212", "js").suffix, - _.cross("212", "native").suffix - )) - ) - 'first - check( - "cross[_,jvm].suffix", - Right(Seq( - _.cross("210", "jvm").suffix, - _.cross("211", "jvm").suffix, - _.cross("212", "jvm").suffix - )) - ) - 'second - check( - "cross[210,_].suffix", - Right(Seq( - _.cross("210", "jvm").suffix, - _.cross("210", "js").suffix - )) - ) - 'both - check( - "cross[_,_].suffix", - Right(Seq( - _.cross("210", "jvm").suffix, - _.cross("210", "js").suffix, - - _.cross("211", "jvm").suffix, - _.cross("211", "js").suffix, - - _.cross("212", "jvm").suffix, - _.cross("212", "js").suffix, - _.cross("212", "native").suffix - )) - ) - 'both2 - check( - "cross[__].suffix", - Right(Seq( - _.cross("210", "jvm").suffix, - _.cross("210", "js").suffix, - - _.cross("211", "jvm").suffix, - _.cross("211", "js").suffix, - - _.cross("212", "jvm").suffix, - _.cross("212", "js").suffix, - _.cross("212", "native").suffix - )) - ) - } - } - 'nested - { - val check = MainTests.check(nestedCrosses) _ - 'pos1 - check( - "cross[210].cross2[js].suffix", - Right(Seq(_.cross("210").cross2("js").suffix)) - ) - 'pos2 - check( - "cross[211].cross2[jvm].suffix", - Right(Seq(_.cross("211").cross2("jvm").suffix)) - ) - 'wildcard - { - 'first - check( - "cross[_].cross2[jvm].suffix", - Right(Seq( - _.cross("210").cross2("jvm").suffix, - _.cross("211").cross2("jvm").suffix, - _.cross("212").cross2("jvm").suffix - )) - ) - 'second - check( - "cross[210].cross2[_].suffix", - Right(Seq( - _.cross("210").cross2("jvm").suffix, - _.cross("210").cross2("js").suffix, - _.cross("210").cross2("native").suffix - )) - ) - 'both - check( - "cross[_].cross2[_].suffix", - Right(Seq( - _.cross("210").cross2("jvm").suffix, - _.cross("210").cross2("js").suffix, - _.cross("210").cross2("native").suffix, - - _.cross("211").cross2("jvm").suffix, - _.cross("211").cross2("js").suffix, - _.cross("211").cross2("native").suffix, - - _.cross("212").cross2("jvm").suffix, - _.cross("212").cross2("js").suffix, - _.cross("212").cross2("native").suffix - )) - ) - } - } - } - } -} diff --git a/core/test/src/mill/main/ParseArgsTest.scala b/core/test/src/mill/main/ParseArgsTest.scala deleted file mode 100644 index 6678f12c..00000000 --- a/core/test/src/mill/main/ParseArgsTest.scala +++ /dev/null @@ -1,241 +0,0 @@ -package mill.main - -import mill.define.Segment -import mill.define.Segment.{Cross, Label} -import utest._ - -object ParseArgsTest extends TestSuite { - - val tests = Tests { - 'extractSelsAndArgs - { - def check(input: Seq[String], - expectedSelectors: Seq[String], - expectedArgs: Seq[String], - expectedIsMulti: Boolean) = { - val (selectors, args, isMulti) = ParseArgs.extractSelsAndArgs(input) - - assert( - selectors == expectedSelectors, - args == expectedArgs, - isMulti == expectedIsMulti - ) - } - - 'empty - check(input = Seq.empty, - expectedSelectors = Seq.empty, - expectedArgs = Seq.empty, - expectedIsMulti = false) - 'singleSelector - check( - input = Seq("core.compile"), - expectedSelectors = Seq("core.compile"), - expectedArgs = Seq.empty, - expectedIsMulti = false - ) - 'singleSelectorWithArgs - check( - input = Seq("application.run", "hello", "world"), - expectedSelectors = Seq("application.run"), - expectedArgs = Seq("hello", "world"), - expectedIsMulti = false - ) - 'singleSelectorWithAllInArgs - check( - input = Seq("application.run", "hello", "world", "--all"), - expectedSelectors = Seq("application.run"), - expectedArgs = Seq("hello", "world", "--all"), - expectedIsMulti = false - ) - 'multiSelectors - check( - input = Seq("--all", "core.jar", "core.docsJar", "core.sourcesJar"), - expectedSelectors = Seq("core.jar", "core.docsJar", "core.sourcesJar"), - expectedArgs = Seq.empty, - expectedIsMulti = true - ) - 'multiSelectorsSeq - check( - input = Seq("--seq", "core.jar", "core.docsJar", "core.sourcesJar"), - expectedSelectors = Seq("core.jar", "core.docsJar", "core.sourcesJar"), - expectedArgs = Seq.empty, - expectedIsMulti = true - ) - 'multiSelectorsWithArgs - check( - input = Seq("--all", - "core.compile", - "application.runMain", - "--", - "Main", - "hello", - "world"), - expectedSelectors = Seq("core.compile", "application.runMain"), - expectedArgs = Seq("Main", "hello", "world"), - expectedIsMulti = true - ) - 'multiSelectorsWithArgsWithAllInArgs - check( - input = Seq("--all", - "core.compile", - "application.runMain", - "--", - "Main", - "--all", - "world"), - expectedSelectors = Seq("core.compile", "application.runMain"), - expectedArgs = Seq("Main", "--all", "world"), - expectedIsMulti = true - ) - } - 'expandBraces - { - def check(input: String, expectedExpansion: List[String]) = { - val Right(expanded) = ParseArgs.expandBraces(input) - - assert(expanded == expectedExpansion) - } - - 'expandLeft - check( - "{application,core}.compile", - List("application.compile", "core.compile") - ) - 'expandRight - check( - "application.{jar,docsJar,sourcesJar}", - List("application.jar", "application.docsJar", "application.sourcesJar") - ) - 'expandBoth - check( - "{core,application}.{jar,docsJar}", - List( - "core.jar", - "core.docsJar", - "application.jar", - "application.docsJar" - ) - ) - 'expandNested - { - check("{hello,world.{cow,moo}}", - List("hello", "world.cow", "world.moo")) - check("{a,b{c,d}}", List("a", "bc", "bd")) - check("{a,b,{c,d}}", List("a", "b", "c", "d")) - check("{a,b{c,d{e,f}}}", List("a", "bc", "bde", "bdf")) - check("{a{b,c},d}", List("ab", "ac", "d")) - check("{a,{b,c}d}", List("a", "bd", "cd")) - check("{a{b,c},d{e,f}}", List("ab", "ac", "de", "df")) - check("{a,b{c,d},e{f,g}}", List("a", "bc", "bd", "ef", "eg")) - } - 'expandMixed - check( - "{a,b}.{c}.{}.e", - List("a.{c}.{}.e", "b.{c}.{}.e") - ) - 'malformed - { - val malformed = Seq("core.{compile", "core.{compile,test]") - - malformed.foreach { m => - val Left(error) = ParseArgs.expandBraces(m) - assert(error.contains("Parsing exception")) - } - } - 'dontExpand - { - check("core.compile", List("core.compile")) - check("{}.compile", List("{}.compile")) - check("{core}.compile", List("{core}.compile")) - } - 'keepUnknownSymbols - { - check("{a,b}.e<>", List("a.e<>", "b.e<>")) - check("a[99]&&", List("a[99]&&")) - check( - "{a,b}.<%%>.{c,d}", - List("a.<%%>.c", "a.<%%>.d", "b.<%%>.c", "b.<%%>.d") - ) - } - } - - 'apply - { - def check(input: Seq[String], - expectedSelectors: List[(Option[List[Segment]], List[Segment])], - expectedArgs: Seq[String]) = { - val Right((selectors0, args)) = ParseArgs(input) - - val selectors = selectors0.map{ - case (Some(v1), v2) => (Some(v1.value), v2.value) - case (None, v2) => (None, v2.value) - } - assert( - selectors == expectedSelectors, - args == expectedArgs - ) - } - - 'rejectEmpty { - assert(ParseArgs(Seq.empty) == Left("Selector cannot be empty")) - } - 'singleSelector - check( - input = Seq("core.compile"), - expectedSelectors = List( - None -> List(Label("core"), Label("compile")) - ), - expectedArgs = Seq.empty - ) - 'externalSelector - check( - input = Seq("foo.bar/core.compile"), - expectedSelectors = List( - Some(List(Label("foo"), Label("bar"))) -> List(Label("core"), Label("compile")) - ), - expectedArgs = Seq.empty - ) - 'singleSelectorWithArgs - check( - input = Seq("application.run", "hello", "world"), - expectedSelectors = List( - None -> List(Label("application"), Label("run")) - ), - expectedArgs = Seq("hello", "world") - ) - 'singleSelectorWithCross - check( - input = Seq("bridges[2.12.4,jvm].compile"), - expectedSelectors = List( - None -> List(Label("bridges"), Cross(Seq("2.12.4", "jvm")), Label("compile")) - ), - expectedArgs = Seq.empty - ) - 'multiSelectorsBraceExpansion - check( - input = Seq("--all", "{core,application}.compile"), - expectedSelectors = List( - None -> List(Label("core"), Label("compile")), - None -> List(Label("application"), Label("compile")) - ), - expectedArgs = Seq.empty - ) - 'multiSelectorsBraceExpansionWithArgs - check( - input = Seq("--all", "{core,application}.run", "--", "hello", "world"), - expectedSelectors = List( - None -> List(Label("core"), Label("run")), - None -> List(Label("application"), Label("run")) - ), - expectedArgs = Seq("hello", "world") - ) - 'multiSelectorsBraceExpansionWithCross - check( - input = Seq("--all", "bridges[2.12.4,jvm].{test,jar}"), - expectedSelectors = List( - None -> List(Label("bridges"), Cross(Seq("2.12.4", "jvm")), Label("test")), - None -> List(Label("bridges"), Cross(Seq("2.12.4", "jvm")), Label("jar")) - ), - expectedArgs = Seq.empty - ) - 'multiSelectorsBraceExpansionInsideCross - check( - input = Seq("--all", "bridges[{2.11.11,2.11.8}].jar"), - expectedSelectors = List( - None -> List(Label("bridges"), Cross(Seq("2.11.11")), Label("jar")), - None -> List(Label("bridges"), Cross(Seq("2.11.8")), Label("jar")) - ), - expectedArgs = Seq.empty - ) - 'multiSelectorsBraceExpansionWithoutAll - { - assert( - ParseArgs(Seq("{core,application}.compile")) == Left( - "Please use --all flag to run multiple tasks") - ) - } - 'multiSelectorsWithoutAllAsSingle - check( - // this is how it works when we pass multiple tasks without --all flag - input = Seq("core.compile", "application.compile"), - expectedSelectors = List( - None -> List(Label("core"), Label("compile")) - ), - expectedArgs = Seq("application.compile") - ) - } - } - -} diff --git a/core/test/src/mill/util/ScriptTestSuite.scala b/core/test/src/mill/util/ScriptTestSuite.scala deleted file mode 100644 index 1aa74de1..00000000 --- a/core/test/src/mill/util/ScriptTestSuite.scala +++ /dev/null @@ -1,38 +0,0 @@ -package mill.util - -import java.io.{ByteArrayInputStream, ByteArrayOutputStream, PrintStream} - -import ammonite.ops._ -import mill.main.ParseArgs -import utest._ - -abstract class ScriptTestSuite extends TestSuite{ - def workspaceSlug: String - def scriptSourcePath: Path - - val workspacePath = pwd / 'target / 'workspace / workspaceSlug - val stdOutErr = new PrintStream(new ByteArrayOutputStream()) -// val stdOutErr = new PrintStream(System.out) - val stdIn = new ByteArrayInputStream(Array()) - val runner = new mill.main.MainRunner( - ammonite.main.Cli.Config(wd = workspacePath), false, - stdOutErr, stdOutErr, stdIn - ) - def eval(s: String*) = runner.runScript(workspacePath / "build.sc", s.toList) - def meta(s: String) = { - val (List(selector), args) = ParseArgs.apply(Seq(s)).right.get - - read(workspacePath / "out" / selector._2.value.flatMap(_.pathSegments) / "meta.json") - } - - - def initWorkspace() = { - rm(workspacePath) - mkdir(workspacePath / up) - // The unzipped git repo snapshots we get from github come with a - // wrapper-folder inside the zip file, so copy the wrapper folder to the - // destination instead of the folder containing the wrapper. - - cp(scriptSourcePath, workspacePath) - } -} diff --git a/core/test/src/mill/util/TestEvaluator.scala b/core/test/src/mill/util/TestEvaluator.scala deleted file mode 100644 index a5be0488..00000000 --- a/core/test/src/mill/util/TestEvaluator.scala +++ /dev/null @@ -1,83 +0,0 @@ -package mill.util - -import ammonite.ops.{Path, pwd} -import mill.define.Discover.applyImpl -import mill.define.{Discover, Input, Target, Task} -import mill.eval.Result.OuterStack -import mill.eval.{Evaluator, Result} -import mill.util.Strict.Agg -import utest.assert -import utest.framework.TestPath - -import language.experimental.macros -object TestEvaluator{ - implicit def implicitDisover[T]: Discover[T] = macro applyImpl[T] - val externalOutPath = pwd / 'target / 'external - - - def static[T <: TestUtil.BaseModule](module: T) - (implicit discover: Discover[T], - fullName: sourcecode.FullName) = { - new TestEvaluator[T](module)(discover, fullName, TestPath(Nil)) - } -} - -class TestEvaluator[T <: TestUtil.BaseModule](module: T) - (implicit discover: Discover[T], - fullName: sourcecode.FullName, - tp: TestPath){ - val outPath = TestUtil.getOutPath() - - val logger = DummyLogger -// val logger = new PrintLogger(true, ammonite.util.Colors.Default, System.out, System.out, System.err) - val evaluator = new Evaluator(outPath, TestEvaluator.externalOutPath, module, discover, logger) - - def apply[T](t: Task[T]): Either[Result.Failing[T], (T, Int)] = { - val evaluated = evaluator.evaluate(Agg(t)) - - if (evaluated.failing.keyCount == 0) { - Right( - Tuple2( - evaluated.rawValues.head.asInstanceOf[Result.Success[T]].value, - evaluated.evaluated.collect { - case t: Target[_] - if module.millInternal.targets.contains(t) - && !t.isInstanceOf[Input[_]] - && !t.ctx.external => t - case t: mill.define.Command[_] => t - }.size - )) - } else { - Left( - evaluated.failing.lookupKey(evaluated.failing.keys().next).items.next() - .asInstanceOf[Result.Failing[T]] - ) - } - } - - def fail(target: Target[_], expectedFailCount: Int, expectedRawValues: Seq[Result[_]]) = { - - val res = evaluator.evaluate(Agg(target)) - - val cleaned = res.rawValues.map{ - case Result.Exception(ex, _) => Result.Exception(ex, new OuterStack(Nil)) - case x => x - } - - assert( - cleaned == expectedRawValues, - res.failing.keyCount == expectedFailCount - ) - - } - - def check(targets: Agg[Task[_]], expected: Agg[Task[_]]) = { - val evaluated = evaluator.evaluate(targets) - .evaluated - .flatMap(_.asTarget) - .filter(module.millInternal.targets.contains) - .filter(!_.isInstanceOf[Input[_]]) - assert(evaluated == expected) - } - -} diff --git a/core/test/src/mill/util/TestGraphs.scala b/core/test/src/mill/util/TestGraphs.scala deleted file mode 100644 index 581d5e0a..00000000 --- a/core/test/src/mill/util/TestGraphs.scala +++ /dev/null @@ -1,242 +0,0 @@ -package mill.util -import TestUtil.test -import mill.define.Cross -import mill.{Module, T} - -/** - * Example dependency graphs for us to use in our test suite. - * - * The graphs using `test()` live in the `class` and need to be instantiated - * every time you use them, because they are mutable (you can poke at the - * `test`'s `counter`/`failure`/`exception` fields to test various graph - * evaluation scenarios. - * - * The immutable graphs, used for testing discovery & target resolution, - * live in the companion object. - */ -class TestGraphs(){ - // single - object singleton extends TestUtil.BaseModule { - val single = test() - } - - // up---down - object pair extends TestUtil.BaseModule{ - val up = test() - val down = test(up) - } - - // up---o---down - object anonTriple extends TestUtil.BaseModule { - val up = test() - val down = test(test.anon(up)) - } - - // left - // / \ - // up down - // \ / - // right - object diamond extends TestUtil.BaseModule { - val up = test() - val left = test(up) - val right = test(up) - val down = test(left, right) - } - - // o - // / \ - // up down - // \ / - // o - object anonDiamond extends TestUtil.BaseModule { - val up = test() - val down = test(test.anon(up), test.anon(up)) - } - - object defCachedDiamond extends TestUtil.BaseModule { - def up = T{ test() } - def left = T{ test(up) } - def right = T{ test(up) } - def down = T{ test(left, right) } - } - - - object borkedCachedDiamond2 extends TestUtil.BaseModule { - def up = test() - def left = test(up) - def right = test(up) - def down = test(left, right) - } - - object borkedCachedDiamond3 extends TestUtil.BaseModule { - def up = test() - def left = test(up) - def right = test(up) - def down = test(left, right) - } - - // o g-----o - // \ \ \ - // o o h-----I---o - // \ / \ / \ / \ \ - // A---c--o E o-o \ \ - // / \ / \ / \ o---J - // o d o--o o / / - // \ / \ / / - // o o---F---o - // / / - // o--B o - object bigSingleTerminal extends TestUtil.BaseModule { - val a = test(test.anon(), test.anon()) - val b = test(test.anon()) - val e = { - val c = test.anon(a) - val d = test.anon(a) - test( - test.anon(test.anon(), test.anon(c)), - test.anon(test.anon(c, test.anon(d, b))) - ) - } - val f = test(test.anon(test.anon(), test.anon(e))) - - val i = { - val g = test.anon() - val h = test.anon(g, e) - test(test.anon(g), test.anon(test.anon(h))) - } - val j = test(test.anon(i), test.anon(i, f), test.anon(f)) - } - // _ left _ - // / \ - // task1 -------- right - // _/ - // change - task2 - object separateGroups extends TestUtil.BaseModule { - val task1 = T.task{ 1 } - def left = T{ task1() } - val change = test() - val task2 = T.task{ change() } - def right = T{ task1() + task2() + left() + 1 } - - } -} - - -object TestGraphs{ - // _ left _ - // / \ - // task -------- right - object triangleTask extends TestUtil.BaseModule { - val task = T.task{ 1 } - def left = T{ task() } - def right = T{ task() + left() + 1 } - } - - - // _ left - // / - // task -------- right - object multiTerminalGroup extends TestUtil.BaseModule { - val task = T.task{ 1 } - def left = T{ task() } - def right = T{ task() } - } - - // _ left _____________ - // / \ \ - // task1 -------- right ----- task2 - object multiTerminalBoundary extends TestUtil.BaseModule { - val task1 = T.task{ 1 } - def left = T{ task1() } - def right = T{ task1() + left() + 1 } - val task2 = T.task{ left() + right() } - } - - - trait CanNest extends Module{ - def single = T{ 1 } - def invisible: Any = T{ 2 } - def invisible2: mill.define.Task[Int] = T{ 3 } - def invisible3: mill.define.Task[_] = T{ 4 } - } - object nestedModule extends TestUtil.BaseModule { - def single = T{ 5 } - def invisible: Any = T{ 6 } - object nested extends Module{ - def single = T{ 7 } - def invisible: Any = T{ 8 } - - } - object classInstance extends CanNest - - } - - trait BaseModule extends Module { - def foo = T{ Seq("base") } - def cmd(i: Int) = T.command{ Seq("base" + i) } - } - - object canOverrideSuper extends TestUtil.BaseModule with BaseModule { - override def foo = T{ super.foo() ++ Seq("object") } - override def cmd(i: Int) = T.command{ super.cmd(i)() ++ Seq("object" + i) } - } - - trait TraitWithModule extends Module{ outer => - object TraitModule extends Module{ - def testFramework = T{ "mill.UTestFramework" } - def test() = T.command{ ()/*donothing*/ } - } - } - - - // Make sure nested objects inherited from traits work - object TraitWithModuleObject extends TestUtil.BaseModule with TraitWithModule - - - object singleCross extends TestUtil.BaseModule { - object cross extends mill.Cross[Cross]("210", "211", "212") - class Cross(scalaVersion: String) extends Module{ - def suffix = T{ scalaVersion } - } - } - object crossResolved extends TestUtil.BaseModule { - trait MyModule extends Module{ - def crossVersion: String - implicit object resolver extends mill.define.Cross.Resolver[MyModule]{ - def resolve[V <: MyModule](c: Cross[V]): V = c.itemMap(List(crossVersion)) - } - } - - object foo extends mill.Cross[FooModule]("2.10", "2.11", "2.12") - class FooModule(val crossVersion: String) extends MyModule{ - def suffix = T{ crossVersion } - } - - object bar extends mill.Cross[BarModule]("2.10", "2.11", "2.12") - class BarModule(val crossVersion: String) extends MyModule{ - def longSuffix = T{ "_" + foo().suffix() } - } - } - object doubleCross extends TestUtil.BaseModule { - val crossMatrix = for{ - scalaVersion <- Seq("210", "211", "212") - platform <- Seq("jvm", "js", "native") - if !(platform == "native" && scalaVersion != "212") - } yield (scalaVersion, platform) - object cross extends mill.Cross[Cross](crossMatrix:_*) - class Cross(scalaVersion: String, platform: String) extends Module{ - def suffix = T{ scalaVersion + "_" + platform } - } - } - - object nestedCrosses extends TestUtil.BaseModule { - object cross extends mill.Cross[Cross]("210", "211", "212") - class Cross(scalaVersion: String) extends mill.Module{ - object cross2 extends mill.Cross[Cross]("jvm", "js", "native") - class Cross(platform: String) extends mill.Module{ - def suffix = T{ scalaVersion + "_" + platform } - } - } - } -} diff --git a/core/test/src/mill/util/TestUtil.scala b/core/test/src/mill/util/TestUtil.scala deleted file mode 100644 index 7ef43943..00000000 --- a/core/test/src/mill/util/TestUtil.scala +++ /dev/null @@ -1,81 +0,0 @@ -package mill.util - -import mill.main.Router.Overrides -import ammonite.ops.pwd -import mill.define._ -import mill.eval.Result -import mill.eval.Result.OuterStack -import utest.assert -import mill.util.Strict.Agg -import utest.framework.TestPath - -import scala.collection.mutable - -object TestUtil { - def getOutPath()(implicit fullName: sourcecode.FullName, - tp: TestPath) = { - pwd / 'target / 'workspace / (fullName.value.split('.') ++ tp.value) - } - def getOutPathStatic()(implicit fullName: sourcecode.FullName) = { - pwd / 'target / 'workspace / fullName.value.split('.') - } - - def getSrcPathStatic()(implicit fullName: sourcecode.FullName) = { - pwd / 'target / 'worksources / fullName.value.split('.') - } - def getSrcPathBase() = { - pwd / 'target / 'worksources - } - - class BaseModule(implicit millModuleEnclosing0: sourcecode.Enclosing, - millModuleLine0: sourcecode.Line, - millName0: sourcecode.Name, - overrides: Overrides) - extends mill.define.BaseModule(getSrcPathBase() / millModuleEnclosing0.value.split("\\.| |#")){ - def millDiscover: Discover[this.type] = Discover[this.type] - } - - object test{ - - def anon(inputs: Task[Int]*) = new Test(inputs) - def apply(inputs: Task[Int]*) - (implicit ctx: mill.define.Ctx)= { - new TestTarget(inputs, pure = inputs.nonEmpty) - } - } - - class Test(val inputs: Seq[Task[Int]]) extends Task[Int]{ - var counter = 0 - var failure = Option.empty[String] - var exception = Option.empty[Throwable] - override def evaluate(args: Ctx) = { - failure.map(Result.Failure(_)) orElse - exception.map(Result.Exception(_, new OuterStack(Nil))) getOrElse - Result.Success(counter + args.args.map(_.asInstanceOf[Int]).sum) - } - override def sideHash = counter + failure.hashCode() + exception.hashCode() - } - /** - * A dummy target that takes any number of inputs, and whose output can be - * controlled externally, so you can construct arbitrary dataflow graphs and - * test how changes propagate. - */ - class TestTarget(inputs: Seq[Task[Int]], - val pure: Boolean) - (implicit ctx0: mill.define.Ctx) - extends Test(inputs) with Target[Int]{ - val ctx = ctx0.copy(segments = ctx0.segments ++ Seq(ctx0.segment)) - val readWrite = upickle.default.IntRW - - - } - def checkTopological(targets: Agg[Task[_]]) = { - val seen = mutable.Set.empty[Task[_]] - for(t <- targets.indexed.reverseIterator){ - seen.add(t) - for(upstream <- t.inputs){ - assert(!seen(upstream)) - } - } - } -} |