Skip to content

Commit c0823da

Browse files
authored
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: #2719
1 parent a2a8c86 commit c0823da

File tree

10 files changed

+240
-90
lines changed

10 files changed

+240
-90
lines changed

build.sc

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ import $file.ci.shared
44
import $file.ci.upload
55
import $ivy.`org.scalaj::scalaj-http:2.4.2`
66
import $ivy.`de.tototec::de.tobiasroeser.mill.vcs.version::0.4.0`
7-
87
import $ivy.`com.github.lolgab::mill-mima::0.0.23`
98
import $ivy.`net.sourceforge.htmlcleaner:htmlcleaner:2.25`
9+
import mill.define.NamedTask
10+
import mill.main.Tasks
1011

1112
// imports
1213
import com.github.lolgab.mill.mima.{CheckDirection, ProblemFilter, Mima}
@@ -369,7 +370,7 @@ trait MillStableScalaModule extends MillPublishScalaModule with Mima {
369370
ProblemFilter.exclude[Problem]("mill.eval.ProfileLogger*"),
370371
ProblemFilter.exclude[Problem]("mill.eval.GroupEvaluator*"),
371372
ProblemFilter.exclude[Problem]("mill.eval.Tarjans*"),
372-
ProblemFilter.exclude[Problem]("mill.define.Ctx#Impl*"),
373+
ProblemFilter.exclude[Problem]("mill.define.Ctx#Impl*")
373374
)
374375
def mimaPreviousVersions: T[Seq[String]] = Settings.mimaBaseVersions
375376

@@ -486,33 +487,34 @@ object main extends MillStableScalaModule with BuildInfo {
486487
}
487488

