diff options
Diffstat (limited to 'main/src/main/MainModule.scala')
-rw-r--r-- | main/src/main/MainModule.scala | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/main/src/main/MainModule.scala b/main/src/main/MainModule.scala new file mode 100644 index 00000000..dbe92cc2 --- /dev/null +++ b/main/src/main/MainModule.scala @@ -0,0 +1,273 @@ +package mill.main + +import java.util.concurrent.LinkedBlockingQueue + +import mill.T +import mill.define.{NamedTask, Task} +import mill.eval.{Evaluator, PathRef, Result} +import mill.util.{Ctx, PrintLogger, Watched} +import pprint.{Renderer, Truncated} +object MainModule{ + def resolveTasks[T](evaluator: Evaluator, targets: Seq[String], multiSelect: Boolean) + (f: List[NamedTask[Any]] => T) = { + RunScript.resolveTasks(mill.main.ResolveTasks, evaluator, targets, multiSelect) match{ + case Left(err) => Result.Failure(err) + case Right(tasks) => Result.Success(f(tasks)) + } + } + def evaluateTasks[T](evaluator: Evaluator, targets: Seq[String], multiSelect: Boolean) + (f: Seq[(Any, Option[ujson.Value])] => T) = { + RunScript.evaluateTasks(evaluator, targets, multiSelect) match{ + case Left(err) => Result.Failure(err) + case Right((watched, Left(err))) => Result.Failure(err, Some(Watched((), watched))) + case Right((watched, Right(res))) => + f(res) + Result.Success(Watched((), watched)) + } + } +} + +trait MainModule extends mill.Module{ + + implicit def millDiscover: mill.define.Discover[_] + implicit def millScoptTasksReads[T] = new mill.main.Tasks.Scopt[T]() + implicit def millScoptEvaluatorReads[T] = new mill.main.EvaluatorScopt[T]() + + /** + * Show the mill version. + */ + def version() = mill.T.command { + val res = System.getProperty("MILL_VERSION") + println(res) + res + } + + private val OutDir: String = "out" + + /** + * Resolves a mill query string and prints out the tasks it resolves to. + */ + def resolve(evaluator: Evaluator, targets: String*) = mill.T.command{ + val resolved = RunScript.resolveTasks( + mill.main.ResolveMetadata, evaluator, targets, multiSelect = true + ) + + resolved match{ + case Left(err) => Result.Failure(err) + case Right(rs) => + for(r <- rs.sorted) { + println(r) + } + Result.Success(()) + } + } + + /** + * Given a set of tasks, prints out the execution plan of what tasks will be + * executed in what order, without actually executing them. + */ + def plan(evaluator: Evaluator, targets: String*) = mill.T.command{ + plan0(evaluator, targets) match{ + case Right(success) => { + val renderedTasks = success.map{ _.segments.render} + renderedTasks.foreach(println) + Result.Success(renderedTasks) + } + case Left(err) => Result.Failure(err) + } + } + + private def plan0(evaluator: Evaluator, targets: Seq[String]) = { + RunScript.resolveTasks( + mill.main.ResolveTasks, evaluator, targets, multiSelect = true + ) match { + case Left(err) => Left(err) + case Right(rs) => + val (sortedGroups, _) = Evaluator.plan(evaluator.rootModule, rs) + Right(sortedGroups.keys().collect{ case Right(r) => r}.toArray) + } + } + + /** + * Prints out some dependency path from the `src` task to the `dest` task. + * + * If there are multiple dependency paths between `src` and `dest`, the path + * chosen is arbitrary. + */ + def path(evaluator: Evaluator, src: String, dest: String) = mill.T.command{ + val resolved = RunScript.resolveTasks( + mill.main.ResolveTasks, evaluator, List(src, dest), multiSelect = true + ) + + resolved match{ + case Left(err) => Result.Failure(err) + case Right(Seq(src1, dest1)) => + val queue = collection.mutable.Queue[List[Task[_]]](List(src1)) + var found = Option.empty[List[Task[_]]] + val seen = collection.mutable.Set.empty[Task[_]] + while(queue.nonEmpty && found.isEmpty){ + val current = queue.dequeue() + if (current.head == dest1) found = Some(current) + else{ + for{ + next <- current.head.inputs + if !seen.contains(next) + }{ + seen.add(next) + queue.enqueue(next :: current) + } + } + } + found match{ + case None => + Result.Failure(s"No path found between $src and $dest") + case Some(list) => + val labels = list + .collect{case n: NamedTask[_] => n.ctx.segments.render} + + labels.foreach(mill.T.ctx().log.outputStream.println(_)) + + Result.Success(labels) + } + } + } + + /** + * Displays metadata about the given task without actually running it. + */ + def inspect(evaluator: Evaluator, targets: String*) = mill.T.command{ + MainModule.resolveTasks(evaluator, targets, multiSelect = true){ tasks => + val output = new StringBuilder + for{ + task <- tasks + tree = ReplApplyHandler.pprintTask(task, evaluator) + val defaults = pprint.PPrinter() + val renderer = new Renderer( + defaults.defaultWidth, + defaults.colorApplyPrefix, + defaults.colorLiteral, + defaults.defaultIndent + ) + val rendered = renderer.rec(tree, 0, 0).iter + val truncated = new Truncated(rendered, defaults.defaultWidth, defaults.defaultHeight) + str <- truncated ++ Iterator("\n") + } { + output.append(str) + } + println(output) + output.toString + } + } + + /** + * Runs multiple tasks in a single call. + * + * + */ + def all(evaluator: Evaluator, targets: String*) = mill.T.command{ + MainModule.evaluateTasks(evaluator, targets, multiSelect = true) {res => + res.flatMap(_._2) + } + } + + /** + * Runs a given task and prints the JSON result to stdout. This is useful + * to integrate Mill into external scripts and tooling. + */ + def show(evaluator: Evaluator, targets: String*) = mill.T.command{ + MainModule.evaluateTasks( + evaluator.copy( + // When using `show`, redirect all stdout of the evaluated tasks so the + // printed JSON is the only thing printed to stdout. + log = evaluator.log match{ + case PrintLogger(c1, d, c2, o, i, e, in, de) => PrintLogger(c1, d, c2, e, i, e, in, de) + case l => l + } + ), + targets, + multiSelect = false + ) {res => + for(json <- res.flatMap(_._2)){ + println(json.render(indent = 4)) + } + } + } + + /** + * Deletes the given targets from the out directory. Providing no targets + * will clean everything. + */ + def clean(evaluator: Evaluator, targets: String*) = mill.T.command { + val rootDir = ammonite.ops.pwd / OutDir + + val KeepPattern = "(mill-.+)".r.anchored + + def keepPath(path: os.Path) = path.segments.toSeq.lastOption match { + case Some(KeepPattern(_)) => true + case _ => false + } + + val pathsToRemove = + if (targets.isEmpty) + Right(ammonite.ops.ls(rootDir).filterNot(keepPath)) + else + RunScript.resolveTasks( + mill.main.ResolveSegments, evaluator, targets, multiSelect = true + ).map( + _.map { segments => + Evaluator.resolveDestPaths(rootDir, segments).out + }) + + pathsToRemove match { + case Left(err) => + Result.Failure(err) + case Right(paths) => + paths.foreach(os.remove.all) + Result.Success(()) + } + } + + + /** + * Renders the dependencies between the given tasks as a SVG for you to look at + */ + def visualize(evaluator: Evaluator, targets: String*) = mill.T.command{ + visualize0(evaluator, targets, T.ctx(), mill.main.VisualizeModule.worker()) + } + + /** + * Renders the dependencies between the given tasks, and all their dependencies, as a SVG + */ + def visualizePlan(evaluator: Evaluator, targets: String*) = mill.T.command{ + plan0(evaluator, targets) match { + case Left(err) => Result.Failure(err) + case Right(planResults) => visualize0( + evaluator, targets, T.ctx(), mill.main.VisualizeModule.worker(), Some(planResults.toList.map(_.task)) + ) + } + } + + private type VizWorker = (LinkedBlockingQueue[(scala.Seq[_], scala.Seq[_], os.Path)], + LinkedBlockingQueue[Result[scala.Seq[PathRef]]]) + + private def visualize0(evaluator: Evaluator, targets: Seq[String], ctx: Ctx, vizWorker: VizWorker, + planTasks: Option[List[NamedTask[_]]] = None) = { + def callVisualizeModule(rs: List[NamedTask[Any]], allRs: List[NamedTask[Any]]) = { + val (in, out) = vizWorker + in.put((rs, allRs, ctx.dest)) + out.take() + } + + RunScript.resolveTasks( + mill.main.ResolveTasks, evaluator, targets, multiSelect = true + ) match { + case Left(err) => Result.Failure(err) + case Right(rs) => planTasks match { + case Some(allRs) => { + callVisualizeModule(rs, allRs) + } + case None => callVisualizeModule(rs, rs) + } + } + } +} |