Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package mill.main
import java.util.concurrent.LinkedBlockingQueue
import mill.define.{BaseModule0, Command, NamedTask, Segments, Target, Task}
import mill.api.{Ctx, Logger, PathRef, Result, Val}
import mill.eval.{Evaluator, EvaluatorPaths, Terminal}
import mill.resolve.{Resolve, SelectMode}
import mill.resolve.SelectMode.Separated
import mill.util.Watchable
import pprint.{Renderer, Tree, Truncated}
import scala.collection.mutable
object MainModule {
def resolveTasks[T](
evaluator: Evaluator,
targets: Seq[String],
selectMode: SelectMode
)(f: List[NamedTask[Any]] => T): Result[T] = {
Resolve.Tasks.resolve(evaluator.rootModule, targets, selectMode) match {
case Left(err) => Result.Failure(err)
case Right(tasks) => Result.Success(f(tasks))
}
}
private def show0(
evaluator: Evaluator,
targets: Seq[String],
log: Logger,
watch0: Watchable => Unit
)(f: Seq[(Any, Option[(RunScript.TaskName, ujson.Value)])] => ujson.Value)
: Result[ujson.Value] = {
RunScript.evaluateTasksNamed(
// When using `show`, redirect all stdout of the evaluated tasks so the
// printed JSON is the only thing printed to stdout.
evaluator.withBaseLogger(
evaluator.baseLogger.withOutStream(evaluator.baseLogger.errorStream)
),
targets,
Separated
) match {
case Left(err) => Result.Failure(err)
case Right((watched, Left(err))) =>
watched.foreach(watch0)
Result.Failure(err)
case Right((watched, Right(res))) =>
val output = f(res)
watched.foreach(watch0)
log.withPromptPaused {
println(output.render(indent = 2))
}
Result.Success(output)
}
}
}
/**
* [[mill.define.Module]] containing all the default tasks that Mill provides: [[resolve]],
* [[show]], [[inspect]], [[plan]], etc.
*/
trait MainModule extends BaseModule0 {
object interp extends Interp
// implicit def millDiscover: mill.define.Discover[_]
/**
* Show the mill version.
*/
def version(): Command[String] = Target.command {
val res = BuildInfo.millVersion
Task.log.withPromptPaused {
println(res)
}
res
}
/**
* Resolves a mill query string and prints out the tasks it resolves to.
*/
def resolve(evaluator: Evaluator, targets: String*): Command[List[String]] = Target.command {
val resolved = Resolve.Segments.resolve(
evaluator.rootModule,
targets,
SelectMode.Multi
)
resolved match {
case Left(err) => Result.Failure(err)
case Right(resolvedSegmentsList) =>
val resolvedStrings = resolvedSegmentsList.map(_.render)
Task.log.withPromptPaused {
resolvedStrings.sorted.foreach(println)
}
Result.Success(resolvedStrings)
}
}
/**
* 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*): Command[Array[String]] = Target.command {
plan0(evaluator, targets) match {
case Left(err) => Result.Failure(err)
case Right(success) =>
val renderedTasks = success.map(_.segments.render)
Task.log.withPromptPaused {
renderedTasks.foreach(println)
}
Result.Success(renderedTasks)
}
}
private def plan0(evaluator: Evaluator, targets: Seq[String]) = {
Resolve.Tasks.resolve(
evaluator.rootModule,
targets,
SelectMode.Multi
) match {
case Left(err) => Left(err)
case Right(rs) =>
val (sortedGroups, _) = evaluator.plan(rs)
Right(sortedGroups.keys().collect { case r: Terminal.Labelled[_] => 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,
@mainargs.arg(positional = true) src: String,
@mainargs.arg(positional = true) dest: String
): Command[List[String]] =
Target.command {
val resolved = Resolve.Tasks.resolve(
evaluator.rootModule,
List(src, dest),
SelectMode.Multi
)
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 }
Task.log.withPromptPaused {
labels.foreach(println)
}
Result.Success(labels)
}
}
}
/**
* Displays metadata about the given task without actually running it.
*/
def inspect(evaluator: Evaluator, targets: String*): Command[String] = Target.command {
def resolveParents(c: Class[_]): Seq[Class[_]] = {
Seq(c) ++ Option(c.getSuperclass).toSeq.flatMap(resolveParents) ++ c.getInterfaces.flatMap(
resolveParents
)
}
def pprintTask(t: NamedTask[_], evaluator: Evaluator): Tree.Lazy = {
val seen = mutable.Set.empty[Task[_]]
def rec(t: Task[_]): Seq[Segments] = {
if (seen(t)) Nil // do nothing
else t match {
case t: mill.define.Target[_]
if evaluator.rootModule.millInternal.targets.contains(t) =>
Seq(t.ctx.segments)
case _ =>
seen.add(t)
t.inputs.flatMap(rec)
}
}
val annots = for {
c <- resolveParents(t.ctx.enclosingCls)
m <- c.getMethods
if m.getName == t.ctx.segment.pathSegments.head
a = m.getAnnotation(classOf[mill.moduledefs.Scaladoc])
if a != null
} yield a
val allDocs =
for (a <- annots.distinct)
yield mill.util.Util.cleanupScaladoc(a.value).map("\n " + _).mkString
pprint.Tree.Lazy { ctx =>
val mainMethodSig =
if (t.asCommand.isEmpty) List()
else {
val mainDataOpt = evaluator
.rootModule
.millDiscover
.value
.get(t.ctx.enclosingCls)
.flatMap(_._2.find(_.name == t.ctx.segments.parts.last))
.headOption
mainDataOpt match {
case Some(mainData) if mainData.renderedArgSigs.nonEmpty =>
val rendered = mainargs.Renderer.formatMainMethodSignature(
mainDataOpt.get,
leftIndent = 2,
totalWidth = 100,
leftColWidth = mainargs.Renderer.getLeftColWidth(mainData.renderedArgSigs),
docsOnNewLine = false,
customName = None,
customDoc = None,
sorted = true
)
// trim first line containing command name, since we already render
// the command name below with the filename and line num
val trimmedRendered = rendered
.linesIterator
.drop(1)
.mkString("\n")
List("\n", trimmedRendered, "\n")
case _ => List()
}
}
Iterator(
ctx.applyPrefixColor(t.toString).toString,
"(",
// handle both Windows or Unix separators
t.ctx.fileName.split('/').last.split('\\').last,
":",
t.ctx.lineNum.toString,
")",
allDocs.mkString("\n"),
"\n"
) ++
mainMethodSig.iterator ++
Iterator(
"\n",
ctx.applyPrefixColor("Inputs").toString,
":"
) ++ t.inputs.iterator.flatMap(rec).map("\n " + _.render).distinct
}
}
MainModule.resolveTasks(evaluator, targets, SelectMode.Multi) { tasks =>
val output = (for {
task <- tasks
tree = pprintTask(task, evaluator)
defaults = pprint.PPrinter()
renderer = new Renderer(
defaults.defaultWidth,
defaults.colorApplyPrefix,
defaults.colorLiteral,
defaults.defaultIndent
)
rendered = renderer.rec(tree, 0, 0).iter
truncated = new Truncated(rendered, defaults.defaultWidth, defaults.defaultHeight)
} yield {
val sb = new StringBuilder()
for { str <- truncated ++ Iterator("\n") } sb.append(str)
sb.toString()
}).mkString("\n")
Task.log.withPromptPaused {
println(output)
}
fansi.Str(output).plainText
}
}
/**
* 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*): Command[ujson.Value] = Target.command {
MainModule.show0(evaluator, targets, Target.log, interp.evalWatch0) { res =>
res.flatMap(_._2) match {
case Seq((k, singleValue)) => singleValue
case multiple => ujson.Obj.from(multiple)
}
}
}
/**
* Runs a given task and prints the results as JSON dictionary to stdout. This is useful
* to integrate Mill into external scripts and tooling.
*/
def showNamed(evaluator: Evaluator, targets: String*): Command[ujson.Value] = Target.command {
MainModule.show0(evaluator, targets, Target.log, interp.evalWatch0) { res =>
ujson.Obj.from(res.flatMap(_._2))
}
}
/**
* Deletes the given targets from the out directory. Providing no targets
* will clean everything.
*/
def clean(evaluator: Evaluator, targets: String*): Command[Seq[PathRef]] = Target.command {
val rootDir = evaluator.outPath
val KeepPattern = "(mill-.+)".r.anchored
def keepPath(path: os.Path) = path.last match {
case KeepPattern(_) => true
case _ => false
}
val pathsToRemove =
if (targets.isEmpty)
Right((os.list(rootDir).filterNot(keepPath), List(mill.define.Segments())))
else
mill.resolve.Resolve.Segments.resolve(
evaluator.rootModule,
targets,
SelectMode.Multi
).map { ts =>
val allPaths = ts.flatMap { segments =>
val evPaths = EvaluatorPaths.resolveDestPaths(rootDir, segments)
val paths = Seq(evPaths.dest, evPaths.meta, evPaths.log)
val potentialModulePath = rootDir / EvaluatorPaths.makeSegmentStrings(segments)
if (os.exists(potentialModulePath)) {
// this is either because of some pre-Mill-0.10 files lying around
// or most likely because the segments denote a module but not a task
// in which case we want to remove the module and all its sub-modules
// (If this logic is later found to be to harsh, we could further guard it,
// to when non of the other paths exists.)
paths :+ potentialModulePath
} else paths
}
(allPaths, ts)
}
pathsToRemove match {
case Left(err) =>
Result.Failure(err)
case Right((paths, allSegments)) =>
for {
workerSegments <- evaluator.workerCache.keys.toList
if allSegments.exists(workerSegments.startsWith)
(_, Val(closeable: AutoCloseable)) <- evaluator.mutableWorkerCache.remove(workerSegments)
} {
closeable.close()
}
val existing = paths.filter(p => os.exists(p))
Target.log.debug(s"Cleaning ${existing.size} paths ...")
existing.foreach(os.remove.all)
Result.Success(existing.map(PathRef(_)))
}
}
/**
* Renders the dependencies between the given tasks as a SVG for you to look at
*/
def visualize(evaluator: Evaluator, targets: String*): Command[Seq[PathRef]] = Target.command {
visualize0(evaluator, targets, Target.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*): Command[Seq[PathRef]] =
Target.command {
plan0(evaluator, targets) match {
case Left(err) => Result.Failure(err)
case Right(planResults) => visualize0(
evaluator,
targets,
Target.ctx(),
mill.main.VisualizeModule.worker(),
Some(planResults.toList.map(_.task))
)
}
}
/**
* Shuts down mill's background server
*/
def shutdown(): Command[Unit] = Target.command {
Target.log.info("Shutting down Mill server...")
Target.ctx.systemExit(0)
()
}
/**
* The `init` command generates a project based on a Giter8 template. It
* prompts you to enter project name and creates a folder with that name.
* You can use it to quickly generate a starter project. There are lots of
* templates out there for many frameworks and tools!
*/
def init(evaluator: Evaluator, args: String*): Command[Unit] = Target.command {
RunScript.evaluateTasksNamed(
evaluator,
Seq("mill.scalalib.giter8.Giter8Module/init") ++ args,
SelectMode.Separated
)
()
}
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
): Result[Seq[PathRef]] = {
def callVisualizeModule(
rs: List[NamedTask[Any]],
allRs: List[NamedTask[Any]]
): Result[Seq[PathRef]] = {
val (in, out) = vizWorker
in.put((rs, allRs, ctx.dest))
val res = out.take()
res.map { v =>
ctx.log.withPromptPaused {
println(upickle.default.write(v.map(_.path.toString()), indent = 2))
}
v
}
}
Resolve.Tasks.resolve(
evaluator.rootModule,
targets,
SelectMode.Multi
) match {
case Left(err) => Result.Failure(err)
case Right(rs) => planTasks match {
case Some(allRs) => {
callVisualizeModule(rs, allRs)
}
case None => callVisualizeModule(rs, rs)
}
}
}
}