summaryrefslogtreecommitdiff
path: root/main/src/main/MainModule.scala
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/main/MainModule.scala')
-rw-r--r--main/src/main/MainModule.scala273
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)
+ }
+ }
+ }
+}