From e3eea969a54c2d53322111ac22ac685b52918c9c Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Fri, 27 Sep 2024 17:17:40 +0700 Subject: [PATCH 1/5] . --- main/define/src/mill/define/Task.scala | 40 ++++++++++++++++++++- main/eval/src/mill/eval/EvaluatorCore.scala | 11 ++++-- scalalib/src/mill/scalalib/TestModule.scala | 8 ++--- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/main/define/src/mill/define/Task.scala b/main/define/src/mill/define/Task.scala index 1757d762a7a..d3cbd39e5d9 100644 --- a/main/define/src/mill/define/Task.scala +++ b/main/define/src/mill/define/Task.scala @@ -122,6 +122,15 @@ object Task extends TaskBase { cls: EnclosingClass ): Command[T] = macro Target.Internal.commandImpl[T] + /** + * [[ParallelCommand]] is a variant of [[Command]] that is safe to run in parallel + */ + def ParallelCommand[T](t: Result[T])(implicit + w: W[T], + ctx: mill.define.Ctx, + cls: EnclosingClass + ): Command[T] = macro Target.Internal.parallelCommandImpl[T] + /** * [[Worker]] is a [[NamedTask]] that lives entirely in-memory, defined using * `Task.Worker{...}`. The value returned by `Task.Worker{...}` is long-lived, @@ -521,6 +530,27 @@ object Target extends TaskBase { ) } + def parallelCommandImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T])( + w: c.Expr[W[T]], + ctx: c.Expr[mill.define.Ctx], + cls: c.Expr[EnclosingClass] + ): c.Expr[Command[T]] = { + import c.universe._ + + val taskIsPrivate = isPrivateTargetOption(c) + + reify( + new Command[T]( + Applicative.impl[Task, T, mill.api.Ctx](c)(t).splice, + ctx.splice, + w.splice, + cls.splice.value, + taskIsPrivate.splice, + parallelizable = true + ) + ) + } + def workerImpl1[T: c.WeakTypeTag](c: Context)(t: c.Expr[Task[T]])(ctx: c.Expr[mill.define.Ctx]) : c.Expr[Worker[T]] = { import c.universe._ @@ -693,8 +723,16 @@ class Command[+T]( val ctx0: mill.define.Ctx, val writer: W[_], val cls: Class[_], - val isPrivate: Option[Boolean] + val isPrivate: Option[Boolean], + val parallelizable: Boolean ) extends NamedTask[T] { + def this( + t: Task[T], + ctx0: mill.define.Ctx, + writer: W[_], + cls: Class[_], + isPrivate: Option[Boolean] + ) = this(t, ctx0, writer, cls, isPrivate, false) override def asCommand: Some[Command[T]] = Some(this) // FIXME: deprecated return type: Change to Option override def writerOpt: Some[W[_]] = Some(writer) diff --git a/main/eval/src/mill/eval/EvaluatorCore.scala b/main/eval/src/mill/eval/EvaluatorCore.scala index 8e556bf06a0..ddadf2c6ff6 100644 --- a/main/eval/src/mill/eval/EvaluatorCore.scala +++ b/main/eval/src/mill/eval/EvaluatorCore.scala @@ -180,8 +180,13 @@ private[mill] trait EvaluatorCore extends GroupEvaluator { val (_, tasksTransitive0) = Plan.plan(Agg.from(tasks0.map(_.task))) val tasksTransitive = tasksTransitive0.toSet - val (tasks, leafCommands) = terminals0.partition { - case Terminal.Labelled(t, _) if tasksTransitive.contains(t) => true + val (tasks, leafSerialCommands) = terminals0.partition { + case Terminal.Labelled(t, _) => + if (tasksTransitive.contains(t)) true + else t match { + case t: Command[_] => t.parallelizable + case _ => false + } case _ => !serialCommandExec } @@ -194,7 +199,7 @@ private[mill] trait EvaluatorCore extends GroupEvaluator { if (sys.env.contains(EnvVars.MILL_TEST_SUITE)) _ => "" else contextLoggerMsg0 )(ec) - evaluateTerminals(leafCommands, _ => "")(ExecutionContexts.RunNow) + evaluateTerminals(leafSerialCommands, _ => "")(ExecutionContexts.RunNow) logger.clearAllTickers() val finishedOptsMap = terminals0 diff --git a/scalalib/src/mill/scalalib/TestModule.scala b/scalalib/src/mill/scalalib/TestModule.scala index 1c7df978626..ac29d3fe59e 100644 --- a/scalalib/src/mill/scalalib/TestModule.scala +++ b/scalalib/src/mill/scalalib/TestModule.scala @@ -72,12 +72,12 @@ trait TestModule * @see [[testCached]] */ def test(args: String*): Command[(String, Seq[TestResult])] = - Task.Command { + Task.ParallelCommand { testTask(Task.Anon { args }, Task.Anon { Seq.empty[String] })() } def getTestEnvironmentVars(args: String*): Command[(String, String, String, Seq[String])] = { - Task.Command { + Task.ParallelCommand { getTestEnvironmentVarsTask(Task.Anon { args })() } } @@ -113,7 +113,7 @@ trait TestModule val (s, t) = args.splitAt(pos) (s, t.tail) } - Task.Command { + Task.ParallelCommand { testTask(Task.Anon { testArgs }, Task.Anon { selector })() } } @@ -265,7 +265,7 @@ trait TestModule * Discovers and runs the module's tests in-process in an isolated classloader, * reporting the results to the console */ - def testLocal(args: String*): Command[(String, Seq[TestResult])] = Task.Command { + def testLocal(args: String*): Command[(String, Seq[TestResult])] = Task.ParallelCommand { val (doneMsg, results) = TestRunner.runTestFramework( Framework.framework(testFramework()), runClasspath().map(_.path), From d1b7789e6a9301c9964a0c923b0e0e2da38b3a3a Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Fri, 27 Sep 2024 20:41:04 +0700 Subject: [PATCH 2/5] . --- .../src/mill/playlib/RouterModule.scala | 2 +- .../contrib/scalapblib/ScalaPBModule.scala | 2 +- .../contrib/scoverage/ScoverageModule.scala | 2 +- .../src/mill/twirllib/TwirlModule.scala | 2 +- .../tasks/5-persistent-targets/build.mill | 2 +- .../mill/define/NamedParameterOnlyDummy.scala | 6 + main/define/src/mill/define/Task.scala | 112 +++-- .../src/mill/define/MacroErrorTests.scala | 2 +- main/eval/src/mill/eval/EvaluatorCore.scala | 2 +- main/eval/src/mill/eval/Terminal.scala | 2 +- main/eval/test/src/mill/eval/TaskTests.scala | 2 +- main/src/mill/main/MainModule.scala | 398 +++++++++--------- .../src/mill/runner/MillBuildRootModule.scala | 2 +- .../src/mill/scalajslib/ScalaJSModule.scala | 6 +- .../src/mill/scalalib/GenIdeaModule.scala | 2 +- scalalib/src/mill/scalalib/JavaModule.scala | 2 +- scalalib/src/mill/scalalib/ScalaModule.scala | 8 +- .../mill/scalalib/SemanticDbJavaModule.scala | 2 +- scalalib/src/mill/scalalib/TestModule.scala | 8 +- 19 files changed, 308 insertions(+), 256 deletions(-) create mode 100644 main/define/src/mill/define/NamedParameterOnlyDummy.scala diff --git a/contrib/playlib/src/mill/playlib/RouterModule.scala b/contrib/playlib/src/mill/playlib/RouterModule.scala index 057af085f27..fdfeaa2605f 100644 --- a/contrib/playlib/src/mill/playlib/RouterModule.scala +++ b/contrib/playlib/src/mill/playlib/RouterModule.scala @@ -60,7 +60,7 @@ trait RouterModule extends ScalaModule with Version { protected val routeCompilerWorker: RouteCompilerWorkerModule = RouteCompilerWorkerModule - def compileRouter: T[CompilationResult] = Task.Persistent { + def compileRouter: T[CompilationResult] = Task(persistent = true) { T.log.debug(s"compiling play routes with ${playVersion()} worker") routeCompilerWorker.routeCompilerWorker().compile( routerClasspath = playRouterToolsClasspath(), diff --git a/contrib/scalapblib/src/mill/contrib/scalapblib/ScalaPBModule.scala b/contrib/scalapblib/src/mill/contrib/scalapblib/ScalaPBModule.scala index e9f47410dfa..e28fa336f02 100644 --- a/contrib/scalapblib/src/mill/contrib/scalapblib/ScalaPBModule.scala +++ b/contrib/scalapblib/src/mill/contrib/scalapblib/ScalaPBModule.scala @@ -126,7 +126,7 @@ trait ScalaPBModule extends ScalaModule { ) } - def compileScalaPB: T[PathRef] = Task.Persistent { + def compileScalaPB: T[PathRef] = Task(persistent = true) { ScalaPBWorkerApi.scalaPBWorker() .compile( scalaPBClasspath(), diff --git a/contrib/scoverage/src/mill/contrib/scoverage/ScoverageModule.scala b/contrib/scoverage/src/mill/contrib/scoverage/ScoverageModule.scala index 2a37d264d1d..fba21cb2ccd 100644 --- a/contrib/scoverage/src/mill/contrib/scoverage/ScoverageModule.scala +++ b/contrib/scoverage/src/mill/contrib/scoverage/ScoverageModule.scala @@ -146,7 +146,7 @@ trait ScoverageModule extends ScalaModule { outer: ScalaModule => * The persistent data dir used to store scoverage coverage data. * Use to store coverage data at compile-time and by the various report targets. */ - def data: T[PathRef] = Task.Persistent { + def data: T[PathRef] = Task(persistent = true) { // via the persistent target, we ensure, the dest dir doesn't get cleared PathRef(T.dest) } diff --git a/contrib/twirllib/src/mill/twirllib/TwirlModule.scala b/contrib/twirllib/src/mill/twirllib/TwirlModule.scala index c0abb5e3823..c2d6fad03b8 100644 --- a/contrib/twirllib/src/mill/twirllib/TwirlModule.scala +++ b/contrib/twirllib/src/mill/twirllib/TwirlModule.scala @@ -88,7 +88,7 @@ trait TwirlModule extends mill.Module { twirlModule => def twirlInclusiveDot: Boolean = false - def compileTwirl: T[mill.scalalib.api.CompilationResult] = Task.Persistent { + def compileTwirl: T[mill.scalalib.api.CompilationResult] = Task(persistent = true) { TwirlWorkerApi.twirlWorker .compile( twirlClasspath(), diff --git a/example/depth/tasks/5-persistent-targets/build.mill b/example/depth/tasks/5-persistent-targets/build.mill index e77fec6df17..3358159f9b0 100644 --- a/example/depth/tasks/5-persistent-targets/build.mill +++ b/example/depth/tasks/5-persistent-targets/build.mill @@ -12,7 +12,7 @@ import java.util.zip.GZIPOutputStream def data = Task.Source(millSourcePath / "data") -def compressedData = Task.Persistent{ +def compressedData = Task(persistent = true) { println("Evaluating compressedData") os.makeDir.all(Task.dest / "cache") os.remove.all(Task.dest / "compressed") diff --git a/main/define/src/mill/define/NamedParameterOnlyDummy.scala b/main/define/src/mill/define/NamedParameterOnlyDummy.scala new file mode 100644 index 00000000000..6bd963f09a1 --- /dev/null +++ b/main/define/src/mill/define/NamedParameterOnlyDummy.scala @@ -0,0 +1,6 @@ +package mill.define + +/** + * Dummy class used to mark parameters that come after it as named only parameters + */ +object NamedParameterOnlyDummy diff --git a/main/define/src/mill/define/Task.scala b/main/define/src/mill/define/Task.scala index d3cbd39e5d9..94802ee2a5f 100644 --- a/main/define/src/mill/define/Task.scala +++ b/main/define/src/mill/define/Task.scala @@ -45,22 +45,6 @@ abstract class Task[+T] extends Task.Ops[T] with Applyable[Task, T] { object Task extends TaskBase { - /** - * [[PersistentImpl]] are a flavor of [[TargetImpl]], normally defined using - * the `Task.Persistent{...}` syntax. The main difference is that while - * [[TargetImpl]] deletes the `T.dest` folder in between runs, - * [[PersistentImpl]] preserves it. This lets the user make use of files on - * disk that persistent between runs of the task, e.g. to implement their own - * fine-grained caching beyond what Mill provides by default. - * - * Note that the user defining a `Task.Persistent` task is taking on the - * responsibility of ensuring that their implementation is idempotent, i.e. - * that it computes the same result whether or not there is data in `T.dest`. - * Violating that invariant can result in confusing mis-behaviors - */ - def Persistent[T](t: Result[T])(implicit rw: RW[T], ctx: mill.define.Ctx): Target[T] = - macro Target.Internal.persistentImpl[T] - /** * A specialization of [[InputImpl]] defined via `Task.Sources`, [[SourcesImpl]] * uses [[PathRef]]s to compute a signature for a set of source files and @@ -122,14 +106,17 @@ object Task extends TaskBase { cls: EnclosingClass ): Command[T] = macro Target.Internal.commandImpl[T] - /** - * [[ParallelCommand]] is a variant of [[Command]] that is safe to run in parallel - */ - def ParallelCommand[T](t: Result[T])(implicit - w: W[T], - ctx: mill.define.Ctx, - cls: EnclosingClass - ): Command[T] = macro Target.Internal.parallelCommandImpl[T] + def Command( + t: NamedParameterOnlyDummy.type = NamedParameterOnlyDummy, + serial: Boolean = false + ): CommandFactory = new CommandFactory(serial) + class CommandFactory private[mill] (val serial: Boolean) extends TaskBase.TraverseCtxHolder { + def apply[T](t: Result[T])(implicit + w: W[T], + ctx: mill.define.Ctx, + cls: EnclosingClass + ): Command[T] = macro Target.Internal.serialCommandImpl[T] + } /** * [[Worker]] is a [[NamedTask]] that lives entirely in-memory, defined using @@ -169,6 +156,30 @@ object Task extends TaskBase { def apply[T](t: Result[T])(implicit rw: RW[T], ctx: mill.define.Ctx): Target[T] = macro Target.Internal.targetResultImpl[T] + /** + * Persistent tasks are defined using + * the `Task(persistent = true){...}` syntax. The main difference is that while + * [[TargetImpl]] deletes the `T.dest` folder in between runs, + * [[PersistentImpl]] preserves it. This lets the user make use of files on + * disk that persistent between runs of the task, e.g. to implement their own + * fine-grained caching beyond what Mill provides by default. + * + * Note that the user defining a `Task(persistent = true)` task is taking on the + * responsibility of ensuring that their implementation is idempotent, i.e. + * that it computes the same result whether or not there is data in `T.dest`. + * Violating that invariant can result in confusing mis-behaviors + */ + def apply( + t: NamedParameterOnlyDummy.type = NamedParameterOnlyDummy, + persistent: Boolean = false + ): ApplyFactory = new ApplyFactory(persistent) + class ApplyFactory private[mill] (val persistent: Boolean) extends TaskBase.TraverseCtxHolder { + def apply[T](t: Result[T])(implicit + rw: RW[T], + ctx: mill.define.Ctx + ): Target[T] = macro Target.Internal.persistentTargetResultImpl[T] + } + abstract class Ops[+T] { this: Task[T] => def map[V](f: T => V): Task[V] = new Task.Mapped(this, f) def filter(f: T => Boolean): Task[T] = this @@ -247,7 +258,7 @@ trait NamedTask[+T] extends Task[T] { trait Target[+T] extends NamedTask[T] object Target extends TaskBase { - @deprecated("Use Task.Persistent instead", "Mill after 0.12.0-RC1") + @deprecated("Use Task(persistent = true){...} instead", "Mill after 0.12.0-RC1") def persistent[T](t: Result[T])(implicit rw: RW[T], ctx: mill.define.Ctx): Target[T] = macro Target.Internal.persistentImpl[T] @@ -371,6 +382,28 @@ object Target extends TaskBase { ) ) } + def persistentTargetResultImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[Result[T]])( + rw: c.Expr[RW[T]], + ctx: c.Expr[mill.define.Ctx] + ): c.Expr[Target[T]] = { + import c.universe._ + + val taskIsPrivate = isPrivateTargetOption(c) + + mill.moduledefs.Cacher.impl0[Target[T]](c)( + reify { + val s1 = Applicative.impl0[Task, T, mill.api.Ctx](c)(t.tree).splice + val c1 = ctx.splice + val r1 = rw.splice + val t1 = taskIsPrivate.splice + if (c.prefix.splice.asInstanceOf[Task.ApplyFactory].persistent) { + new PersistentImpl[T](s1, c1, r1, t1) + } else { + new TargetImpl[T](s1, c1, r1, t1) + } + } + ) + } def targetTaskImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[Task[T]])( rw: c.Expr[RW[T]], @@ -530,7 +563,7 @@ object Target extends TaskBase { ) } - def parallelCommandImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T])( + def serialCommandImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T])( w: c.Expr[W[T]], ctx: c.Expr[mill.define.Ctx], cls: c.Expr[EnclosingClass] @@ -546,7 +579,7 @@ object Target extends TaskBase { w.splice, cls.splice.value, taskIsPrivate.splice, - parallelizable = true + serial = c.prefix.splice.asInstanceOf[Task.CommandFactory].serial ) ) } @@ -611,7 +644,8 @@ object Target extends TaskBase { * define the tasks, while methods like `T.`[[dest]], `T.`[[log]] or * `T.`[[env]] provide the core APIs that are provided to a task implementation */ -class TaskBase extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { +class TaskBase extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] + with TaskBase.TraverseCtxHolder { /** * `T.dest` is a unique `os.Path` (e.g. `out/classFiles.dest/` or `out/run.dest/`) @@ -686,16 +720,20 @@ class TaskBase extends Applicative.Applyer[Task, Task, Result, mill.api.Ctx] { def traverse[T, V](source: Seq[T])(f: T => Task[V]): Task[Seq[V]] = { new Task.Sequence[V](source.map(f)) } +} - /** - * A variant of [[traverse]] that also provides the [[mill.api.Ctx]] to the - * function [[f]] - */ - def traverseCtx[I, R](xs: Seq[Task[I]])(f: (IndexedSeq[I], mill.api.Ctx) => Result[R]) - : Task[R] = { - new Task.TraverseCtx[I, R](xs, f) - } +object TaskBase { + trait TraverseCtxHolder { + /** + * A variant of [[traverse]] that also provides the [[mill.api.Ctx]] to the + * function [[f]] + */ + def traverseCtx[I, R](xs: Seq[Task[I]])(f: (IndexedSeq[I], mill.api.Ctx) => Result[R]) + : Task[R] = { + new Task.TraverseCtx[I, R](xs, f) + } + } } class TargetImpl[+T]( @@ -724,7 +762,7 @@ class Command[+T]( val writer: W[_], val cls: Class[_], val isPrivate: Option[Boolean], - val parallelizable: Boolean + val serial: Boolean ) extends NamedTask[T] { def this( t: Task[T], diff --git a/main/define/test/src/mill/define/MacroErrorTests.scala b/main/define/test/src/mill/define/MacroErrorTests.scala index 0ca8263319c..a4ecbfa2e46 100644 --- a/main/define/test/src/mill/define/MacroErrorTests.scala +++ b/main/define/test/src/mill/define/MacroErrorTests.scala @@ -69,7 +69,7 @@ object MacroErrorTests extends TestSuite { test("persistent") { val e = compileError(""" object foo extends TestBaseModule{ - def a() = Task.Persistent{1} + def a() = Task(persistent = true){1} } mill.define.Discover[foo.type] """) diff --git a/main/eval/src/mill/eval/EvaluatorCore.scala b/main/eval/src/mill/eval/EvaluatorCore.scala index ddadf2c6ff6..d8e334a2adb 100644 --- a/main/eval/src/mill/eval/EvaluatorCore.scala +++ b/main/eval/src/mill/eval/EvaluatorCore.scala @@ -184,7 +184,7 @@ private[mill] trait EvaluatorCore extends GroupEvaluator { case Terminal.Labelled(t, _) => if (tasksTransitive.contains(t)) true else t match { - case t: Command[_] => t.parallelizable + case t: Command[_] => !t.serial case _ => false } case _ => !serialCommandExec diff --git a/main/eval/src/mill/eval/Terminal.scala b/main/eval/src/mill/eval/Terminal.scala index 0bb6c952406..23955c82e05 100644 --- a/main/eval/src/mill/eval/Terminal.scala +++ b/main/eval/src/mill/eval/Terminal.scala @@ -5,7 +5,7 @@ import mill.define.{NamedTask, Segment, Segments} /** * A terminal or terminal target is some important work unit, that in most cases has a name (Right[Labelled]) * or was directly called by the user (Left[Task]). - * It's a T, Task.Worker, Task.Input, Task.Source, Task.Sources, Task.Persistent + * It's a Task, Task.Worker, Task.Input, Task.Source, Task.Sources, Task.Command */ sealed trait Terminal { def render: String diff --git a/main/eval/test/src/mill/eval/TaskTests.scala b/main/eval/test/src/mill/eval/TaskTests.scala index 146beddbc91..167d2cd459a 100644 --- a/main/eval/test/src/mill/eval/TaskTests.scala +++ b/main/eval/test/src/mill/eval/TaskTests.scala @@ -70,7 +70,7 @@ trait TaskTests extends TestSuite { def taskInput = Task { input() } def taskNoInput = Task { task() } - def persistent = Task.Persistent { + def persistent = Task(persistent = true) { input() // force re-computation os.makeDir.all(T.dest) os.write.append(T.dest / "count", "hello\n") diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index c84bc031705..22503e3c821 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -69,7 +69,7 @@ trait MainModule extends BaseModule0 { /** * Show the mill version. */ - def version(): Command[String] = Target.command { + def version(): Command[String] = Task.Command(serial = true) { val res = BuildInfo.millVersion Task.log.withPromptPaused { println(res) @@ -80,39 +80,41 @@ trait MainModule extends BaseModule0 { /** * 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 - ) + def resolve(evaluator: Evaluator, targets: String*): Command[List[String]] = + Task.Command(serial = true) { + 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) + 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) + def plan(evaluator: Evaluator, targets: String*): Command[Array[String]] = + Task.Command(serial = true) { + 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( @@ -139,7 +141,7 @@ trait MainModule extends BaseModule0 { @mainargs.arg(positional = true) src: String, @mainargs.arg(positional = true) dest: String ): Command[List[String]] = - Target.command { + Task.Command(serial = true) { val resolved = Resolve.Tasks.resolve( evaluator.rootModule, List(src, dest), @@ -183,216 +185,222 @@ trait MainModule extends BaseModule0 { /** * Displays metadata about the given task without actually running it. */ - def inspect(evaluator: Evaluator, targets: String*): Command[String] = Target.command { + def inspect(evaluator: Evaluator, targets: String*): Command[String] = + Task.Command(serial = true) { - 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) - } + 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() + 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 + 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) + 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 } - 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) + def show(evaluator: Evaluator, targets: String*): Command[ujson.Value] = + Task.Command(serial = true) { + 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)) + def showNamed(evaluator: Evaluator, targets: String*): Command[ujson.Value] = + Task.Command(serial = true) { + 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 + def clean(evaluator: Evaluator, targets: String*): Command[Seq[PathRef]] = + Task.Command(serial = true) { + val rootDir = evaluator.outPath - val KeepPattern = "(mill-.+)".r.anchored + val KeepPattern = "(mill-.+)".r.anchored - def keepPath(path: os.Path) = path.last match { - case KeepPattern(_) => true - case _ => false - } + 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 + 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) } - (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() - } + 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(_))) + 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()) - } + def visualize(evaluator: Evaluator, targets: String*): Command[Seq[PathRef]] = + Task.Command(serial = true) { + 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 { + Task.Command(serial = true) { plan0(evaluator, targets) match { case Left(err) => Result.Failure(err) case Right(planResults) => visualize0( @@ -408,7 +416,7 @@ trait MainModule extends BaseModule0 { /** * Shuts down mill's background server */ - def shutdown(): Command[Unit] = Target.command { + def shutdown(): Command[Unit] = Task.Command(serial = true) { Target.log.info("Shutting down Mill server...") Target.ctx.systemExit(0) () @@ -420,7 +428,7 @@ trait MainModule extends BaseModule0 { * 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 { + def init(evaluator: Evaluator, args: String*): Command[Unit] = Task.Command(serial = true) { RunScript.evaluateTasksNamed( evaluator, Seq("mill.scalalib.giter8.Giter8Module/init") ++ args, diff --git a/runner/src/mill/runner/MillBuildRootModule.scala b/runner/src/mill/runner/MillBuildRootModule.scala index 729e6b2f240..af4ba784834 100644 --- a/runner/src/mill/runner/MillBuildRootModule.scala +++ b/runner/src/mill/runner/MillBuildRootModule.scala @@ -127,7 +127,7 @@ abstract class MillBuildRootModule()(implicit } } - def methodCodeHashSignatures: T[Map[String, Int]] = Task.Persistent { + def methodCodeHashSignatures: T[Map[String, Int]] = Task(persistent = true) { os.remove.all(T.dest / "previous") if (os.exists(T.dest / "current")) os.move.over(T.dest / "current", T.dest / "previous") val debugEnabled = T.log.debugEnabled diff --git a/scalajslib/src/mill/scalajslib/ScalaJSModule.scala b/scalajslib/src/mill/scalajslib/ScalaJSModule.scala index 1b99eb8ca6d..1c8c2788e6b 100644 --- a/scalajslib/src/mill/scalajslib/ScalaJSModule.scala +++ b/scalajslib/src/mill/scalajslib/ScalaJSModule.scala @@ -106,11 +106,11 @@ trait ScalaJSModule extends scalalib.ScalaModule { outer => def scalaJSToolsClasspath = Task { scalaJSWorkerClasspath() ++ scalaJSLinkerClasspath() } - def fastLinkJS: T[Report] = Task.Persistent { + def fastLinkJS: T[Report] = Task(persistent = true) { linkTask(isFullLinkJS = false, forceOutJs = false)() } - def fullLinkJS: T[Report] = Task.Persistent { + def fullLinkJS: T[Report] = Task(persistent = true) { linkTask(isFullLinkJS = true, forceOutJs = false)() } @@ -354,7 +354,7 @@ trait TestScalaJSModule extends ScalaJSModule with TestModule { ) } - def fastLinkJSTest: T[Report] = Task.Persistent { + def fastLinkJSTest: T[Report] = Task(persistent = true) { linkJs( worker = ScalaJSWorkerExternalModule.scalaJSWorker(), toolsClasspath = scalaJSToolsClasspath(), diff --git a/scalalib/src/mill/scalalib/GenIdeaModule.scala b/scalalib/src/mill/scalalib/GenIdeaModule.scala index 43d3134c747..204d175d993 100644 --- a/scalalib/src/mill/scalalib/GenIdeaModule.scala +++ b/scalalib/src/mill/scalalib/GenIdeaModule.scala @@ -31,7 +31,7 @@ trait GenIdeaModule extends Module { def ideaConfigFiles(ideaConfigVersion: Int): Task[Seq[IdeaConfigFile]] = Task.Anon { Seq[IdeaConfigFile]() } - def ideaCompileOutput: T[PathRef] = Task.Persistent { + def ideaCompileOutput: T[PathRef] = Task(persistent = true) { PathRef(T.dest / "classes") } diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala index d45daeebedc..67aa7dd5dfc 100644 --- a/scalalib/src/mill/scalalib/JavaModule.scala +++ b/scalalib/src/mill/scalalib/JavaModule.scala @@ -420,7 +420,7 @@ trait JavaModule * * Keep in sync with [[bspCompileClassesPath]] */ - def compile: T[mill.scalalib.api.CompilationResult] = Task.Persistent { + def compile: T[mill.scalalib.api.CompilationResult] = Task(persistent = true) { zincWorker() .worker() .compileJava( diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala index 53003161dbb..21663a92eb4 100644 --- a/scalalib/src/mill/scalalib/ScalaModule.scala +++ b/scalalib/src/mill/scalalib/ScalaModule.scala @@ -267,7 +267,7 @@ trait ScalaModule extends JavaModule with TestModule.ScalaModuleBase { outer => } // Keep in sync with [[bspCompileClassesPath]] - override def compile: T[CompilationResult] = Task.Persistent { + override def compile: T[CompilationResult] = Task(persistent = true) { val sv = scalaVersion() if (sv == "2.12.4") T.log.error( """Attention: Zinc is known to not work properly for Scala version 2.12.4. @@ -432,7 +432,7 @@ trait ScalaModule extends JavaModule with TestModule.ScalaModuleBase { outer => * Opens up a Scala console with your module and all dependencies present, * for you to test and operate your code interactively. */ - def console(): Command[Unit] = Task.Command { + def console(): Command[Unit] = Task.Command(serial = true) { if (!Util.isInteractive()) { Result.Failure("console needs to be run with the -i/--interactive flag") } else { @@ -507,7 +507,7 @@ trait ScalaModule extends JavaModule with TestModule.ScalaModuleBase { outer => * for you to test and operate your code interactively. * Use [[ammoniteVersion]] to customize the Ammonite version to use. */ - def repl(replOptions: String*): Command[Unit] = Task.Command { + def repl(replOptions: String*): Command[Unit] = Task.Command(serial = true) { if (T.log.inStream == DummyInputStream) { Result.Failure("repl needs to be run with the -i/--interactive flag") } else { @@ -624,7 +624,7 @@ trait ScalaModule extends JavaModule with TestModule.ScalaModuleBase { outer => ) } - override def semanticDbData: T[PathRef] = Task.Persistent { + override def semanticDbData: T[PathRef] = Task(persistent = true) { val sv = scalaVersion() val scalacOptions = ( diff --git a/scalalib/src/mill/scalalib/SemanticDbJavaModule.scala b/scalalib/src/mill/scalalib/SemanticDbJavaModule.scala index 85f86d8c7a5..2e250ee4c37 100644 --- a/scalalib/src/mill/scalalib/SemanticDbJavaModule.scala +++ b/scalalib/src/mill/scalalib/SemanticDbJavaModule.scala @@ -98,7 +98,7 @@ trait SemanticDbJavaModule extends CoursierModule { defaultResolver().resolveDeps(semanticDbJavaPluginIvyDeps()) } - def semanticDbData: T[PathRef] = Task.Persistent { + def semanticDbData: T[PathRef] = Task(persistent = true) { val javacOpts = SemanticDbJavaModule.javacOptionsTask( javacOptions() ++ mandatoryJavacOptions(), semanticDbJavaVersion() diff --git a/scalalib/src/mill/scalalib/TestModule.scala b/scalalib/src/mill/scalalib/TestModule.scala index ac29d3fe59e..1c7df978626 100644 --- a/scalalib/src/mill/scalalib/TestModule.scala +++ b/scalalib/src/mill/scalalib/TestModule.scala @@ -72,12 +72,12 @@ trait TestModule * @see [[testCached]] */ def test(args: String*): Command[(String, Seq[TestResult])] = - Task.ParallelCommand { + Task.Command { testTask(Task.Anon { args }, Task.Anon { Seq.empty[String] })() } def getTestEnvironmentVars(args: String*): Command[(String, String, String, Seq[String])] = { - Task.ParallelCommand { + Task.Command { getTestEnvironmentVarsTask(Task.Anon { args })() } } @@ -113,7 +113,7 @@ trait TestModule val (s, t) = args.splitAt(pos) (s, t.tail) } - Task.ParallelCommand { + Task.Command { testTask(Task.Anon { testArgs }, Task.Anon { selector })() } } @@ -265,7 +265,7 @@ trait TestModule * Discovers and runs the module's tests in-process in an isolated classloader, * reporting the results to the console */ - def testLocal(args: String*): Command[(String, Seq[TestResult])] = Task.ParallelCommand { + def testLocal(args: String*): Command[(String, Seq[TestResult])] = Task.Command { val (doneMsg, results) = TestRunner.runTestFramework( Framework.framework(testFramework()), runClasspath().map(_.path), From 13710bb9577d450303f4994c9ee98541e97e8230 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Fri, 27 Sep 2024 20:48:09 +0700 Subject: [PATCH 3/5] . --- main/define/src/mill/define/Task.scala | 10 ++++---- main/src/mill/main/MainModule.scala | 24 ++++++++++---------- scalalib/src/mill/scalalib/ScalaModule.scala | 4 ++-- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/main/define/src/mill/define/Task.scala b/main/define/src/mill/define/Task.scala index 94802ee2a5f..dffd0f24b52 100644 --- a/main/define/src/mill/define/Task.scala +++ b/main/define/src/mill/define/Task.scala @@ -107,10 +107,10 @@ object Task extends TaskBase { ): Command[T] = macro Target.Internal.commandImpl[T] def Command( - t: NamedParameterOnlyDummy.type = NamedParameterOnlyDummy, - serial: Boolean = false - ): CommandFactory = new CommandFactory(serial) - class CommandFactory private[mill] (val serial: Boolean) extends TaskBase.TraverseCtxHolder { + t: NamedParameterOnlyDummy.type = NamedParameterOnlyDummy, + exclusive: Boolean = false + ): CommandFactory = new CommandFactory(exclusive) + class CommandFactory private[mill] (val exclusive: Boolean) extends TaskBase.TraverseCtxHolder { def apply[T](t: Result[T])(implicit w: W[T], ctx: mill.define.Ctx, @@ -579,7 +579,7 @@ object Target extends TaskBase { w.splice, cls.splice.value, taskIsPrivate.splice, - serial = c.prefix.splice.asInstanceOf[Task.CommandFactory].serial + serial = c.prefix.splice.asInstanceOf[Task.CommandFactory].exclusive ) ) } diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index 22503e3c821..cc11dd03ff3 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -69,7 +69,7 @@ trait MainModule extends BaseModule0 { /** * Show the mill version. */ - def version(): Command[String] = Task.Command(serial = true) { + def version(): Command[String] = Task.Command(exclusive = true) { val res = BuildInfo.millVersion Task.log.withPromptPaused { println(res) @@ -81,7 +81,7 @@ trait MainModule extends BaseModule0 { * Resolves a mill query string and prints out the tasks it resolves to. */ def resolve(evaluator: Evaluator, targets: String*): Command[List[String]] = - Task.Command(serial = true) { + Task.Command(exclusive = true) { val resolved = Resolve.Segments.resolve( evaluator.rootModule, targets, @@ -104,7 +104,7 @@ trait MainModule extends BaseModule0 { * executed in what order, without actually executing them. */ def plan(evaluator: Evaluator, targets: String*): Command[Array[String]] = - Task.Command(serial = true) { + Task.Command(exclusive = true) { plan0(evaluator, targets) match { case Left(err) => Result.Failure(err) case Right(success) => @@ -141,7 +141,7 @@ trait MainModule extends BaseModule0 { @mainargs.arg(positional = true) src: String, @mainargs.arg(positional = true) dest: String ): Command[List[String]] = - Task.Command(serial = true) { + Task.Command(exclusive = true) { val resolved = Resolve.Tasks.resolve( evaluator.rootModule, List(src, dest), @@ -186,7 +186,7 @@ trait MainModule extends BaseModule0 { * Displays metadata about the given task without actually running it. */ def inspect(evaluator: Evaluator, targets: String*): Command[String] = - Task.Command(serial = true) { + Task.Command(exclusive = true) { def resolveParents(c: Class[_]): Seq[Class[_]] = { Seq(c) ++ Option(c.getSuperclass).toSeq.flatMap(resolveParents) ++ c.getInterfaces.flatMap( @@ -308,7 +308,7 @@ trait MainModule extends BaseModule0 { * to integrate Mill into external scripts and tooling. */ def show(evaluator: Evaluator, targets: String*): Command[ujson.Value] = - Task.Command(serial = true) { + Task.Command(exclusive = true) { MainModule.show0(evaluator, targets, Target.log, interp.evalWatch0) { res => res.flatMap(_._2) match { case Seq((k, singleValue)) => singleValue @@ -322,7 +322,7 @@ trait MainModule extends BaseModule0 { * to integrate Mill into external scripts and tooling. */ def showNamed(evaluator: Evaluator, targets: String*): Command[ujson.Value] = - Task.Command(serial = true) { + Task.Command(exclusive = true) { MainModule.show0(evaluator, targets, Target.log, interp.evalWatch0) { res => ujson.Obj.from(res.flatMap(_._2)) } @@ -333,7 +333,7 @@ trait MainModule extends BaseModule0 { * will clean everything. */ def clean(evaluator: Evaluator, targets: String*): Command[Seq[PathRef]] = - Task.Command(serial = true) { + Task.Command(exclusive = true) { val rootDir = evaluator.outPath val KeepPattern = "(mill-.+)".r.anchored @@ -392,7 +392,7 @@ trait MainModule extends BaseModule0 { * Renders the dependencies between the given tasks as a SVG for you to look at */ def visualize(evaluator: Evaluator, targets: String*): Command[Seq[PathRef]] = - Task.Command(serial = true) { + Task.Command(exclusive = true) { visualize0(evaluator, targets, Target.ctx(), mill.main.VisualizeModule.worker()) } @@ -400,7 +400,7 @@ trait MainModule extends BaseModule0 { * Renders the dependencies between the given tasks, and all their dependencies, as a SVG */ def visualizePlan(evaluator: Evaluator, targets: String*): Command[Seq[PathRef]] = - Task.Command(serial = true) { + Task.Command(exclusive = true) { plan0(evaluator, targets) match { case Left(err) => Result.Failure(err) case Right(planResults) => visualize0( @@ -416,7 +416,7 @@ trait MainModule extends BaseModule0 { /** * Shuts down mill's background server */ - def shutdown(): Command[Unit] = Task.Command(serial = true) { + def shutdown(): Command[Unit] = Task.Command(exclusive = true) { Target.log.info("Shutting down Mill server...") Target.ctx.systemExit(0) () @@ -428,7 +428,7 @@ trait MainModule extends BaseModule0 { * 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] = Task.Command(serial = true) { + def init(evaluator: Evaluator, args: String*): Command[Unit] = Task.Command(exclusive = true) { RunScript.evaluateTasksNamed( evaluator, Seq("mill.scalalib.giter8.Giter8Module/init") ++ args, diff --git a/scalalib/src/mill/scalalib/ScalaModule.scala b/scalalib/src/mill/scalalib/ScalaModule.scala index 21663a92eb4..3dd61de1f88 100644 --- a/scalalib/src/mill/scalalib/ScalaModule.scala +++ b/scalalib/src/mill/scalalib/ScalaModule.scala @@ -432,7 +432,7 @@ trait ScalaModule extends JavaModule with TestModule.ScalaModuleBase { outer => * Opens up a Scala console with your module and all dependencies present, * for you to test and operate your code interactively. */ - def console(): Command[Unit] = Task.Command(serial = true) { + def console(): Command[Unit] = Task.Command(exclusive = true) { if (!Util.isInteractive()) { Result.Failure("console needs to be run with the -i/--interactive flag") } else { @@ -507,7 +507,7 @@ trait ScalaModule extends JavaModule with TestModule.ScalaModuleBase { outer => * for you to test and operate your code interactively. * Use [[ammoniteVersion]] to customize the Ammonite version to use. */ - def repl(replOptions: String*): Command[Unit] = Task.Command(serial = true) { + def repl(replOptions: String*): Command[Unit] = Task.Command(exclusive = true) { if (T.log.inStream == DummyInputStream) { Result.Failure("repl needs to be run with the -i/--interactive flag") } else { From 4afb6d714f374779a4028f7475d18c47e1ef4aa2 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Fri, 27 Sep 2024 21:17:37 +0700 Subject: [PATCH 4/5] . --- main/define/src/mill/define/Task.scala | 4 ++-- main/eval/src/mill/eval/EvaluatorCore.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main/define/src/mill/define/Task.scala b/main/define/src/mill/define/Task.scala index dffd0f24b52..e27845aad09 100644 --- a/main/define/src/mill/define/Task.scala +++ b/main/define/src/mill/define/Task.scala @@ -579,7 +579,7 @@ object Target extends TaskBase { w.splice, cls.splice.value, taskIsPrivate.splice, - serial = c.prefix.splice.asInstanceOf[Task.CommandFactory].exclusive + exclusive = c.prefix.splice.asInstanceOf[Task.CommandFactory].exclusive ) ) } @@ -762,7 +762,7 @@ class Command[+T]( val writer: W[_], val cls: Class[_], val isPrivate: Option[Boolean], - val serial: Boolean + val exclusive: Boolean ) extends NamedTask[T] { def this( t: Task[T], diff --git a/main/eval/src/mill/eval/EvaluatorCore.scala b/main/eval/src/mill/eval/EvaluatorCore.scala index d8e334a2adb..40d256dba0f 100644 --- a/main/eval/src/mill/eval/EvaluatorCore.scala +++ b/main/eval/src/mill/eval/EvaluatorCore.scala @@ -184,7 +184,7 @@ private[mill] trait EvaluatorCore extends GroupEvaluator { case Terminal.Labelled(t, _) => if (tasksTransitive.contains(t)) true else t match { - case t: Command[_] => !t.serial + case t: Command[_] => !t.exclusive case _ => false } case _ => !serialCommandExec From 871e3445b2d3f51e23c96a3d49b9024cfc789f96 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sun, 29 Sep 2024 06:46:01 +0700 Subject: [PATCH 5/5] . --- .../mill/define/NamedParameterOnlyDummy.scala | 2 +- main/define/src/mill/define/Task.scala | 6 +- main/src/mill/main/MainModule.scala | 484 +++++++++--------- 3 files changed, 250 insertions(+), 242 deletions(-) diff --git a/main/define/src/mill/define/NamedParameterOnlyDummy.scala b/main/define/src/mill/define/NamedParameterOnlyDummy.scala index 6bd963f09a1..89670f1f5b3 100644 --- a/main/define/src/mill/define/NamedParameterOnlyDummy.scala +++ b/main/define/src/mill/define/NamedParameterOnlyDummy.scala @@ -3,4 +3,4 @@ package mill.define /** * Dummy class used to mark parameters that come after it as named only parameters */ -object NamedParameterOnlyDummy +class NamedParameterOnlyDummy private[mill] () diff --git a/main/define/src/mill/define/Task.scala b/main/define/src/mill/define/Task.scala index e27845aad09..beca0cfb32c 100644 --- a/main/define/src/mill/define/Task.scala +++ b/main/define/src/mill/define/Task.scala @@ -107,8 +107,8 @@ object Task extends TaskBase { ): Command[T] = macro Target.Internal.commandImpl[T] def Command( - t: NamedParameterOnlyDummy.type = NamedParameterOnlyDummy, - exclusive: Boolean = false + t: NamedParameterOnlyDummy = new NamedParameterOnlyDummy, + exclusive: Boolean = false ): CommandFactory = new CommandFactory(exclusive) class CommandFactory private[mill] (val exclusive: Boolean) extends TaskBase.TraverseCtxHolder { def apply[T](t: Result[T])(implicit @@ -170,7 +170,7 @@ object Task extends TaskBase { * Violating that invariant can result in confusing mis-behaviors */ def apply( - t: NamedParameterOnlyDummy.type = NamedParameterOnlyDummy, + t: NamedParameterOnlyDummy = new NamedParameterOnlyDummy, persistent: Boolean = false ): ApplyFactory = new ApplyFactory(persistent) class ApplyFactory private[mill] (val persistent: Boolean) extends TaskBase.TraverseCtxHolder { diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index f45af202fd7..aead83caada 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -99,39 +99,41 @@ trait MainModule extends BaseModule0 { /** * Resolves a mill query string and prints out the tasks it resolves to. */ - def resolve(evaluator: Evaluator, targets: String*): Command[List[String]] = Task.Command(exclusive = true) { - val resolved = Resolve.Segments.resolve( - evaluator.rootModule, - targets, - SelectMode.Multi - ) + def resolve(evaluator: Evaluator, targets: String*): Command[List[String]] = + Task.Command(exclusive = true) { + 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) + 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]] = Task.Command(exclusive = true) { - 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) + def plan(evaluator: Evaluator, targets: String*): Command[Array[String]] = + Task.Command(exclusive = true) { + 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( @@ -202,264 +204,270 @@ trait MainModule extends BaseModule0 { /** * Displays metadata about the given task without actually running it. */ - def inspect(evaluator: Evaluator, targets: String*): Command[String] = Task.Command(exclusive = true) { - def resolveParents(c: Class[_]): Seq[Class[_]] = { - Seq(c) ++ Option(c.getSuperclass).toSeq.flatMap(resolveParents) ++ c.getInterfaces.flatMap( - resolveParents - ) - } + def inspect(evaluator: Evaluator, targets: String*): Command[String] = + Task.Command(exclusive = true) { + 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) + 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() + 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 + 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 + } } - } - def pprintModule(t: ModuleTask[_], evaluator: Evaluator): Tree.Lazy = { - val cls = t.module.getClass - val annotation = cls.getAnnotation(classOf[Scaladoc]) - val scaladocOpt = Option.when(annotation != null)( - Util.cleanupScaladoc(annotation.value).map("\n " + _).mkString - ) - val fileName = t.ctx.fileName.split('/').last.split('\\').last - val parents = cls.getInterfaces ++ Option(cls.getSuperclass).toSeq - val inheritedModules = - parents.distinct.filter(classOf[Module].isAssignableFrom(_)).map(_.getSimpleName) - val moduleDepsOpt = cls.getMethods.find(m => decode(m.getName) == "moduleDeps").map( - _.invoke(t.module).asInstanceOf[Seq[Module]] - ).filter(_.nonEmpty) - val defaultTaskOpt = t.module match { - case taskMod: TaskModule => Some(s"${t.module}.${taskMod.defaultCommandName()}") - case _ => None - } - val methodMap = evaluator.rootModule.millDiscover.value - val tasksOpt = methodMap.get(cls).map { - case (_, _, tasks) => tasks.map(task => s"${t.module}.$task") - } - pprint.Tree.Lazy { ctx => - Iterator( - ctx.applyPrefixColor(t.module.toString).toString, - s"($fileName:${t.ctx.lineNum})" - ) ++ Iterator(scaladocOpt).flatten ++ Iterator( - "\n\n", - ctx.applyPrefixColor("Inherited Modules").toString, - ": ", - inheritedModules.mkString(", ") - ) ++ moduleDepsOpt.fold(Iterator.empty[String])(deps => - Iterator( - "\n\n", - ctx.applyPrefixColor("Module Dependencies").toString, - ": ", - deps.mkString(", ") - ) - ) ++ defaultTaskOpt.fold(Iterator.empty[String])(task => - Iterator("\n\n", ctx.applyPrefixColor("Default Task").toString, ": ", task) - ) ++ tasksOpt.fold(Iterator.empty[String])(tasks => + def pprintModule(t: ModuleTask[_], evaluator: Evaluator): Tree.Lazy = { + val cls = t.module.getClass + val annotation = cls.getAnnotation(classOf[Scaladoc]) + val scaladocOpt = Option.when(annotation != null)( + Util.cleanupScaladoc(annotation.value).map("\n " + _).mkString + ) + val fileName = t.ctx.fileName.split('/').last.split('\\').last + val parents = cls.getInterfaces ++ Option(cls.getSuperclass).toSeq + val inheritedModules = + parents.distinct.filter(classOf[Module].isAssignableFrom(_)).map(_.getSimpleName) + val moduleDepsOpt = cls.getMethods.find(m => decode(m.getName) == "moduleDeps").map( + _.invoke(t.module).asInstanceOf[Seq[Module]] + ).filter(_.nonEmpty) + val defaultTaskOpt = t.module match { + case taskMod: TaskModule => Some(s"${t.module}.${taskMod.defaultCommandName()}") + case _ => None + } + val methodMap = evaluator.rootModule.millDiscover.value + val tasksOpt = methodMap.get(cls).map { + case (_, _, tasks) => tasks.map(task => s"${t.module}.$task") + } + pprint.Tree.Lazy { ctx => Iterator( + ctx.applyPrefixColor(t.module.toString).toString, + s"($fileName:${t.ctx.lineNum})" + ) ++ Iterator(scaladocOpt).flatten ++ Iterator( "\n\n", - ctx.applyPrefixColor("Tasks").toString, + ctx.applyPrefixColor("Inherited Modules").toString, ": ", - tasks.mkString(", ") + inheritedModules.mkString(", ") + ) ++ moduleDepsOpt.fold(Iterator.empty[String])(deps => + Iterator( + "\n\n", + ctx.applyPrefixColor("Module Dependencies").toString, + ": ", + deps.mkString(", ") + ) + ) ++ defaultTaskOpt.fold(Iterator.empty[String])(task => + Iterator("\n\n", ctx.applyPrefixColor("Default Task").toString, ": ", task) + ) ++ tasksOpt.fold(Iterator.empty[String])(tasks => + Iterator( + "\n\n", + ctx.applyPrefixColor("Tasks").toString, + ": ", + tasks.mkString(", ") + ) ) - ) + } } - } - MainModule.resolveTasks(evaluator, targets, SelectMode.Multi, resolveToModuleTasks = true) { - tasks => - val output = (for { - task <- tasks - tree = task match { - case t: ModuleTask[_] => pprintModule(t, evaluator) - case t => pprintTask(t, evaluator) + MainModule.resolveTasks(evaluator, targets, SelectMode.Multi, resolveToModuleTasks = true) { + tasks => + val output = (for { + task <- tasks + tree = task match { + case t: ModuleTask[_] => pprintModule(t, evaluator) + case t => pprintTask(t, 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) } - 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 + 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] = Task.Command(exclusive = true) { - MainModule.show0(evaluator, targets, Target.log, interp.evalWatch0) { res => - res.flatMap(_._2) match { - case Seq((k, singleValue)) => singleValue - case multiple => ujson.Obj.from(multiple) + def show(evaluator: Evaluator, targets: String*): Command[ujson.Value] = + Task.Command(exclusive = true) { + 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] = Task.Command(exclusive = true) { - MainModule.show0(evaluator, targets, Target.log, interp.evalWatch0) { res => - ujson.Obj.from(res.flatMap(_._2)) + def showNamed(evaluator: Evaluator, targets: String*): Command[ujson.Value] = + Task.Command(exclusive = true) { + 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]] = Task.Command(exclusive = true) { - val rootDir = evaluator.outPath + def clean(evaluator: Evaluator, targets: String*): Command[Seq[PathRef]] = + Task.Command(exclusive = true) { + val rootDir = evaluator.outPath - val KeepPattern = "(mill-.+)".r.anchored + val KeepPattern = "(mill-.+)".r.anchored - def keepPath(path: os.Path) = path.last match { - case KeepPattern(_) => true - case _ => false - } + 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 + 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) } - (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() - } + 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(_))) + 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]] = Task.Command(exclusive = true) { - visualize0(evaluator, targets, Target.ctx(), mill.main.VisualizeModule.worker()) - } + def visualize(evaluator: Evaluator, targets: String*): Command[Seq[PathRef]] = + Task.Command(exclusive = true) { + visualize0(evaluator, targets, Target.ctx(), mill.main.VisualizeModule.worker()) + } /** * Renders the dependencies between the given tasks, and all their dependencies, as a SVG