From c0823daaa5bd89f73de3b010cede98ce19d97d03 Mon Sep 17 00:00:00 2001 From: Tobias Roeser Date: Sat, 2 Sep 2023 10:01:43 +0200 Subject: [PATCH] Allow Mill CLI to select the meta-build frame it operates on (#2719) Add a new CLI option `--meta-level` accepting an `Int`. Default is `0` and means the root project, `1` is the parent meta-build, if defined, or the built-in bootstrap module, and so on. **Example: Find version updates in the meta build** Here is some example output (applied to the mill repo): ``` $ mill --meta-level 1 mill.scalalib.Dependency/showUpdates [1657/1657] dev.run Found 3 dependency update for net.sourceforge.htmlcleaner:htmlcleaner : 2.25 -> 2.26 -> 2.27 -> 2.28 -> 2.29 com.lihaoyi:mill-contrib-buildinfo_2.13 : 0.11.2-6-261437 -> 0.11.2 com.github.lolgab:mill-mima_mill0.11_2.13 : 0.0.23 -> 0.0.24 ``` **Meta information about the build** I also added a new external module `mill.runner.MillBuild` to get some meta-information about the project, for now, the meta-module count or frame count. Here on a project with one meta-build: ``` $ mill show mill.runner.MillBuild/levelCount 3 ``` Pull request: https://github.com/com-lihaoyi/mill/pull/2719 --- build.sc | 65 ++++++++-------- docs/modules/ROOT/pages/Extending_Mill.adoc | 40 ++++++++++ docs/modules/ROOT/pages/Intro_to_Mill.adoc | 71 +++++++++++------- example/misc/4-mill-build-folder/build.sc | 16 ++++ example/misc/5-module-run-task/build.sc | 4 +- runner/src/mill/runner/MillBuild.scala | 21 ++++++ .../src/mill/runner/MillBuildBootstrap.scala | 75 ++++++++++++++----- runner/src/mill/runner/MillCliConfig.scala | 23 ++++-- runner/src/mill/runner/MillMain.scala | 11 ++- runner/src/mill/runner/RunnerState.scala | 4 +- 10 files changed, 240 insertions(+), 90 deletions(-) create mode 100644 runner/src/mill/runner/MillBuild.scala diff --git a/build.sc b/build.sc index 428a2df2a4d..ad89b1756e7 100644 --- a/build.sc +++ b/build.sc @@ -4,9 +4,10 @@ import $file.ci.shared import $file.ci.upload import $ivy.`org.scalaj::scalaj-http:2.4.2` import $ivy.`de.tototec::de.tobiasroeser.mill.vcs.version::0.4.0` - import $ivy.`com.github.lolgab::mill-mima::0.0.23` import $ivy.`net.sourceforge.htmlcleaner:htmlcleaner:2.25` +import mill.define.NamedTask +import mill.main.Tasks // imports import com.github.lolgab.mill.mima.{CheckDirection, ProblemFilter, Mima} @@ -369,7 +370,7 @@ trait MillStableScalaModule extends MillPublishScalaModule with Mima { ProblemFilter.exclude[Problem]("mill.eval.ProfileLogger*"), ProblemFilter.exclude[Problem]("mill.eval.GroupEvaluator*"), ProblemFilter.exclude[Problem]("mill.eval.Tarjans*"), - ProblemFilter.exclude[Problem]("mill.define.Ctx#Impl*"), + ProblemFilter.exclude[Problem]("mill.define.Ctx#Impl*") ) def mimaPreviousVersions: T[Seq[String]] = Settings.mimaBaseVersions @@ -486,33 +487,34 @@ object main extends MillStableScalaModule with BuildInfo { } object codesig extends MillPublishScalaModule { - override def ivyDeps = Agg(ivy"org.ow2.asm:asm-tree:9.5", Deps.osLib, ivy"com.lihaoyi::pprint:0.8.1") + override def ivyDeps = + Agg(ivy"org.ow2.asm:asm-tree:9.5", Deps.osLib, ivy"com.lihaoyi::pprint:0.8.1") def moduleDeps = Seq(util) - override lazy val test: CodeSigTests = new CodeSigTests{} + override lazy val test: CodeSigTests = new CodeSigTests {} trait CodeSigTests extends MillScalaTests { val caseKeys = interp.watchValue( os.walk(millSourcePath / "cases", maxDepth = 3) .map(_.subRelativeTo(millSourcePath / "cases").segments) - .collect{case Seq(a, b, c) => s"$a-$b-$c"} + .collect { case Seq(a, b, c) => s"$a-$b-$c" } ) - def testLogFolder = T{ T.dest } + def testLogFolder = T { T.dest } def caseEnvs[V](f1: CaseModule => Task[V])(s: String, f2: V => String) = { T.traverse(caseKeys) { i => f1(cases(i)).map(v => s"MILL_TEST_${s}_$i" -> f2(v)) } } - def forkEnv = T{ + def forkEnv = T { Map("MILL_TEST_LOGS" -> testLogFolder().toString) ++ - caseEnvs(_.compile)("CLASSES", _.classes.path.toString)() ++ - caseEnvs(_.compileClasspath)("CLASSPATH", _.map(_.path).mkString(","))() ++ - caseEnvs(_.sources)("SOURCES", _.head.path.toString)() + caseEnvs(_.compile)("CLASSES", _.classes.path.toString)() ++ + caseEnvs(_.compileClasspath)("CLASSPATH", _.map(_.path).mkString(","))() ++ + caseEnvs(_.sources)("SOURCES", _.head.path.toString)() } object cases extends Cross[CaseModule](caseKeys) - trait CaseModule extends ScalaModule with Cross.Module[String]{ + trait CaseModule extends ScalaModule with Cross.Module[String] { def caseName = crossValue - object external extends ScalaModule{ + object external extends ScalaModule { def scalaVersion = "2.13.10" } @@ -521,7 +523,7 @@ object main extends MillStableScalaModule with BuildInfo { val Array(prefix, suffix, rest) = caseName.split("-", 3) def millSourcePath = super.millSourcePath / prefix / suffix / rest def scalaVersion = "2.13.10" - def ivyDeps = T{ + def ivyDeps = T { if (!caseName.contains("realistic") && !caseName.contains("sourcecode")) super.ivyDeps() else Agg( Deps.fastparse, @@ -531,7 +533,7 @@ object main extends MillStableScalaModule with BuildInfo { Deps.mainargs, Deps.requests, Deps.osLib, - Deps.upickle, + Deps.upickle ) } } @@ -1454,7 +1456,7 @@ object docs extends Module { val pagesWd = T.dest / "modules" / "ROOT" / "pages" val partialsWd = T.dest / "modules" / "ROOT" / "partials" - os.copy(projectReadme().path, partialsWd / "project-readme.adoc", createFolders = true) + os.copy(projectReadme().path, partialsWd / "project-readme.adoc", createFolders = true) val renderedExamples: Seq[(os.SubPath, PathRef)] = T.traverse(example.exampleModules)(m => @@ -1763,23 +1765,24 @@ def uploadToGithub(authKey: String) = T.command { } } -def validate(ev: Evaluator): Command[Unit] = T.command { - mill.main.RunScript.evaluateTasksNamed( - ev.withFailFast(false), - Seq( - "__.compile", - "+", - "__.mimaReportBinaryIssues", - "+", - "mill.scalalib.scalafmt.ScalafmtModule/checkFormatAll", - "__.sources", - "+", - "docs.localPages" - ), - selectMode = SelectMode.Separated - ) +private def resolveTasks[T](taskNames: String*): Seq[NamedTask[T]] = { + mill.resolve.Resolve.Tasks.resolve( + build, + taskNames, + SelectMode.Separated + ).map(x => x.asInstanceOf[Seq[mill.define.NamedTask[T]]]).getOrElse(???) +} + +def validate(): Command[Unit] = { + val tasks = resolveTasks("__.compile", "__.minaReportBinaryIssues") + val sources = resolveTasks("__.sources") - () + T.command { + T.sequence(tasks)() + mill.scalalib.scalafmt.ScalafmtModule.checkFormatAll(Tasks(sources))() + docs.localPages() + () + } } /** Dummy module to let Scala-Steward find and bump dependency versions we use at runtime */ diff --git a/docs/modules/ROOT/pages/Extending_Mill.adoc b/docs/modules/ROOT/pages/Extending_Mill.adoc index e35461ee74d..0294fc03001 100644 --- a/docs/modules/ROOT/pages/Extending_Mill.adoc +++ b/docs/modules/ROOT/pages/Extending_Mill.adoc @@ -11,6 +11,46 @@ include::example/misc/3-import-file-ivy.adoc[] == The Mill Meta-Build +The meta-build manages the compilation of the `build.sc`. +If you don't configure it explicitly, a built-in synthetic meta-build is used. + +To customize it, you need to explicitly enable it with `import $meta._`. +Once enabled, the meta-build lives in the `mill-build/` directory. +It needs to contain a top-level module of type `MillBuildRootModule`. + +Meta-builds are recursive, which means, it can itself have a nested meta-builds, and so on. + +To run a task on a meta-build, you specifying the `--meta-level` option to select the meta-build level. + +=== Example: Format the `build.sc` + +As an example of running a task on the meta-build, you can format the `build.sc` with Scalafmt. +Everything is already provided by Mill. +You only need a `.scalafmt.conf` config file which at least needs configure the Scalafmt version. + +.Run Scalafmt on the `build.sc` (and potentially included files) +---- +$ mill --meta-level 1 mill.scalalib.scalafmt.ScalafmtModule/reformatAll sources +---- + +* `--meta-level 1` selects the first meta-build. Without any customization, this is the only built-in meta-build. +* `mill.scalalib.scalafmt.ScalafmtModule/reformatAll` is a generic task to format scala source files with Scalafmt. It requires the targets that refer to the source files as argument +* `sources` this selects the `sources` targets of the meta-build, which at least contains the `build.sc`. + +=== Example: Find plugin updates + +Mill plugins are defined as `ivyDeps` in the meta-build. +Hence, you can easily search for updates with the external `mill.scalalib.Dependency` module. + +.Check for Mill Plugin updates +---- +$ mill --meta-level 1 mill.scalalib.Dependency/showUpdates +Found 1 dependency update for + de.tototec:de.tobiasroeser.mill.vcs.version_mill0.11_2.13 : 0.3.1-> 0.4.0 +---- + +=== Example: Customizing the Meta-Build + include::example/misc/4-mill-build-folder.adoc[] == Using ScalaModule.run as a task diff --git a/docs/modules/ROOT/pages/Intro_to_Mill.adoc b/docs/modules/ROOT/pages/Intro_to_Mill.adoc index c30e9ae0ae8..3afe16f9852 100644 --- a/docs/modules/ROOT/pages/Intro_to_Mill.adoc +++ b/docs/modules/ROOT/pages/Intro_to_Mill.adoc @@ -124,35 +124,48 @@ Run `mill --help` for a complete list of options ---- Mill Build Tool, version {mill-version} usage: mill [options] [[target [target-options]] [+ [target ...]]] - -D --define Define (or overwrite) a system property. - -b --bell Ring the bell once if the run completes successfully, twice if it fails. - --bsp Enable BSP server mode. - --color Enable or disable colored output; by default colors are enabled in both - REPL and scripts mode if the console is interactive, and disabled - otherwise. - -d --debug Show debug output on STDOUT - --disable-ticker Disable ticker log (e.g. short-lived prints of stages and progress bars). - --enable-ticker Enable ticker log (e.g. short-lived prints of stages and progress bars). - -h --home (internal) The home directory of internally used Ammonite script engine; - where it looks for config and caches. - --help Print this help message and exit. - -i --interactive Run Mill in interactive mode, suitable for opening REPLs and taking user - input. This implies --no-server and no mill server will be used. Must be - the first argument. - --import Additional ivy dependencies to load into mill, e.g. plugins. - -j --jobs Allow processing N targets in parallel. Use 1 to disable parallel and 0 to - use as much threads as available processors. - -k --keep-going Continue build, even after build failures. - --no-server Run Mill in single-process mode. In this mode, no Mill server will be - started or used. Must be the first argument. - --repl This flag is no longer supported. - -s --silent Make ivy logs during script import resolution go silent instead of - printing; though failures will still throw exception. - -v --version Show mill version information and exit. - -w --watch Watch and re-run your scripts when they change. - target ... The name or a pattern of the target(s) you want to build, followed by any - parameters you wish to pass to those targets. To specify multiple target - names or patterns, use the `+` separator. + -D --define Define (or overwrite) a system property. + -b --bell Ring the bell once if the run completes successfully, twice if + it fails. + --bsp Enable BSP server mode. + --color Enable or disable colored output; by default colors are enabled + in both REPL and scripts mode if the console is interactive, and + disabled otherwise. + -d --debug Show debug output on STDOUT + --disable-callgraph-invalidation Disable the fine-grained callgraph-based target invalidation in + response to code changes, and instead fall back to the previous + coarse-grained implementation relying on the script `import + $file` graph + --disable-ticker Disable ticker log (e.g. short-lived prints of stages and + progress bars). + --enable-ticker Enable ticker log (e.g. short-lived prints of stages and + progress bars). + -h --home (internal) The home directory of internally used Ammonite script + engine; where it looks for config and caches. + --help Print this help message and exit. + -i --interactive Run Mill in interactive mode, suitable for opening REPLs and + taking user input. This implies --no-server and no mill server + will be used. Must be the first argument. + --import Additional ivy dependencies to load into mill, e.g. plugins. + -j --jobs Allow processing N targets in parallel. Use 1 to disable + parallel and 0 to use as much threads as available processors. + -k --keep-going Continue build, even after build failures. + --meta-level Experimental: Select a meta-build level to run the given + targets. Level 0 is the normal project, level 1 the first + meta-build, and so on. The last level is the built-in synthetic + meta-build which Mill uses to bootstrap the project. + --no-server Run Mill in single-process mode. In this mode, no Mill server + will be started or used. Must be the first argument. + --repl This flag is no longer supported. + -s --silent Make ivy logs during script import resolution go silent instead + of printing; though failures will still throw exception. + -v --version Show mill version information and exit. + -w --watch Watch and re-run your scripts when they change. + target ... The name or a pattern of the target(s) you want to build, + followed by any parameters you wish to pass to those targets. To + specify multiple target names or patterns, use the `+` + separator. + ---- All _options_ must be given before the first target. diff --git a/example/misc/4-mill-build-folder/build.sc b/example/misc/4-mill-build-folder/build.sc index bcf6ff01081..a7a1e92b563 100644 --- a/example/misc/4-mill-build-folder/build.sc +++ b/example/misc/4-mill-build-folder/build.sc @@ -63,3 +63,19 @@ scalatagsVersion: 0.8.2 */ +// You can also run tasks on the meta-build by using the `--meta-level` +// cli option. + +/** Usage + +> ./mill --meta-level 1 show sources +[ +.../build.sc", +.../mill-build/src" +] + +> ./mill --meta-level 2 show sources +.../mill-build/build.sc" + + +*/ \ No newline at end of file diff --git a/example/misc/5-module-run-task/build.sc b/example/misc/5-module-run-task/build.sc index de5be4f5cf6..73f0c2a362d 100644 --- a/example/misc/5-module-run-task/build.sc +++ b/example/misc/5-module-run-task/build.sc @@ -17,7 +17,7 @@ object bar extends ScalaModule{ def ivyDeps = Agg(ivy"com.lihaoyi::os-lib:0.9.1") } -// This example demonstrates using Mill `ScalaModule`s as build tasks: rather +// This example demonstrates using Mill ``ScalaModule``s as build tasks: rather // than defining the task logic in the `build.sc`, we instead put the build // logic within the `bar` module as `bar/src/Bar.scala`. In this example, we use // `Bar.scala` as a source-code pre-processor on the `foo` module source code: @@ -34,7 +34,7 @@ Foo.value: HELLO */ // This example does a trivial string-replace of "hello" with "HELLO", but is -// enough to demonstrate how you can use Mill `ScalaModule`s to implement your +// enough to demonstrate how you can use Mill ``ScalaModule``s to implement your // own arbitrarily complex transformations. This is useful for build logic that // may not fit nicely inside a `build.sc` file, whether due to the sheer lines // of code or due to dependencies that may conflict with the Mill classpath diff --git a/runner/src/mill/runner/MillBuild.scala b/runner/src/mill/runner/MillBuild.scala new file mode 100644 index 00000000000..bf3a94606d8 --- /dev/null +++ b/runner/src/mill/runner/MillBuild.scala @@ -0,0 +1,21 @@ +package mill.runner + +import mill.T +import mill.define.{Command, Discover, ExternalModule, Module} +import mill.eval.Evaluator.AllBootstrapEvaluators + +trait MillBuild extends Module { + + /** + * Count of the nested build-levels, the main project and all its nested meta-builds. + * If you run this on a meta-build, the non-meta-builds are not included. + */ + def levelCount(evaluators: AllBootstrapEvaluators): Command[Int] = T.command { + evaluators.value.size + } + +} + +object MillBuild extends ExternalModule with MillBuild { + override lazy val millDiscover = Discover[this.type] +} diff --git a/runner/src/mill/runner/MillBuildBootstrap.scala b/runner/src/mill/runner/MillBuildBootstrap.scala index 0b14b50a456..d638d155bbc 100644 --- a/runner/src/mill/runner/MillBuildBootstrap.scala +++ b/runner/src/mill/runner/MillBuildBootstrap.scala @@ -1,6 +1,5 @@ package mill.runner -import mill.util.{ColorLogger, PrefixLogger, Util, Watchable} -import mill.T +import mill.util.{ColorLogger, PrefixLogger, Watchable} import mill.main.BuildInfo import mill.api.{PathRef, Val, internal} import mill.eval.Evaluator @@ -37,7 +36,8 @@ class MillBuildBootstrap( prevRunnerState: RunnerState, logger: ColorLogger, disableCallgraphInvalidation: Boolean, - needBuildSc: Boolean + needBuildSc: Boolean, + requestedMetaLevel: Option[Int] ) { import MillBuildBootstrap._ @@ -66,6 +66,8 @@ class MillBuildBootstrap( val prevFrameOpt = prevRunnerState.frames.lift(depth) val prevOuterFrameOpt = prevRunnerState.frames.lift(depth - 1) + val requestedDepth = requestedMetaLevel.filter(_ >= 0).getOrElse(0) + val nestedState = if (depth == 0) { // On this level we typically want assume a Mill project, which means we want to require an existing `build.sc`. @@ -112,7 +114,30 @@ class MillBuildBootstrap( val res = if (nestedState.errorOpt.isDefined) nestedState.add(errorOpt = nestedState.errorOpt) - else { + else if (depth == 0 && requestedDepth > nestedState.frames.size) { + // User has requested a frame depth, we actually don't have + nestedState.add(errorOpt = + Some( + s"Invalid selected meta-level ${requestedDepth}. Valid range: 0 .. ${nestedState.frames.size}" + ) + ) + } else if (depth < requestedDepth) { + // We already evaluated on a deeper level, hence we just need to make sure, + // we return a proper structure with all already existing watch data + val evalState = RunnerState.Frame( + prevFrameOpt.map(_.workerCache).getOrElse(Map.empty), + Seq.empty, + Seq.empty, + Map.empty, + Map.empty, + None, + Nil, + // We don't want to evaluate anything in this depth (and above), so we just skip creating an evaluator, + // mainly because we didn't even constructed (compiled) it's classpath + None + ) + nestedState.add(frame = evalState, errorOpt = None) + } else { val validatedRootModuleOrErr = nestedState.frames.headOption match { case None => getChildRootModule(nestedState.bootstrapModuleOpt.get, depth, projectRoot) @@ -149,14 +174,26 @@ class MillBuildBootstrap( depth ) - if (depth != 0) processRunClasspath( - nestedState, - rootModule, - evaluator, - prevFrameOpt, - prevOuterFrameOpt - ) - else processFinalTargets(nestedState, rootModule, evaluator) + if (depth != 0) { + val retState = processRunClasspath( + nestedState, + rootModule, + evaluator, + prevFrameOpt, + prevOuterFrameOpt + ) + + if (retState.errorOpt.isEmpty && depth == requestedDepth) { + // TODO: print some message and indicate actual evaluated frame + val evalRet = processFinalTargets(nestedState, rootModule, evaluator) + if (evalRet.errorOpt.isEmpty) retState + else evalRet + } else + retState + + } else { + processFinalTargets(nestedState, rootModule, evaluator) + } } } // println(s"-evaluateRec($depth) " + recRoot(projectRoot, depth)) @@ -194,7 +231,7 @@ class MillBuildBootstrap( Map.empty, None, Nil, - evaluator + Option(evaluator) ) nestedState.add(frame = evalState, errorOpt = Some(error)) @@ -244,7 +281,7 @@ class MillBuildBootstrap( methodCodeHashSignatures, Some(classLoader), runClasspath, - evaluator + Option(evaluator) ) nestedState.add(frame = evalState) @@ -262,8 +299,10 @@ class MillBuildBootstrap( evaluator: Evaluator ): RunnerState = { + assert(nestedState.frames.forall(_.evaluator.isDefined)) + val (evaled, evalWatched, moduleWatches) = Evaluator.allBootstrapEvaluators.withValue( - Evaluator.AllBootstrapEvaluators(Seq(evaluator) ++ nestedState.frames.map(_.evaluator)) + Evaluator.AllBootstrapEvaluators(Seq(evaluator) ++ nestedState.frames.flatMap(_.evaluator)) ) { evaluateWithWatches(rootModule, evaluator, targetsAndParams) } @@ -276,7 +315,7 @@ class MillBuildBootstrap( Map.empty, None, Nil, - evaluator + Option(evaluator) ) nestedState.add(frame = evalState, errorOpt = evaled.left.toOption) @@ -414,11 +453,11 @@ object MillBuildBootstrap { ) } - def recRoot(projectRoot: os.Path, depth: Int) = { + def recRoot(projectRoot: os.Path, depth: Int): os.Path = { projectRoot / Seq.fill(depth)("mill-build") } - def recOut(projectRoot: os.Path, depth: Int) = { + def recOut(projectRoot: os.Path, depth: Int): os.Path = { projectRoot / "out" / Seq.fill(depth)("mill-build") } } diff --git a/runner/src/mill/runner/MillCliConfig.scala b/runner/src/mill/runner/MillCliConfig.scala index b8d1b1a5a62..6d3206ffc2b 100644 --- a/runner/src/mill/runner/MillCliConfig.scala +++ b/runner/src/mill/runner/MillCliConfig.scala @@ -117,7 +117,15 @@ class MillCliConfig private ( code changes, and instead fall back to the previous coarse-grained implementation relying on the script `import $file` graph""" ) - val disableCallgraphInvalidation: Flag + val disableCallgraphInvalidation: Flag, + @arg( + name = "meta-level", + doc = + """Experimental: Select a meta-build level to run the given targets. + Level 0 is the normal project, level 1 the first meta-build, and so on. + The last level is the built-in synthetic meta-build which Mill uses to bootstrap the project.""" + ) + val metaLevel: Option[Int] ) { override def toString: String = Seq( "home" -> home, @@ -139,7 +147,8 @@ class MillCliConfig private ( "silent" -> silent, "leftoverArgs" -> leftoverArgs, "color" -> color, - "disableCallgraphInvalidation" -> disableCallgraphInvalidation + "disableCallgraphInvalidation" -> disableCallgraphInvalidation, + "metaLevel" -> metaLevel ).map(p => s"${p._1}=${p._2}").mkString(getClass().getSimpleName + "(", ",", ")") } @@ -148,8 +157,8 @@ object MillCliConfig { * mainargs requires us to keep this apply method in sync with the private ctr of the class. * mainargs is designed to work with case classes, * but case classes can't be evolved in a binary compatible fashion. - * mainargs parses the class ctr for itss internal model, - * but used the companion's apply to actually create an instance of the config class, + * mainargs parses the class ctr for its internal model, + * but uses the companion's apply to actually create an instance of the config class, * hence we need both in sync. */ def apply( @@ -173,7 +182,8 @@ object MillCliConfig { silent: Flag = Flag(), leftoverArgs: Leftover[String] = Leftover(), color: Option[Boolean] = None, - disableCallgraphInvalidation: Flag = Flag() + disableCallgraphInvalidation: Flag = Flag(), + metaLevel: Option[Int] = None ): MillCliConfig = new MillCliConfig( home = home, repl = repl, @@ -194,7 +204,8 @@ object MillCliConfig { silent = silent, leftoverArgs = leftoverArgs, color = color, - disableCallgraphInvalidation + disableCallgraphInvalidation, + metaLevel = metaLevel ) @deprecated("Bin-compat shim", "Mill after 0.11.0") diff --git a/runner/src/mill/runner/MillMain.scala b/runner/src/mill/runner/MillMain.scala index 4c2ff9578c8..c7873bd1c09 100644 --- a/runner/src/mill/runner/MillMain.scala +++ b/runner/src/mill/runner/MillMain.scala @@ -126,6 +126,11 @@ object MillMain { ) (false, RunnerState.empty) + // Check non-negative --meta-level option + case Right(config) if config.metaLevel.exists(_ < 0) => + streams.err.println("--meta-level cannot be negative") + (false, RunnerState.empty) + case Right(config) => val logger = getLogger( streams, @@ -204,7 +209,8 @@ object MillMain { prevRunnerState = prevState.getOrElse(stateCache), logger = logger, disableCallgraphInvalidation = config.disableCallgraphInvalidation.value, - needBuildSc = needBuildSc(config) + needBuildSc = needBuildSc(config), + requestedMetaLevel = config.metaLevel ).evaluate() } ) @@ -224,8 +230,9 @@ object MillMain { ) BspContext.bspServerHandle.stop() } - loopRes + // return with evaluation result + loopRes } } if (config.ringBell.value) { diff --git a/runner/src/mill/runner/RunnerState.scala b/runner/src/mill/runner/RunnerState.scala index 8a91535a021..1f76ab34647 100644 --- a/runner/src/mill/runner/RunnerState.scala +++ b/runner/src/mill/runner/RunnerState.scala @@ -62,10 +62,10 @@ object RunnerState { methodCodeHashSignatures: Map[String, Int], classLoaderOpt: Option[RunnerState.URLClassLoader], runClasspath: Seq[PathRef], - evaluator: Evaluator + evaluator: Option[Evaluator] ) { - def loggedData = { + def loggedData: Frame.Logged = { Frame.Logged( workerCache.map { case (k, (i, v)) => (k.render, Frame.WorkerInfo(System.identityHashCode(v), i))