From 391ea9d3390ab8bf71984d09cd1bb575f3a98bde Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 1 May 2023 22:41:24 +0800 Subject: [PATCH 01/27] wip --- bsp/src/mill/bsp/BSP.scala | 2 +- bsp/worker/src/mill/bsp/worker/Utils.scala | 2 +- example/basic/1-simple-scala/build.sc | 125 +----------------- main/api/src/mill/api/Result.scala | 17 ++- main/eval/src/mill/eval/Evaluator.scala | 8 +- main/src/mill/main/MainModule.scala | 31 ++++- main/src/mill/main/RootModule.scala | 21 +-- main/src/mill/main/RunScript.scala | 27 ++-- main/src/mill/main/VisualizeModule.scala | 2 +- main/src/mill/modules/CoursierSupport.scala | 9 +- main/test/src/mill/main/MainModuleTests.scala | 12 +- .../src/mill/util}/Watchable.scala | 6 +- main/util/src/mill/util/Watched.scala | 6 +- .../src/mill/runner/MillBuildBootstrap.scala | 13 +- runner/src/mill/runner/RunnerState.scala | 3 +- runner/src/mill/runner/Watching.scala | 3 +- scalalib/src/mill/scalalib/GenIdeaImpl.scala | 2 +- .../src/mill/scalalib/ResolveDepsTests.scala | 12 +- 18 files changed, 111 insertions(+), 190 deletions(-) rename main/{define/src/mill/define => util/src/mill/util}/Watchable.scala (80%) diff --git a/bsp/src/mill/bsp/BSP.scala b/bsp/src/mill/bsp/BSP.scala index cdac63920e3..e295b75204b 100644 --- a/bsp/src/mill/bsp/BSP.scala +++ b/bsp/src/mill/bsp/BSP.scala @@ -96,7 +96,7 @@ object BSP extends ExternalModule with CoursierModule with BspServerStarter { val worker = BspWorker(ctx) worker match { - case Result.Success(worker) => + case Result.Success(worker, _) => worker.startBspServer( initialEvaluator, streams, diff --git a/bsp/worker/src/mill/bsp/worker/Utils.scala b/bsp/worker/src/mill/bsp/worker/Utils.scala index dba6b0faeef..64c54cc0fe6 100644 --- a/bsp/worker/src/mill/bsp/worker/Utils.scala +++ b/bsp/worker/src/mill/bsp/worker/Utils.scala @@ -46,7 +46,7 @@ object Utils { task: mill.define.Task[_] ): StatusCode = { results.results(task) match { - case Success(_) => StatusCode.OK + case Success(_, _) => StatusCode.OK case Skipped => StatusCode.CANCELLED case _ => StatusCode.ERROR } diff --git a/example/basic/1-simple-scala/build.sc b/example/basic/1-simple-scala/build.sc index 3be43e9ade7..a24fd0f20c8 100644 --- a/example/basic/1-simple-scala/build.sc +++ b/example/basic/1-simple-scala/build.sc @@ -1,124 +1,9 @@ -import mill._, scalalib._ +import mill._ -object foo extends RootModule with ScalaModule { - def scalaVersion = "2.13.8" - def ivyDeps = Agg( - ivy"com.lihaoyi::scalatags:0.8.2", - ivy"com.lihaoyi::mainargs:0.4.0" - ) +//interp.watchValue(System.currentTimeMillis() / 1000) - object test extends Tests { - def ivyDeps = Agg(ivy"com.lihaoyi::utest:0.7.11") - def testFramework = "utest.runner.Framework" - } -} +println("Setting up build.sc") +def foo = T.input{ System.currentTimeMillis() / 1000 } -// This is a basic Mill build for a single `ScalaModule`, with two -// third-party dependencies and a test suite using the uTest framework. As a -// single-module project, it `extends RootModule` to mark `object foo` as the -// top-level module in the build. This lets us directly perform operations -// `./mill compile` or `./mill run` without needing to prefix it as -// `foo.compile` or `foo.run`. -// -// You can download this example project using the *download* link above -// if you want to try out the commands below yourself. The only requirement is -// that you have some version of the JVM installed; the `./mill` script takes -// care of any further dependencies that need to be downloaded. -// -// The source code for this module lives in the `src/` folder. -// Output for this module (compiled files, resolved dependency lists, ...) -// lives in `out/`. -// -// This example project uses two third-party dependencies - MainArgs for CLI -// argument parsing, Scalatags for HTML generation - and uses them to wrap a -// given input string in HTML templates with proper escaping. -// -// You can run `assembly` to generate a standalone executable jar, which then -// can be run from the command line or deployed to be run elsewhere. - -/** Usage - -> ./mill resolve _ # List what tasks are available to run -assembly -... -clean -... -compile -... -run -... -show -... -inspect -... - -> ./mill inspect compile # Show documentation and inputs of a task -compile(ScalaModule.scala:...) - Compiles the current module to generate compiled classfiles/bytecode. -Inputs: - scalaVersion - upstreamCompileOutput - allSourceFiles - compileClasspath - -> ./mill compile # compile sources into classfiles -... -compiling 1 Scala source to... - -> ./mill run # run the main method, if any -error: Missing argument: --text - -> ./mill run --text hello -

hello

- -> ./mill test -... -+ foo.FooTests.simple ...

hello

-+ foo.FooTests.escaping ...

<hello>

- -> ./mill assembly # bundle classfiles and libraries into a jar for deployment - -> ./mill show assembly # show the output of the assembly task -".../out/assembly.dest/out.jar" - -> java -jar ./out/assembly.dest/out.jar --text hello -

hello

- -> ./out/assembly.dest/out.jar --text hello # mac/linux -

hello

- -*/ - -// The output of every Mill task is stored in the `out/` folder under a name -// corresponding to the task that created it. e.g. The `assembly` task puts its -// metadata output in `out/assembly.json`, and its output files in -// `out/assembly.dest`. You can also use `show` to make Mill print out the -// metadata output for a particular task. -// -// Additional Mill tasks you would likely need include: -// -// [source,bash] -// ---- -// $ mill runBackground # run the main method in the background -// -// $ mill clean # delete the cached output of a task, terminate any runBackground -// -// $ mill launcher # prepares a foo/launcher.dest/run you can run later -// -// $ mill jar # bundle the classfiles into a jar suitable for publishing -// -// $ mill -i console # start a Scala console within your project -// -// $ mill -i repl # start an Ammonite Scala REPL within your project -// ---- -// -// You can run `+mill resolve __+` to see a full list of the different tasks that -// are available, `+mill resolve _+` to see the tasks within `foo`, -// `mill inspect compile` to inspect a task's doc-comment documentation or what -// it depends on, or `mill show foo.scalaVersion` to show the output of any task. -// -// The most common *tasks* that Mill can run are cached *targets*, such as -// `compile`, and un-cached *commands* such as `foo.run`. Targets do not -// re-evaluate unless one of their inputs changes, whereas commands re-run every -// time. \ No newline at end of file +def bar = T{ foo() + " seconds since the epoch" } \ No newline at end of file diff --git a/main/api/src/mill/api/Result.scala b/main/api/src/mill/api/Result.scala index 5ec9a6e99c3..d5905fcef99 100644 --- a/main/api/src/mill/api/Result.scala +++ b/main/api/src/mill/api/Result.scala @@ -27,11 +27,24 @@ object Result { * @param value The value computed by the task. * @tparam T The result type of the computed task. */ - case class Success[+T](value: T) extends Result[T] { - def map[V](f: T => V): Success[V] = Result.Success(f(value)) + case class Success[+T](value: T, signature: () => Int) extends Result[T] { + def map[V](f: T => V): Success[V] = Result.Success(f(value), signature) def flatMap[V](f: T => Result[V]): Result[V] = f(value) override def asSuccess: Option[Success[T]] = Some(this) } + object Success{ + def apply[T](value: => T) = { + new Success( + value, + () => { +// if (value.toString.length < 100) { +// mill.api.SystemStreams.originalErr.println("Signature " + System.identityHashCode(value)) +// } + value.hashCode() + } + ) + } + } /** * A task execution was skipped because of failures in it's dependencies. diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index 32eeb2a8b3a..bd26d4744a7 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -423,7 +423,7 @@ class Evaluator private ( upToDateWorker.map((_, inputsHash)) orElse cached match { case Some((v, hashCode)) => val newResults = mutable.LinkedHashMap.empty[Task[_], mill.api.Result[(Any, Int)]] - newResults(labelledNamedTask.task) = mill.api.Result.Success((v, hashCode)) + newResults(labelledNamedTask.task) = mill.api.Result.Success((v, hashCode), () => 0) Evaluated(newResults, Nil, cached = true) @@ -452,7 +452,7 @@ class Evaluator private ( case mill.api.Result.Failure(_, Some((v, _))) => handleTaskResult(v, v.##, paths.meta, inputsHash, labelledNamedTask) - case mill.api.Result.Success((v, _)) => + case mill.api.Result.Success((v, _), _) => handleTaskResult(v, v.##, paths.meta, inputsHash, labelledNamedTask) case _ => @@ -557,7 +557,7 @@ class Evaluator private ( newEvaluated.append(task) val targetInputValues = task.inputs .map { x => newResults.getOrElse(x, results(x)) } - .collect { case mill.api.Result.Success((v, _)) => v } + .collect { case mill.api.Result.Success((v, _), _) => v } val res = if (targetInputValues.length != task.inputs.length) mill.api.Result.Skipped @@ -771,7 +771,7 @@ object Evaluator { failing: MultiBiMap[Either[Task[_], Labelled[_]], mill.api.Result.Failing[_]], results: collection.Map[Task[_], mill.api.Result[Any]] ) { - def values: Seq[Any] = rawValues.collect { case mill.api.Result.Success(v) => v } + def values: Seq[Any] = rawValues.collect { case mill.api.Result.Success(v, _) => v } private def copy( rawValues: Seq[Result[Any]] = rawValues, evaluated: Agg[Task[_]] = evaluated, diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index c3a54290856..a9948128da7 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -3,9 +3,9 @@ package mill.main import java.util.concurrent.LinkedBlockingQueue import mill.{BuildInfo, T} import mill.api.{Ctx, PathRef, Result, internal} -import mill.define.{Command, Segments, NamedTask, TargetImpl, Task} +import mill.define.{Command, NamedTask, Segments, TargetImpl, Task} import mill.eval.{Evaluator, EvaluatorPaths} -import mill.util.{PrintLogger, Watched} +import mill.util.{PrintLogger, Watchable, Watched} import pprint.{Renderer, Tree, Truncated} import ujson.Value @@ -60,6 +60,32 @@ object MainModule { * [[show]], [[inspect]], [[plan]], etc. */ trait MainModule extends mill.Module { + protected[mill] val watchedValues = mutable.Buffer.empty[Watchable] + + object interp { + + def watchValue[T](v0: => T)(implicit fn: sourcecode.FileName, ln: sourcecode.Line): T = { + val v = v0 + val watchable = Watchable.Value( + () => v0.hashCode, + v.hashCode(), + fn.value + ":" + ln.value + ) + watchedValues.append(watchable) + v + } + + def watch(p: os.Path): os.Path = { + val watchable = Watchable.Path(PathRef(p)) + watchedValues.append(watchable) + p + } + + def watch0(w: Watchable): Unit = { + watchedValues.append(w) + } + } + implicit def millDiscover: mill.define.Discover[_] @@ -265,6 +291,7 @@ trait MainModule extends mill.Module { T.log.outputStream.println(output.render(indent = 2)) output }.map { res: Watched[Option[Value]] => + res.watched.foreach(interp.watch0) res.value.getOrElse(ujson.Null) } } diff --git a/main/src/mill/main/RootModule.scala b/main/src/mill/main/RootModule.scala index 7c19b0871a4..3ba1bfe54ab 100644 --- a/main/src/mill/main/RootModule.scala +++ b/main/src/mill/main/RootModule.scala @@ -1,7 +1,8 @@ package mill.main import mill.api.{PathRef, internal} -import mill.define.{Caller, Discover, Segments, Watchable} +import mill.util.Watchable +import mill.define.{Caller, Discover, Segments} import TokenReaders._ import scala.collection.mutable @@ -36,24 +37,6 @@ abstract class RootModule()(implicit // user-defined BaseModule can have a complete Discover[_] instance without // needing to tediously call `override lazy val millDiscover = Discover[this.type]` override lazy val millDiscover = baseModuleInfo.discover.asInstanceOf[Discover[this.type]] - - object interp { - - def watchValue[T](v0: => T): T = { - val v = v0 - val watchable = Watchable.Value(() => v0.hashCode, v.hashCode()) - watchedValues.append(watchable) - v - } - - def watch(p: os.Path): os.Path = { - val watchable = Watchable.Path(PathRef(p)) - watchedValues.append(watchable) - p - } - } - - protected[mill] val watchedValues = mutable.Buffer.empty[Watchable] } @internal diff --git a/main/src/mill/main/RunScript.scala b/main/src/mill/main/RunScript.scala index 36227805f89..109c56262d6 100644 --- a/main/src/mill/main/RunScript.scala +++ b/main/src/mill/main/RunScript.scala @@ -2,11 +2,10 @@ package mill.main import mill.define._ import mill.eval.{Evaluator, EvaluatorPaths} -import mill.util.{EitherOps, Watched} +import mill.util.{Watchable, Watched} import mill.api.{PathRef, Result} import mill.api.Strict.Agg -import scala.reflect.ClassTag -import mill.main.ParseArgs.TargetsWithParams + object RunScript { @@ -16,7 +15,7 @@ object RunScript { evaluator: Evaluator, scriptArgs: Seq[String], selectMode: SelectMode - ): Either[String, (Seq[PathRef], Either[String, Seq[(Any, Option[ujson.Value])]])] = { + ): Either[String, (Seq[Watchable], Either[String, Seq[(Any, Option[ujson.Value])]])] = { for (targets <- ResolveTasks.resolve(evaluator, scriptArgs, selectMode)) yield { val (watched, res) = evaluate(evaluator, Agg.from(targets.distinct)) @@ -27,6 +26,8 @@ object RunScript { w <- extraWatched } yield w + pprint.log(watched.map(_.pretty)) + pprint.log(watched2.map(_.pretty)) (watched ++ watched2, res) } } @@ -35,7 +36,7 @@ object RunScript { evaluator: Evaluator, scriptArgs: Seq[String], selectMode: SelectMode - ): Either[String, (Seq[PathRef], Either[String, Seq[(Any, Option[(TaskName, ujson.Value)])]])] = { + ): Either[String, (Seq[Watchable], Either[String, Seq[(Any, Option[(TaskName, ujson.Value)])]])] = { for (targets <- ResolveTasks.resolve(evaluator, scriptArgs, selectMode)) yield { val (watched, res) = evaluateNamed(evaluator, Agg.from(targets.distinct)) @@ -53,8 +54,9 @@ object RunScript { def evaluate( evaluator: Evaluator, targets: Agg[Task[Any]] - ): (Seq[PathRef], Either[String, Seq[(Any, Option[ujson.Value])]]) = { + ): (Seq[Watchable], Either[String, Seq[(Any, Option[ujson.Value])]]) = { val (watched, results) = evaluateNamed(evaluator, targets) + pprint.log(watched.map(_.pretty)) // we drop the task name in the inner tuple (watched, results.map(_.map(p => (p._1, p._2.map(_._2))))) } @@ -67,17 +69,24 @@ object RunScript { def evaluateNamed( evaluator: Evaluator, targets: Agg[Task[Any]] - ): (Seq[PathRef], Either[String, Seq[(Any, Option[(TaskName, ujson.Value)])]]) = { + ): (Seq[Watchable], Either[String, Seq[(Any, Option[(TaskName, ujson.Value)])]]) = { val evaluated: Evaluator.Results = evaluator.evaluate(targets) val watched = evaluated.results .iterator .collect { - case (t: SourcesImpl, Result.Success(ps: Seq[PathRef])) => ps - case (t: SourceImpl, Result.Success(p: PathRef)) => Seq(p) + case (t: SourcesImpl, Result.Success(ps: Seq[PathRef], _)) => ps.map(Watchable.Path(_)) + case (t: SourceImpl, Result.Success(p: PathRef, _)) => Seq(Watchable.Path(p)) + case (t: InputImpl[_], Result.Success(v, signature)) => + val pretty = t.ctx0.fileName + ":" + t.ctx0.lineNum + println("INPUT FOUND " + pretty) + Seq(Watchable.Value(() => signature(), signature(), pretty)) } .flatten .toSeq + pprint.log(targets) + pprint.log(watched.map(_.pretty)) + val errorStr = Evaluator.formatFailing(evaluated) evaluated.failing.keyCount match { diff --git a/main/src/mill/main/VisualizeModule.scala b/main/src/mill/main/VisualizeModule.scala index f7ed10b61ee..809835d3513 100644 --- a/main/src/mill/main/VisualizeModule.scala +++ b/main/src/mill/main/VisualizeModule.scala @@ -43,7 +43,7 @@ trait VisualizeModule extends mill.define.TaskModule { ) val visualizeThread = new java.lang.Thread(() => while (true) { - val res = Result.create { + val res = Result.Success{ val (targets, tasks, dest) = in.take() cl.loadClass("mill.main.graphviz.GraphvizTools") .getMethod("apply", classOf[Seq[_]], classOf[Seq[_]], classOf[os.Path]) diff --git a/main/src/mill/modules/CoursierSupport.scala b/main/src/mill/modules/CoursierSupport.scala index b51f2c6f269..707474908b7 100644 --- a/main/src/mill/modules/CoursierSupport.scala +++ b/main/src/mill/modules/CoursierSupport.scala @@ -192,10 +192,11 @@ trait CoursierSupport { } if (errors.isEmpty) { - mill.Agg.from( - successes.map(os.Path(_)).filter(_.ext == "jar").map(PathRef(_, quick = true)) - ).filter(x => resolveFilter(x.path)) ++ localTestDeps.flatten - + Result.Success( + mill.Agg.from( + successes.map(os.Path(_)).filter(_.ext == "jar").map(PathRef(_, quick = true)) + ).filter(x => resolveFilter(x.path)) ++ localTestDeps.flatten + ) } else { val errorDetails = errors.map(e => s"${System.lineSeparator()} ${e.describe}").mkString Result.Failure( diff --git a/main/test/src/mill/main/MainModuleTests.scala b/main/test/src/mill/main/MainModuleTests.scala index aca9b114add..4572e4247a6 100644 --- a/main/test/src/mill/main/MainModuleTests.scala +++ b/main/test/src/mill/main/MainModuleTests.scala @@ -49,7 +49,7 @@ object MainModuleTests extends TestSuite { val eval = new TestEvaluator(mainModule) test("single") { val res = eval.evaluator.evaluate(Agg(mainModule.inspect(eval.evaluator, "hello"))) - val Result.Success(value: String) = res.rawValues.head + val Result.Success(value: String, _) = res.rawValues.head assert( res.failing.keyCount == 0, value.startsWith("hello("), @@ -59,7 +59,7 @@ object MainModuleTests extends TestSuite { test("multi") { val res = eval.evaluator.evaluate(Agg(mainModule.inspect(eval.evaluator, "hello", "hello2"))) - val Result.Success(value: String) = res.rawValues.head + val Result.Success(value: String, _) = res.rawValues.head assert( res.failing.keyCount == 0, value.startsWith("hello("), @@ -77,7 +77,7 @@ object MainModuleTests extends TestSuite { assert(results.failing.keyCount == 0) - val Result.Success(value) = results.rawValues.head + val Result.Success(value, _) = results.rawValues.head assert(value == ujson.Arr.from(Seq("hello", "world"))) } @@ -92,7 +92,7 @@ object MainModuleTests extends TestSuite { assert(results.failing.keyCount == 0) - val Result.Success(value) = results.rawValues.head + val Result.Success(value, _) = results.rawValues.head assert(value == ujson.Arr.from(Seq( ujson.Arr.from(Seq("hello", "world")), @@ -109,7 +109,7 @@ object MainModuleTests extends TestSuite { assert(results.failing.keyCount == 0) - val Result.Success(value) = results.rawValues.head + val Result.Success(value, _) = results.rawValues.head assert(value == ujson.Obj.from(Map( "hello" -> ujson.Arr.from(Seq("hello", "world")) @@ -126,7 +126,7 @@ object MainModuleTests extends TestSuite { assert(results.failing.keyCount == 0) - val Result.Success(value) = results.rawValues.head + val Result.Success(value, _) = results.rawValues.head assert(value == ujson.Obj.from(Map( "hello" -> ujson.Arr.from(Seq("hello", "world")), diff --git a/main/define/src/mill/define/Watchable.scala b/main/util/src/mill/util/Watchable.scala similarity index 80% rename from main/define/src/mill/define/Watchable.scala rename to main/util/src/mill/util/Watchable.scala index 66949e664d8..f465379937e 100644 --- a/main/define/src/mill/define/Watchable.scala +++ b/main/util/src/mill/util/Watchable.scala @@ -1,4 +1,4 @@ -package mill.define +package mill.util import mill.api.internal @@ -13,14 +13,16 @@ private[mill] trait Watchable { def poll(): Long def signature: Long def validate(): Boolean = poll() == signature + def pretty: String } @internal private[mill] object Watchable { case class Path(p: mill.api.PathRef) extends Watchable { def poll() = p.recomputeSig() def signature = p.sig + def pretty = p.toString } - case class Value(f: () => Long, signature: Long) extends Watchable { + case class Value(f: () => Long, signature: Long, pretty: String) extends Watchable { def poll() = f() } } diff --git a/main/util/src/mill/util/Watched.scala b/main/util/src/mill/util/Watched.scala index 4e7ee66a6ee..93ecc9704a5 100644 --- a/main/util/src/mill/util/Watched.scala +++ b/main/util/src/mill/util/Watched.scala @@ -2,8 +2,8 @@ package mill.util import mill.api.PathRef -case class Watched[T](value: T, watched: Seq[PathRef]) +case class Watched[T](value: T, watched: Seq[Watchable]) object Watched { - implicit def readWrite[T: upickle.default.ReadWriter]: upickle.default.ReadWriter[Watched[T]] = - upickle.default.macroRW[Watched[T]] +// implicit def readWrite[T: upickle.default.ReadWriter]: upickle.default.ReadWriter[Watched[T]] = +// upickle.default.macroRW[Watched[T]] } diff --git a/runner/src/mill/runner/MillBuildBootstrap.scala b/runner/src/mill/runner/MillBuildBootstrap.scala index 04b8c5f00af..5af8ba8768b 100644 --- a/runner/src/mill/runner/MillBuildBootstrap.scala +++ b/runner/src/mill/runner/MillBuildBootstrap.scala @@ -1,11 +1,11 @@ package mill.runner -import mill.util.{ColorLogger, PrefixLogger, Util} +import mill.util.{ColorLogger, PrefixLogger, Util, Watchable} import mill.{BuildInfo, T} import mill.api.{PathRef, internal} import mill.eval.Evaluator import mill.main.{RootModule, RunScript, SelectMode} import mill.main.TokenReaders._ -import mill.define.{Discover, Segments, Watchable} +import mill.define.{Discover, Segments} import java.net.URLClassLoader @@ -48,6 +48,7 @@ class MillBuildBootstrap( ) } + pprint.log(runnerState.frames.map(_.evalWatched.map(_.pretty))) Watching.Result( watched = runnerState.frames.flatMap(_.evalWatched), error = runnerState.errorOpt, @@ -254,6 +255,7 @@ class MillBuildBootstrap( val (evaled, evalWatched, moduleWatches) = MillBuildBootstrap.evaluateWithWatches(rootModule, evaluator, targetsAndParams) + val evalState = RunnerState.Frame( evaluator.workerCache.toMap, evalWatched, @@ -338,11 +340,10 @@ object MillBuildBootstrap { evalTaskResult match { case Left(msg) => (Left(msg), Nil, moduleWatched) - case Right((watchedPaths, evaluated)) => - val evalWatched = watchedPaths.map(Watchable.Path) + case Right((watched, evaluated)) => evaluated match { - case Left(msg) => (Left(msg), evalWatched, moduleWatched) - case Right(results) => (Right(results.map(_._1)), evalWatched, moduleWatched) + case Left(msg) => (Left(msg), watched, moduleWatched) + case Right(results) => (Right(results.map(_._1)), watched, moduleWatched) } } } diff --git a/runner/src/mill/runner/RunnerState.scala b/runner/src/mill/runner/RunnerState.scala index 3c60d76a951..c7b05545661 100644 --- a/runner/src/mill/runner/RunnerState.scala +++ b/runner/src/mill/runner/RunnerState.scala @@ -1,7 +1,8 @@ package mill.runner import mill.api.{PathRef, internal} -import mill.define.{BaseModule, Segments, Watchable} +import mill.define.{BaseModule, Segments} +import mill.util.Watchable import upickle.default.{ReadWriter, macroRW} import mill.api.JsonFormatters._ import mill.main.RootModule diff --git a/runner/src/mill/runner/Watching.scala b/runner/src/mill/runner/Watching.scala index a55f21bce62..2e65e36b161 100644 --- a/runner/src/mill/runner/Watching.scala +++ b/runner/src/mill/runner/Watching.scala @@ -1,8 +1,7 @@ package mill.runner import mill.api.internal -import mill.define.Watchable -import mill.util.ColorLogger +import mill.util.{ColorLogger, Watchable} import mill.api.SystemStreams import java.io.InputStream import scala.annotation.tailrec diff --git a/scalalib/src/mill/scalalib/GenIdeaImpl.scala b/scalalib/src/mill/scalalib/GenIdeaImpl.scala index 4f0c97cc613..ae6b46793a5 100755 --- a/scalalib/src/mill/scalalib/GenIdeaImpl.scala +++ b/scalalib/src/mill/scalalib/GenIdeaImpl.scala @@ -105,7 +105,7 @@ case class GenIdeaImpl( val millDeps = BuildInfo.millEmbeddedDeps.split(",").map(d => ivy"$d").map(dep => BoundDep(Lib.depToDependency(dep, BuildInfo.scalaVersion, ""), dep.force) ) - val Result.Success(res) = scalalib.Lib.resolveDependencies( + val Result.Success(res, _) = scalalib.Lib.resolveDependencies( repositories = repos.toList, deps = millDeps, sources = false, diff --git a/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala b/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala index 3fe25078286..61404c4add5 100644 --- a/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala +++ b/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala @@ -19,20 +19,20 @@ object ResolveDepsTests extends TestSuite { val tests = Tests { "resolveValidDeps" - { val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3") - val Success(paths) = evalDeps(deps) + val Success(paths, _) = evalDeps(deps) assert(paths.nonEmpty) } "resolveValidDepsWithClassifier" - { val deps = Agg(ivy"org.lwjgl:lwjgl:3.1.1;classifier=natives-macos") - val Success(paths) = evalDeps(deps) + val Success(paths, _) = evalDeps(deps) assert(paths.nonEmpty) assert(paths.items.next().path.toString.contains("natives-macos")) } "resolveTransitiveRuntimeDeps" - { val deps = Agg(ivy"org.mockito:mockito-core:2.7.22") - val Success(paths) = evalDeps(deps) + val Success(paths, _) = evalDeps(deps) assert(paths.nonEmpty) assert(paths.exists(_.path.toString.contains("objenesis"))) assert(paths.exists(_.path.toString.contains("byte-buddy"))) @@ -40,13 +40,13 @@ object ResolveDepsTests extends TestSuite { "excludeTransitiveDeps" - { val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3".exclude("com.lihaoyi" -> "fansi_2.12")) - val Success(paths) = evalDeps(deps) + val Success(paths, _) = evalDeps(deps) assert(!paths.exists(_.path.toString.contains("fansi_2.12"))) } "excludeTransitiveDepsByOrg" - { val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3".excludeOrg("com.lihaoyi")) - val Success(paths) = evalDeps(deps) + val Success(paths, _) = evalDeps(deps) assert(!paths.exists(path => path.path.toString.contains("com/lihaoyi") && !path.path.toString.contains("pprint_2.12") )) @@ -54,7 +54,7 @@ object ResolveDepsTests extends TestSuite { "excludeTransitiveDepsByName" - { val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3".excludeName("fansi_2.12")) - val Success(paths) = evalDeps(deps) + val Success(paths, _) = evalDeps(deps) assert(!paths.exists(_.path.toString.contains("fansi_2.12"))) } From 020964edcaca65891a9e79f828252a4a43251ed1 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 May 2023 00:17:44 +0800 Subject: [PATCH 02/27] . --- example/basic/1-simple-scala/build.sc | 125 +++++++++++++++++- main/api/src/mill/api/Ctx.scala | 6 +- main/define/src/mill/define/Task.scala | 12 +- main/eval/src/mill/eval/Evaluator.scala | 4 +- main/src/mill/main/RunScript.scala | 7 - .../src/mill/runner/MillBuildBootstrap.scala | 1 - 6 files changed, 132 insertions(+), 23 deletions(-) diff --git a/example/basic/1-simple-scala/build.sc b/example/basic/1-simple-scala/build.sc index a24fd0f20c8..3be43e9ade7 100644 --- a/example/basic/1-simple-scala/build.sc +++ b/example/basic/1-simple-scala/build.sc @@ -1,9 +1,124 @@ -import mill._ +import mill._, scalalib._ -//interp.watchValue(System.currentTimeMillis() / 1000) +object foo extends RootModule with ScalaModule { + def scalaVersion = "2.13.8" + def ivyDeps = Agg( + ivy"com.lihaoyi::scalatags:0.8.2", + ivy"com.lihaoyi::mainargs:0.4.0" + ) -println("Setting up build.sc") -def foo = T.input{ System.currentTimeMillis() / 1000 } + object test extends Tests { + def ivyDeps = Agg(ivy"com.lihaoyi::utest:0.7.11") + def testFramework = "utest.runner.Framework" + } +} -def bar = T{ foo() + " seconds since the epoch" } \ No newline at end of file +// This is a basic Mill build for a single `ScalaModule`, with two +// third-party dependencies and a test suite using the uTest framework. As a +// single-module project, it `extends RootModule` to mark `object foo` as the +// top-level module in the build. This lets us directly perform operations +// `./mill compile` or `./mill run` without needing to prefix it as +// `foo.compile` or `foo.run`. +// +// You can download this example project using the *download* link above +// if you want to try out the commands below yourself. The only requirement is +// that you have some version of the JVM installed; the `./mill` script takes +// care of any further dependencies that need to be downloaded. +// +// The source code for this module lives in the `src/` folder. +// Output for this module (compiled files, resolved dependency lists, ...) +// lives in `out/`. +// +// This example project uses two third-party dependencies - MainArgs for CLI +// argument parsing, Scalatags for HTML generation - and uses them to wrap a +// given input string in HTML templates with proper escaping. +// +// You can run `assembly` to generate a standalone executable jar, which then +// can be run from the command line or deployed to be run elsewhere. + +/** Usage + +> ./mill resolve _ # List what tasks are available to run +assembly +... +clean +... +compile +... +run +... +show +... +inspect +... + +> ./mill inspect compile # Show documentation and inputs of a task +compile(ScalaModule.scala:...) + Compiles the current module to generate compiled classfiles/bytecode. +Inputs: + scalaVersion + upstreamCompileOutput + allSourceFiles + compileClasspath + +> ./mill compile # compile sources into classfiles +... +compiling 1 Scala source to... + +> ./mill run # run the main method, if any +error: Missing argument: --text + +> ./mill run --text hello +

hello

+ +> ./mill test +... ++ foo.FooTests.simple ...

hello

++ foo.FooTests.escaping ...

<hello>

+ +> ./mill assembly # bundle classfiles and libraries into a jar for deployment + +> ./mill show assembly # show the output of the assembly task +".../out/assembly.dest/out.jar" + +> java -jar ./out/assembly.dest/out.jar --text hello +

hello

+ +> ./out/assembly.dest/out.jar --text hello # mac/linux +

hello

+ +*/ + +// The output of every Mill task is stored in the `out/` folder under a name +// corresponding to the task that created it. e.g. The `assembly` task puts its +// metadata output in `out/assembly.json`, and its output files in +// `out/assembly.dest`. You can also use `show` to make Mill print out the +// metadata output for a particular task. +// +// Additional Mill tasks you would likely need include: +// +// [source,bash] +// ---- +// $ mill runBackground # run the main method in the background +// +// $ mill clean # delete the cached output of a task, terminate any runBackground +// +// $ mill launcher # prepares a foo/launcher.dest/run you can run later +// +// $ mill jar # bundle the classfiles into a jar suitable for publishing +// +// $ mill -i console # start a Scala console within your project +// +// $ mill -i repl # start an Ammonite Scala REPL within your project +// ---- +// +// You can run `+mill resolve __+` to see a full list of the different tasks that +// are available, `+mill resolve _+` to see the tasks within `foo`, +// `mill inspect compile` to inspect a task's doc-comment documentation or what +// it depends on, or `mill show foo.scalaVersion` to show the output of any task. +// +// The most common *tasks* that Mill can run are cached *targets*, such as +// `compile`, and un-cached *commands* such as `foo.run`. Targets do not +// re-evaluate unless one of their inputs changes, whereas commands re-run every +// time. \ No newline at end of file diff --git a/main/api/src/mill/api/Ctx.scala b/main/api/src/mill/api/Ctx.scala index 8455a9a9c36..c4cc74a22ff 100644 --- a/main/api/src/mill/api/Ctx.scala +++ b/main/api/src/mill/api/Ctx.scala @@ -111,7 +111,7 @@ object Ctx { * implementation of a `Task`. */ class Ctx( - val args: IndexedSeq[_], + val args: IndexedSeq[Result.Success[_]], dest0: () => os.Path, val log: Logger, val home: os.Path, @@ -127,8 +127,8 @@ class Ctx( with Ctx.Workspace { def dest: Path = dest0() - def arg[T](index: Int): T = { - if (index >= 0 && index < args.length) args(index).asInstanceOf[T] + def arg[T](index: Int): Result.Success[T] = { + if (index >= 0 && index < args.length) args(index).asInstanceOf[Result.Success[T]] else throw new IndexOutOfBoundsException(s"Index $index outside of range 0 - ${args.length}") } } diff --git a/main/define/src/mill/define/Task.scala b/main/define/src/mill/define/Task.scala index 2f5cfdf5881..aa1457ff096 100644 --- a/main/define/src/mill/define/Task.scala +++ b/main/define/src/mill/define/Task.scala @@ -58,7 +58,7 @@ object Task { val inputs = inputs0 def evaluate(ctx: mill.api.Ctx) = { for (i <- 0 until ctx.args.length) - yield ctx.args(i).asInstanceOf[T] + yield ctx.arg[T](i).value } } private[define] class TraverseCtx[+T, V]( @@ -69,17 +69,19 @@ object Task { def evaluate(ctx: mill.api.Ctx) = { f( for (i <- 0 until ctx.args.length) - yield ctx.args(i).asInstanceOf[T], + yield ctx.arg[T](i).value, ctx ) } } private[define] class Mapped[+T, +V](source: Task[T], f: T => V) extends Task[V] { - def evaluate(ctx: mill.api.Ctx) = f(ctx.arg(0)) + def evaluate(ctx: mill.api.Ctx) = ctx.arg(0).map(f) val inputs = List(source) } private[define] class Zipped[+T, +V](source1: Task[T], source2: Task[V]) extends Task[(T, V)] { - def evaluate(ctx: mill.api.Ctx) = (ctx.arg(0), ctx.arg(1)) + def evaluate(ctx: mill.api.Ctx) = (ctx.arg[T](0), ctx.arg[V](1)) match{ + case (Result.Success(a, _), Result.Success(b, _)) => (a, b) + } val inputs = List(source1, source2) } } @@ -105,7 +107,7 @@ trait NamedTask[+T] extends Task[T] { } override def toString = ctx.segments.render - def evaluate(ctx: mill.api.Ctx) = ctx.arg[T](0) + def evaluate(ctx: mill.api.Ctx): Result[T] = ctx.arg[T](0) val ctx = ctx0.withSegments(segments = ctx0.segments ++ Seq(ctx0.segment)) val inputs = Seq(t) diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index bd26d4744a7..4bbf0475ecb 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -557,13 +557,13 @@ class Evaluator private ( newEvaluated.append(task) val targetInputValues = task.inputs .map { x => newResults.getOrElse(x, results(x)) } - .collect { case mill.api.Result.Success((v, _), _) => v } + .collect { case Result.Success((v, _), sig) => Result.Success(v, sig) } val res = if (targetInputValues.length != task.inputs.length) mill.api.Result.Skipped else { val args = new Ctx( - args = targetInputValues.toArray[Any].toIndexedSeq, + args = targetInputValues.toArray[Result.Success[Any]].toIndexedSeq, dest0 = () => paths match { case Some(dest) => diff --git a/main/src/mill/main/RunScript.scala b/main/src/mill/main/RunScript.scala index 109c56262d6..7e4f9e9d871 100644 --- a/main/src/mill/main/RunScript.scala +++ b/main/src/mill/main/RunScript.scala @@ -26,8 +26,6 @@ object RunScript { w <- extraWatched } yield w - pprint.log(watched.map(_.pretty)) - pprint.log(watched2.map(_.pretty)) (watched ++ watched2, res) } } @@ -56,7 +54,6 @@ object RunScript { targets: Agg[Task[Any]] ): (Seq[Watchable], Either[String, Seq[(Any, Option[ujson.Value])]]) = { val (watched, results) = evaluateNamed(evaluator, targets) - pprint.log(watched.map(_.pretty)) // we drop the task name in the inner tuple (watched, results.map(_.map(p => (p._1, p._2.map(_._2))))) } @@ -78,15 +75,11 @@ object RunScript { case (t: SourceImpl, Result.Success(p: PathRef, _)) => Seq(Watchable.Path(p)) case (t: InputImpl[_], Result.Success(v, signature)) => val pretty = t.ctx0.fileName + ":" + t.ctx0.lineNum - println("INPUT FOUND " + pretty) Seq(Watchable.Value(() => signature(), signature(), pretty)) } .flatten .toSeq - pprint.log(targets) - pprint.log(watched.map(_.pretty)) - val errorStr = Evaluator.formatFailing(evaluated) evaluated.failing.keyCount match { diff --git a/runner/src/mill/runner/MillBuildBootstrap.scala b/runner/src/mill/runner/MillBuildBootstrap.scala index 5af8ba8768b..75d602509d7 100644 --- a/runner/src/mill/runner/MillBuildBootstrap.scala +++ b/runner/src/mill/runner/MillBuildBootstrap.scala @@ -48,7 +48,6 @@ class MillBuildBootstrap( ) } - pprint.log(runnerState.frames.map(_.evalWatched.map(_.pretty))) Watching.Result( watched = runnerState.frames.flatMap(_.evalWatched), error = runnerState.errorOpt, From f1ca4c5dc409f00b778168af8ad904507888c98c Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 May 2023 00:19:09 +0800 Subject: [PATCH 03/27] . --- main/api/src/mill/api/Result.scala | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/main/api/src/mill/api/Result.scala b/main/api/src/mill/api/Result.scala index d5905fcef99..cb1ec623bf7 100644 --- a/main/api/src/mill/api/Result.scala +++ b/main/api/src/mill/api/Result.scala @@ -33,17 +33,7 @@ object Result { override def asSuccess: Option[Success[T]] = Some(this) } object Success{ - def apply[T](value: => T) = { - new Success( - value, - () => { -// if (value.toString.length < 100) { -// mill.api.SystemStreams.originalErr.println("Signature " + System.identityHashCode(value)) -// } - value.hashCode() - } - ) - } + def apply[T](value: => T) = new Success(value, () => value.hashCode()) } /** From 590b09fc9b75e0ebffbcadf1a23617706522f128 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 May 2023 01:14:37 +0800 Subject: [PATCH 04/27] . --- main/api/src/mill/api/Result.scala | 6 +- main/eval/src/mill/eval/Evaluator.scala | 2 +- main/src/mill/main/MainModule.scala | 113 +++++++----------- main/src/mill/main/RunScript.scala | 47 +------- main/util/src/mill/util/Watched.scala | 9 -- .../src/mill/runner/MillBuildBootstrap.scala | 12 +- 6 files changed, 60 insertions(+), 129 deletions(-) delete mode 100644 main/util/src/mill/util/Watched.scala diff --git a/main/api/src/mill/api/Result.scala b/main/api/src/mill/api/Result.scala index cb1ec623bf7..b88251b3cb3 100644 --- a/main/api/src/mill/api/Result.scala +++ b/main/api/src/mill/api/Result.scala @@ -27,13 +27,13 @@ object Result { * @param value The value computed by the task. * @tparam T The result type of the computed task. */ - case class Success[+T](value: T, signature: () => Int) extends Result[T] { - def map[V](f: T => V): Success[V] = Result.Success(f(value), signature) + case class Success[+T](value: T, recalc: () => T) extends Result[T] { + def map[V](f: T => V): Success[V] = Result.Success(f(value), () => f(recalc())) def flatMap[V](f: T => Result[V]): Result[V] = f(value) override def asSuccess: Option[Success[T]] = Some(this) } object Success{ - def apply[T](value: => T) = new Success(value, () => value.hashCode()) + def apply[T](value: => T) = new Success(value, () => value) } /** diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index 4bbf0475ecb..c57df7066be 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -423,7 +423,7 @@ class Evaluator private ( upToDateWorker.map((_, inputsHash)) orElse cached match { case Some((v, hashCode)) => val newResults = mutable.LinkedHashMap.empty[Task[_], mill.api.Result[(Any, Int)]] - newResults(labelledNamedTask.task) = mill.api.Result.Success((v, hashCode), () => 0) + newResults(labelledNamedTask.task) = Result.Success((v, hashCode), () => (v, hashCode)) Evaluated(newResults, Nil, cached = true) diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index a9948128da7..d59e23a83ab 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -2,10 +2,11 @@ package mill.main import java.util.concurrent.LinkedBlockingQueue import mill.{BuildInfo, T} -import mill.api.{Ctx, PathRef, Result, internal} +import mill.api.{Ctx, Logger, PathRef, Result, internal} import mill.define.{Command, NamedTask, Segments, TargetImpl, Task} import mill.eval.{Evaluator, EvaluatorPaths} -import mill.util.{PrintLogger, Watchable, Watched} +import mill.main.SelectMode.Separated +import mill.util.{PrintLogger, Watchable} import pprint.{Renderer, Tree, Truncated} import ujson.Value @@ -25,32 +26,33 @@ object MainModule { } } - def evaluateTasks[T]( - evaluator: Evaluator, - targets: Seq[String], - selectMode: SelectMode - )(f: Seq[(Any, Option[ujson.Value])] => T): Result[Watched[Unit]] = { - RunScript.evaluateTasks(evaluator, targets, selectMode) match { + private def show0(evaluator: Evaluator, + targets: Seq[String], + log: Logger, + watch0: Watchable => Unit) + (f: Seq[(Any, Option[(RunScript.TaskName, ujson.Value)])] => ujson.Value) = { + RunScript.evaluateTasksNamed( + evaluator.withBaseLogger( + // When using `show`, redirect all stdout of the evaluated tasks so the + // printed JSON is the only thing printed to stdout. + evaluator.baseLogger match { + case p: PrintLogger => p.withOutStream(p.errorStream) + case l => l + } + ), + targets, + Separated + ) 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)) - } - } + case Right((watched, Left(err))) => + watched.foreach(watch0) + Result.Failure(err) - @internal - def evaluateTasksNamed[T]( - evaluator: Evaluator, - targets: Seq[String], - selectMode: SelectMode - )(f: Seq[(Any, Option[(RunScript.TaskName, ujson.Value)])] => T): Result[Watched[Option[T]]] = { - RunScript.evaluateTasksNamed(evaluator, targets, selectMode) match { - case Left(err) => Result.Failure(err) - case Right((watched, Left(err))) => Result.Failure(err, Some(Watched(None, watched))) case Right((watched, Right(res))) => - val fRes = f(res) - Result.Success(Watched(Some(fRes), watched)) + val output = f(res) + watched.foreach(watch0) + log.outputStream.println(output.render(indent = 2)) + Result.Success(output) } } } @@ -61,6 +63,7 @@ object MainModule { */ trait MainModule extends mill.Module { protected[mill] val watchedValues = mutable.Buffer.empty[Watchable] + protected[mill] val evalWatchedValues = mutable.Buffer.empty[Watchable] object interp { @@ -84,6 +87,10 @@ trait MainModule extends mill.Module { def watch0(w: Watchable): Unit = { watchedValues.append(w) } + + def evalWatch0(w: Watchable): Unit = { + evalWatchedValues.append(w) + } } @@ -272,27 +279,11 @@ trait MainModule extends mill.Module { * to integrate Mill into external scripts and tooling. */ def show(evaluator: Evaluator, targets: String*): Command[ujson.Value] = T.command { - MainModule.evaluateTasksNamed( - evaluator.withBaseLogger( - // When using `show`, redirect all stdout of the evaluated tasks so the - // printed JSON is the only thing printed to stdout. - evaluator.baseLogger match { - case p: PrintLogger => p.withOutStream(p.errorStream) - case l => l - } - ), - targets, - SelectMode.Separated - ) { res: Seq[(Any, Option[(String, ujson.Value)])] => - val jsons = res.flatMap(_._2).map(_._2) - val output: ujson.Value = - if (jsons.size == 1) jsons.head - else { ujson.Arr.from(jsons) } - T.log.outputStream.println(output.render(indent = 2)) - output - }.map { res: Watched[Option[Value]] => - res.watched.foreach(interp.watch0) - res.value.getOrElse(ujson.Null) + MainModule.show0(evaluator, targets, T.log, interp.evalWatch0){ res => + res.flatMap(_._2).map(_._2) match{ + case Seq(single) => single + case multiple => multiple + } } } @@ -301,24 +292,8 @@ trait MainModule extends mill.Module { * to integrate Mill into external scripts and tooling. */ def showNamed(evaluator: Evaluator, targets: String*): Command[ujson.Value] = T.command { - MainModule.evaluateTasksNamed( - evaluator.withBaseLogger( - // When using `show`, redirect all stdout of the evaluated tasks so the - // printed JSON is the only thing printed to stdout. - evaluator.baseLogger match { - case p: PrintLogger => p.withOutStream(outStream = p.errorStream) - case l => l - } - ), - targets, - SelectMode.Separated - ) { res: Seq[(Any, Option[(String, ujson.Value)])] => - val nameAndJson = res.flatMap(_._2) - val output: ujson.Value = ujson.Obj.from(nameAndJson) - T.log.outputStream.println(output.render(indent = 2)) - output - }.map { res: Watched[Option[Value]] => - res.value.getOrElse(ujson.Null) + MainModule.show0(evaluator, targets, T.log, interp.evalWatch0) { res => + ujson.Obj.from(res.flatMap(_._2)) } } @@ -409,17 +384,19 @@ trait MainModule extends mill.Module { } /** - * The `init`` command generates a project based on a Giter8 template. It + * 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] = T.command { - MainModule.evaluateTasks( + RunScript.evaluateTasksNamed( evaluator, Seq("mill.scalalib.giter8.Giter8Module/init") ++ args, - selectMode = SelectMode.Single - )(identity).map(_.value) + SelectMode.Single + ) + + () } private type VizWorker = ( diff --git a/main/src/mill/main/RunScript.scala b/main/src/mill/main/RunScript.scala index 7e4f9e9d871..7f2c7b1af32 100644 --- a/main/src/mill/main/RunScript.scala +++ b/main/src/mill/main/RunScript.scala @@ -2,7 +2,7 @@ package mill.main import mill.define._ import mill.eval.{Evaluator, EvaluatorPaths} -import mill.util.{Watchable, Watched} +import mill.util.Watchable import mill.api.{PathRef, Result} import mill.api.Strict.Agg @@ -11,51 +11,13 @@ object RunScript { type TaskName = String - def evaluateTasks( - evaluator: Evaluator, - scriptArgs: Seq[String], - selectMode: SelectMode - ): Either[String, (Seq[Watchable], Either[String, Seq[(Any, Option[ujson.Value])]])] = { - for (targets <- ResolveTasks.resolve(evaluator, scriptArgs, selectMode)) - yield { - val (watched, res) = evaluate(evaluator, Agg.from(targets.distinct)) - - val watched2 = for { - x <- res.toSeq - (Watched(_, extraWatched), _) <- x - w <- extraWatched - } yield w - - (watched ++ watched2, res) - } - } - def evaluateTasksNamed[T]( evaluator: Evaluator, scriptArgs: Seq[String], selectMode: SelectMode ): Either[String, (Seq[Watchable], Either[String, Seq[(Any, Option[(TaskName, ujson.Value)])]])] = { for (targets <- ResolveTasks.resolve(evaluator, scriptArgs, selectMode)) - yield { - val (watched, res) = evaluateNamed(evaluator, Agg.from(targets.distinct)) - - val watched2 = for { - x <- res.toSeq - (Watched(_, extraWatched), _) <- x - w <- extraWatched - } yield w - - (watched ++ watched2, res) - } - } - - def evaluate( - evaluator: Evaluator, - targets: Agg[Task[Any]] - ): (Seq[Watchable], Either[String, Seq[(Any, Option[ujson.Value])]]) = { - val (watched, results) = evaluateNamed(evaluator, targets) - // we drop the task name in the inner tuple - (watched, results.map(_.map(p => (p._1, p._2.map(_._2))))) + yield evaluateNamed(evaluator, Agg.from(targets.distinct)) } /** @@ -68,14 +30,15 @@ object RunScript { targets: Agg[Task[Any]] ): (Seq[Watchable], Either[String, Seq[(Any, Option[(TaskName, ujson.Value)])]]) = { val evaluated: Evaluator.Results = evaluator.evaluate(targets) + val watched = evaluated.results .iterator .collect { case (t: SourcesImpl, Result.Success(ps: Seq[PathRef], _)) => ps.map(Watchable.Path(_)) case (t: SourceImpl, Result.Success(p: PathRef, _)) => Seq(Watchable.Path(p)) - case (t: InputImpl[_], Result.Success(v, signature)) => + case (t: InputImpl[_], Result.Success(v, recalc)) => val pretty = t.ctx0.fileName + ":" + t.ctx0.lineNum - Seq(Watchable.Value(() => signature(), signature(), pretty)) + Seq(Watchable.Value(() => recalc().hashCode(), recalc().hashCode(), pretty)) } .flatten .toSeq diff --git a/main/util/src/mill/util/Watched.scala b/main/util/src/mill/util/Watched.scala deleted file mode 100644 index 93ecc9704a5..00000000000 --- a/main/util/src/mill/util/Watched.scala +++ /dev/null @@ -1,9 +0,0 @@ -package mill.util - -import mill.api.PathRef - -case class Watched[T](value: T, watched: Seq[Watchable]) -object Watched { -// implicit def readWrite[T: upickle.default.ReadWriter]: upickle.default.ReadWriter[Watched[T]] = -// upickle.default.macroRW[Watched[T]] -} diff --git a/runner/src/mill/runner/MillBuildBootstrap.scala b/runner/src/mill/runner/MillBuildBootstrap.scala index 75d602509d7..d6583ea28f3 100644 --- a/runner/src/mill/runner/MillBuildBootstrap.scala +++ b/runner/src/mill/runner/MillBuildBootstrap.scala @@ -49,7 +49,7 @@ class MillBuildBootstrap( } Watching.Result( - watched = runnerState.frames.flatMap(_.evalWatched), + watched = runnerState.frames.flatMap(f => f.evalWatched ++ f.moduleWatched), error = runnerState.errorOpt, result = runnerState ) @@ -254,7 +254,6 @@ class MillBuildBootstrap( val (evaled, evalWatched, moduleWatches) = MillBuildBootstrap.evaluateWithWatches(rootModule, evaluator, targetsAndParams) - val evalState = RunnerState.Frame( evaluator.workerCache.toMap, evalWatched, @@ -333,16 +332,17 @@ object MillBuildBootstrap { evaluator: Evaluator, targetsAndParams: Seq[String] ): (Either[String, Seq[Any]], Seq[Watchable], Seq[Watchable]) = { - - val evalTaskResult = RunScript.evaluateTasks(evaluator, targetsAndParams, SelectMode.Separated) + rootModule.evalWatchedValues.clear() + val evalTaskResult = RunScript.evaluateTasksNamed(evaluator, targetsAndParams, SelectMode.Separated) val moduleWatched = rootModule.watchedValues.toVector + val addedEvalWatched = rootModule.evalWatchedValues.toVector evalTaskResult match { case Left(msg) => (Left(msg), Nil, moduleWatched) case Right((watched, evaluated)) => evaluated match { - case Left(msg) => (Left(msg), watched, moduleWatched) - case Right(results) => (Right(results.map(_._1)), watched, moduleWatched) + case Left(msg) => (Left(msg), watched ++ addedEvalWatched, moduleWatched) + case Right(results) => (Right(results.map(_._1)), watched ++ addedEvalWatched, moduleWatched) } } } From 467c0f2e5c7960b4bbe342bc28cacbfde3cb465d Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 May 2023 18:05:07 +0800 Subject: [PATCH 05/27] compiles after moving recalc to Evaluator.EvalResult --- bsp/src/mill/bsp/BSP.scala | 2 +- bsp/worker/src/mill/bsp/worker/Utils.scala | 4 +- main/api/src/mill/api/Ctx.scala | 6 +- main/api/src/mill/api/Result.scala | 7 +- main/define/src/mill/define/Task.scala | 10 +-- main/eval/src/mill/eval/Evaluator.scala | 80 +++++++++++-------- main/src/mill/main/RunScript.scala | 6 +- main/test/src/mill/main/MainModuleTests.scala | 12 +-- scalalib/src/mill/scalalib/GenIdeaImpl.scala | 2 +- .../src/mill/scalalib/ResolveDepsTests.scala | 12 +-- 10 files changed, 74 insertions(+), 67 deletions(-) diff --git a/bsp/src/mill/bsp/BSP.scala b/bsp/src/mill/bsp/BSP.scala index 3b16b7b54fb..a71c6844f30 100644 --- a/bsp/src/mill/bsp/BSP.scala +++ b/bsp/src/mill/bsp/BSP.scala @@ -95,7 +95,7 @@ object BSP extends ExternalModule with CoursierModule with BspServerStarter { val worker = BspWorker(ctx) worker match { - case Result.Success(worker, _) => + case Result.Success(worker) => worker.startBspServer( initialEvaluator, streams, diff --git a/bsp/worker/src/mill/bsp/worker/Utils.scala b/bsp/worker/src/mill/bsp/worker/Utils.scala index 64c54cc0fe6..b05c58ee9c8 100644 --- a/bsp/worker/src/mill/bsp/worker/Utils.scala +++ b/bsp/worker/src/mill/bsp/worker/Utils.scala @@ -45,8 +45,8 @@ object Utils { results: Evaluator.Results, task: mill.define.Task[_] ): StatusCode = { - results.results(task) match { - case Success(_, _) => StatusCode.OK + results.results(task).result match { + case Success(_) => StatusCode.OK case Skipped => StatusCode.CANCELLED case _ => StatusCode.ERROR } diff --git a/main/api/src/mill/api/Ctx.scala b/main/api/src/mill/api/Ctx.scala index c4cc74a22ff..8455a9a9c36 100644 --- a/main/api/src/mill/api/Ctx.scala +++ b/main/api/src/mill/api/Ctx.scala @@ -111,7 +111,7 @@ object Ctx { * implementation of a `Task`. */ class Ctx( - val args: IndexedSeq[Result.Success[_]], + val args: IndexedSeq[_], dest0: () => os.Path, val log: Logger, val home: os.Path, @@ -127,8 +127,8 @@ class Ctx( with Ctx.Workspace { def dest: Path = dest0() - def arg[T](index: Int): Result.Success[T] = { - if (index >= 0 && index < args.length) args(index).asInstanceOf[Result.Success[T]] + def arg[T](index: Int): T = { + if (index >= 0 && index < args.length) args(index).asInstanceOf[T] else throw new IndexOutOfBoundsException(s"Index $index outside of range 0 - ${args.length}") } } diff --git a/main/api/src/mill/api/Result.scala b/main/api/src/mill/api/Result.scala index b88251b3cb3..5ec9a6e99c3 100644 --- a/main/api/src/mill/api/Result.scala +++ b/main/api/src/mill/api/Result.scala @@ -27,14 +27,11 @@ object Result { * @param value The value computed by the task. * @tparam T The result type of the computed task. */ - case class Success[+T](value: T, recalc: () => T) extends Result[T] { - def map[V](f: T => V): Success[V] = Result.Success(f(value), () => f(recalc())) + case class Success[+T](value: T) extends Result[T] { + def map[V](f: T => V): Success[V] = Result.Success(f(value)) def flatMap[V](f: T => Result[V]): Result[V] = f(value) override def asSuccess: Option[Success[T]] = Some(this) } - object Success{ - def apply[T](value: => T) = new Success(value, () => value) - } /** * A task execution was skipped because of failures in it's dependencies. diff --git a/main/define/src/mill/define/Task.scala b/main/define/src/mill/define/Task.scala index 22758da9d15..9a8ac145b7d 100644 --- a/main/define/src/mill/define/Task.scala +++ b/main/define/src/mill/define/Task.scala @@ -56,7 +56,7 @@ object Task { val inputs = inputs0 def evaluate(ctx: mill.api.Ctx) = { for (i <- 0 until ctx.args.length) - yield ctx.arg[T](i).value + yield ctx.arg[T](i) } } private[define] class TraverseCtx[+T, V]( @@ -67,21 +67,19 @@ object Task { def evaluate(ctx: mill.api.Ctx) = { f( for (i <- 0 until ctx.args.length) - yield ctx.arg[T](i).value, + yield ctx.arg[T](i), ctx ) } } private[define] class Mapped[+T, +V](source: Task[T], f: T => V) extends Task[V] { - def evaluate(ctx: mill.api.Ctx) = ctx.arg(0).map(f) + def evaluate(ctx: mill.api.Ctx) = f(ctx.arg(0)) val inputs = List(source) } private[define] class Zipped[+T, +V](source1: Task[T], source2: Task[V]) extends Task[(T, V)] { - def evaluate(ctx: mill.api.Ctx) = (ctx.arg[T](0), ctx.arg[V](1)) match{ - case (Result.Success(a, _), Result.Success(b, _)) => (a, b) - } + def evaluate(ctx: mill.api.Ctx) = (ctx.arg[T](0), ctx.arg[V](1)) val inputs = List(source1, source2) } } diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index c57df7066be..86967c2acd5 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -122,14 +122,14 @@ class Evaluator private ( val (sortedGroups, transitive) = Evaluator.plan(goals) val evaluated = new Agg.Mutable[Task[_]] - val results = mutable.LinkedHashMap.empty[Task[_], mill.api.Result[(Any, Int)]] + val results = mutable.LinkedHashMap.empty[Task[_], Evaluator.EvalResult[(_, Int)]] var someTaskFailed: Boolean = false val timings = mutable.ArrayBuffer.empty[(Either[Task[_], Labelled[_]], Int, Boolean)] for (((terminal, group), i) <- sortedGroups.items().zipWithIndex) { if (failFast && someTaskFailed) { // we exit early and set aborted state for all left tasks - group.iterator.foreach { task => results.put(task, Aborted) } + group.iterator.foreach { task => results.put(task, Evaluator.EvalResult(Aborted, None)) } } else { @@ -170,21 +170,21 @@ class Evaluator private ( evaluated = evaluated, transitive = transitive, failing = getFailing(sortedGroups, results), - results = results.map { case (k, v) => (k, v.map(_._1)) } + results = results ) } def getFailing( sortedGroups: MultiBiMap[Either[Task[_], Labelled[Any]], Task[_]], - results: collection.Map[Task[_], mill.api.Result[(Any, Int)]] + results: collection.Map[Task[_], Evaluator.EvalResult[(_, Int)]] ): MultiBiMap.Mutable[Either[Task[_], Labelled[_]], Failing[_]] = { val failing = new MultiBiMap.Mutable[Either[Task[_], Labelled[_]], mill.api.Result.Failing[_]] for ((k, vs) <- sortedGroups.items()) { failing.addAll( k, Loose.Agg.from( - vs.items.flatMap(results.get).collect { case f: mill.api.Result.Failing[_] => - f.map(_._1) + vs.items.flatMap(results.get).collect { case er @ Evaluator.EvalResult(f: mill.api.Result.Failing[_], _) => + f } ) ) @@ -254,7 +254,7 @@ class Evaluator private ( contextLogger ) - if (failFast && res.newResults.values.exists(_.asSuccess.isEmpty)) failed.set(true) + if (failFast && res.newResults.values.exists(_.result.asSuccess.isEmpty)) failed.set(true) val endTime = System.currentTimeMillis() timeLog.timeTrace( @@ -275,16 +275,18 @@ class Evaluator private ( val finishedOptsMap = finishedOpts.toMap - val results = terminals + val results0: Vector[(Task[_], Evaluator.EvalResult[(_, Int)])] = terminals .flatMap { t => sortedGroups.lookupKey(t).flatMap { t0 => finishedOptsMap(t) match { - case None => Some((t0, Aborted)) + case None => Some((t0, Evaluator.EvalResult(Aborted, None))) case Some(res) => res.newResults.get(t0).map(r => (t0, r)) } } } - .toMap + + val results: Map[Task[_], Evaluator.EvalResult[(_, Int)]] = + results0.toMap timeLog.close() @@ -304,7 +306,7 @@ class Evaluator private ( protected def evaluateGroupCached( terminal: Terminal, group: Agg[Task[_]], - results: collection.Map[Task[_], mill.api.Result[(Any, Int)]], + results: collection.Map[Task[_], Evaluator.EvalResult[(_, Int)]], counterMsg: String, zincProblemReporter: Int => Option[CompileProblemReporter], testReporter: TestReporter, @@ -313,7 +315,7 @@ class Evaluator private ( val externalInputsHash = scala.util.hashing.MurmurHash3.orderedHash( group.items.flatMap(_.inputs).filter(!group.contains(_)) - .flatMap(results(_).asSuccess.map(_.value._2)) + .flatMap(results(_).result.asSuccess.map(_.value._2)) ) val sideHashes = scala.util.hashing.MurmurHash3.orderedHash( @@ -422,8 +424,8 @@ class Evaluator private ( upToDateWorker.map((_, inputsHash)) orElse cached match { case Some((v, hashCode)) => - val newResults = mutable.LinkedHashMap.empty[Task[_], mill.api.Result[(Any, Int)]] - newResults(labelledNamedTask.task) = Result.Success((v, hashCode), () => (v, hashCode)) + val newResults = mutable.LinkedHashMap.empty[Task[_], Evaluator.EvalResult[(Any, Int)]] + newResults(labelledNamedTask.task) = Evaluator.EvalResult(Result.Success((v, hashCode)), None) Evaluated(newResults, Nil, cached = true) @@ -449,10 +451,10 @@ class Evaluator private ( } newResults(labelledNamedTask.task) match { - case mill.api.Result.Failure(_, Some((v, _))) => + case Evaluator.EvalResult(mill.api.Result.Failure(_, Some((v, _))), _) => handleTaskResult(v, v.##, paths.meta, inputsHash, labelledNamedTask) - case mill.api.Result.Success((v, _), _) => + case Evaluator.EvalResult(mill.api.Result.Success((v, _)), _) => handleTaskResult(v, v.##, paths.meta, inputsHash, labelledNamedTask) case _ => @@ -512,7 +514,7 @@ class Evaluator private ( protected def evaluateGroup( group: Agg[Task[_]], - results: collection.Map[Task[_], mill.api.Result[(Any, Int)]], + results: collection.Map[Task[_], Evaluator.EvalResult[(_, Int)]], inputsHash: Int, paths: Option[EvaluatorPaths], maybeTargetLabel: Option[String], @@ -520,10 +522,10 @@ class Evaluator private ( reporter: Int => Option[CompileProblemReporter], testReporter: TestReporter, logger: mill.api.Logger - ): (mutable.LinkedHashMap[Task[_], mill.api.Result[(Any, Int)]], mutable.Buffer[Task[_]]) = { + ): (mutable.LinkedHashMap[Task[_], Evaluator.EvalResult[(Any, Int)]], mutable.Buffer[Task[_]]) = { val newEvaluated = mutable.Buffer.empty[Task[_]] - val newResults = mutable.LinkedHashMap.empty[Task[_], mill.api.Result[(Any, Int)]] + val newResults = mutable.LinkedHashMap.empty[Task[_], Evaluator.EvalResult[(Any, Int)]] val nonEvaluatedTargets = group.indexed.filterNot(results.contains) @@ -557,7 +559,7 @@ class Evaluator private ( newEvaluated.append(task) val targetInputValues = task.inputs .map { x => newResults.getOrElse(x, results(x)) } - .collect { case Result.Success((v, _), sig) => Result.Success(v, sig) } + .collect { case Evaluator.EvalResult(Result.Success((v, _)), _) => Result.Success(v) } val res = if (targetInputValues.length != task.inputs.length) mill.api.Result.Skipped @@ -600,13 +602,16 @@ class Evaluator private ( } - newResults(task) = for (v <- res) yield { - ( - v, - if (task.isInstanceOf[Worker[_]]) inputsHash - else v.## - ) - } + newResults(task) = Evaluator.EvalResult( + for (v <- res) yield { + ( + v, + if (task.isInstanceOf[Worker[_]]) inputsHash + else v.## + ) + }, + None + ) } if (!failFast) maybeTargetLabel.foreach { targetLabel => @@ -764,20 +769,27 @@ object Evaluator { ) } + case class EvalResult[T](result: Result[T], + recalcOpt: Option[() => mill.api.Result[T]]){ + def map[V](f: T => V) = EvalResult[V]( + result.map(f), + recalcOpt.map(r => () => r().map(f)) + ) + } case class Results( rawValues: Seq[mill.api.Result[Any]], evaluated: Agg[Task[_]], transitive: Agg[Task[_]], failing: MultiBiMap[Either[Task[_], Labelled[_]], mill.api.Result.Failing[_]], - results: collection.Map[Task[_], mill.api.Result[Any]] + results: collection.Map[Task[_], EvalResult[_]] ) { - def values: Seq[Any] = rawValues.collect { case mill.api.Result.Success(v, _) => v } + def values: Seq[Any] = rawValues.collect { case mill.api.Result.Success(v) => v } private def copy( rawValues: Seq[Result[Any]] = rawValues, evaluated: Agg[Task[_]] = evaluated, transitive: Agg[Task[_]] = transitive, failing: MultiBiMap[Either[Task[_], Labelled[_]], Result.Failing[_]] = failing, - results: collection.Map[Task[_], Result[Any]] = results + results: collection.Map[Task[_], EvalResult[_]] ): Results = new Results( rawValues, evaluated, @@ -793,7 +805,7 @@ object Evaluator { Agg[Task[_]], Agg[Task[_]], MultiBiMap[Either[Task[_], Labelled[_]], Failing[_]], - collection.Map[Task[_], Result[Any]] + collection.Map[Task[_], EvalResult[_]] )] = Some(( results.rawValues, results.evaluated, @@ -844,12 +856,12 @@ object Evaluator { } case class Evaluated( - newResults: collection.Map[Task[_], Result[(Any, Int)]], + newResults: collection.Map[Task[_], EvalResult[(_, Int)]], newEvaluated: Seq[Task[_]], cached: Boolean ) { private[Evaluator] def copy( - newResults: collection.Map[Task[_], Result[(Any, Int)]] = newResults, + newResults: collection.Map[Task[_], EvalResult[(_, Int)]], newEvaluated: Seq[Task[_]] = newEvaluated, cached: Boolean = cached ): Evaluated = new Evaluated(newResults, newEvaluated, cached) @@ -857,7 +869,7 @@ object Evaluator { object Evaluated { private[Evaluator] def unapply(evaluated: Evaluated) - : Option[(collection.Map[Task[_], Result[(Any, Int)]], Seq[Task[_]], Boolean)] = + : Option[(collection.Map[Task[_], EvalResult[(_, Int)]], Seq[Task[_]], Boolean)] = Some((evaluated.newResults, evaluated.newEvaluated, evaluated.cached)) } diff --git a/main/src/mill/main/RunScript.scala b/main/src/mill/main/RunScript.scala index 7f2c7b1af32..37b9309e028 100644 --- a/main/src/mill/main/RunScript.scala +++ b/main/src/mill/main/RunScript.scala @@ -34,9 +34,9 @@ object RunScript { val watched = evaluated.results .iterator .collect { - case (t: SourcesImpl, Result.Success(ps: Seq[PathRef], _)) => ps.map(Watchable.Path(_)) - case (t: SourceImpl, Result.Success(p: PathRef, _)) => Seq(Watchable.Path(p)) - case (t: InputImpl[_], Result.Success(v, recalc)) => + case (t: SourcesImpl, Evaluator.EvalResult(Result.Success(ps: Seq[PathRef]), None)) => ps.map(Watchable.Path(_)) + case (t: SourceImpl, Evaluator.EvalResult(Result.Success(p: PathRef), None)) => Seq(Watchable.Path(p)) + case (t: InputImpl[_], Evaluator.EvalResult(Result.Success(v), Some(recalc))) => val pretty = t.ctx0.fileName + ":" + t.ctx0.lineNum Seq(Watchable.Value(() => recalc().hashCode(), recalc().hashCode(), pretty)) } diff --git a/main/test/src/mill/main/MainModuleTests.scala b/main/test/src/mill/main/MainModuleTests.scala index 4572e4247a6..aca9b114add 100644 --- a/main/test/src/mill/main/MainModuleTests.scala +++ b/main/test/src/mill/main/MainModuleTests.scala @@ -49,7 +49,7 @@ object MainModuleTests extends TestSuite { val eval = new TestEvaluator(mainModule) test("single") { val res = eval.evaluator.evaluate(Agg(mainModule.inspect(eval.evaluator, "hello"))) - val Result.Success(value: String, _) = res.rawValues.head + val Result.Success(value: String) = res.rawValues.head assert( res.failing.keyCount == 0, value.startsWith("hello("), @@ -59,7 +59,7 @@ object MainModuleTests extends TestSuite { test("multi") { val res = eval.evaluator.evaluate(Agg(mainModule.inspect(eval.evaluator, "hello", "hello2"))) - val Result.Success(value: String, _) = res.rawValues.head + val Result.Success(value: String) = res.rawValues.head assert( res.failing.keyCount == 0, value.startsWith("hello("), @@ -77,7 +77,7 @@ object MainModuleTests extends TestSuite { assert(results.failing.keyCount == 0) - val Result.Success(value, _) = results.rawValues.head + val Result.Success(value) = results.rawValues.head assert(value == ujson.Arr.from(Seq("hello", "world"))) } @@ -92,7 +92,7 @@ object MainModuleTests extends TestSuite { assert(results.failing.keyCount == 0) - val Result.Success(value, _) = results.rawValues.head + val Result.Success(value) = results.rawValues.head assert(value == ujson.Arr.from(Seq( ujson.Arr.from(Seq("hello", "world")), @@ -109,7 +109,7 @@ object MainModuleTests extends TestSuite { assert(results.failing.keyCount == 0) - val Result.Success(value, _) = results.rawValues.head + val Result.Success(value) = results.rawValues.head assert(value == ujson.Obj.from(Map( "hello" -> ujson.Arr.from(Seq("hello", "world")) @@ -126,7 +126,7 @@ object MainModuleTests extends TestSuite { assert(results.failing.keyCount == 0) - val Result.Success(value, _) = results.rawValues.head + val Result.Success(value) = results.rawValues.head assert(value == ujson.Obj.from(Map( "hello" -> ujson.Arr.from(Seq("hello", "world")), diff --git a/scalalib/src/mill/scalalib/GenIdeaImpl.scala b/scalalib/src/mill/scalalib/GenIdeaImpl.scala index ae6b46793a5..4f0c97cc613 100755 --- a/scalalib/src/mill/scalalib/GenIdeaImpl.scala +++ b/scalalib/src/mill/scalalib/GenIdeaImpl.scala @@ -105,7 +105,7 @@ case class GenIdeaImpl( val millDeps = BuildInfo.millEmbeddedDeps.split(",").map(d => ivy"$d").map(dep => BoundDep(Lib.depToDependency(dep, BuildInfo.scalaVersion, ""), dep.force) ) - val Result.Success(res, _) = scalalib.Lib.resolveDependencies( + val Result.Success(res) = scalalib.Lib.resolveDependencies( repositories = repos.toList, deps = millDeps, sources = false, diff --git a/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala b/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala index 61404c4add5..3fe25078286 100644 --- a/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala +++ b/scalalib/test/src/mill/scalalib/ResolveDepsTests.scala @@ -19,20 +19,20 @@ object ResolveDepsTests extends TestSuite { val tests = Tests { "resolveValidDeps" - { val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3") - val Success(paths, _) = evalDeps(deps) + val Success(paths) = evalDeps(deps) assert(paths.nonEmpty) } "resolveValidDepsWithClassifier" - { val deps = Agg(ivy"org.lwjgl:lwjgl:3.1.1;classifier=natives-macos") - val Success(paths, _) = evalDeps(deps) + val Success(paths) = evalDeps(deps) assert(paths.nonEmpty) assert(paths.items.next().path.toString.contains("natives-macos")) } "resolveTransitiveRuntimeDeps" - { val deps = Agg(ivy"org.mockito:mockito-core:2.7.22") - val Success(paths, _) = evalDeps(deps) + val Success(paths) = evalDeps(deps) assert(paths.nonEmpty) assert(paths.exists(_.path.toString.contains("objenesis"))) assert(paths.exists(_.path.toString.contains("byte-buddy"))) @@ -40,13 +40,13 @@ object ResolveDepsTests extends TestSuite { "excludeTransitiveDeps" - { val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3".exclude("com.lihaoyi" -> "fansi_2.12")) - val Success(paths, _) = evalDeps(deps) + val Success(paths) = evalDeps(deps) assert(!paths.exists(_.path.toString.contains("fansi_2.12"))) } "excludeTransitiveDepsByOrg" - { val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3".excludeOrg("com.lihaoyi")) - val Success(paths, _) = evalDeps(deps) + val Success(paths) = evalDeps(deps) assert(!paths.exists(path => path.path.toString.contains("com/lihaoyi") && !path.path.toString.contains("pprint_2.12") )) @@ -54,7 +54,7 @@ object ResolveDepsTests extends TestSuite { "excludeTransitiveDepsByName" - { val deps = Agg(ivy"com.lihaoyi::pprint:0.5.3".excludeName("fansi_2.12")) - val Success(paths, _) = evalDeps(deps) + val Success(paths) = evalDeps(deps) assert(!paths.exists(_.path.toString.contains("fansi_2.12"))) } From 02f0cefd4ab37b827e52a60e2a02ee2d202a4714 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 May 2023 18:18:10 +0800 Subject: [PATCH 06/27] wip --- example/basic/1-simple-scala/build.sc | 125 +----------------------- main/define/src/mill/define/Task.scala | 8 +- main/eval/src/mill/eval/Evaluator.scala | 5 + 3 files changed, 14 insertions(+), 124 deletions(-) diff --git a/example/basic/1-simple-scala/build.sc b/example/basic/1-simple-scala/build.sc index 3be43e9ade7..f0cbc900579 100644 --- a/example/basic/1-simple-scala/build.sc +++ b/example/basic/1-simple-scala/build.sc @@ -1,124 +1,9 @@ -import mill._, scalalib._ +import mill._ -object foo extends RootModule with ScalaModule { - def scalaVersion = "2.13.8" - def ivyDeps = Agg( - ivy"com.lihaoyi::scalatags:0.8.2", - ivy"com.lihaoyi::mainargs:0.4.0" - ) +interp.watchValue(System.currentTimeMillis() / 5000) - object test extends Tests { - def ivyDeps = Agg(ivy"com.lihaoyi::utest:0.7.11") - def testFramework = "utest.runner.Framework" - } -} +println("Setting up build.sc") +def foo = T.input{ System.currentTimeMillis() / 1000 } -// This is a basic Mill build for a single `ScalaModule`, with two -// third-party dependencies and a test suite using the uTest framework. As a -// single-module project, it `extends RootModule` to mark `object foo` as the -// top-level module in the build. This lets us directly perform operations -// `./mill compile` or `./mill run` without needing to prefix it as -// `foo.compile` or `foo.run`. -// -// You can download this example project using the *download* link above -// if you want to try out the commands below yourself. The only requirement is -// that you have some version of the JVM installed; the `./mill` script takes -// care of any further dependencies that need to be downloaded. -// -// The source code for this module lives in the `src/` folder. -// Output for this module (compiled files, resolved dependency lists, ...) -// lives in `out/`. -// -// This example project uses two third-party dependencies - MainArgs for CLI -// argument parsing, Scalatags for HTML generation - and uses them to wrap a -// given input string in HTML templates with proper escaping. -// -// You can run `assembly` to generate a standalone executable jar, which then -// can be run from the command line or deployed to be run elsewhere. - -/** Usage - -> ./mill resolve _ # List what tasks are available to run -assembly -... -clean -... -compile -... -run -... -show -... -inspect -... - -> ./mill inspect compile # Show documentation and inputs of a task -compile(ScalaModule.scala:...) - Compiles the current module to generate compiled classfiles/bytecode. -Inputs: - scalaVersion - upstreamCompileOutput - allSourceFiles - compileClasspath - -> ./mill compile # compile sources into classfiles -... -compiling 1 Scala source to... - -> ./mill run # run the main method, if any -error: Missing argument: --text - -> ./mill run --text hello -

hello

- -> ./mill test -... -+ foo.FooTests.simple ...

hello

-+ foo.FooTests.escaping ...

<hello>

- -> ./mill assembly # bundle classfiles and libraries into a jar for deployment - -> ./mill show assembly # show the output of the assembly task -".../out/assembly.dest/out.jar" - -> java -jar ./out/assembly.dest/out.jar --text hello -

hello

- -> ./out/assembly.dest/out.jar --text hello # mac/linux -

hello

- -*/ - -// The output of every Mill task is stored in the `out/` folder under a name -// corresponding to the task that created it. e.g. The `assembly` task puts its -// metadata output in `out/assembly.json`, and its output files in -// `out/assembly.dest`. You can also use `show` to make Mill print out the -// metadata output for a particular task. -// -// Additional Mill tasks you would likely need include: -// -// [source,bash] -// ---- -// $ mill runBackground # run the main method in the background -// -// $ mill clean # delete the cached output of a task, terminate any runBackground -// -// $ mill launcher # prepares a foo/launcher.dest/run you can run later -// -// $ mill jar # bundle the classfiles into a jar suitable for publishing -// -// $ mill -i console # start a Scala console within your project -// -// $ mill -i repl # start an Ammonite Scala REPL within your project -// ---- -// -// You can run `+mill resolve __+` to see a full list of the different tasks that -// are available, `+mill resolve _+` to see the tasks within `foo`, -// `mill inspect compile` to inspect a task's doc-comment documentation or what -// it depends on, or `mill show foo.scalaVersion` to show the output of any task. -// -// The most common *tasks* that Mill can run are cached *targets*, such as -// `compile`, and un-cached *commands* such as `foo.run`. Targets do not -// re-evaluate unless one of their inputs changes, whereas commands re-run every -// time. \ No newline at end of file +def bar = T{ foo() + " seconds since the epoch" } \ No newline at end of file diff --git a/main/define/src/mill/define/Task.scala b/main/define/src/mill/define/Task.scala index 9a8ac145b7d..7a94d6d6c72 100644 --- a/main/define/src/mill/define/Task.scala +++ b/main/define/src/mill/define/Task.scala @@ -56,7 +56,7 @@ object Task { val inputs = inputs0 def evaluate(ctx: mill.api.Ctx) = { for (i <- 0 until ctx.args.length) - yield ctx.arg[T](i) + yield ctx.args(i).asInstanceOf[T] } } private[define] class TraverseCtx[+T, V]( @@ -67,7 +67,7 @@ object Task { def evaluate(ctx: mill.api.Ctx) = { f( for (i <- 0 until ctx.args.length) - yield ctx.arg[T](i), + yield ctx.args(i).asInstanceOf[T], ctx ) } @@ -79,7 +79,7 @@ object Task { } private[define] class Zipped[+T, +V](source1: Task[T], source2: Task[V]) extends Task[(T, V)] { - def evaluate(ctx: mill.api.Ctx) = (ctx.arg[T](0), ctx.arg[V](1)) + def evaluate(ctx: mill.api.Ctx) = (ctx.arg(0), ctx.arg(1)) val inputs = List(source1, source2) } } @@ -105,7 +105,7 @@ trait NamedTask[+T] extends Task[T] { } override def toString = ctx.segments.render - def evaluate(ctx: mill.api.Ctx): Result[T] = ctx.arg[T](0) + def evaluate(ctx: mill.api.Ctx) = ctx.arg[T](0) val ctx = ctx0.withSegments(segments = ctx0.segments ++ Seq(ctx0.segment)) val inputs = Seq(t) diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index 86967c2acd5..f3f576371f7 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -452,9 +452,11 @@ class Evaluator private ( newResults(labelledNamedTask.task) match { case Evaluator.EvalResult(mill.api.Result.Failure(_, Some((v, _))), _) => + pprint.log(v) handleTaskResult(v, v.##, paths.meta, inputsHash, labelledNamedTask) case Evaluator.EvalResult(mill.api.Result.Success((v, _)), _) => + pprint.log(v) handleTaskResult(v, v.##, paths.meta, inputsHash, labelledNamedTask) case _ => @@ -602,6 +604,7 @@ class Evaluator private ( } + pprint.log(res) newResults(task) = Evaluator.EvalResult( for (v <- res) yield { ( @@ -612,6 +615,8 @@ class Evaluator private ( }, None ) + + pprint.log(newResults(task)) } if (!failFast) maybeTargetLabel.foreach { targetLabel => From 0524586d359dae1e83308da5c23a28869dbd8eb7 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 May 2023 18:20:32 +0800 Subject: [PATCH 07/27] wip --- main/eval/src/mill/eval/Evaluator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index f3f576371f7..2585b9eaafd 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -170,7 +170,7 @@ class Evaluator private ( evaluated = evaluated, transitive = transitive, failing = getFailing(sortedGroups, results), - results = results + results = results.map { case (k, v) => (k, v.map(_._1)) } ) } From f2a797e0472e3eb254b56a343bfaeebc2b81bb5f Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 May 2023 18:47:26 +0800 Subject: [PATCH 08/27] things pass converted to Val --- main/eval/src/mill/eval/Evaluator.scala | 78 +++++++++---------- main/src/mill/main/RunScript.scala | 10 +-- .../src/mill/runner/MillBuildBootstrap.scala | 4 +- 3 files changed, 43 insertions(+), 49 deletions(-) diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index 2585b9eaafd..683a96c2b8a 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -42,7 +42,7 @@ class Evaluator private ( _scriptImportGraph: Map[os.Path, (Int, Seq[os.Path])] ) { - import Evaluator.Terminal + import Evaluator._ def home: os.Path = _home @@ -122,14 +122,14 @@ class Evaluator private ( val (sortedGroups, transitive) = Evaluator.plan(goals) val evaluated = new Agg.Mutable[Task[_]] - val results = mutable.LinkedHashMap.empty[Task[_], Evaluator.EvalResult[(_, Int)]] + val results = mutable.LinkedHashMap.empty[Task[_], TaskResult[(Val, Int)]] var someTaskFailed: Boolean = false val timings = mutable.ArrayBuffer.empty[(Either[Task[_], Labelled[_]], Int, Boolean)] for (((terminal, group), i) <- sortedGroups.items().zipWithIndex) { if (failFast && someTaskFailed) { // we exit early and set aborted state for all left tasks - group.iterator.foreach { task => results.put(task, Evaluator.EvalResult(Aborted, None)) } + group.iterator.foreach { task => results.put(task, TaskResult(Aborted, None)) } } else { @@ -154,7 +154,7 @@ class Evaluator private ( logger = contextLogger ) someTaskFailed = - someTaskFailed || newResults.exists(task => !task._2.isInstanceOf[Success[_]]) + someTaskFailed || newResults.exists(task => !task._2.result.isInstanceOf[Success[_]]) for (ev <- newEvaluated) evaluated.append(ev) for ((k, v) <- newResults) results.put(k, v) @@ -165,8 +165,9 @@ class Evaluator private ( } Evaluator.writeTimings(timings.toSeq, outPath) + Evaluator.Results( - rawValues = goals.indexed.map(results(_).map(_._1)), + rawValues = goals.indexed.map(results(_).result.map(_._1)), evaluated = evaluated, transitive = transitive, failing = getFailing(sortedGroups, results), @@ -176,14 +177,14 @@ class Evaluator private ( def getFailing( sortedGroups: MultiBiMap[Either[Task[_], Labelled[Any]], Task[_]], - results: collection.Map[Task[_], Evaluator.EvalResult[(_, Int)]] + results: collection.Map[Task[_], TaskResult[(Val, Int)]] ): MultiBiMap.Mutable[Either[Task[_], Labelled[_]], Failing[_]] = { val failing = new MultiBiMap.Mutable[Either[Task[_], Labelled[_]], mill.api.Result.Failing[_]] for ((k, vs) <- sortedGroups.items()) { failing.addAll( k, Loose.Agg.from( - vs.items.flatMap(results.get).collect { case er @ Evaluator.EvalResult(f: mill.api.Result.Failing[_], _) => + vs.items.flatMap(results.get).collect { case er @ TaskResult(f: mill.api.Result.Failing[_], _) => f } ) @@ -275,17 +276,17 @@ class Evaluator private ( val finishedOptsMap = finishedOpts.toMap - val results0: Vector[(Task[_], Evaluator.EvalResult[(_, Int)])] = terminals + val results0: Vector[(Task[_], TaskResult[(Val, Int)])] = terminals .flatMap { t => sortedGroups.lookupKey(t).flatMap { t0 => finishedOptsMap(t) match { - case None => Some((t0, Evaluator.EvalResult(Aborted, None))) + case None => Some((t0, TaskResult(Aborted, None))) case Some(res) => res.newResults.get(t0).map(r => (t0, r)) } } } - val results: Map[Task[_], Evaluator.EvalResult[(_, Int)]] = + val results: Map[Task[_], TaskResult[(Val, Int)]] = results0.toMap timeLog.close() @@ -306,7 +307,7 @@ class Evaluator private ( protected def evaluateGroupCached( terminal: Terminal, group: Agg[Task[_]], - results: collection.Map[Task[_], Evaluator.EvalResult[(_, Int)]], + results: collection.Map[Task[_], TaskResult[(Val, Int)]], counterMsg: String, zincProblemReporter: Int => Option[CompileProblemReporter], testReporter: TestReporter, @@ -424,8 +425,8 @@ class Evaluator private ( upToDateWorker.map((_, inputsHash)) orElse cached match { case Some((v, hashCode)) => - val newResults = mutable.LinkedHashMap.empty[Task[_], Evaluator.EvalResult[(Any, Int)]] - newResults(labelledNamedTask.task) = Evaluator.EvalResult(Result.Success((v, hashCode)), None) + val newResults = mutable.LinkedHashMap.empty[Task[_], TaskResult[(Val, Int)]] + newResults(labelledNamedTask.task) = TaskResult(Result.Success((Val(v), hashCode)), None) Evaluated(newResults, Nil, cached = true) @@ -451,12 +452,10 @@ class Evaluator private ( } newResults(labelledNamedTask.task) match { - case Evaluator.EvalResult(mill.api.Result.Failure(_, Some((v, _))), _) => - pprint.log(v) + case TaskResult(mill.api.Result.Failure(_, Some((v, _))), _) => handleTaskResult(v, v.##, paths.meta, inputsHash, labelledNamedTask) - case Evaluator.EvalResult(mill.api.Result.Success((v, _)), _) => - pprint.log(v) + case TaskResult(mill.api.Result.Success((v, _)), _) => handleTaskResult(v, v.##, paths.meta, inputsHash, labelledNamedTask) case _ => @@ -483,7 +482,7 @@ class Evaluator private ( } def handleTaskResult( - v: Any, + v: Val, hashCode: Int, metaPath: os.Path, inputsHash: Int, @@ -499,9 +498,9 @@ class Evaluator private ( .task .writerOpt .asInstanceOf[Option[upickle.default.Writer[Any]]] - .map(w => upickle.default.writeJs(v)(w) -> v) + .map{w => upickle.default.writeJs(v.value)(w)} - for ((json, _) <- terminalResult) { + for (json <- terminalResult) { os.write.over( metaPath, upickle.default.stream( @@ -516,7 +515,7 @@ class Evaluator private ( protected def evaluateGroup( group: Agg[Task[_]], - results: collection.Map[Task[_], Evaluator.EvalResult[(_, Int)]], + results: collection.Map[Task[_], TaskResult[(Val, Int)]], inputsHash: Int, paths: Option[EvaluatorPaths], maybeTargetLabel: Option[String], @@ -524,10 +523,10 @@ class Evaluator private ( reporter: Int => Option[CompileProblemReporter], testReporter: TestReporter, logger: mill.api.Logger - ): (mutable.LinkedHashMap[Task[_], Evaluator.EvalResult[(Any, Int)]], mutable.Buffer[Task[_]]) = { + ): (mutable.LinkedHashMap[Task[_], TaskResult[(Val, Int)]], mutable.Buffer[Task[_]]) = { val newEvaluated = mutable.Buffer.empty[Task[_]] - val newResults = mutable.LinkedHashMap.empty[Task[_], Evaluator.EvalResult[(Any, Int)]] + val newResults = mutable.LinkedHashMap.empty[Task[_], TaskResult[(Val, Int)]] val nonEvaluatedTargets = group.indexed.filterNot(results.contains) @@ -537,7 +536,7 @@ class Evaluator private ( target <- nonEvaluatedTargets item <- target.inputs.filterNot(group.contains) } yield results(item).map(_._1) - inputResults.forall(_.isInstanceOf[mill.api.Result.Success[_]]) + inputResults.forall(_.result.isInstanceOf[mill.api.Result.Success[_]]) } val tickerPrefix = maybeTargetLabel.map { targetLabel => @@ -561,13 +560,13 @@ class Evaluator private ( newEvaluated.append(task) val targetInputValues = task.inputs .map { x => newResults.getOrElse(x, results(x)) } - .collect { case Evaluator.EvalResult(Result.Success((v, _)), _) => Result.Success(v) } + .collect { case TaskResult(Result.Success((v, _)), _) => v } val res = if (targetInputValues.length != task.inputs.length) mill.api.Result.Skipped else { val args = new Ctx( - args = targetInputValues.toArray[Result.Success[Any]].toIndexedSeq, + args = targetInputValues.map(_.value).toIndexedSeq, dest0 = () => paths match { case Some(dest) => @@ -588,11 +587,8 @@ class Evaluator private ( override def jobs: Int = effectiveThreadCount } - val out = System.out - val in = System.in - val err = System.err mill.api.SystemStreams.withStreams(multiLogger.systemStreams) { - try task.evaluate(args) + try task.evaluate(args).map(Val(_)) catch { case NonFatal(e) => mill.api.Result.Exception( @@ -601,11 +597,9 @@ class Evaluator private ( ) } } - } - pprint.log(res) - newResults(task) = Evaluator.EvalResult( + newResults(task) = TaskResult( for (v <- res) yield { ( v, @@ -616,7 +610,6 @@ class Evaluator private ( None ) - pprint.log(newResults(task)) } if (!failFast) maybeTargetLabel.foreach { targetLabel => @@ -774,19 +767,20 @@ object Evaluator { ) } - case class EvalResult[T](result: Result[T], + case class TaskResult[T](result: Result[T], recalcOpt: Option[() => mill.api.Result[T]]){ - def map[V](f: T => V) = EvalResult[V]( + def map[V](f: T => V) = TaskResult[V]( result.map(f), recalcOpt.map(r => () => r().map(f)) ) } + case class Val(value: Any) case class Results( rawValues: Seq[mill.api.Result[Any]], evaluated: Agg[Task[_]], transitive: Agg[Task[_]], failing: MultiBiMap[Either[Task[_], Labelled[_]], mill.api.Result.Failing[_]], - results: collection.Map[Task[_], EvalResult[_]] + results: collection.Map[Task[_], TaskResult[Val]] ) { def values: Seq[Any] = rawValues.collect { case mill.api.Result.Success(v) => v } private def copy( @@ -794,7 +788,7 @@ object Evaluator { evaluated: Agg[Task[_]] = evaluated, transitive: Agg[Task[_]] = transitive, failing: MultiBiMap[Either[Task[_], Labelled[_]], Result.Failing[_]] = failing, - results: collection.Map[Task[_], EvalResult[_]] + results: collection.Map[Task[_], TaskResult[Val]] ): Results = new Results( rawValues, evaluated, @@ -810,7 +804,7 @@ object Evaluator { Agg[Task[_]], Agg[Task[_]], MultiBiMap[Either[Task[_], Labelled[_]], Failing[_]], - collection.Map[Task[_], EvalResult[_]] + collection.Map[Task[_], TaskResult[_]] )] = Some(( results.rawValues, results.evaluated, @@ -861,12 +855,12 @@ object Evaluator { } case class Evaluated( - newResults: collection.Map[Task[_], EvalResult[(_, Int)]], + newResults: collection.Map[Task[_], TaskResult[(Val, Int)]], newEvaluated: Seq[Task[_]], cached: Boolean ) { private[Evaluator] def copy( - newResults: collection.Map[Task[_], EvalResult[(_, Int)]], + newResults: collection.Map[Task[_], TaskResult[(Val, Int)]], newEvaluated: Seq[Task[_]] = newEvaluated, cached: Boolean = cached ): Evaluated = new Evaluated(newResults, newEvaluated, cached) @@ -874,7 +868,7 @@ object Evaluator { object Evaluated { private[Evaluator] def unapply(evaluated: Evaluated) - : Option[(collection.Map[Task[_], EvalResult[(_, Int)]], Seq[Task[_]], Boolean)] = + : Option[(collection.Map[Task[_], TaskResult[(Val, Int)]], Seq[Task[_]], Boolean)] = Some((evaluated.newResults, evaluated.newEvaluated, evaluated.cached)) } diff --git a/main/src/mill/main/RunScript.scala b/main/src/mill/main/RunScript.scala index 37b9309e028..56382384525 100644 --- a/main/src/mill/main/RunScript.scala +++ b/main/src/mill/main/RunScript.scala @@ -5,7 +5,7 @@ import mill.eval.{Evaluator, EvaluatorPaths} import mill.util.Watchable import mill.api.{PathRef, Result} import mill.api.Strict.Agg - +import Evaluator._ object RunScript { @@ -29,14 +29,14 @@ object RunScript { evaluator: Evaluator, targets: Agg[Task[Any]] ): (Seq[Watchable], Either[String, Seq[(Any, Option[(TaskName, ujson.Value)])]]) = { - val evaluated: Evaluator.Results = evaluator.evaluate(targets) + val evaluated: Results = evaluator.evaluate(targets) val watched = evaluated.results .iterator .collect { - case (t: SourcesImpl, Evaluator.EvalResult(Result.Success(ps: Seq[PathRef]), None)) => ps.map(Watchable.Path(_)) - case (t: SourceImpl, Evaluator.EvalResult(Result.Success(p: PathRef), None)) => Seq(Watchable.Path(p)) - case (t: InputImpl[_], Evaluator.EvalResult(Result.Success(v), Some(recalc))) => + case (t: SourcesImpl, TaskResult(Result.Success(Val(ps: Seq[PathRef])), None)) => ps.map(Watchable.Path(_)) + case (t: SourceImpl, TaskResult(Result.Success(Val(p: PathRef)), None)) => Seq(Watchable.Path(p)) + case (t: InputImpl[_], TaskResult(Result.Success(Val(v)), Some(recalc))) => val pretty = t.ctx0.fileName + ":" + t.ctx0.lineNum Seq(Watchable.Value(() => recalc().hashCode(), recalc().hashCode(), pretty)) } diff --git a/runner/src/mill/runner/MillBuildBootstrap.scala b/runner/src/mill/runner/MillBuildBootstrap.scala index 4b0b8f22b9c..36455e6b3c2 100644 --- a/runner/src/mill/runner/MillBuildBootstrap.scala +++ b/runner/src/mill/runner/MillBuildBootstrap.scala @@ -194,8 +194,8 @@ class MillBuildBootstrap( case ( Right(Seq( - runClasspath: Seq[PathRef], - scriptImportGraph: Map[os.Path, (Int, Seq[os.Path])] + Evaluator.Val(runClasspath: Seq[PathRef]), + Evaluator.Val(scriptImportGraph: Map[os.Path, (Int, Seq[os.Path])]) )), evalWatches, moduleWatches From a823018569553b0a36a38202961155d41a5ca1e2 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 May 2023 18:48:23 +0800 Subject: [PATCH 09/27] things pass converted to Val --- main/eval/src/mill/eval/Evaluator.scala | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index 683a96c2b8a..f66b82abfaa 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -599,15 +599,16 @@ class Evaluator private ( } } + def makeTuple() = for (v <- res) yield { + ( + v, + if (task.isInstanceOf[Worker[_]]) inputsHash + else v.## + ) + } newResults(task) = TaskResult( - for (v <- res) yield { - ( - v, - if (task.isInstanceOf[Worker[_]]) inputsHash - else v.## - ) - }, - None + makeTuple(), + Option.when(task.isInstanceOf[InputImpl[_]])(() => makeTuple()) ) } From bd4326fe6685f19b82ce9153534f83e6d7159c27 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 2 May 2023 19:19:48 +0800 Subject: [PATCH 10/27] fix crash --- example/basic/1-simple-scala/build.sc | 4 +- main/eval/src/mill/eval/Evaluator.scala | 177 +++++++++++++----------- main/src/mill/main/RunScript.scala | 6 +- 3 files changed, 101 insertions(+), 86 deletions(-) diff --git a/example/basic/1-simple-scala/build.sc b/example/basic/1-simple-scala/build.sc index f0cbc900579..e9b3366cd8b 100644 --- a/example/basic/1-simple-scala/build.sc +++ b/example/basic/1-simple-scala/build.sc @@ -1,9 +1,9 @@ import mill._ -interp.watchValue(System.currentTimeMillis() / 5000) +interp.watchValue{ System.currentTimeMillis() / 5000 } println("Setting up build.sc") def foo = T.input{ System.currentTimeMillis() / 1000 } -def bar = T{ foo() + " seconds since the epoch" } \ No newline at end of file +def bar = T{ foo() + " seconds since the epoch" } diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index f66b82abfaa..5f9b14dff3d 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -354,7 +354,7 @@ class Evaluator private ( case Left(task) => val (newResults, newEvaluated) = evaluateGroup( group, - results, + results.toMap, inputsHash, paths = None, maybeTargetLabel = None, @@ -440,7 +440,7 @@ class Evaluator private ( Evaluator.dynamicTickerPrefix.withValue(s"[$counterMsg] $targetLabel > ") { evaluateGroup( group, - results, + results.toMap, inputsHash, paths = Some(paths), maybeTargetLabel = Some(targetLabel), @@ -491,7 +491,7 @@ class Evaluator private ( labelledNamedTask.task.asWorker match { case Some(w) => workerCache.synchronized { - workerCache.update(w.ctx.segments, (inputsHash, v)) + workerCache.update(w.ctx.segments, (inputsHash, v.value)) } case None => val terminalResult = labelledNamedTask @@ -525,94 +525,97 @@ class Evaluator private ( logger: mill.api.Logger ): (mutable.LinkedHashMap[Task[_], TaskResult[(Val, Int)]], mutable.Buffer[Task[_]]) = { - val newEvaluated = mutable.Buffer.empty[Task[_]] - val newResults = mutable.LinkedHashMap.empty[Task[_], TaskResult[(Val, Int)]] + def computeAll() = { + val newEvaluated = mutable.Buffer.empty[Task[_]] + val newResults = mutable.LinkedHashMap.empty[Task[_], Result[(Val, Int)]] - val nonEvaluatedTargets = group.indexed.filterNot(results.contains) + val nonEvaluatedTargets = group.indexed.filterNot(results.contains) - // should we log progress? - val logRun = maybeTargetLabel.isDefined && { - val inputResults = for { - target <- nonEvaluatedTargets - item <- target.inputs.filterNot(group.contains) - } yield results(item).map(_._1) - inputResults.forall(_.result.isInstanceOf[mill.api.Result.Success[_]]) - } + // should we log progress? + val logRun = maybeTargetLabel.isDefined && { + val inputResults = for { + target <- nonEvaluatedTargets + item <- target.inputs.filterNot(group.contains) + } yield results(item).map(_._1) + inputResults.forall(_.result.isInstanceOf[mill.api.Result.Success[_]]) + } - val tickerPrefix = maybeTargetLabel.map { targetLabel => - val prefix = s"[$counterMsg] $targetLabel " - if (logRun) logger.ticker(prefix) - prefix + "| " - } + val tickerPrefix = maybeTargetLabel.map { targetLabel => + val prefix = s"[$counterMsg] $targetLabel " + if (logRun) logger.ticker(prefix) + prefix + "| " + } - val multiLogger = new ProxyLogger(resolveLogger(paths.map(_.log), logger)) { - override def ticker(s: String): Unit = { - super.ticker(tickerPrefix.getOrElse("") + s) + val multiLogger = new ProxyLogger(resolveLogger(paths.map(_.log), logger)) { + override def ticker(s: String): Unit = { + super.ticker(tickerPrefix.getOrElse("") + s) + } } - } - // This is used to track the usage of `T.dest` in more than one Task - // But it's not really clear what issue we try to prevent here - // Vice versa, being able to use T.dest in multiple `T.task` - // is rather essential to split up larger tasks into small parts - // So I like to disable this detection for now - var usedDest = Option.empty[(Task[_], Array[StackTraceElement])] - for (task <- nonEvaluatedTargets) { - newEvaluated.append(task) - val targetInputValues = task.inputs - .map { x => newResults.getOrElse(x, results(x)) } - .collect { case TaskResult(Result.Success((v, _)), _) => v } - - val res = - if (targetInputValues.length != task.inputs.length) mill.api.Result.Skipped - else { - val args = new Ctx( - args = targetInputValues.map(_.value).toIndexedSeq, - dest0 = () => - paths match { - case Some(dest) => - if (usedDest.isEmpty) os.makeDir.all(dest.dest) - usedDest = Some((task, new Exception().getStackTrace)) - dest.dest - case None => - throw new Exception("No `dest` folder available here") - // } - }, - log = multiLogger, - home = home, - env = env, - reporter = reporter, - testReporter = testReporter, - workspace = rootModule.millSourcePath - ) with mill.api.Ctx.Jobs { - override def jobs: Int = effectiveThreadCount - } + // This is used to track the usage of `T.dest` in more than one Task + // But it's not really clear what issue we try to prevent here + // Vice versa, being able to use T.dest in multiple `T.task` + // is rather essential to split up larger tasks into small parts + // So I like to disable this detection for now + var usedDest = Option.empty[(Task[_], Array[StackTraceElement])] + for (task <- nonEvaluatedTargets) { + newEvaluated.append(task) + val targetInputValues = task.inputs + .map { x => newResults.getOrElse(x, results(x).result) } + .collect { case Result.Success((v, _)) => v } + + val compute = { + if (targetInputValues.length != task.inputs.length) () => mill.api.Result.Skipped + else { () => + val args = new Ctx( + args = targetInputValues.map(_.value).toIndexedSeq, + dest0 = () => + paths match { + case Some(dest) => + if (usedDest.isEmpty) os.makeDir.all(dest.dest) + usedDest = Some((task, new Exception().getStackTrace)) + dest.dest + case None => + throw new Exception("No `dest` folder available here") + // } + }, + log = multiLogger, + home = home, + env = env, + reporter = reporter, + testReporter = testReporter, + workspace = rootModule.millSourcePath + ) with mill.api.Ctx.Jobs { + override def jobs: Int = effectiveThreadCount + } - mill.api.SystemStreams.withStreams(multiLogger.systemStreams) { - try task.evaluate(args).map(Val(_)) - catch { - case NonFatal(e) => - mill.api.Result.Exception( - e, - new OuterStack(new Exception().getStackTrace.toIndexedSeq) - ) + mill.api.SystemStreams.withStreams(multiLogger.systemStreams) { + try task.evaluate(args).map(Val(_)) + catch { + case NonFatal(e) => + mill.api.Result.Exception( + e, + new OuterStack(new Exception().getStackTrace.toIndexedSeq) + ) + } } } } - def makeTuple() = for (v <- res) yield { - ( - v, - if (task.isInstanceOf[Worker[_]]) inputsHash - else v.## - ) + newResults(task) = for (v <- compute()) yield { + ( + v, + if (task.isInstanceOf[Worker[_]]) inputsHash + else v.## + ) + } } - newResults(task) = TaskResult( - makeTuple(), - Option.when(task.isInstanceOf[InputImpl[_]])(() => makeTuple()) - ) - + multiLogger.close() + (newResults, newEvaluated) } + val (newResults, newEvaluated) = computeAll() + + if (!failFast) maybeTargetLabel.foreach { targetLabel => val taskFailed = newResults.exists(task => !task._2.isInstanceOf[Success[_]]) if (taskFailed) { @@ -620,9 +623,21 @@ class Evaluator private ( } } - multiLogger.close() - - (newResults, newEvaluated) + ( + newResults.map{case (k, v) => + ( + k, + TaskResult( + v, + Some{ () => + val (recalced, _) = computeAll() + recalced.apply(k) + } + ) + ) + }, + newEvaluated + ) } def resolveLogger(logPath: Option[os.Path], logger: mill.api.Logger): mill.api.Logger = diff --git a/main/src/mill/main/RunScript.scala b/main/src/mill/main/RunScript.scala index 56382384525..f7201b07ccb 100644 --- a/main/src/mill/main/RunScript.scala +++ b/main/src/mill/main/RunScript.scala @@ -34,9 +34,9 @@ object RunScript { val watched = evaluated.results .iterator .collect { - case (t: SourcesImpl, TaskResult(Result.Success(Val(ps: Seq[PathRef])), None)) => ps.map(Watchable.Path(_)) - case (t: SourceImpl, TaskResult(Result.Success(Val(p: PathRef)), None)) => Seq(Watchable.Path(p)) - case (t: InputImpl[_], TaskResult(Result.Success(Val(v)), Some(recalc))) => + case (t: SourcesImpl, TaskResult(Result.Success(Val(ps: Seq[PathRef])), _)) => ps.map(Watchable.Path(_)) + case (t: SourceImpl, TaskResult(Result.Success(Val(p: PathRef)), _)) => Seq(Watchable.Path(p)) + case (t: InputImpl[_], TaskResult(_, Some(recalc))) => val pretty = t.ctx0.fileName + ":" + t.ctx0.lineNum Seq(Watchable.Value(() => recalc().hashCode(), recalc().hashCode(), pretty)) } From ad0f6f9f8db9a2d0b981558c4d607d9af3aad807 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 10:24:08 +0800 Subject: [PATCH 11/27] wip --- example/basic/1-simple-scala/build.sc | 125 +++++++++++++++++- .../feature/watch-inputs/repo/build.sc | 15 +++ .../test/src/WatchInputsTests.scala | 15 +++ 3 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 integration/feature/watch-inputs/repo/build.sc create mode 100644 integration/feature/watch-inputs/test/src/WatchInputsTests.scala diff --git a/example/basic/1-simple-scala/build.sc b/example/basic/1-simple-scala/build.sc index e9b3366cd8b..3be43e9ade7 100644 --- a/example/basic/1-simple-scala/build.sc +++ b/example/basic/1-simple-scala/build.sc @@ -1,9 +1,124 @@ -import mill._ +import mill._, scalalib._ -interp.watchValue{ System.currentTimeMillis() / 5000 } +object foo extends RootModule with ScalaModule { + def scalaVersion = "2.13.8" + def ivyDeps = Agg( + ivy"com.lihaoyi::scalatags:0.8.2", + ivy"com.lihaoyi::mainargs:0.4.0" + ) -println("Setting up build.sc") -def foo = T.input{ System.currentTimeMillis() / 1000 } + object test extends Tests { + def ivyDeps = Agg(ivy"com.lihaoyi::utest:0.7.11") + def testFramework = "utest.runner.Framework" + } +} -def bar = T{ foo() + " seconds since the epoch" } +// This is a basic Mill build for a single `ScalaModule`, with two +// third-party dependencies and a test suite using the uTest framework. As a +// single-module project, it `extends RootModule` to mark `object foo` as the +// top-level module in the build. This lets us directly perform operations +// `./mill compile` or `./mill run` without needing to prefix it as +// `foo.compile` or `foo.run`. +// +// You can download this example project using the *download* link above +// if you want to try out the commands below yourself. The only requirement is +// that you have some version of the JVM installed; the `./mill` script takes +// care of any further dependencies that need to be downloaded. +// +// The source code for this module lives in the `src/` folder. +// Output for this module (compiled files, resolved dependency lists, ...) +// lives in `out/`. +// +// This example project uses two third-party dependencies - MainArgs for CLI +// argument parsing, Scalatags for HTML generation - and uses them to wrap a +// given input string in HTML templates with proper escaping. +// +// You can run `assembly` to generate a standalone executable jar, which then +// can be run from the command line or deployed to be run elsewhere. + +/** Usage + +> ./mill resolve _ # List what tasks are available to run +assembly +... +clean +... +compile +... +run +... +show +... +inspect +... + +> ./mill inspect compile # Show documentation and inputs of a task +compile(ScalaModule.scala:...) + Compiles the current module to generate compiled classfiles/bytecode. +Inputs: + scalaVersion + upstreamCompileOutput + allSourceFiles + compileClasspath + +> ./mill compile # compile sources into classfiles +... +compiling 1 Scala source to... + +> ./mill run # run the main method, if any +error: Missing argument: --text + +> ./mill run --text hello +

hello

+ +> ./mill test +... ++ foo.FooTests.simple ...

hello

++ foo.FooTests.escaping ...

<hello>

+ +> ./mill assembly # bundle classfiles and libraries into a jar for deployment + +> ./mill show assembly # show the output of the assembly task +".../out/assembly.dest/out.jar" + +> java -jar ./out/assembly.dest/out.jar --text hello +

hello

+ +> ./out/assembly.dest/out.jar --text hello # mac/linux +

hello

+ +*/ + +// The output of every Mill task is stored in the `out/` folder under a name +// corresponding to the task that created it. e.g. The `assembly` task puts its +// metadata output in `out/assembly.json`, and its output files in +// `out/assembly.dest`. You can also use `show` to make Mill print out the +// metadata output for a particular task. +// +// Additional Mill tasks you would likely need include: +// +// [source,bash] +// ---- +// $ mill runBackground # run the main method in the background +// +// $ mill clean # delete the cached output of a task, terminate any runBackground +// +// $ mill launcher # prepares a foo/launcher.dest/run you can run later +// +// $ mill jar # bundle the classfiles into a jar suitable for publishing +// +// $ mill -i console # start a Scala console within your project +// +// $ mill -i repl # start an Ammonite Scala REPL within your project +// ---- +// +// You can run `+mill resolve __+` to see a full list of the different tasks that +// are available, `+mill resolve _+` to see the tasks within `foo`, +// `mill inspect compile` to inspect a task's doc-comment documentation or what +// it depends on, or `mill show foo.scalaVersion` to show the output of any task. +// +// The most common *tasks* that Mill can run are cached *targets*, such as +// `compile`, and un-cached *commands* such as `foo.run`. Targets do not +// re-evaluate unless one of their inputs changes, whereas commands re-run every +// time. \ No newline at end of file diff --git a/integration/feature/watch-inputs/repo/build.sc b/integration/feature/watch-inputs/repo/build.sc new file mode 100644 index 00000000000..ef50bef1627 --- /dev/null +++ b/integration/feature/watch-inputs/repo/build.sc @@ -0,0 +1,15 @@ + +import mill._ + +interp.watchValue{ System.currentTimeMillis() / 5000 } + +println("Setting up build.sc") +def foo = T.input{ + println("Running foo") + System.currentTimeMillis() / 1000 +} + +def bar = T{ + println("Running bar") + foo() + " seconds since the epoch" +} diff --git a/integration/feature/watch-inputs/test/src/WatchInputsTests.scala b/integration/feature/watch-inputs/test/src/WatchInputsTests.scala new file mode 100644 index 00000000000..f5d7c4703f9 --- /dev/null +++ b/integration/feature/watch-inputs/test/src/WatchInputsTests.scala @@ -0,0 +1,15 @@ +package mill.integration + +import utest._ + +object WatchInputsTests extends IntegrationTestSuite { + val tests = Tests { + initWorkspace() + test{ + val t = new Thread(() => { + evalStdout("--watch", "bar") + }) + + } + } +} From 6f443cf47059c288533f959b16c33f02835a639d Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 12:47:20 +0800 Subject: [PATCH 12/27] WatchSourceInputTests pass --- .../feature/watch-inputs/repo/build.sc | 15 -- .../test/src/WatchInputsTests.scala | 15 -- .../feature/watch-source-input/repo/bar.txt | 1 + .../feature/watch-source-input/repo/baz.txt | 1 + .../feature/watch-source-input/repo/build.sc | 47 ++++++ .../feature/watch-source-input/repo/foo1.txt | 1 + .../feature/watch-source-input/repo/foo2.txt | 1 + .../watch-source-input/repo/watchValue.txt | 1 + .../test/src/WatchSourceInputTests.scala | 157 ++++++++++++++++++ .../integration/IntegrationTestSuite.scala | 27 ++- 10 files changed, 222 insertions(+), 44 deletions(-) delete mode 100644 integration/feature/watch-inputs/repo/build.sc delete mode 100644 integration/feature/watch-inputs/test/src/WatchInputsTests.scala create mode 100644 integration/feature/watch-source-input/repo/bar.txt create mode 100644 integration/feature/watch-source-input/repo/baz.txt create mode 100644 integration/feature/watch-source-input/repo/build.sc create mode 100644 integration/feature/watch-source-input/repo/foo1.txt create mode 100644 integration/feature/watch-source-input/repo/foo2.txt create mode 100644 integration/feature/watch-source-input/repo/watchValue.txt create mode 100644 integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala diff --git a/integration/feature/watch-inputs/repo/build.sc b/integration/feature/watch-inputs/repo/build.sc deleted file mode 100644 index ef50bef1627..00000000000 --- a/integration/feature/watch-inputs/repo/build.sc +++ /dev/null @@ -1,15 +0,0 @@ - -import mill._ - -interp.watchValue{ System.currentTimeMillis() / 5000 } - -println("Setting up build.sc") -def foo = T.input{ - println("Running foo") - System.currentTimeMillis() / 1000 -} - -def bar = T{ - println("Running bar") - foo() + " seconds since the epoch" -} diff --git a/integration/feature/watch-inputs/test/src/WatchInputsTests.scala b/integration/feature/watch-inputs/test/src/WatchInputsTests.scala deleted file mode 100644 index f5d7c4703f9..00000000000 --- a/integration/feature/watch-inputs/test/src/WatchInputsTests.scala +++ /dev/null @@ -1,15 +0,0 @@ -package mill.integration - -import utest._ - -object WatchInputsTests extends IntegrationTestSuite { - val tests = Tests { - initWorkspace() - test{ - val t = new Thread(() => { - evalStdout("--watch", "bar") - }) - - } - } -} diff --git a/integration/feature/watch-source-input/repo/bar.txt b/integration/feature/watch-source-input/repo/bar.txt new file mode 100644 index 00000000000..8634afda766 --- /dev/null +++ b/integration/feature/watch-source-input/repo/bar.txt @@ -0,0 +1 @@ +initial-bar \ No newline at end of file diff --git a/integration/feature/watch-source-input/repo/baz.txt b/integration/feature/watch-source-input/repo/baz.txt new file mode 100644 index 00000000000..62fca0a765b --- /dev/null +++ b/integration/feature/watch-source-input/repo/baz.txt @@ -0,0 +1 @@ +initial-baz \ No newline at end of file diff --git a/integration/feature/watch-source-input/repo/build.sc b/integration/feature/watch-source-input/repo/build.sc new file mode 100644 index 00000000000..b033c0aecce --- /dev/null +++ b/integration/feature/watch-source-input/repo/build.sc @@ -0,0 +1,47 @@ +import mill._ + +println("Setting up build.sc") + +def foo = T.sources(millSourcePath / "foo1.txt", millSourcePath / "foo2.txt") +def bar = T.source(millSourcePath / "bar.txt") + +def qux = T{ + val fooMsg = "Running qux foo contents " + foo().map(p => os.read(p.path)).mkString(" ") + println(fooMsg) + + val barMsg = "Running qux bar contents " + os.read(bar().path) + println(barMsg) + + writeCompletionMarker("quxRan") + + fooMsg + " " + barMsg +} + +interp.watchValue(PathRef(millSourcePath / "watchValue.txt")) + +def baz = T.input(PathRef(millSourcePath / "baz.txt")) + +def lol = T{ + val barMsg = "Running lol baz contents " + os.read(baz().path) + println(barMsg) + + writeCompletionMarker("lolRan") + + barMsg +} + + +def writeCompletionMarker(name: String) = { + + Range(0, 10) + .map(i => os.pwd / s"$name$i") + .find(!os.exists(_)) + .foreach(os.write(_, "")) +} + +writeCompletionMarker("initialized") + +if (os.read(millSourcePath / "watchValue.txt").contains("exit")){ + Thread.sleep(1000) + System.exit(0) +} \ No newline at end of file diff --git a/integration/feature/watch-source-input/repo/foo1.txt b/integration/feature/watch-source-input/repo/foo1.txt new file mode 100644 index 00000000000..acc9bce3c86 --- /dev/null +++ b/integration/feature/watch-source-input/repo/foo1.txt @@ -0,0 +1 @@ +initial-foo1 \ No newline at end of file diff --git a/integration/feature/watch-source-input/repo/foo2.txt b/integration/feature/watch-source-input/repo/foo2.txt new file mode 100644 index 00000000000..b9491ded04b --- /dev/null +++ b/integration/feature/watch-source-input/repo/foo2.txt @@ -0,0 +1 @@ +initial-foo2 \ No newline at end of file diff --git a/integration/feature/watch-source-input/repo/watchValue.txt b/integration/feature/watch-source-input/repo/watchValue.txt new file mode 100644 index 00000000000..49e2fc36b56 --- /dev/null +++ b/integration/feature/watch-source-input/repo/watchValue.txt @@ -0,0 +1 @@ +initial-watchValue2 \ No newline at end of file diff --git a/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala b/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala new file mode 100644 index 00000000000..1801fa488d0 --- /dev/null +++ b/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala @@ -0,0 +1,157 @@ +package mill.integration + +import utest._ +import scala.concurrent.{Await, Future} +import scala.concurrent.duration.Duration +import scala.concurrent.duration.SECONDS +import scala.concurrent.ExecutionContext.Implicits.global + +/** + * Test to make sure that `--watch` works in the following cases: + * + * 1. `T.source` + * 2. `T.sources` + * 3. `T.input` + * 4. `interp.watchValue` + * 5. Implicitly watched files, like `build.sc` + * + */ +object WatchSourceInputTests extends IntegrationTestSuite { + + val maxDuration = 30000 + val tests = Tests { + val wsRoot = initWorkspace() + + def awaitCompletionMarker(name: String) = { + val maxTime = System.currentTimeMillis() + maxDuration + while (!os.exists(wsRoot / name)) { + if (System.currentTimeMillis() > maxTime) { + sys.error(s"awaitCompletionMarker($name) timed out") + } + Thread.sleep(100) + } + } + + def testWatchSource(show: Boolean) = { + val showArgs = if (show) Seq("show") else Nil + + val evalResult = Future { evalTimeoutStdout(maxDuration, "--watch", showArgs, "qux") } + val expectedPrints = collection.mutable.Buffer.empty[String] + val expectedShows = collection.mutable.Buffer.empty[String] + + awaitCompletionMarker("initialized0") + awaitCompletionMarker("quxRan0") + expectedPrints.append( + "Setting up build.sc", + "Running qux foo contents initial-foo1 initial-foo2", + "Running qux bar contents initial-bar" + ) + expectedShows.append( + "Running qux foo contents initial-foo1 initial-foo2 Running qux bar contents initial-bar" + ) + + os.write.over(wsRoot / "foo1.txt", "edited-foo1") + awaitCompletionMarker("quxRan1") + expectedPrints.append( + "Running qux foo contents edited-foo1 initial-foo2", + "Running qux bar contents initial-bar", + ) + expectedShows.append( + "Running qux foo contents edited-foo1 initial-foo2 Running qux bar contents initial-bar", + ) + + os.write.over(wsRoot / "foo2.txt", "edited-foo2") + awaitCompletionMarker("quxRan2") + expectedPrints.append( + "Running qux foo contents edited-foo1 edited-foo2", + "Running qux bar contents initial-bar", + ) + expectedShows.append( + "Running qux foo contents edited-foo1 edited-foo2 Running qux bar contents initial-bar", + ) + + os.write.over(wsRoot / "bar.txt", "edited-bar") + awaitCompletionMarker("quxRan3") + expectedPrints.append( + "Running qux foo contents edited-foo1 edited-foo2", + "Running qux bar contents edited-bar", + ) + expectedShows.append( + "Running qux foo contents edited-foo1 edited-foo2 Running qux bar contents edited-bar", + ) + + os.write.append(wsRoot / "build.sc", "\ndef unrelated = true") + awaitCompletionMarker("initialized1") + expectedPrints.append( + "Setting up build.sc", + "Running qux foo contents edited-foo1 edited-foo2", + "Running qux bar contents edited-bar", + ) + expectedShows.append( + "Running qux foo contents edited-foo1 edited-foo2 Running qux bar contents edited-bar" + ) + + os.write.over(wsRoot / "watchValue.txt", "exit") + awaitCompletionMarker("initialized2") + expectedPrints.append("Setting up build.sc") + + val res = Await.result(evalResult, Duration.apply(maxDuration, SECONDS)) + + + + val (shows, prints) = res.out.linesIterator.toVector.partition(_.startsWith("\"")) + + assert(prints == expectedPrints) + if (show) assert(shows == expectedShows.map('"' + _ + '"')) + } + + test("sources"){ + + test("noshow") - testWatchSource(false) + test("show") - testWatchSource(true) + } + + def testWatchInput(show: Boolean) = { + val showArgs = if (show) Seq("show") else Nil + + val evalResult = Future{ evalTimeoutStdout(maxDuration, "--watch", showArgs, "lol") } + val expectedPrints = collection.mutable.Buffer.empty[String] + val expectedShows = collection.mutable.Buffer.empty[String] + + awaitCompletionMarker("initialized0") + awaitCompletionMarker("lolRan0") + expectedPrints.append( + "Setting up build.sc", + "Running lol baz contents initial-baz", + ) + expectedShows.append("Running lol baz contents initial-baz") + + os.write.over(wsRoot / "baz.txt", "edited-baz") + awaitCompletionMarker("lolRan1") + expectedPrints.append("Running lol baz contents edited-baz") + expectedShows.append("Running lol baz contents edited-baz") + + os.write.over(wsRoot / "watchValue.txt", "edited-watchValue") + awaitCompletionMarker("initialized1") + expectedPrints.append("Setting up build.sc") + expectedShows.append("Running lol baz contents edited-baz") + + os.write.over(wsRoot / "watchValue.txt", "exit") + awaitCompletionMarker("initialized2") + expectedPrints.append("Setting up build.sc") + + val res = Await.result(evalResult, Duration.apply(maxDuration, SECONDS)) + + + val (shows, prints) = res.out.linesIterator.toVector.partition(_.startsWith("\"")) + assert(prints == expectedPrints) + if (show) assert(shows == expectedShows.map('"' + _ + '"')) + } + + test("input"){ + + test("noshow") - testWatchInput(false) + test("show") - testWatchInput(true) + } + } +} diff --git a/integration/src/mill/integration/IntegrationTestSuite.scala b/integration/src/mill/integration/IntegrationTestSuite.scala index e59e275e44d..b8f32182831 100644 --- a/integration/src/mill/integration/IntegrationTestSuite.scala +++ b/integration/src/mill/integration/IntegrationTestSuite.scala @@ -1,21 +1,14 @@ package mill.integration -import mainargs.Flag -import mill.runner.MillCliConfig import mill.main.SelectMode -import mill.runner.{MillBuildBootstrap, MillMain, RunnerState, Watching} -import mill.api.SystemStreams -import mill.util.PrintLogger -import os.Path +import mill.runner.RunnerState +import os.{Path, Shellable} import utest._ -import java.io.{ByteArrayInputStream, ByteArrayOutputStream, PrintStream} -import java.nio.file.NoSuchFileException import scala.util.control.NonFatal object IntegrationTestSuite { case class EvalResult(isSuccess: Boolean, out: String, err: String) - } abstract class IntegrationTestSuite extends TestSuite { @@ -37,16 +30,20 @@ abstract class IntegrationTestSuite extends TestSuite { var runnerState = RunnerState.empty - def eval(s: String*): Boolean = evalFork(os.Inherit, os.Inherit, s) + def eval(s: Shellable*): Boolean = evalFork(os.Inherit, os.Inherit, s, -1) + + def evalStdout(s: Shellable*): IntegrationTestSuite.EvalResult = { + evalTimeoutStdout(-1, s:_*) + } - def evalStdout(s: String*): IntegrationTestSuite.EvalResult = { + def evalTimeoutStdout(timeout: Long, s: Shellable*): IntegrationTestSuite.EvalResult = { val output = Seq.newBuilder[String] val error = Seq.newBuilder[String] val processOutput = os.ProcessOutput.Readlines(output += _) val processError = os.ProcessOutput.Readlines(error += _) - val result = evalFork(processOutput, processError, s) + val result = evalFork(processOutput, processError, s, timeout) IntegrationTestSuite.EvalResult( result, output.result().mkString("\n"), @@ -61,7 +58,8 @@ abstract class IntegrationTestSuite extends TestSuite { private def evalFork( stdout: os.ProcessOutput, stderr: os.ProcessOutput, - s: Seq[String] + s: Seq[Shellable], + timeout: Long ): Boolean = { val serverArgs = if (integrationTestMode == "server" || integrationTestMode == "local") Seq() @@ -75,7 +73,8 @@ abstract class IntegrationTestSuite extends TestSuite { stdin = os.Inherit, stdout = stdout, stderr = stderr, - env = millTestSuiteEnv + env = millTestSuiteEnv, + timeout = timeout ) true } catch { From 1e3e92d4a4f21978f49160baae683c3680a8339c Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 13:10:13 +0800 Subject: [PATCH 13/27] wip --- .../mill/integration/ExampleTestSuite.scala | 2 +- .../test/src/WatchSourceInputTests.scala | 26 ++++------ .../integration/IntegrationTestSuite.scala | 2 +- main/api/src/mill/api/Val.scala | 10 ++++ main/eval/src/mill/eval/Evaluator.scala | 51 ++++++++++--------- main/src/mill/main/MainModule.scala | 16 +++--- main/src/mill/main/RunScript.scala | 15 ++++-- main/src/mill/main/VisualizeModule.scala | 2 +- main/test/src/mill/main/MainModuleTests.scala | 14 ++--- .../src/mill/runner/MillBuildBootstrap.scala | 15 +++--- runner/src/mill/runner/RunnerState.scala | 5 +- 11 files changed, 86 insertions(+), 72 deletions(-) create mode 100644 main/api/src/mill/api/Val.scala diff --git a/example/src/mill/integration/ExampleTestSuite.scala b/example/src/mill/integration/ExampleTestSuite.scala index cd068300fc8..15d996f1a84 100644 --- a/example/src/mill/integration/ExampleTestSuite.scala +++ b/example/src/mill/integration/ExampleTestSuite.scala @@ -88,7 +88,7 @@ object ExampleTestSuite extends IntegrationTestSuite { BashTokenizer.tokenize(commandStr) match { case Seq(s"./$command", rest @ _*) => val evalResult = command match { - case "mill" => evalStdout(rest: _*) + case "mill" => evalStdout(rest) case cmd => val tokens = cmd +: rest val executable = workspaceRoot / os.RelPath(tokens.head) diff --git a/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala b/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala index 1801fa488d0..38b8f0ca221 100644 --- a/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala +++ b/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala @@ -14,7 +14,6 @@ import scala.concurrent.ExecutionContext.Implicits.global * 3. `T.input` * 4. `interp.watchValue` * 5. Implicitly watched files, like `build.sc` - * */ object WatchSourceInputTests extends IntegrationTestSuite { @@ -54,30 +53,30 @@ object WatchSourceInputTests extends IntegrationTestSuite { awaitCompletionMarker("quxRan1") expectedPrints.append( "Running qux foo contents edited-foo1 initial-foo2", - "Running qux bar contents initial-bar", + "Running qux bar contents initial-bar" ) expectedShows.append( - "Running qux foo contents edited-foo1 initial-foo2 Running qux bar contents initial-bar", + "Running qux foo contents edited-foo1 initial-foo2 Running qux bar contents initial-bar" ) os.write.over(wsRoot / "foo2.txt", "edited-foo2") awaitCompletionMarker("quxRan2") expectedPrints.append( "Running qux foo contents edited-foo1 edited-foo2", - "Running qux bar contents initial-bar", + "Running qux bar contents initial-bar" ) expectedShows.append( - "Running qux foo contents edited-foo1 edited-foo2 Running qux bar contents initial-bar", + "Running qux foo contents edited-foo1 edited-foo2 Running qux bar contents initial-bar" ) os.write.over(wsRoot / "bar.txt", "edited-bar") awaitCompletionMarker("quxRan3") expectedPrints.append( "Running qux foo contents edited-foo1 edited-foo2", - "Running qux bar contents edited-bar", + "Running qux bar contents edited-bar" ) expectedShows.append( - "Running qux foo contents edited-foo1 edited-foo2 Running qux bar contents edited-bar", + "Running qux foo contents edited-foo1 edited-foo2 Running qux bar contents edited-bar" ) os.write.append(wsRoot / "build.sc", "\ndef unrelated = true") @@ -85,7 +84,7 @@ object WatchSourceInputTests extends IntegrationTestSuite { expectedPrints.append( "Setting up build.sc", "Running qux foo contents edited-foo1 edited-foo2", - "Running qux bar contents edited-bar", + "Running qux bar contents edited-bar" ) expectedShows.append( "Running qux foo contents edited-foo1 edited-foo2 Running qux bar contents edited-bar" @@ -97,15 +96,13 @@ object WatchSourceInputTests extends IntegrationTestSuite { val res = Await.result(evalResult, Duration.apply(maxDuration, SECONDS)) - - val (shows, prints) = res.out.linesIterator.toVector.partition(_.startsWith("\"")) assert(prints == expectedPrints) if (show) assert(shows == expectedShows.map('"' + _ + '"')) } - test("sources"){ + test("sources") { test("noshow") - testWatchSource(false) test("show") - testWatchSource(true) @@ -114,7 +111,7 @@ object WatchSourceInputTests extends IntegrationTestSuite { def testWatchInput(show: Boolean) = { val showArgs = if (show) Seq("show") else Nil - val evalResult = Future{ evalTimeoutStdout(maxDuration, "--watch", showArgs, "lol") } + val evalResult = Future { evalTimeoutStdout(maxDuration, "--watch", showArgs, "lol") } val expectedPrints = collection.mutable.Buffer.empty[String] val expectedShows = collection.mutable.Buffer.empty[String] @@ -122,7 +119,7 @@ object WatchSourceInputTests extends IntegrationTestSuite { awaitCompletionMarker("lolRan0") expectedPrints.append( "Setting up build.sc", - "Running lol baz contents initial-baz", + "Running lol baz contents initial-baz" ) expectedShows.append("Running lol baz contents initial-baz") @@ -142,13 +139,12 @@ object WatchSourceInputTests extends IntegrationTestSuite { val res = Await.result(evalResult, Duration.apply(maxDuration, SECONDS)) - val (shows, prints) = res.out.linesIterator.toVector.partition(_.startsWith("\"")) assert(prints == expectedPrints) if (show) assert(shows == expectedShows.map('"' + _ + '"')) } - test("input"){ + test("input") { test("noshow") - testWatchInput(false) test("show") - testWatchInput(true) diff --git a/integration/src/mill/integration/IntegrationTestSuite.scala b/integration/src/mill/integration/IntegrationTestSuite.scala index b8f32182831..75086dd1b15 100644 --- a/integration/src/mill/integration/IntegrationTestSuite.scala +++ b/integration/src/mill/integration/IntegrationTestSuite.scala @@ -33,7 +33,7 @@ abstract class IntegrationTestSuite extends TestSuite { def eval(s: Shellable*): Boolean = evalFork(os.Inherit, os.Inherit, s, -1) def evalStdout(s: Shellable*): IntegrationTestSuite.EvalResult = { - evalTimeoutStdout(-1, s:_*) + evalTimeoutStdout(-1, s: _*) } def evalTimeoutStdout(timeout: Long, s: Shellable*): IntegrationTestSuite.EvalResult = { diff --git a/main/api/src/mill/api/Val.scala b/main/api/src/mill/api/Val.scala new file mode 100644 index 00000000000..e80d92b393d --- /dev/null +++ b/main/api/src/mill/api/Val.scala @@ -0,0 +1,10 @@ +package mill.api + +/** + * A somewhat-type-safe wrapper around `Any`. Stores an un-typed value, but + * can only be created explicitly by wrapping in `Val(_)` and de-constructed + * explicitly via `.value`. That makes it much less likely to introduce bugs + * passing the wrong thing, e.g. `(Any, Int)` can be passed to `Any`, but + * `(Val, Int)` cannot be passed to `Val` + */ +case class Val(value: Any) diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index 5f9b14dff3d..0ffefc46cef 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -10,7 +10,8 @@ import mill.api.{ PathRef, Result, Strict, - TestReporter + TestReporter, + Val } import mill.api.Result.{Aborted, Failing, OuterStack, Success} import mill.api.Strict.Agg @@ -35,7 +36,7 @@ class Evaluator private ( _rootModule: mill.define.BaseModule, _baseLogger: ColorLogger, _classLoaderSigHash: Int, - _workerCache: mutable.Map[Segments, (Int, Any)], + _workerCache: mutable.Map[Segments, (Int, Val)], _env: Map[String, String], _failFast: Boolean, _threadCount: Option[Int], @@ -65,7 +66,7 @@ class Evaluator private ( /** * Mutable worker cache. */ - def workerCache: mutable.Map[Segments, (Int, Any)] = _workerCache + def workerCache: mutable.Map[Segments, (Int, Val)] = _workerCache def env: Map[String, String] = _env /** @@ -184,8 +185,9 @@ class Evaluator private ( failing.addAll( k, Loose.Agg.from( - vs.items.flatMap(results.get).collect { case er @ TaskResult(f: mill.api.Result.Failing[_], _) => - f + vs.items.flatMap(results.get).collect { + case er @ TaskResult(f: mill.api.Result.Failing[_], _) => + f } ) ) @@ -255,7 +257,8 @@ class Evaluator private ( contextLogger ) - if (failFast && res.newResults.values.exists(_.result.asSuccess.isEmpty)) failed.set(true) + if (failFast && res.newResults.values.exists(_.result.asSuccess.isEmpty)) + failed.set(true) val endTime = System.currentTimeMillis() timeLog.timeTrace( @@ -292,7 +295,7 @@ class Evaluator private ( timeLog.close() Evaluator.Results( - goals.indexed.map(results(_).map(_._1)), + goals.indexed.map(results(_).map(_._1).result), finishedOpts.map(_._2).flatMap(_.toSeq.flatMap(_.newEvaluated)), transitive, getFailing(sortedGroups, results), @@ -375,7 +378,7 @@ class Evaluator private ( destSegments(labelledNamedTask) ) - val cached: Option[(Any, Int)] = for { + val cached: Option[(Val, Int)] = for { cached <- try Some(upickle.default.read[Evaluator.Cached](paths.meta.toIO)) catch { @@ -393,12 +396,12 @@ class Evaluator private ( None case NonFatal(_) => None } - } yield (parsed, cached.valueHash) + } yield (Val(parsed), cached.valueHash) val previousWorker = labelledNamedTask.task.asWorker.flatMap { w => workerCache.synchronized { workerCache.get(w.ctx.segments) } } - val upToDateWorker: Option[Any] = previousWorker.flatMap { + val upToDateWorker: Option[Val] = previousWorker.flatMap { case (`inputsHash`, upToDate) => // worker cached and up-to-date Some(upToDate) @@ -426,7 +429,7 @@ class Evaluator private ( upToDateWorker.map((_, inputsHash)) orElse cached match { case Some((v, hashCode)) => val newResults = mutable.LinkedHashMap.empty[Task[_], TaskResult[(Val, Int)]] - newResults(labelledNamedTask.task) = TaskResult(Result.Success((Val(v), hashCode)), None) + newResults(labelledNamedTask.task) = TaskResult(Result.Success((v, hashCode)), None) Evaluated(newResults, Nil, cached = true) @@ -491,14 +494,14 @@ class Evaluator private ( labelledNamedTask.task.asWorker match { case Some(w) => workerCache.synchronized { - workerCache.update(w.ctx.segments, (inputsHash, v.value)) + workerCache.update(w.ctx.segments, (inputsHash, v)) } case None => val terminalResult = labelledNamedTask .task .writerOpt .asInstanceOf[Option[upickle.default.Writer[Any]]] - .map{w => upickle.default.writeJs(v.value)(w)} + .map { w => upickle.default.writeJs(v.value)(w) } for (json <- terminalResult) { os.write.over( @@ -565,7 +568,7 @@ class Evaluator private ( val compute = { if (targetInputValues.length != task.inputs.length) () => mill.api.Result.Skipped - else { () => + else { () => val args = new Ctx( args = targetInputValues.map(_.value).toIndexedSeq, dest0 = () => @@ -615,7 +618,6 @@ class Evaluator private ( val (newResults, newEvaluated) = computeAll() - if (!failFast) maybeTargetLabel.foreach { targetLabel => val taskFailed = newResults.exists(task => !task._2.isInstanceOf[Success[_]]) if (taskFailed) { @@ -624,12 +626,12 @@ class Evaluator private ( } ( - newResults.map{case (k, v) => + newResults.map { case (k, v) => ( k, TaskResult( v, - Some{ () => + Some { () => val (recalced, _) = computeAll() recalced.apply(k) } @@ -704,7 +706,7 @@ class Evaluator private ( rootModule: mill.define.BaseModule = this.rootModule, baseLogger: ColorLogger = this.baseLogger, classLoaderSigHash: Int = this.classLoaderSigHash, - workerCache: mutable.Map[Segments, (Int, Any)] = this.workerCache, + workerCache: mutable.Map[Segments, (Int, Val)] = this.workerCache, env: Map[String, String] = this.env, failFast: Boolean = this.failFast, threadCount: Option[Int] = this.threadCount, @@ -731,7 +733,7 @@ class Evaluator private ( copy(rootModule = rootModule) def withBaseLogger(baseLogger: ColorLogger): Evaluator = copy(baseLogger = baseLogger) - def withWorkerCache(workerCache: mutable.Map[Segments, (Int, Any)]): Evaluator = + def withWorkerCache(workerCache: mutable.Map[Segments, (Int, Val)]): Evaluator = copy(workerCache = workerCache) def withEnv(env: Map[String, String]): Evaluator = copy(env = env) def withFailFast(failFast: Boolean): Evaluator = copy(failFast = failFast) @@ -783,16 +785,15 @@ object Evaluator { ) } - case class TaskResult[T](result: Result[T], - recalcOpt: Option[() => mill.api.Result[T]]){ + case class TaskResult[T](result: Result[T], recalcOpt: Option[() => mill.api.Result[T]]) { def map[V](f: T => V) = TaskResult[V]( result.map(f), recalcOpt.map(r => () => r().map(f)) ) } - case class Val(value: Any) + case class Results( - rawValues: Seq[mill.api.Result[Any]], + rawValues: Seq[mill.api.Result[Val]], evaluated: Agg[Task[_]], transitive: Agg[Task[_]], failing: MultiBiMap[Either[Task[_], Labelled[_]], mill.api.Result.Failing[_]], @@ -800,7 +801,7 @@ object Evaluator { ) { def values: Seq[Any] = rawValues.collect { case mill.api.Result.Success(v) => v } private def copy( - rawValues: Seq[Result[Any]] = rawValues, + rawValues: Seq[Result[Val]] = rawValues, evaluated: Agg[Task[_]] = evaluated, transitive: Agg[Task[_]] = transitive, failing: MultiBiMap[Either[Task[_], Labelled[_]], Result.Failing[_]] = failing, @@ -820,7 +821,7 @@ object Evaluator { Agg[Task[_]], Agg[Task[_]], MultiBiMap[Either[Task[_], Labelled[_]], Failing[_]], - collection.Map[Task[_], TaskResult[_]] + collection.Map[Task[_], TaskResult[_]] )] = Some(( results.rawValues, results.evaluated, diff --git a/main/src/mill/main/MainModule.scala b/main/src/mill/main/MainModule.scala index d59e23a83ab..ba6116b9cb6 100644 --- a/main/src/mill/main/MainModule.scala +++ b/main/src/mill/main/MainModule.scala @@ -26,11 +26,12 @@ object MainModule { } } - private def show0(evaluator: Evaluator, - targets: Seq[String], - log: Logger, - watch0: Watchable => Unit) - (f: Seq[(Any, Option[(RunScript.TaskName, ujson.Value)])] => ujson.Value) = { + private def show0( + evaluator: Evaluator, + targets: Seq[String], + log: Logger, + watch0: Watchable => Unit + )(f: Seq[(Any, Option[(RunScript.TaskName, ujson.Value)])] => ujson.Value) = { RunScript.evaluateTasksNamed( evaluator.withBaseLogger( // When using `show`, redirect all stdout of the evaluated tasks so the @@ -93,7 +94,6 @@ trait MainModule extends mill.Module { } } - implicit def millDiscover: mill.define.Discover[_] /** @@ -279,8 +279,8 @@ trait MainModule extends mill.Module { * to integrate Mill into external scripts and tooling. */ def show(evaluator: Evaluator, targets: String*): Command[ujson.Value] = T.command { - MainModule.show0(evaluator, targets, T.log, interp.evalWatch0){ res => - res.flatMap(_._2).map(_._2) match{ + MainModule.show0(evaluator, targets, T.log, interp.evalWatch0) { res => + res.flatMap(_._2).map(_._2) match { case Seq(single) => single case multiple => multiple } diff --git a/main/src/mill/main/RunScript.scala b/main/src/mill/main/RunScript.scala index f7201b07ccb..713012cb224 100644 --- a/main/src/mill/main/RunScript.scala +++ b/main/src/mill/main/RunScript.scala @@ -3,7 +3,7 @@ package mill.main import mill.define._ import mill.eval.{Evaluator, EvaluatorPaths} import mill.util.Watchable -import mill.api.{PathRef, Result} +import mill.api.{PathRef, Result, Val} import mill.api.Strict.Agg import Evaluator._ @@ -15,9 +15,12 @@ object RunScript { evaluator: Evaluator, scriptArgs: Seq[String], selectMode: SelectMode - ): Either[String, (Seq[Watchable], Either[String, Seq[(Any, Option[(TaskName, ujson.Value)])]])] = { + ): Either[ + String, + (Seq[Watchable], Either[String, Seq[(Any, Option[(TaskName, ujson.Value)])]]) + ] = { for (targets <- ResolveTasks.resolve(evaluator, scriptArgs, selectMode)) - yield evaluateNamed(evaluator, Agg.from(targets.distinct)) + yield evaluateNamed(evaluator, Agg.from(targets.distinct)) } /** @@ -34,8 +37,10 @@ object RunScript { val watched = evaluated.results .iterator .collect { - case (t: SourcesImpl, TaskResult(Result.Success(Val(ps: Seq[PathRef])), _)) => ps.map(Watchable.Path(_)) - case (t: SourceImpl, TaskResult(Result.Success(Val(p: PathRef)), _)) => Seq(Watchable.Path(p)) + case (t: SourcesImpl, TaskResult(Result.Success(Val(ps: Seq[PathRef])), _)) => + ps.map(Watchable.Path(_)) + case (t: SourceImpl, TaskResult(Result.Success(Val(p: PathRef)), _)) => + Seq(Watchable.Path(p)) case (t: InputImpl[_], TaskResult(_, Some(recalc))) => val pretty = t.ctx0.fileName + ":" + t.ctx0.lineNum Seq(Watchable.Value(() => recalc().hashCode(), recalc().hashCode(), pretty)) diff --git a/main/src/mill/main/VisualizeModule.scala b/main/src/mill/main/VisualizeModule.scala index fa13cbe2927..e4309949db5 100644 --- a/main/src/mill/main/VisualizeModule.scala +++ b/main/src/mill/main/VisualizeModule.scala @@ -42,7 +42,7 @@ trait VisualizeModule extends mill.define.TaskModule { ) val visualizeThread = new java.lang.Thread(() => while (true) { - val res = Result.Success{ + val res = Result.Success { val (targets, tasks, dest) = in.take() cl.loadClass("mill.main.graphviz.GraphvizTools") .getMethod("apply", classOf[Seq[_]], classOf[Seq[_]], classOf[os.Path]) diff --git a/main/test/src/mill/main/MainModuleTests.scala b/main/test/src/mill/main/MainModuleTests.scala index aca9b114add..9a4115fcafa 100644 --- a/main/test/src/mill/main/MainModuleTests.scala +++ b/main/test/src/mill/main/MainModuleTests.scala @@ -1,6 +1,6 @@ package mill.main -import mill.api.{PathRef, Result} +import mill.api.{PathRef, Result, Val} import mill.{Agg, T} import mill.define.{Cross, Module} import mill.util.{TestEvaluator, TestUtil} @@ -49,7 +49,7 @@ object MainModuleTests extends TestSuite { val eval = new TestEvaluator(mainModule) test("single") { val res = eval.evaluator.evaluate(Agg(mainModule.inspect(eval.evaluator, "hello"))) - val Result.Success(value: String) = res.rawValues.head + val Result.Success(Val(value: String)) = res.rawValues.head assert( res.failing.keyCount == 0, value.startsWith("hello("), @@ -59,7 +59,7 @@ object MainModuleTests extends TestSuite { test("multi") { val res = eval.evaluator.evaluate(Agg(mainModule.inspect(eval.evaluator, "hello", "hello2"))) - val Result.Success(value: String) = res.rawValues.head + val Result.Success(Val(value: String)) = res.rawValues.head assert( res.failing.keyCount == 0, value.startsWith("hello("), @@ -77,7 +77,7 @@ object MainModuleTests extends TestSuite { assert(results.failing.keyCount == 0) - val Result.Success(value) = results.rawValues.head + val Result.Success(Val(value)) = results.rawValues.head assert(value == ujson.Arr.from(Seq("hello", "world"))) } @@ -92,7 +92,7 @@ object MainModuleTests extends TestSuite { assert(results.failing.keyCount == 0) - val Result.Success(value) = results.rawValues.head + val Result.Success(Val(value)) = results.rawValues.head assert(value == ujson.Arr.from(Seq( ujson.Arr.from(Seq("hello", "world")), @@ -109,7 +109,7 @@ object MainModuleTests extends TestSuite { assert(results.failing.keyCount == 0) - val Result.Success(value) = results.rawValues.head + val Result.Success(Val(value)) = results.rawValues.head assert(value == ujson.Obj.from(Map( "hello" -> ujson.Arr.from(Seq("hello", "world")) @@ -126,7 +126,7 @@ object MainModuleTests extends TestSuite { assert(results.failing.keyCount == 0) - val Result.Success(value) = results.rawValues.head + val Result.Success(Val(value)) = results.rawValues.head assert(value == ujson.Obj.from(Map( "hello" -> ujson.Arr.from(Seq("hello", "world")), diff --git a/runner/src/mill/runner/MillBuildBootstrap.scala b/runner/src/mill/runner/MillBuildBootstrap.scala index 36455e6b3c2..0b09606e8e3 100644 --- a/runner/src/mill/runner/MillBuildBootstrap.scala +++ b/runner/src/mill/runner/MillBuildBootstrap.scala @@ -1,9 +1,8 @@ package mill.runner import mill.util.{ColorLogger, PrefixLogger, Util, Watchable} import mill.{BuildInfo, T} -import mill.api.{PathRef, internal} +import mill.api.{PathRef, Val, internal} import mill.eval.Evaluator - import mill.main.{RootModule, RunScript, SelectMode} import mill.main.TokenReaders._ import mill.define.{Discover, Segments} @@ -194,8 +193,8 @@ class MillBuildBootstrap( case ( Right(Seq( - Evaluator.Val(runClasspath: Seq[PathRef]), - Evaluator.Val(scriptImportGraph: Map[os.Path, (Int, Seq[os.Path])]) + Val(runClasspath: Seq[PathRef]), + Val(scriptImportGraph: Map[os.Path, (Int, Seq[os.Path])]) )), evalWatches, moduleWatches @@ -268,7 +267,7 @@ class MillBuildBootstrap( } def makeEvaluator( - workerCache: Map[Segments, (Int, Any)], + workerCache: Map[Segments, (Int, Val)], scriptImportGraph: Map[os.Path, (Int, Seq[os.Path])], rootModule: RootModule, millClassloaderSigHash: Int, @@ -334,7 +333,8 @@ object MillBuildBootstrap { targetsAndParams: Seq[String] ): (Either[String, Seq[Any]], Seq[Watchable], Seq[Watchable]) = { rootModule.evalWatchedValues.clear() - val evalTaskResult = RunScript.evaluateTasksNamed(evaluator, targetsAndParams, SelectMode.Separated) + val evalTaskResult = + RunScript.evaluateTasksNamed(evaluator, targetsAndParams, SelectMode.Separated) val moduleWatched = rootModule.watchedValues.toVector val addedEvalWatched = rootModule.evalWatchedValues.toVector @@ -343,7 +343,8 @@ object MillBuildBootstrap { case Right((watched, evaluated)) => evaluated match { case Left(msg) => (Left(msg), watched ++ addedEvalWatched, moduleWatched) - case Right(results) => (Right(results.map(_._1)), watched ++ addedEvalWatched, moduleWatched) + case Right(results) => + (Right(results.map(_._1)), watched ++ addedEvalWatched, moduleWatched) } } } diff --git a/runner/src/mill/runner/RunnerState.scala b/runner/src/mill/runner/RunnerState.scala index c7b05545661..399d8c20fa5 100644 --- a/runner/src/mill/runner/RunnerState.scala +++ b/runner/src/mill/runner/RunnerState.scala @@ -1,10 +1,11 @@ package mill.runner -import mill.api.{PathRef, internal} +import mill.api.{PathRef, Val, internal} import mill.define.{BaseModule, Segments} import mill.util.Watchable import upickle.default.{ReadWriter, macroRW} import mill.api.JsonFormatters._ +import mill.eval.Evaluator import mill.main.RootModule /** @@ -51,7 +52,7 @@ object RunnerState { @internal case class Frame( - workerCache: Map[Segments, (Int, Any)], + workerCache: Map[Segments, (Int, Val)], evalWatched: Seq[Watchable], moduleWatched: Seq[Watchable], scriptImportGraph: Map[os.Path, (Int, Seq[os.Path])], From 2d79417e6dbec889886a78e49fbf8f208ea13ef9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 13:38:34 +0800 Subject: [PATCH 14/27] pass --- example/tasks/2-primary-tasks/src/Foo.java | 2 -- .../feature/watch-source-input/repo/build.sc | 2 +- .../test/src/WatchSourceInputTests.scala | 2 +- main/eval/src/mill/eval/Evaluator.scala | 17 +++++++++-------- .../src/mill/runner/MillBuildRootModule.scala | 10 +++++----- 5 files changed, 16 insertions(+), 17 deletions(-) diff --git a/example/tasks/2-primary-tasks/src/Foo.java b/example/tasks/2-primary-tasks/src/Foo.java index 0c688a3e9ec..7f6eaa4135a 100644 --- a/example/tasks/2-primary-tasks/src/Foo.java +++ b/example/tasks/2-primary-tasks/src/Foo.java @@ -13,6 +13,4 @@ public static void main(String[] args) throws IOException{ System.out.println("foo.txt resource: " + br.readLine()); } } - - } diff --git a/integration/feature/watch-source-input/repo/build.sc b/integration/feature/watch-source-input/repo/build.sc index b033c0aecce..70bf339d6c2 100644 --- a/integration/feature/watch-source-input/repo/build.sc +++ b/integration/feature/watch-source-input/repo/build.sc @@ -34,7 +34,7 @@ def lol = T{ def writeCompletionMarker(name: String) = { Range(0, 10) - .map(i => os.pwd / s"$name$i") + .map(i => os.pwd / "out" / s"$name$i") .find(!os.exists(_)) .foreach(os.write(_, "")) } diff --git a/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala b/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala index 38b8f0ca221..c2f869007c0 100644 --- a/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala +++ b/integration/feature/watch-source-input/test/src/WatchSourceInputTests.scala @@ -23,7 +23,7 @@ object WatchSourceInputTests extends IntegrationTestSuite { def awaitCompletionMarker(name: String) = { val maxTime = System.currentTimeMillis() + maxDuration - while (!os.exists(wsRoot / name)) { + while (!os.exists(wsRoot / "out" / name)) { if (System.currentTimeMillis() > maxTime) { sys.error(s"awaitCompletionMarker($name) timed out") } diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index 0ffefc46cef..865c64e8b9c 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -528,7 +528,7 @@ class Evaluator private ( logger: mill.api.Logger ): (mutable.LinkedHashMap[Task[_], TaskResult[(Val, Int)]], mutable.Buffer[Task[_]]) = { - def computeAll() = { + def computeAll(enableTicker: Boolean) = { val newEvaluated = mutable.Buffer.empty[Task[_]] val newResults = mutable.LinkedHashMap.empty[Task[_], Result[(Val, Int)]] @@ -551,7 +551,8 @@ class Evaluator private ( val multiLogger = new ProxyLogger(resolveLogger(paths.map(_.log), logger)) { override def ticker(s: String): Unit = { - super.ticker(tickerPrefix.getOrElse("") + s) + if (enableTicker) super.ticker(tickerPrefix.getOrElse("") + s) + else () // do nothing } } // This is used to track the usage of `T.dest` in more than one Task @@ -566,9 +567,9 @@ class Evaluator private ( .map { x => newResults.getOrElse(x, results(x).result) } .collect { case Result.Success((v, _)) => v } - val compute = { - if (targetInputValues.length != task.inputs.length) () => mill.api.Result.Skipped - else { () => + val res = { + if (targetInputValues.length != task.inputs.length) mill.api.Result.Skipped + else { val args = new Ctx( args = targetInputValues.map(_.value).toIndexedSeq, dest0 = () => @@ -604,7 +605,7 @@ class Evaluator private ( } } - newResults(task) = for (v <- compute()) yield { + newResults(task) = for (v <- res) yield { ( v, if (task.isInstanceOf[Worker[_]]) inputsHash @@ -616,7 +617,7 @@ class Evaluator private ( (newResults, newEvaluated) } - val (newResults, newEvaluated) = computeAll() + val (newResults, newEvaluated) = computeAll(enableTicker = true) if (!failFast) maybeTargetLabel.foreach { targetLabel => val taskFailed = newResults.exists(task => !task._2.isInstanceOf[Success[_]]) @@ -632,7 +633,7 @@ class Evaluator private ( TaskResult( v, Some { () => - val (recalced, _) = computeAll() + val (recalced, _) = computeAll(enableTicker = false) recalced.apply(k) } ) diff --git a/runner/src/mill/runner/MillBuildRootModule.scala b/runner/src/mill/runner/MillBuildRootModule.scala index 4c321a73fb1..62698df9c0a 100644 --- a/runner/src/mill/runner/MillBuildRootModule.scala +++ b/runner/src/mill/runner/MillBuildRootModule.scala @@ -129,11 +129,11 @@ class MillBuildRootModule()(implicit Lib.findSourceFiles(allSources(), Seq("scala", "java", "sc")).map(PathRef(_)) } - override def unmanagedClasspath: T[Agg[PathRef]] = mill.define.Target.input { - mill.api.Loose.Agg.from( - millBuildRootModule.enclosingClasspath.map(p => mill.api.PathRef(p, quick = true)) - ) ++ - lineNumberPluginClasspath() + def enclosingClasspath = T.sources { + millBuildRootModule.enclosingClasspath.map(p => mill.api.PathRef(p, quick = true)) + } + override def unmanagedClasspath: T[Agg[PathRef]] = T{ + enclosingClasspath() ++ lineNumberPluginClasspath() } override def scalacPluginIvyDeps = Agg( From 1a69380a918fd92c18b3771acc772af23a519439 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 14:32:25 +0800 Subject: [PATCH 15/27] . --- .../src/mill/runner/MillBuildRootModule.scala | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/runner/src/mill/runner/MillBuildRootModule.scala b/runner/src/mill/runner/MillBuildRootModule.scala index 62698df9c0a..7936ebd7eb3 100644 --- a/runner/src/mill/runner/MillBuildRootModule.scala +++ b/runner/src/mill/runner/MillBuildRootModule.scala @@ -27,7 +27,7 @@ import scala.util.Try @internal class MillBuildRootModule()(implicit baseModuleInfo: RootModule.Info, - millBuildRootModule: MillBuildRootModule.Info + millBuildRootModuleInfo: MillBuildRootModule.Info ) extends RootModule() with ScalaModule { override def millSourcePath = millBuildRootModule.projectRoot / os.up / "mill-build" @@ -46,11 +46,18 @@ class MillBuildRootModule()(implicit override def scalaVersion = "2.13.10" - def parseBuildFiles: T[FileImportGraph] = T.input { - FileImportGraph.parseBuildFiles( - millBuildRootModule.topLevelProjectRoot, - millBuildRootModule.projectRoot / os.up - ) + def allBuildFiles = T.sources { + MillBuildRootModule + .parseBuildFiles(millBuildRootModuleInfo) + .seenScripts + .keys + .map(PathRef(_)) + .toSeq + } + + def parseBuildFiles = T { + allBuildFiles() + MillBuildRootModule.parseBuildFiles(millBuildRootModuleInfo) } override def repositoriesTask: Task[Seq[Repository]] = { @@ -132,7 +139,7 @@ class MillBuildRootModule()(implicit def enclosingClasspath = T.sources { millBuildRootModule.enclosingClasspath.map(p => mill.api.PathRef(p, quick = true)) } - override def unmanagedClasspath: T[Agg[PathRef]] = T{ + override def unmanagedClasspath: T[Agg[PathRef]] = T { enclosingClasspath() ++ lineNumberPluginClasspath() } @@ -185,6 +192,13 @@ object MillBuildRootModule { topLevelProjectRoot: os.Path ) + def parseBuildFiles(millBuildRootModule: MillBuildRootModule.Info) = { + FileImportGraph.parseBuildFiles( + millBuildRootModule.topLevelProjectRoot, + millBuildRootModule.projectRoot / os.up + ) + } + def generateWrappedSources( base: os.Path, scriptSources: Seq[PathRef], From 2ff49e4bc14dcf207265107ab96268c4fec84978 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 14:33:08 +0800 Subject: [PATCH 16/27] . --- runner/src/mill/runner/MillBuildRootModule.scala | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/runner/src/mill/runner/MillBuildRootModule.scala b/runner/src/mill/runner/MillBuildRootModule.scala index 7936ebd7eb3..87a948953c8 100644 --- a/runner/src/mill/runner/MillBuildRootModule.scala +++ b/runner/src/mill/runner/MillBuildRootModule.scala @@ -30,7 +30,7 @@ class MillBuildRootModule()(implicit millBuildRootModuleInfo: MillBuildRootModule.Info ) extends RootModule() with ScalaModule { - override def millSourcePath = millBuildRootModule.projectRoot / os.up / "mill-build" + override def millSourcePath = millBuildRootModuleInfo.projectRoot / os.up / "mill-build" override def resolveDeps( deps: Task[Agg[BoundDep]], @@ -113,12 +113,12 @@ class MillBuildRootModule()(implicit if (parsed.errors.nonEmpty) Result.Failure(parsed.errors.mkString("\n")) else { MillBuildRootModule.generateWrappedSources( - millBuildRootModule.projectRoot / os.up, + millBuildRootModuleInfo.projectRoot / os.up, scriptSources(), parsed.seenScripts, T.dest, - millBuildRootModule.enclosingClasspath, - millBuildRootModule.topLevelProjectRoot + millBuildRootModuleInfo.enclosingClasspath, + millBuildRootModuleInfo.topLevelProjectRoot ) Result.Success(Seq(PathRef(T.dest))) } @@ -137,7 +137,7 @@ class MillBuildRootModule()(implicit } def enclosingClasspath = T.sources { - millBuildRootModule.enclosingClasspath.map(p => mill.api.PathRef(p, quick = true)) + millBuildRootModuleInfo.enclosingClasspath.map(p => mill.api.PathRef(p, quick = true)) } override def unmanagedClasspath: T[Agg[PathRef]] = T { enclosingClasspath() ++ lineNumberPluginClasspath() @@ -192,10 +192,10 @@ object MillBuildRootModule { topLevelProjectRoot: os.Path ) - def parseBuildFiles(millBuildRootModule: MillBuildRootModule.Info) = { + def parseBuildFiles(millBuildRootModuleInfo: MillBuildRootModule.Info) = { FileImportGraph.parseBuildFiles( - millBuildRootModule.topLevelProjectRoot, - millBuildRootModule.projectRoot / os.up + millBuildRootModuleInfo.topLevelProjectRoot, + millBuildRootModuleInfo.projectRoot / os.up ) } From d227ee654f3b1eab4ad1b66711c306b5e9ca2f9d Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 14:52:38 +0800 Subject: [PATCH 17/27] update-patch --- ci/mill-bootstrap.patch | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/ci/mill-bootstrap.patch b/ci/mill-bootstrap.patch index 13c9465df92..42b80893553 100644 --- a/ci/mill-bootstrap.patch +++ b/ci/mill-bootstrap.patch @@ -1,5 +1,5 @@ diff --git a/build.sc b/build.sc -index 0f664c99c3..7d8788e7a8 100644 +index 0f664c99c3..d738fbe351 100644 --- a/build.sc +++ b/build.sc @@ -19,17 +19,18 @@ import com.github.lolgab.mill.mima.{ @@ -518,7 +518,7 @@ index 0f664c99c3..7d8788e7a8 100644 os.copy(examplePath, T.dest / exampleStr, createFolders = true) os.copy(bootstrapLauncher().path, T.dest / exampleStr / "mill") val zip = T.dest / s"$exampleStr.zip" -@@ -2018,47 +1735,6 @@ def exampleZips: Target[Seq[PathRef]] = T { +@@ -2018,51 +1735,10 @@ def exampleZips: Target[Seq[PathRef]] = T { } def uploadToGithub(authKey: String) = T.command { @@ -566,3 +566,18 @@ index 0f664c99c3..7d8788e7a8 100644 } def validate(ev: Evaluator): Command[Unit] = T.command { +- T.task(MainModule.evaluateTasks( ++ mill.main.RunScript.evaluateTasksNamed( + ev.withFailFast(false), + Seq( + "__.compile", +@@ -2075,7 +1751,8 @@ def validate(ev: Evaluator): Command[Unit] = T.command { + "docs.localPages" + ), + selectMode = SelectMode.Separated +- )(identity))() ++ ) ++ + () + } + From e7ded2d20fde8563f68a5d0935bb03f3274fca90 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 14:55:37 +0800 Subject: [PATCH 18/27] . --- main/testkit/src/mill/testkit/MillTestkit.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/main/testkit/src/mill/testkit/MillTestkit.scala b/main/testkit/src/mill/testkit/MillTestkit.scala index cfd7d3cc2fd..f9a09c6dfe8 100644 --- a/main/testkit/src/mill/testkit/MillTestkit.scala +++ b/main/testkit/src/mill/testkit/MillTestkit.scala @@ -1,8 +1,8 @@ package mill.testkit import mill._ -import mill.define.{Discover, TargetImpl, InputImpl} -import mill.api.{DummyInputStream, Result, SystemStreams} +import mill.define.{Discover, InputImpl, TargetImpl} +import mill.api.{DummyInputStream, Result, SystemStreams, Val} import mill.api.Result.OuterStack import mill.api.Strict.Agg @@ -109,7 +109,7 @@ trait MillTestKit { if (evaluated.failing.keyCount == 0) { Right( Tuple2( - evaluated.rawValues.head.asInstanceOf[Result.Success[T]].value, + evaluated.rawValues.head.asInstanceOf[Result.Success[Val]].value.value.asInstanceOf[T], evaluated.evaluated.collect { case t: TargetImpl[_] if module.millInternal.targets.contains(t) From d38944ed0960df6542f69f2baed87f0f49583cc6 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 15:11:49 +0800 Subject: [PATCH 19/27] cleanup --- .../feature/editing/test/src/MultiLevelBuildTests.scala | 2 +- main/eval/src/mill/eval/Evaluator.scala | 2 +- runner/src/mill/runner/MillBuildRootModule.scala | 8 ++------ 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/integration/feature/editing/test/src/MultiLevelBuildTests.scala b/integration/feature/editing/test/src/MultiLevelBuildTests.scala index b962f70cd1c..48b55d99264 100644 --- a/integration/feature/editing/test/src/MultiLevelBuildTests.scala +++ b/integration/feature/editing/test/src/MultiLevelBuildTests.scala @@ -58,7 +58,7 @@ object MultiLevelBuildTests extends IntegrationTestSuite { */ def checkWatchedFiles(expected0: Seq[os.Path]*) = { for ((expectedWatched0, (frame, path)) <- expected0.zip(loadFrames(expected0.length))) { - val frameWatched = frame.evalWatched.map(_.path).sorted + val frameWatched = frame.evalWatched.map(_.path).sorted.filter(_.startsWith(wsRoot)) val expectedWatched = expectedWatched0.sorted assert(frameWatched == expectedWatched) } diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index 865c64e8b9c..9520d4aa1bc 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -545,7 +545,7 @@ class Evaluator private ( val tickerPrefix = maybeTargetLabel.map { targetLabel => val prefix = s"[$counterMsg] $targetLabel " - if (logRun) logger.ticker(prefix) + if (logRun && enableTicker) logger.ticker(prefix) prefix + "| " } diff --git a/runner/src/mill/runner/MillBuildRootModule.scala b/runner/src/mill/runner/MillBuildRootModule.scala index 87a948953c8..af94a7d52f3 100644 --- a/runner/src/mill/runner/MillBuildRootModule.scala +++ b/runner/src/mill/runner/MillBuildRootModule.scala @@ -46,7 +46,7 @@ class MillBuildRootModule()(implicit override def scalaVersion = "2.13.10" - def allBuildFiles = T.sources { + def scriptSources = T.sources { MillBuildRootModule .parseBuildFiles(millBuildRootModuleInfo) .seenScripts @@ -56,7 +56,7 @@ class MillBuildRootModule()(implicit } def parseBuildFiles = T { - allBuildFiles() + scriptSources() MillBuildRootModule.parseBuildFiles(millBuildRootModuleInfo) } @@ -100,10 +100,6 @@ class MillBuildRootModule()(implicit Seq(ivy"com.lihaoyi::mill-moduledefs:${Versions.millModuledefsVersion}") } - def scriptSources: T[Seq[PathRef]] = T.sources { - for ((p, s) <- parseBuildFiles().seenScripts.toSeq) yield PathRef(p) - } - override def generatedSources: T[Seq[PathRef]] = T { generateScriptSources() } From f406c1089c0d15ca16573c33521f19170f92ec14 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 16:41:13 +0800 Subject: [PATCH 20/27] wip --- example/tasks/2-primary-tasks/build.sc | 4 +-- main/eval/src/mill/eval/Evaluator.scala | 30 +++++++++---------- .../src/mill/testkit/MillTestkit.scala | 12 ++++++-- .../mill/scalajslib/HelloJSWorldTests.scala | 4 +-- .../HelloNativeWorldTests.scala | 4 +-- 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/example/tasks/2-primary-tasks/build.sc b/example/tasks/2-primary-tasks/build.sc index 670ad446e97..ea06479d28a 100644 --- a/example/tasks/2-primary-tasks/build.sc +++ b/example/tasks/2-primary-tasks/build.sc @@ -34,10 +34,10 @@ def lineCount: T[Int] = T { > ./mill show lineCount Computing line count -18 +16 > ./mill show lineCount # line count already cached, doesn't need to be computed -18 +16 */ diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index 9520d4aa1bc..00262c454f4 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -179,15 +179,15 @@ class Evaluator private ( def getFailing( sortedGroups: MultiBiMap[Either[Task[_], Labelled[Any]], Task[_]], results: collection.Map[Task[_], TaskResult[(Val, Int)]] - ): MultiBiMap.Mutable[Either[Task[_], Labelled[_]], Failing[_]] = { - val failing = new MultiBiMap.Mutable[Either[Task[_], Labelled[_]], mill.api.Result.Failing[_]] + ): MultiBiMap.Mutable[Either[Task[_], Labelled[_]], Failing[Val]] = { + val failing = new MultiBiMap.Mutable[Either[Task[_], Labelled[_]], Result.Failing[Val]] for ((k, vs) <- sortedGroups.items()) { failing.addAll( k, Loose.Agg.from( vs.items.flatMap(results.get).collect { - case er @ TaskResult(f: mill.api.Result.Failing[_], _) => - f + case er @ TaskResult(f: Result.Failing[(Val, Int)], _) => + f.map(_._1) } ) ) @@ -455,10 +455,10 @@ class Evaluator private ( } newResults(labelledNamedTask.task) match { - case TaskResult(mill.api.Result.Failure(_, Some((v, _))), _) => + case TaskResult(Result.Failure(_, Some((v, _))), _) => handleTaskResult(v, v.##, paths.meta, inputsHash, labelledNamedTask) - case TaskResult(mill.api.Result.Success((v, _)), _) => + case TaskResult(Result.Success((v, _)), _) => handleTaskResult(v, v.##, paths.meta, inputsHash, labelledNamedTask) case _ => @@ -540,7 +540,7 @@ class Evaluator private ( target <- nonEvaluatedTargets item <- target.inputs.filterNot(group.contains) } yield results(item).map(_._1) - inputResults.forall(_.result.isInstanceOf[mill.api.Result.Success[_]]) + inputResults.forall(_.result.isInstanceOf[Result.Success[_]]) } val tickerPrefix = maybeTargetLabel.map { targetLabel => @@ -568,7 +568,7 @@ class Evaluator private ( .collect { case Result.Success((v, _)) => v } val res = { - if (targetInputValues.length != task.inputs.length) mill.api.Result.Skipped + if (targetInputValues.length != task.inputs.length) Result.Skipped else { val args = new Ctx( args = targetInputValues.map(_.value).toIndexedSeq, @@ -596,7 +596,7 @@ class Evaluator private ( try task.evaluate(args).map(Val(_)) catch { case NonFatal(e) => - mill.api.Result.Exception( + Result.Exception( e, new OuterStack(new Exception().getStackTrace.toIndexedSeq) ) @@ -786,7 +786,7 @@ object Evaluator { ) } - case class TaskResult[T](result: Result[T], recalcOpt: Option[() => mill.api.Result[T]]) { + case class TaskResult[T](result: Result[T], recalcOpt: Option[() => Result[T]]) { def map[V](f: T => V) = TaskResult[V]( result.map(f), recalcOpt.map(r => () => r().map(f)) @@ -794,18 +794,18 @@ object Evaluator { } case class Results( - rawValues: Seq[mill.api.Result[Val]], + rawValues: Seq[Result[Val]], evaluated: Agg[Task[_]], transitive: Agg[Task[_]], - failing: MultiBiMap[Either[Task[_], Labelled[_]], mill.api.Result.Failing[_]], + failing: MultiBiMap[Either[Task[_], Labelled[_]], Result.Failing[Val]], results: collection.Map[Task[_], TaskResult[Val]] ) { - def values: Seq[Any] = rawValues.collect { case mill.api.Result.Success(v) => v } + def values: Seq[Any] = rawValues.collect { case Result.Success(v) => v } private def copy( rawValues: Seq[Result[Val]] = rawValues, evaluated: Agg[Task[_]] = evaluated, transitive: Agg[Task[_]] = transitive, - failing: MultiBiMap[Either[Task[_], Labelled[_]], Result.Failing[_]] = failing, + failing: MultiBiMap[Either[Task[_], Labelled[_]], Result.Failing[Val]] = failing, results: collection.Map[Task[_], TaskResult[Val]] ): Results = new Results( rawValues, @@ -821,7 +821,7 @@ object Evaluator { Seq[Result[Any]], Agg[Task[_]], Agg[Task[_]], - MultiBiMap[Either[Task[_], Labelled[_]], Failing[_]], + MultiBiMap[Either[Task[_], Labelled[_]], Failing[Val]], collection.Map[Task[_], TaskResult[_]] )] = Some(( results.rawValues, diff --git a/main/testkit/src/mill/testkit/MillTestkit.scala b/main/testkit/src/mill/testkit/MillTestkit.scala index f9a09c6dfe8..2581050ba32 100644 --- a/main/testkit/src/mill/testkit/MillTestkit.scala +++ b/main/testkit/src/mill/testkit/MillTestkit.scala @@ -120,8 +120,16 @@ trait MillTestKit { ) } else { Left( - evaluated.failing.lookupKey(evaluated.failing.keys().next).items.next() - .asInstanceOf[Result.Failing[T]] + evaluated + .failing + .lookupKey(evaluated.failing.keys().next) + .items + .next() + .asInstanceOf[Result.Failing[Val]] + .map{(x: Val) => + x.value.asInstanceOf[T] + } + ) } } diff --git a/scalajslib/test/src/mill/scalajslib/HelloJSWorldTests.scala b/scalajslib/test/src/mill/scalajslib/HelloJSWorldTests.scala index 50ae3bc5265..af794e3de18 100644 --- a/scalajslib/test/src/mill/scalajslib/HelloJSWorldTests.scala +++ b/scalajslib/test/src/mill/scalajslib/HelloJSWorldTests.scala @@ -28,8 +28,8 @@ object HelloJSWorldTests extends TestSuite { } object HelloJSWorld extends TestUtil.BaseModule { - val scalaVersions = Seq("2.13.3", "3.0.0-RC1", "2.12.12", "2.11.12") - val scalaJSVersions = Seq("1.8.0", "1.3.1", "1.0.1") + val scalaVersions = Seq("2.13.3", "2.12.12", "2.11.12") + val scalaJSVersions = Seq("1.8.0") val matrix = for { scala <- scalaVersions scalaJS <- scalaJSVersions diff --git a/scalanativelib/test/src/mill/scalanativelib/HelloNativeWorldTests.scala b/scalanativelib/test/src/mill/scalanativelib/HelloNativeWorldTests.scala index 1019ddcec35..4ce32ef1ab4 100644 --- a/scalanativelib/test/src/mill/scalanativelib/HelloNativeWorldTests.scala +++ b/scalanativelib/test/src/mill/scalanativelib/HelloNativeWorldTests.scala @@ -38,8 +38,8 @@ object HelloNativeWorldTests extends TestSuite { extends Cross.ToSegments[ReleaseMode](v => List(v.toString)) val matrix = for { - scala <- Seq("3.2.1", "3.1.3", scala213, "2.12.13", "2.11.12") - scalaNative <- Seq(scalaNative04, "0.4.9") + scala <- Seq("3.2.1", scala213, "2.12.13", "2.11.12") + scalaNative <- Seq(scalaNative04) mode <- List(ReleaseMode.Debug, ReleaseMode.ReleaseFast) if !(ZincWorkerUtil.isScala3(scala) && scalaNative == scalaNative04) } yield (scala, scalaNative, mode) From a07fc7d044640bd59d76bea8809842b30153afe9 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 16:42:27 +0800 Subject: [PATCH 21/27] wip --- example/tasks/2-primary-tasks/build.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/tasks/2-primary-tasks/build.sc b/example/tasks/2-primary-tasks/build.sc index ea06479d28a..d8483daebe2 100644 --- a/example/tasks/2-primary-tasks/build.sc +++ b/example/tasks/2-primary-tasks/build.sc @@ -111,7 +111,7 @@ def hugeFileName = T{ /** Usage > ./mill show lineCount -18 +16 > ./mill show hugeFileName # This still runs `largestFile` even though `lineCount() < 999` Finding Largest File From 5eae38170bfc403137f8d41a0ec97a8861fb0b9b Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 20:14:27 +0800 Subject: [PATCH 22/27] . --- main/eval/src/mill/eval/Evaluator.scala | 6 +++--- scalajslib/test/src/mill/scalajslib/HelloJSWorldTests.scala | 4 ++-- .../src/mill/scalanativelib/HelloNativeWorldTests.scala | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index 00262c454f4..7dfacffc04d 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -800,7 +800,7 @@ object Evaluator { failing: MultiBiMap[Either[Task[_], Labelled[_]], Result.Failing[Val]], results: collection.Map[Task[_], TaskResult[Val]] ) { - def values: Seq[Any] = rawValues.collect { case Result.Success(v) => v } + def values: Seq[Val] = rawValues.collect { case Result.Success(v) => v } private def copy( rawValues: Seq[Result[Val]] = rawValues, evaluated: Agg[Task[_]] = evaluated, @@ -914,14 +914,14 @@ object Evaluator { throw exceptionFactory(r) case r => // Input is a single-item Agg, so we also expect a single-item result - val Seq(e: T) = r.values + val Seq(Val(e: T)) = r.values e } def apply[T: ClassTag](tasks: Seq[Task[T]]): Seq[T] = evaluator.evaluate(tasks) match { case r if r.failing.items().nonEmpty => throw exceptionFactory(r) - case r => r.values.asInstanceOf[Seq[T]] + case r => r.values.map(_.value).asInstanceOf[Seq[T]] } } diff --git a/scalajslib/test/src/mill/scalajslib/HelloJSWorldTests.scala b/scalajslib/test/src/mill/scalajslib/HelloJSWorldTests.scala index af794e3de18..50ae3bc5265 100644 --- a/scalajslib/test/src/mill/scalajslib/HelloJSWorldTests.scala +++ b/scalajslib/test/src/mill/scalajslib/HelloJSWorldTests.scala @@ -28,8 +28,8 @@ object HelloJSWorldTests extends TestSuite { } object HelloJSWorld extends TestUtil.BaseModule { - val scalaVersions = Seq("2.13.3", "2.12.12", "2.11.12") - val scalaJSVersions = Seq("1.8.0") + val scalaVersions = Seq("2.13.3", "3.0.0-RC1", "2.12.12", "2.11.12") + val scalaJSVersions = Seq("1.8.0", "1.3.1", "1.0.1") val matrix = for { scala <- scalaVersions scalaJS <- scalaJSVersions diff --git a/scalanativelib/test/src/mill/scalanativelib/HelloNativeWorldTests.scala b/scalanativelib/test/src/mill/scalanativelib/HelloNativeWorldTests.scala index 4ce32ef1ab4..1019ddcec35 100644 --- a/scalanativelib/test/src/mill/scalanativelib/HelloNativeWorldTests.scala +++ b/scalanativelib/test/src/mill/scalanativelib/HelloNativeWorldTests.scala @@ -38,8 +38,8 @@ object HelloNativeWorldTests extends TestSuite { extends Cross.ToSegments[ReleaseMode](v => List(v.toString)) val matrix = for { - scala <- Seq("3.2.1", scala213, "2.12.13", "2.11.12") - scalaNative <- Seq(scalaNative04) + scala <- Seq("3.2.1", "3.1.3", scala213, "2.12.13", "2.11.12") + scalaNative <- Seq(scalaNative04, "0.4.9") mode <- List(ReleaseMode.Debug, ReleaseMode.ReleaseFast) if !(ZincWorkerUtil.isScala3(scala) && scalaNative == scalaNative04) } yield (scala, scalaNative, mode) From 080527d91a4b437eab2fe7637052e051d5713863 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 21:14:00 +0800 Subject: [PATCH 23/27] fix main.__.test --- main/eval/src/mill/eval/Evaluator.scala | 2 +- main/eval/test/src/mill/eval/EvaluationTests.scala | 4 ++-- main/testkit/src/mill/testkit/MillTestkit.scala | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index 7dfacffc04d..6eb69d3488c 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -405,7 +405,7 @@ class Evaluator private ( case (`inputsHash`, upToDate) => // worker cached and up-to-date Some(upToDate) - case (_, obsolete: AutoCloseable) => + case (_, Val(obsolete: AutoCloseable)) => // worker cached but obsolete, needs to be closed try { logger.debug(s"Closing previous worker: ${labelledNamedTask.segments.render}") diff --git a/main/eval/test/src/mill/eval/EvaluationTests.scala b/main/eval/test/src/mill/eval/EvaluationTests.scala index 47d0d3241f8..fd2ee31f4be 100644 --- a/main/eval/test/src/mill/eval/EvaluationTests.scala +++ b/main/eval/test/src/mill/eval/EvaluationTests.scala @@ -43,7 +43,7 @@ class EvaluationTests(threadCount: Option[Int]) extends TestSuite { evaled.evaluated.indexed.partition(expEvaled.contains) assert( - evaled.values == Seq(expValue), + evaled.values.map(_.value) == Seq(expValue), matchingReturnedEvaled.toSet == expEvaled.toSet, extraEvaled == -1 || extra.length == extraEvaled ) @@ -53,7 +53,7 @@ class EvaluationTests(threadCount: Option[Int]) extends TestSuite { val evaled2 = evaluator.evaluate(Agg(target)) val expecteSecondRunEvaluated = Agg() assert( - evaled2.values == evaled.values, + evaled2.values.map(_.value) == evaled.values.map(_.value), evaled2.evaluated == expecteSecondRunEvaluated ) } diff --git a/main/testkit/src/mill/testkit/MillTestkit.scala b/main/testkit/src/mill/testkit/MillTestkit.scala index 2581050ba32..016cda0eaca 100644 --- a/main/testkit/src/mill/testkit/MillTestkit.scala +++ b/main/testkit/src/mill/testkit/MillTestkit.scala @@ -140,7 +140,7 @@ trait MillTestKit { val cleaned = res.rawValues.map { case Result.Exception(ex, _) => Result.Exception(ex, new OuterStack(Nil)) - case x => x + case x => x.map(_.value) } assert( From 137e47986cfb4e6433cf3a106bd47bd72bd63e04 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 21:22:06 +0800 Subject: [PATCH 24/27] . --- main/api/src/mill/api/Result.scala | 4 ++++ scalalib/src/mill/scalalib/GenIdeaImpl.scala | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/main/api/src/mill/api/Result.scala b/main/api/src/mill/api/Result.scala index ab55c0aaad3..efc85651890 100644 --- a/main/api/src/mill/api/Result.scala +++ b/main/api/src/mill/api/Result.scala @@ -11,6 +11,8 @@ sealed trait Result[+T] { def map[V](f: T => V): Result[V] def flatMap[V](f: T => Result[V]): Result[V] def asSuccess: Option[Result.Success[T]] = None + def asFailing: Option[Result.Failing[T]] = None + } object Result { @@ -56,6 +58,8 @@ object Result { sealed trait Failing[+T] extends Result[T] { def map[V](f: T => V): Failing[V] def flatMap[V](f: T => Result[V]): Failing[V] + override def asFailing: Option[Result.Failing[T]] = Some(this) + } /** diff --git a/scalalib/src/mill/scalalib/GenIdeaImpl.scala b/scalalib/src/mill/scalalib/GenIdeaImpl.scala index 4f0c97cc613..10433d13257 100755 --- a/scalalib/src/mill/scalalib/GenIdeaImpl.scala +++ b/scalalib/src/mill/scalalib/GenIdeaImpl.scala @@ -243,7 +243,7 @@ case class GenIdeaImpl( evaluator.evaluate(resolveTasks) match { case r if r.failing.items().nonEmpty => throw GenIdeaException(s"Failure during resolving modules: ${Evaluator.formatFailing(r)}") - case r => r.values.asInstanceOf[Seq[ResolvedModule]] + case r => r.values.map(_.value).asInstanceOf[Seq[ResolvedModule]] } val moduleLabels = modules.map(_.swap).toMap @@ -537,6 +537,7 @@ case class GenIdeaImpl( ) ) .values + .map(_.value) val generatedSourcePaths = generatedSourcePathRefs.map(_.path) val normalSourcePaths = (allSourcesPathRefs @@ -550,6 +551,7 @@ case class GenIdeaImpl( .evaluate(Agg(x.scalaVersion)) .values .head + .value .asInstanceOf[String] ) case _ => None From 28d6ac4e050529e083c6fe58682918e5e4829d23 Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 3 May 2023 21:28:43 +0800 Subject: [PATCH 25/27] . --- .../feature/editing/test/src/MultiLevelBuildTests.scala | 7 ++++++- main/testkit/src/mill/testkit/MillTestkit.scala | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/integration/feature/editing/test/src/MultiLevelBuildTests.scala b/integration/feature/editing/test/src/MultiLevelBuildTests.scala index 48b55d99264..c0a703033c5 100644 --- a/integration/feature/editing/test/src/MultiLevelBuildTests.scala +++ b/integration/feature/editing/test/src/MultiLevelBuildTests.scala @@ -58,7 +58,12 @@ object MultiLevelBuildTests extends IntegrationTestSuite { */ def checkWatchedFiles(expected0: Seq[os.Path]*) = { for ((expectedWatched0, (frame, path)) <- expected0.zip(loadFrames(expected0.length))) { - val frameWatched = frame.evalWatched.map(_.path).sorted.filter(_.startsWith(wsRoot)) + val frameWatched = frame + .evalWatched + .map(_.path) + .sorted.filter(_.startsWith(wsRoot)) + .filter(!_.segments.contains("mill-launcher")) + val expectedWatched = expectedWatched0.sorted assert(frameWatched == expectedWatched) } diff --git a/main/testkit/src/mill/testkit/MillTestkit.scala b/main/testkit/src/mill/testkit/MillTestkit.scala index 016cda0eaca..63ab77f77df 100644 --- a/main/testkit/src/mill/testkit/MillTestkit.scala +++ b/main/testkit/src/mill/testkit/MillTestkit.scala @@ -125,7 +125,8 @@ trait MillTestKit { .lookupKey(evaluated.failing.keys().next) .items .next() - .asInstanceOf[Result.Failing[Val]] + .asFailing + .get .map{(x: Val) => x.value.asInstanceOf[T] } From 8a623c4fa6fd6f2c9cbeebdf6ddd098bb11867ca Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 4 May 2023 09:34:55 +0800 Subject: [PATCH 26/27] cleanups --- main/eval/src/mill/eval/Evaluator.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/main/eval/src/mill/eval/Evaluator.scala b/main/eval/src/mill/eval/Evaluator.scala index 6eb69d3488c..f7d6c6d88b8 100644 --- a/main/eval/src/mill/eval/Evaluator.scala +++ b/main/eval/src/mill/eval/Evaluator.scala @@ -123,14 +123,14 @@ class Evaluator private ( val (sortedGroups, transitive) = Evaluator.plan(goals) val evaluated = new Agg.Mutable[Task[_]] - val results = mutable.LinkedHashMap.empty[Task[_], TaskResult[(Val, Int)]] + var results = Map.empty[Task[_], TaskResult[(Val, Int)]] var someTaskFailed: Boolean = false val timings = mutable.ArrayBuffer.empty[(Either[Task[_], Labelled[_]], Int, Boolean)] for (((terminal, group), i) <- sortedGroups.items().zipWithIndex) { if (failFast && someTaskFailed) { // we exit early and set aborted state for all left tasks - group.iterator.foreach { task => results.put(task, TaskResult(Aborted, None)) } + group.iterator.foreach { task => results += (task -> TaskResult(Aborted, None)) } } else { @@ -143,7 +143,7 @@ class Evaluator private ( val startTime = System.currentTimeMillis() // Increment the counter message by 1 to go from 1/10 to 10/10 instead of 0/10 to 9/10 - val counterMsg = s"${(i + 1)}/${sortedGroups.keyCount}" + val counterMsg = s"${i + 1}/${sortedGroups.keyCount}" val Evaluated(newResults, newEvaluated, cached) = evaluateGroupCached( terminal = terminal, @@ -157,8 +157,8 @@ class Evaluator private ( someTaskFailed = someTaskFailed || newResults.exists(task => !task._2.result.isInstanceOf[Success[_]]) - for (ev <- newEvaluated) evaluated.append(ev) - for ((k, v) <- newResults) results.put(k, v) + evaluated.appendAll(newEvaluated) + results ++= newResults val endTime = System.currentTimeMillis() timings.append((terminal, (endTime - startTime).toInt, cached)) @@ -310,7 +310,7 @@ class Evaluator private ( protected def evaluateGroupCached( terminal: Terminal, group: Agg[Task[_]], - results: collection.Map[Task[_], TaskResult[(Val, Int)]], + results: Map[Task[_], TaskResult[(Val, Int)]], counterMsg: String, zincProblemReporter: Int => Option[CompileProblemReporter], testReporter: TestReporter, @@ -357,7 +357,7 @@ class Evaluator private ( case Left(task) => val (newResults, newEvaluated) = evaluateGroup( group, - results.toMap, + results, inputsHash, paths = None, maybeTargetLabel = None, From 3a35c1fff223fefe3420b41007f83f19c98e5d49 Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 4 May 2023 09:41:08 +0800 Subject: [PATCH 27/27] . --- main/testkit/src/mill/testkit/MillTestkit.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/main/testkit/src/mill/testkit/MillTestkit.scala b/main/testkit/src/mill/testkit/MillTestkit.scala index 63ab77f77df..a923a63410d 100644 --- a/main/testkit/src/mill/testkit/MillTestkit.scala +++ b/main/testkit/src/mill/testkit/MillTestkit.scala @@ -127,10 +127,9 @@ trait MillTestKit { .next() .asFailing .get - .map{(x: Val) => + .map { (x: Val) => x.value.asInstanceOf[T] } - ) } }