[Mill](https://github.com/lihaoyi/mill) is your shiny new Scala build tool!
[Scared of SBT](http://www.lihaoyi.com/post/SowhatswrongwithSBT.html)?
Melancholy over Maven? Grumbling about Gradle? Baffled by Bazel? Give Mill a
try!
Mill aims for simplicity by re-using concepts you are already
[familiar with](http://www.lihaoyi.com/post/BuildToolsasPureFunctionalPrograms.html),
borrowing ideas from modern tools like [Bazel](https://bazel.build/), to let you
build your projects in a way that's simple, fast, and predictable.
Mill has built in support for the [Scala](https://www.scala-lang.org/)
programming language, and can serve as a replacement for
[SBT](http://www.scala-sbt.org/), but can also be
[extended](http://www.lihaoyi.com/mill/page/extending-mill.html) to support any
other language or platform via modules (written in Java or Scala) or through
external subprocesses.
## Installation
### OS X
Installation via [homebrew](https://github.com/Homebrew/homebrew-core/blob/master/Formula/mill.rb):
```sh
brew install mill
```
### Arch Linux
Arch Linux has an [AUR package for mill](https://aur.archlinux.org/packages/mill/):
```bash
pacaur -S mill
```
### Manual
To get started, download Mill and install it into your system via the following
`curl`/`chmod` command:
```bash
sudo curl -L -o /usr/local/bin/mill https://github.com/lihaoyi/mill/releases/download/0.1.7/0.1.7 && sudo chmod +x /usr/local/bin/mill
```
### Development Releases
More recent, unstable versions of Mill are also
[available](https://github.com/lihaoyi/mill/releases/tag/unstable), if you want
to try out the latest features and improvements that are currently in master.
Come by our [Gitter Channel](https://gitter.im/lihaoyi/mill) if you want to ask
questions or say hi!
## Getting Started
The simplest Mill build for a Scala project looks as follows:
```scala
// build.sc
import mill._
import mill.scalalib._
object foo extends ScalaModule {
def scalaVersion = "2.12.4"
}
```
This would build a project laid out as follows:
```
build.sc
foo/
src/
Main.scala
resources/
...
out/
foo/
...
```
You can download an example project with this layout here:
- [Example 1](example-1.zip)
The source code for this module would live in the `foo/src/` folder, matching
the name you assigned to the module. Output for this module (compiled files,
resolved dependency lists, ...) would live in `out/foo/`.
This can be run from the Bash shell via:
```bash
$ mill foo.compile # compile sources into classfiles
$ mill foo.run # run the main method, if any
$ mill foo.launcher # prepares a foo/launcher/dest/run you can run later
$ mill foo.jar # bundle the classfiles into a jar
$ mill foo.assembly # bundle classfiles and all dependencies into a jar
$ mill -i foo.console # start a Scala console within your project (in interactive mode: "-i")
$ mill -i foo.repl # start an Ammonite REPL within your project (in interactive mode: "-i")
```
You can run `mill resolve __` to see a full list of the different tasks that are
available, `mill resolve foo._` to see the tasks within `foo`, `mill inspect
foo.compile` to see what an individual task depends on, or `mill show
foo.scalaVersion` to inspect 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, where-as commands re-run every
time.
## Output
Mill puts all it's output in the top-level `out/` folder. The above commands
would end up in:
```text
out/
foo/
compile/
run/
jar/
assembly/
```
Within the output folder for each task, there's a `meta.json` file containing
the metadata returned by that task, and a `dest/` folder containing any files
that the task generates. For example, `out/foo/compile/dest/` contains the
compiled classfiles, while `out/foo/assembly/dest/` contains the self-contained
assembly with the project's classfiles jar-ed up with all it's dependencies.
Given a task `foo.bar`, all it's output and results can be found be within it's
respective `out/foo/bar/` folder.
## Multiple Modules
```scala
// build.sc
import mill._
import mill.scalalib._
object foo extends ScalaModule {
def scalaVersion = "2.12.4"
}
object bar extends ScalaModule {
def moduleDeps = Seq(foo)
def scalaVersion = "2.12.4"
}
```
You can define multiple modules the same way you define a single module, using
`def moduleDeps` to define the relationship between them. The above build
expects the following project layout:
```
build.sc
foo/
src/
Main.scala
resources/
...
bar/
src/
Main2.scala
resources/
...
out/
foo/
...
bar/
...
```
And can be built/run using:
```bash
$ mill foo.compile
$ mill bar.compile
$ mill foo.run
$ mill bar.run
$ mill foo.jar
$ mill bar.jar
$ mill foo.assembly
$ mill bar.assembly
```
Mill's evaluator will ensure that the modules are compiled in the right order,
and re-compiled as necessary when source code in each module changes.
Modules can also be nested:
```scala
// build.sc
import mill._
import mill.scalalib._
object foo extends ScalaModule {
def scalaVersion = "2.12.4"
object bar extends ScalaModule {
def moduleDeps = Seq(foo)
def scalaVersion = "2.12.4"
}
}
```
Which would result in a similarly nested project layout:
```
build.sc
foo/
src/
Main.scala
resources/
...
bar/
src/
Main2.scala
resources/
...
out/
foo/
...
bar/
...
```
Where the nested modules can be run via:
```bash
$ mill foo.compile
$ mill foo.bar.compile
$ mill foo.run
$ mill foo.bar.run
$ mill foo.jar
$ mill foo.bar.jar
$ mill foo.assembly
$ mill foo.bar.assembly
```
## Watch and Re-evaluate
You can use the `--watch` flag to make Mill watch a task's inputs, re-evaluating
the task as necessary when the inputs change:
```bash
$ mill --watch foo.compile
$ mill --watch foo.run
```
Mill's `--watch` flag watches both the files you are building using Mill, as
well as Mill's own `build.sc` file and anything it imports, so any changes to
your `build.sc` will automatically get picked up.
## Command-line Tools
Mill comes built in with a small number of useful command-line utilities:
### all
```bash
mill all foo.{compile,run}
mill all "foo.{compile,run}"
mill all foo.compile foo.run
mill all _.compile # run compile for every top-level module
mill all __.compile # run compile for every module
```
`all` runs multiple tasks in a single command
### resolve
```bash
$ mill resolve _
main
moduledefs
core
scalaworker
scalalib
scalajslib
integration
testRepos
...
$ mill resolve _.compile
main.compile
moduledefs.compile
core.compile
scalaworker.compile
scalalib.compile
scalajslib.compile
integration.compile
$ mill resolve core._
core.test
core.compile
core.publishVersion
core.runClasspath
core.testArgs
core.sources
...
```
`resolve` lists the tasks that match a particular query, without running them.
This is useful for "dry running" an `mill all` command to see what would be run
before you run them, or to explore what modules or tasks are available from the
command line using `resolve _`, `resolve foo._`, etc.
```bash
mill resolve foo.{compile,run}
mill resolve "foo.{compile,run}"
mill resolve foo.compile foo.run
mill resolve _.compile # list the compile tasks for every top-level module
mill resolve __.compile # list the compile tasks for every module
mill resolve _ # list every top level module or task
mill resolve foo._ # list every task directly within the foo module
mill resolve __ # list every module or task recursively
mill resolve foo.__ # list every task recursively within the foo module
```
### inspect
```bash
$ mill inspect core.run
core.run(ScalaModule.scala:211)
Inputs:
core.mainClass
core.runClasspath
core.forkArgs
core.forkEnv
```
`inspect` is a more verbose version of [resolve](#resolve). In addition to
printing out the name of one-or-more tasks, it also display's it's source
location and a list of input tasks. This is very useful for debugging and
interactively exploring the structure of your build from the command line.
`inspect` also works with the same `_`/`__` wildcard/query syntaxes that
[all](#all)/[resolve](#resolve) do:
```bash
mill inspect foo.compile
mill inspect foo.{compile,run}
mill inspect "foo.{compile,run}"
mill inspect foo.compile foo.run
mill inspect _.compile
mill inspect __.compile
mill inspect _
mill inspect foo._
mill inspect __
mill inspect foo._
```
### show
```bash
$ mill show core.scalaVersion
"2.12.4"
```
By default, Mill does not print out the metadata from evaluating a task. Most
people would not be interested in e.g. viewing the metadata related to
incremental compilation: they just want to compile their code! However, if you
want to inspect the build to debug problems, you can make Mill show you the
metadata output for a task using the `show` command:
All tasks return values that can be `show`n, not just configuration values. e.g.
`compile` returns that path to the `classes` and `analysisFile` that are
produced by the compilation:
```bash
$ mill show foo.compile
{
"analysisFile": "/Users/lihaoyi/Dropbox/Github/test/out/foo/compile/dest/zinc",
"classes": {
"path": "/Users/lihaoyi/Dropbox/Github/test/out/foo/compile/dest/classes"
}
}
```
`show` is generally useful as a debugging tool, to see what is going on in your
build:
```bash
$ mill show foo.sources
[
{"path": "/Users/lihaoyi/Dropbox/Github/test/foo/src"}
]
$ mill show foo.compileDepClasspath
[
{"path": ".../org/scala-lang/scala-compiler/2.12.4/scala-compiler-2.12.4.jar"},
{"path": ".../org/scala-lang/scala-library/2.12.4/scala-library-2.12.4.jar"},
{"path": ".../org/scala-lang/scala-reflect/2.12.4/scala-reflect-2.12.4.jar"},
{"path": ".../org/scala-lang/modules/scala-xml_2.12/1.0.6/scala-xml_2.12-1.0.6.jar"}
]
```
`show` is also useful for interacting with Mill from external tools, since the
JSON it outputs is structured and easily parsed & manipulated.
## IntelliJ Support
Mill supports IntelliJ by default. Use `mill mill.scalalib.GenIdeaModule/idea` to
generate an IntelliJ project config for your build.
This also configures IntelliJ to allow easy navigate & code-completion within
your build file itself.
## The Build Repl
```bash
$ mill -i
Loading...
@ foo
res1: foo.type = ammonite.predef.build#foo:2
Commands:
.runLocal(args: String*)()
.run(args: String*)()
.runMainLocal(mainClass: String, args: String*)()
.runMain(mainClass: String, args: String*)()
.console()()
Targets:
.allSources()
.artifactId()
.artifactName()
...
@ foo.compile
res3: mill.package.T[mill.scalalib.CompilationResult] = mill.scalalib.ScalaModule#compile:152
Inputs:
foo.scalaVersion
foo.allSources
foo.compileDepClasspath
...
@ foo.compile()
res2: mill.scalalib.CompilationResult = CompilationResult(
root/'Users/'lihaoyi/'Dropbox/'Github/'test/'out/'foo/'compile/'dest/'zinc,
PathRef(root/'Users/'lihaoyi/'Dropbox/'Github/'test/'out/'foo/'compile/'dest/'classes, false)
)
```
You can run `mill -i` to open a build REPL; this is a Scala console with your
`build.sc` loaded, which lets you run tasks interactively. The task-running
syntax is slightly different from the command-line, but more in-line with how
you would depend on tasks from within your build file.
You can use this REPL to interactively explore your build to see what is available.
## Deploying your code
The two most common things to do once your code is complete is to make an
assembly (e.g. for deployment/installation) or publishing (e.g. to Maven
Central). Mill comes with both capabilities built in.
Mill comes built-in with the ability to make assemblies. Given a simple Mill
build:
```scala
// build.sc
import mill._, scalalib._
object foo extends ScalaModule{
def scalaVersion = "2.12.4"
}
```
You can make a self-contained assembly via:
```bash
$ mill foo.assembly
$ ls -lh out/foo/assembly/dest/out.jar
-rw-r--r-- 1 lihaoyi staff 5.0M Feb 17 11:14 out/foo/assembly/dest/out.jar
```
You can then move the `out.jar` file anywhere you would like, and run it
standalone using `java`:
```bash
$ java -cp out/foo/assembly/dest/out.jar foo.Example
Hello World!
```
To publish to Maven Central, you need to make `foo` extend Mill's
`PublishModule` trait:
```scala
// build.sc
import mill._, scalalib._, publish._
object foo extends PublishModule{
def scalaVersion = "2.12.4"
def publishVersion = "0.0.1"
def pomSettings = PomSettings(
description = "Hello",
organization = "com.lihaoyi",
url = "https://github.com/lihaoyi/example",
licenses = Seq(License.MIT),
versionControl = VersionControl.github("lihaoyi", "example"),
developers = Seq(
Developer("lihaoyi", "Li Haoyi","https://github.com/lihaoyi")
)
)
}
```
You can download an example project with this layout here:
- [Example 2](example-2.zip)
Which you can then publish using the `mill foo.publish` command, which takes
your sonatype credentials (e.g. `lihaoyi:foobarbaz`) and GPG password as inputs:
```bash
$ mill foo.publish
Missing arguments: (--sonatypeCreds: String, --gpgPassphrase: String, --release: Boolean)
Arguments provided did not match expected signature:
publish
--sonatypeCreds String (format: "username:password")
--gpgPassphrase String
--release Boolean
```
You also need to specify `release` as `true` or `false`, depending on whether
you just want to stage your module on `oss.sonatype.org` or you want Mill to
complete the release process to Maven Central.
If you are publishing multiple artifacts, you can also use `mill mill.scalalib.PublishModule/publishAll` as described
[here](http://www.lihaoyi.com/mill/page/common-project-layouts.html#publishing)