Skip to content

Commit

Permalink
Make ./mill without any arguments point you towards --help, flesh…
Browse files Browse the repository at this point in the history
… out `--help` into a cheat sheet (#3556)

Fixes #3547

I added the cheat sheet for task syntax and compacted the flag
documentation so it all fits on one page in the default macbook pro 15"
terminal half screen at the default font size, with some room to spare
vertically and horizontally: ~55 characters tall and ~100 characters
wide. Could always squeeze it more or less, and the choice of target
size is arbitrary, but this should be both concise and information-dense
while still giving newbies a lot of useful tips and pointers. Notably,
having too much detail in any part of this blurb is a net negative: it
needs to be maximally concise while still containing the most useful
pieces of information. More detailed explanations and exposition can go
to the doc-site, which I linked at the bottom of the cheat sheet


I also split the `usageText` into `shortUsageText` which is printed on
`./mill`, and long usage text which is printed on `./mill --help`, with
the short usage text pointing you at `./mill --help` for more details:

```
lihaoyi mill$ ./mill
Please specify a task to evaluate

Usage: mill [options] task [task-options] [+ task ...]
Run `./mill --help` for more details
```
```
lihaoyi mill$ ./mill --help
Mill Build Tool, version 0.12.0-RC2-13-921d96-DIRTY16b6c61b
Usage: mill [options] [[task [task-options]] [+ [task ...]]]

task cheat sheet:
  mill resolve _                 # see all top-level tasks and modules
  mill resolve __.compile        # see all `compile` tasks in any module (recursively)

  mill foo.bar.compile           # compile the module `foo.bar`

  mill foo.run --arg 1           # run the main method of the module `foo` and pass in `--arg 1`
  mill -i foo.console            # run the Scala console for the module `foo` (if it is a ScalaModule)

  mill foo.__.test               # run tests in module within `foo` (recursively)
  mill foo.test arg1 arg2        # run tests in the `foo` module passing in test arguments `arg1 arg2`
  mill foo.test + bar.test       # run tests in the `foo` module and `bar` module
  mill '{foo,bar,qux}.test'      # run tests in the `foo` module, `bar` module, and `qux` module

  mill foo.assembly              # generate an executable assembly of the module `foo`
  mill show foo.assembly         # print the output path of the assembly of module `foo`
  mill inspect foo.assembly      # show docs and metadata for the `assembly` task on module `foo`

  mill clean foo.assembly        # delete the output of `foo.assembly` to force re-evaluation
  mill clean                     # delete the output of the entire build to force force re-evaluation

  mill path foo.run foo.sources  # print the task chain showing how `foo.run` depends on `foo.sources`
  mill visualize __.compile      # show how the `compile` tasks in each module depend on one another

options:
  -D --define <k=v>    Define (or overwrite) a system property.
  --allow-positional   Allows command args to be passed positionally without `--arg` by default
  -b --bell            Ring the bell once if the run completes successfully, twice if it fails.
  --bsp                Enable BSP server mode.
  --color <bool>       Toggle colored output; by default enabled only if the console is interactive
  -d --debug           Show debug output on STDOUT
  --disable-callgraph  Disables fine-grained invalidation of tasks based on analyzing code changes.
                       If passed, you need to manually run `clean` yourself after build changes.
  --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. Must be the first argument.
  --import <str>       Additional ivy dependencies to load into mill, e.g. plugins.
  -j --jobs <int>      The number of parallel threads. It can be an integer e.g. `5` meaning 5
                       threads, an expression e.g. `0.5C` meaning half as many threads as available
                       cores, or `C-2` meaning 2 threads less than the number of cores. `1` disables
                       parallelism and `0` (the default) uses 1 thread per core.
  -k --keep-going      Continue build, even after build failures.
  --meta-level <int>   Select a meta-level to run the given targets. Level 0 is the main project in
                       `build.mill`, level 1 the first meta-build in `mill-build/build.mill`, etc.
  --no-server          Run without a background server. Must be the first argument.
  -s --silent          Make ivy logs during script import resolution go silent instead of printing
  --ticker <bool>      Enable ticker log (e.g. short-lived prints of stages and progress bars).
  -v --version         Show mill version information and exit.
  -w --watch           Watch and re-run the given tasks when when their inputs change.
  target <str>...      The name or a pattern of the target(s) you want to build.

Please see the documentation at https://mill-build.org for more details
```

In the process I shortened the names and docs of a lot of the flags that
were IMO overly verbose. Especially for more advanced features like
`--meta-level` we can get away with a more terse explanation here since
the docs go into much more detail

As we do not guarantee bincompat for the `mill.runner` package, so we do
not need to add shims and forwarders as we evolve the `case class
MillCliConfig`

Notably this default blurb does not include any information about the
current build, and is very generic. I don't know what the best way of
inferring which modules and tasks are "important" and which are not, and
so for now I just punted on the issue and just try to show syntax and
tasks that should be useful for any Mill build. For now still somewhat
JVM specific with compile and assembly and test; if in the future we
start having more non-JVM stuff built using Mill we can make further
changes then
  • Loading branch information
lihaoyi authored Sep 16, 2024
1 parent b9b6433 commit 0c44ffa
Show file tree
Hide file tree
Showing 10 changed files with 228 additions and 360 deletions.
2 changes: 1 addition & 1 deletion .config/mill-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.12.0-RC2
0.12.0-RC2
100 changes: 56 additions & 44 deletions docs/modules/ROOT/partials/Intro_to_Mill_Footer.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -56,55 +56,67 @@ Tasks that depend on each other can't be processed in parallel.

== Command-line usage

Mill is a command-line tool and supports various options.

Run `mill --help` for a complete list of options
Mill is a command-line tool and supports various options. Run `mill --help` for
a complete list of options and a cheat-sheet of how to work with tasks:

.Output of `mill --help`
[source,subs="verbatim,attributes"]
----
Mill Build Tool, version {mill-version}
usage: mill [options] [[target [target-options]] [+ [target ...]]]
-D --define <k=v> 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 <bool> 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 <bool> Enable ticker log (e.g. short-lived prints of stages and
progress bars).
-h --home <path> (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 <str> Additional ivy dependencies to load into mill, e.g. plugins.
-j --jobs <int> 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 <int> 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.
-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 <str>... 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.
Usage: mill [options] task [task-options] [+ task ...]
task cheat sheet:
mill resolve _ # see all top-level tasks and modules
mill resolve __.compile # see all `compile` tasks in any module (recursively)
mill foo.bar.compile # compile the module `foo.bar`
mill foo.run --arg 1 # run the main method of the module `foo` and pass in `--arg 1`
mill -i foo.console # run the Scala console for the module `foo` (if it is a ScalaModule)
mill foo.__.test # run tests in modules nested within `foo` (recursively)
mill foo.test arg1 arg2 # run tests in the `foo` module passing in test arguments `arg1 arg2`
mill foo.test + bar.test # run tests in the `foo` module and `bar` module
mill '{foo,bar,qux}.test' # run tests in the `foo` module, `bar` module, and `qux` module
mill foo.assembly # generate an executable assembly of the module `foo`
mill show foo.assembly # print the output path of the assembly of module `foo`
mill inspect foo.assembly # show docs and metadata for the `assembly` task on module `foo`
mill clean foo.assembly # delete the output of `foo.assembly` to force re-evaluation
mill clean # delete the output of the entire build to force re-evaluation
mill path foo.run foo.sources # print the task chain showing how `foo.run` depends on `foo.sources`
mill visualize __.compile # show how the `compile` tasks in each module depend on one another
options:
-D --define <k=v> Define (or overwrite) a system property.
--allow-positional Allows command args to be passed positionally without `--arg` by default
-b --bell Ring the bell once if the run completes successfully, twice if it fails.
--bsp Enable BSP server mode.
--color <bool> Toggle colored output; by default enabled only if the console is interactive
-d --debug Show debug output on STDOUT
--disable-callgraph Disables fine-grained invalidation of tasks based on analyzing code changes.
If passed, you need to manually run `clean` yourself after build changes.
--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. Must be the first argument.
--import <str> Additional ivy dependencies to load into mill, e.g. plugins.
-j --jobs <str> The number of parallel threads. It can be an integer e.g. `5` meaning 5
threads, an expression e.g. `0.5C` meaning half as many threads as available
cores, or `C-2` meaning 2 threads less than the number of cores. `1` disables
parallelism and `0` (the default) uses 1 thread per core.
-k --keep-going Continue build, even after build failures.
--meta-level <int> Select a meta-level to run the given tasks. Level 0 is the main project in
`build.mill`, level 1 the first meta-build in `mill-build/build.mill`, etc.
--no-server Run without a background server. Must be the first argument.
-s --silent Make ivy logs during script import resolution go silent instead of printing
--ticker <bool> Enable ticker log (e.g. short-lived prints of stages and progress bars).
-v --version Show mill version information and exit.
-w --watch Watch and re-run the given tasks when when their inputs change.
task <str>... The name or a pattern of the tasks(s) you want to build.
Please see the documentation at https://mill-build.org for more details
----

All _options_ must be given before the first target.
2 changes: 1 addition & 1 deletion example/depth/tasks/2-primary-tasks/build.mill
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ Expected Signature: run
args <str>...
...

> ./mill --allow-positional-command-args run foo.Foo hello world # this succeeds due to --allow-positional-command-args
> ./mill --allow-positional run foo.Foo hello world # this succeeds due to --allow-positional
Foo.value: 31337
args: hello world
foo.txt resource: My Example Text
Expand Down
10 changes: 6 additions & 4 deletions integration/feature/scoverage/src/ScoverageTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import utest._

object ScoverageTests extends UtestIntegrationTestSuite {
val tests: Tests = Tests {
test("test") - integrationTest { tester =>
import tester._
assert(eval("__.compile").isSuccess)
assert(eval("core[2.13.11].scoverage.xmlReport").isSuccess)
test("test") - retry(3) {
integrationTest { tester =>
import tester._
assert(eval("__.compile").isSuccess)
assert(eval("core[2.13.11].scoverage.xmlReport").isSuccess)
}
}
}
}
2 changes: 1 addition & 1 deletion main/eval/src/mill/eval/EvaluatorImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ private[mill] case class EvaluatorImpl(
threadCount: Option[Int] = Some(1),
scriptImportGraph: Map[os.Path, (Int, Seq[os.Path])] = Map.empty,
methodCodeHashSignatures: Map[String, Int],
override val disableCallgraphInvalidation: Boolean,
override val disableCallgraph: Boolean,
override val allowPositionalCommandArgs: Boolean,
val systemExit: Int => Nothing
) extends Evaluator with EvaluatorCore {
Expand Down
105 changes: 53 additions & 52 deletions main/eval/src/mill/eval/GroupEvaluator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ private[mill] trait GroupEvaluator {
def threadCount: Option[Int]
def scriptImportGraph: Map[os.Path, (Int, Seq[os.Path])]
def methodCodeHashSignatures: Map[String, Int]
def disableCallgraphInvalidation: Boolean
def disableCallgraph: Boolean
def systemExit: Int => Nothing

lazy val constructorHashSignatures: Map[String, Seq[(String, Int)]] = methodCodeHashSignatures
Expand Down Expand Up @@ -64,59 +64,60 @@ private[mill] trait GroupEvaluator {

val sideHashes = MurmurHash3.orderedHash(group.iterator.map(_.sideHash))

val scriptsHash = group
.iterator
.collect {
case namedTask: NamedTask[_] =>
val encodedTaskName = encode(namedTask.ctx.segment.pathSegments.head)
val methodOpt = for {
parentCls <- classToTransitiveClasses(namedTask.ctx.enclosingCls).iterator
m <- allTransitiveClassMethods(parentCls).get(encodedTaskName)
} yield m

val methodClass = methodOpt
.nextOption()
.getOrElse(throw new MillException(
s"Could not detect the parent class of target ${namedTask}. " +
s"Please report this at ${BuildInfo.millReportNewIssueUrl} . " +
s"As a workaround, you can run Mill with `--disable-callgraph-invalidation` option."
))
.getDeclaringClass.getName

val name = namedTask.ctx.segment.pathSegments.last
val expectedName = methodClass + "#" + name + "()mill.define.Target"

// We not only need to look up the code hash of the Target method being called,
// but also the code hash of the constructors required to instantiate the Module
// that the Target is being called on. This can be done by walking up the nested
// modules and looking at their constructors (they're `object`s and should each
// have only one)
val allEnclosingModules = Vector.unfold(namedTask.ctx) {
case null => None
case ctx =>
ctx.enclosingModule match {
case null => None
case m: mill.define.Module => Some((m, m.millOuterCtx))
case unknown =>
throw new MillException(s"Unknown ctx of target ${namedTask}: $unknown")
}
}
val scriptsHash =
if (disableCallgraph) 0
else group
.iterator
.collect {
case namedTask: NamedTask[_] =>
val encodedTaskName = encode(namedTask.ctx.segment.pathSegments.head)
val methodOpt = for {
parentCls <- classToTransitiveClasses(namedTask.ctx.enclosingCls).iterator
m <- allTransitiveClassMethods(parentCls).get(encodedTaskName)
} yield m

val methodClass = methodOpt
.nextOption()
.getOrElse(throw new MillException(
s"Could not detect the parent class of target ${namedTask}. " +
s"Please report this at ${BuildInfo.millReportNewIssueUrl} . "
))
.getDeclaringClass.getName

val name = namedTask.ctx.segment.pathSegments.last
val expectedName = methodClass + "#" + name + "()mill.define.Target"

// We not only need to look up the code hash of the Target method being called,
// but also the code hash of the constructors required to instantiate the Module
// that the Target is being called on. This can be done by walking up the nested
// modules and looking at their constructors (they're `object`s and should each
// have only one)
val allEnclosingModules = Vector.unfold(namedTask.ctx) {
case null => None
case ctx =>
ctx.enclosingModule match {
case null => None
case m: mill.define.Module => Some((m, m.millOuterCtx))
case unknown =>
throw new MillException(s"Unknown ctx of target ${namedTask}: $unknown")
}
}

val constructorHashes = allEnclosingModules
.map(m =>
constructorHashSignatures.get(m.getClass.getName) match {
case Some(Seq((singleMethod, hash))) => hash
case Some(multiple) => throw new MillException(
s"Multiple constructors found for module $m: ${multiple.mkString(",")}"
)
case None => 0
}
)
val constructorHashes = allEnclosingModules
.map(m =>
constructorHashSignatures.get(m.getClass.getName) match {
case Some(Seq((singleMethod, hash))) => hash
case Some(multiple) => throw new MillException(
s"Multiple constructors found for module $m: ${multiple.mkString(",")}"
)
case None => 0
}
)

methodCodeHashSignatures.get(expectedName) ++ constructorHashes
}
.flatten
.sum
methodCodeHashSignatures.get(expectedName) ++ constructorHashes
}
.flatten
.sum

val inputsHash = externalInputsHash + sideHashes + classLoaderSigHash + scriptsHash

Expand Down
4 changes: 2 additions & 2 deletions runner/src/mill/runner/MillBuildBootstrap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class MillBuildBootstrap(
targetsAndParams: Seq[String],
prevRunnerState: RunnerState,
logger: ColorLogger,
disableCallgraphInvalidation: Boolean,
disableCallgraph: Boolean,
needBuildSc: Boolean,
requestedMetaLevel: Option[Int],
allowPositionalCommandArgs: Boolean,
Expand Down Expand Up @@ -351,7 +351,7 @@ class MillBuildBootstrap(
failFast = !keepGoing,
threadCount = threadCount,
methodCodeHashSignatures = methodCodeHashSignatures,
disableCallgraphInvalidation = disableCallgraphInvalidation,
disableCallgraph = disableCallgraph,
allowPositionalCommandArgs = allowPositionalCommandArgs,
systemExit = systemExit
)
Expand Down
Loading

0 comments on commit 0c44ffa

Please sign in to comment.