488489
object codesig extends MillPublishScalaModule {
489-
override def ivyDeps = Agg(ivy"org.ow2.asm:asm-tree:9.5", Deps.osLib, ivy"com.lihaoyi::pprint:0.8.1")
490+
override def ivyDeps =
491+
Agg(ivy"org.ow2.asm:asm-tree:9.5", Deps.osLib, ivy"com.lihaoyi::pprint:0.8.1")
490492
def moduleDeps = Seq(util)
491493

492-
override lazy val test: CodeSigTests = new CodeSigTests{}
494+
override lazy val test: CodeSigTests = new CodeSigTests {}
493495
trait CodeSigTests extends MillScalaTests {
494496
val caseKeys = interp.watchValue(
495497
os.walk(millSourcePath / "cases", maxDepth = 3)
496498
.map(_.subRelativeTo(millSourcePath / "cases").segments)
497-
.collect{case Seq(a, b, c) => s"$a-$b-$c"}
499+
.collect { case Seq(a, b, c) => s"$a-$b-$c" }
498500
)
499501

500-
def testLogFolder = T{ T.dest }
502+
def testLogFolder = T { T.dest }
501503

502504
def caseEnvs[V](f1: CaseModule => Task[V])(s: String, f2: V => String) = {
503505
T.traverse(caseKeys) { i => f1(cases(i)).map(v => s"MILL_TEST_${s}_$i" -> f2(v)) }
504506
}
505-
def forkEnv = T{
507+
def forkEnv = T {
506508
Map("MILL_TEST_LOGS" -> testLogFolder().toString) ++
507-
caseEnvs(_.compile)("CLASSES", _.classes.path.toString)() ++
508-
caseEnvs(_.compileClasspath)("CLASSPATH", _.map(_.path).mkString(","))() ++
509-
caseEnvs(_.sources)("SOURCES", _.head.path.toString)()
509+
caseEnvs(_.compile)("CLASSES", _.classes.path.toString)() ++
510+
caseEnvs(_.compileClasspath)("CLASSPATH", _.map(_.path).mkString(","))() ++
511+
caseEnvs(_.sources)("SOURCES", _.head.path.toString)()
510512
}
511513

512514
object cases extends Cross[CaseModule](caseKeys)
513-
trait CaseModule extends ScalaModule with Cross.Module[String]{
515+
trait CaseModule extends ScalaModule with Cross.Module[String] {
514516
def caseName = crossValue
515-
object external extends ScalaModule{
517+
object external extends ScalaModule {
516518
def scalaVersion = "2.13.10"
517519
}
518520

@@ -521,7 +523,7 @@ object main extends MillStableScalaModule with BuildInfo {
521523
val Array(prefix, suffix, rest) = caseName.split("-", 3)
522524
def millSourcePath = super.millSourcePath / prefix / suffix / rest
523525
def scalaVersion = "2.13.10"
524-
def ivyDeps = T{
526+
def ivyDeps = T {
525527
if (!caseName.contains("realistic") && !caseName.contains("sourcecode")) super.ivyDeps()
526528
else Agg(
527529
Deps.fastparse,
@@ -531,7 +533,7 @@ object main extends MillStableScalaModule with BuildInfo {
531533
Deps.mainargs,
532534
Deps.requests,
533535
Deps.osLib,
534-
Deps.upickle,
536+
Deps.upickle
535537
)
536538
}
537539
}
@@ -1454,7 +1456,7 @@ object docs extends Module {
14541456
val pagesWd = T.dest / "modules" / "ROOT" / "pages"
14551457
val partialsWd = T.dest / "modules" / "ROOT" / "partials"
14561458

1457-
os.copy(projectReadme().path, partialsWd / "project-readme.adoc", createFolders = true)
1459+
os.copy(projectReadme().path, partialsWd / "project-readme.adoc", createFolders = true)
14581460

14591461
val renderedExamples: Seq[(os.SubPath, PathRef)] =
14601462
T.traverse(example.exampleModules)(m =>
@@ -1763,23 +1765,24 @@ def uploadToGithub(authKey: String) = T.command {
17631765
}
17641766
}
17651767

1766-
def validate(ev: Evaluator): Command[Unit] = T.command {
1767-
mill.main.RunScript.evaluateTasksNamed(
1768-
ev.withFailFast(false),
1769-
Seq(
1770-
"__.compile",
1771-
"+",
1772-
"__.mimaReportBinaryIssues",
1773-
"+",
1774-
"mill.scalalib.scalafmt.ScalafmtModule/checkFormatAll",
1775-
"__.sources",
1776-
"+",
1777-
"docs.localPages"
1778-
),
1779-
selectMode = SelectMode.Separated
1780-
)
1768+
private def resolveTasks[T](taskNames: String*): Seq[NamedTask[T]] = {
1769+
mill.resolve.Resolve.Tasks.resolve(
1770+
build,
1771+
taskNames,
1772+
SelectMode.Separated
1773+
).map(x => x.asInstanceOf[Seq[mill.define.NamedTask[T]]]).getOrElse(???)
1774+
}
1775+
1776+
def validate(): Command[Unit] = {
1777+
val tasks = resolveTasks("__.compile", "__.minaReportBinaryIssues")
1778+
val sources = resolveTasks("__.sources")
17811779

1782-
()
1780+
T.command {
1781+
T.sequence(tasks)()
1782+
mill.scalalib.scalafmt.ScalafmtModule.checkFormatAll(Tasks(sources))()
1783+
docs.localPages()
1784+
()
1785+
}
17831786
}
17841787

17851788
/** Dummy module to let Scala-Steward find and bump dependency versions we use at runtime */

docs/modules/ROOT/pages/Extending_Mill.adoc

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,46 @@ include::example/misc/3-import-file-ivy.adoc[]
1111

1212
== The Mill Meta-Build
1313

14+
The meta-build manages the compilation of the `build.sc`.
15+
If you don't configure it explicitly, a built-in synthetic meta-build is used.
16+
17+
To customize it, you need to explicitly enable it with `import $meta._`.
18+
Once enabled, the meta-build lives in the `mill-build/` directory.
19+
It needs to contain a top-level module of type `MillBuildRootModule`.
20+
21+
Meta-builds are recursive, which means, it can itself have a nested meta-builds, and so on.
22+
23+
To run a task on a meta-build, you specifying the `--meta-level` option to select the meta-build level.
24+
25+
=== Example: Format the `build.sc`
26+
27+
As an example of running a task on the meta-build, you can format the `build.sc` with Scalafmt.
28+
Everything is already provided by Mill.
29+
You only need a `.scalafmt.conf` config file which at least needs configure the Scalafmt version.
30+
31+
.Run Scalafmt on the `build.sc` (and potentially included files)
32+
----
33+
$ mill --meta-level 1 mill.scalalib.scalafmt.ScalafmtModule/reformatAll sources
34+
----
35+
36+
* `--meta-level 1` selects the first meta-build. Without any customization, this is the only built-in meta-build.
37+
* `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
38+
* `sources` this selects the `sources` targets of the meta-build, which at least contains the `build.sc`.
39+
40+
=== Example: Find plugin updates
41+
42+
Mill plugins are defined as `ivyDeps` in the meta-build.
43+
Hence, you can easily search for updates with the external `mill.scalalib.Dependency` module.
44+
45+
.Check for Mill Plugin updates
46+
----
47+
$ mill --meta-level 1 mill.scalalib.Dependency/showUpdates
48+
Found 1 dependency update for
49+
de.tototec:de.tobiasroeser.mill.vcs.version_mill0.11_2.13 : 0.3.1-> 0.4.0
50+
----
51+
52+
=== Example: Customizing the Meta-Build
53+
1454
include::example/misc/4-mill-build-folder.adoc[]
1555

1656
== Using ScalaModule.run as a task

docs/modules/ROOT/pages/Intro_to_Mill.adoc

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -124,35 +124,48 @@ Run `mill --help` for a complete list of options
124124
----
125125
Mill Build Tool, version {mill-version}
126126
usage: mill [options] [[target [target-options]] [+ [target ...]]]
127-
-D --define <k=v> Define (or overwrite) a system property.
128-
-b --bell Ring the bell once if the run completes successfully, twice if it fails.
129-
--bsp Enable BSP server mode.
130-
--color <bool> Enable or disable colored output; by default colors are enabled in both
131-
REPL and scripts mode if the console is interactive, and disabled
132-
otherwise.
133-
-d --debug Show debug output on STDOUT
134-
--disable-ticker Disable ticker log (e.g. short-lived prints of stages and progress bars).
135-
--enable-ticker <bool> Enable ticker log (e.g. short-lived prints of stages and progress bars).
136-
-h --home <path> (internal) The home directory of internally used Ammonite script engine;
137-
where it looks for config and caches.
138-
--help Print this help message and exit.
139-
-i --interactive Run Mill in interactive mode, suitable for opening REPLs and taking user
140-
input. This implies --no-server and no mill server will be used. Must be
141-
the first argument.
142-
--import <str> Additional ivy dependencies to load into mill, e.g. plugins.
143-
-j --jobs <int> Allow processing N targets in parallel. Use 1 to disable parallel and 0 to
144-
use as much threads as available processors.
145-
-k --keep-going Continue build, even after build failures.
146-
--no-server Run Mill in single-process mode. In this mode, no Mill server will be
147-
started or used. Must be the first argument.
148-
--repl This flag is no longer supported.
149-
-s --silent Make ivy logs during script import resolution go silent instead of
150-
printing; though failures will still throw exception.
151-
-v --version Show mill version information and exit.
152-
-w --watch Watch and re-run your scripts when they change.
153-
target <str>... The name or a pattern of the target(s) you want to build, followed by any
154-
parameters you wish to pass to those targets. To specify multiple target
155-
names or patterns, use the `+` separator.
127+
-D --define <k=v> Define (or overwrite) a system property.
128+
-b --bell Ring the bell once if the run completes successfully, twice if
129+
it fails.
130+
--bsp Enable BSP server mode.
131+
--color <bool> Enable or disable colored output; by default colors are enabled
132+
in both REPL and scripts mode if the console is interactive, and
133+
disabled otherwise.
134+
-d --debug Show debug output on STDOUT
135+
--disable-callgraph-invalidation Disable the fine-grained callgraph-based target invalidation in
136+
response to code changes, and instead fall back to the previous
137+
coarse-grained implementation relying on the script `import
138+
$file` graph
139+
--disable-ticker Disable ticker log (e.g. short-lived prints of stages and
140+
progress bars).
141+
--enable-ticker <bool> Enable ticker log (e.g. short-lived prints of stages and
142+
progress bars).
143+
-h --home <path> (internal) The home directory of internally used Ammonite script
144+
engine; where it looks for config and caches.
145+
--help Print this help message and exit.
146+
-i --interactive Run Mill in interactive mode, suitable for opening REPLs and
147+
taking user input. This implies --no-server and no mill server
148+
will be used. Must be the first argument.
149+
--import <str> Additional ivy dependencies to load into mill, e.g. plugins.
150+
-j --jobs <int> Allow processing N targets in parallel. Use 1 to disable
151+
parallel and 0 to use as much threads as available processors.
152+
-k --keep-going Continue build, even after build failures.
153+
--meta-level <int> Experimental: Select a meta-build level to run the given
154+
targets. Level 0 is the normal project, level 1 the first
155+
meta-build, and so on. The last level is the built-in synthetic
156+
meta-build which Mill uses to bootstrap the project.
157+
--no-server Run Mill in single-process mode. In this mode, no Mill server
158+
will be started or used. Must be the first argument.
159+
--repl This flag is no longer supported.
160+
-s --silent Make ivy logs during script import resolution go silent instead
161+
of printing; though failures will still throw exception.
162+
-v --version Show mill version information and exit.
163+
-w --watch Watch and re-run your scripts when they change.
164+
target <str>... The name or a pattern of the target(s) you want to build,
165+
followed by any parameters you wish to pass to those targets. To
166+
specify multiple target names or patterns, use the `+`
167+
separator.
168+
156169
----
157170

158171
All _options_ must be given before the first target.

example/misc/4-mill-build-folder/build.sc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,19 @@ scalatagsVersion: 0.8.2
6363
6464
*/
6565

66+
// You can also run tasks on the meta-build by using the `--meta-level`
67+
// cli option.
68+
69+
/** Usage
70+
71+
> ./mill --meta-level 1 show sources
72+
[
73+
.../build.sc",
74+
.../mill-build/src"
75+
]
76+
77+
> ./mill --meta-level 2 show sources
78+
.../mill-build/build.sc"
79+
80+
81+
*/

example/misc/5-module-run-task/build.sc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ object bar extends ScalaModule{
1717
def ivyDeps = Agg(ivy"com.lihaoyi::os-lib:0.9.1")
1818
}
1919

20-
// This example demonstrates using Mill `ScalaModule`s as build tasks: rather
20+
// This example demonstrates using Mill ``ScalaModule``s as build tasks: rather
2121
// than defining the task logic in the `build.sc`, we instead put the build
2222
// logic within the `bar` module as `bar/src/Bar.scala`. In this example, we use
2323
// `Bar.scala` as a source-code pre-processor on the `foo` module source code:
@@ -34,7 +34,7 @@ Foo.value: HELLO
3434
*/
3535

3636
// This example does a trivial string-replace of "hello" with "HELLO", but is
37-
// enough to demonstrate how you can use Mill `ScalaModule`s to implement your
37+
// enough to demonstrate how you can use Mill ``ScalaModule``s to implement your
3838
// own arbitrarily complex transformations. This is useful for build logic that
3939
// may not fit nicely inside a `build.sc` file, whether due to the sheer lines
4040
// of code or due to dependencies that may conflict with the Mill classpath
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package mill.runner
2+
3+
import mill.T
4+
import mill.define.{Command, Discover, ExternalModule, Module}
5+
import mill.eval.Evaluator.AllBootstrapEvaluators
6+
7+
trait MillBuild extends Module {
8+
9+
/**
10+
* Count of the nested build-levels, the main project and all its nested meta-builds.
11+
* If you run this on a meta-build, the non-meta-builds are not included.
12+
*/
13+
def levelCount(evaluators: AllBootstrapEvaluators): Command[Int] = T.command {
14+
evaluators.value.size
15+
}
16+
17+
}
18+
19+
object MillBuild extends ExternalModule with MillBuild {
20+
override lazy val millDiscover = Discover[this.type]
21+
}

0 commit comments

Comments
 (0)