Your shiny new Scala build tool! Confused by SBT? Frustrated by Maven? Perplexed by Gradle? Give Mill a try!
If you want to use Mill in your own projects, check out our documentation:
The remainder of this readme is targeted at people who wish to work on Mill's own codebase.
Mill is built using Mill. To begin, first download & install Mill as described in the documentation above.
To run unit test suite:
mill main.test
Build a standalone executable jar:
mill dev.assembly
Now you can re-build this very same project using the build.sc file, e.g. re-run core unit tests
e.g.:
./out/dev/assembly/dest/mill core.compile
./out/dev/assembly/dest/mill main.test.compile
./out/dev/assembly/dest/mill main.test
./out/dev/assembly/dest/mill scalalib.assembly
There is already a watch
option that looks for changes on files, e.g.:
./out/dev/assembly/dest/mill --watch core.compile
You can get Mill to show the JSON-structured output for a particular Target
or
Command
using the show
flag:
./out/dev/assembly/dest/mill show core.scalaVersion
./out/dev/assembly/dest/mill show core.compile
./out/dev/assembly/dest/mill show core.assemblyClasspath
./out/dev/assembly/dest/mill show main.test
Output will be generated into a the ./out
folder.
If you are repeatedly testing Mill manually by running it against the build.sc
file in the repository root, you can skip the assembly process and directly run
it via:
mill --watch dev.run . main.test
mill --watch dev.run .
You can also test out your current Mill code with one of the hello-world example repos via:
mill dev.run docs/example-1 foo.run
Lastly, you can generate IntelliJ Scala project files using Mill via
./out/dev/assembly/dest/mill mill.scalalib.GenIdeaModule/idea
Allowing you to import a Mill project into Intellij without using SBT
There is a number of ways to run targets and commands via command line:
- Run single target:
mill core.compile
- Run single command with arguments:
mill bridges[2.12.4].publish --credentials foo --gpgPassphrase bar
- Run multiple targets:
mill all main.test scalalib.test
Note: don't forget to put all
flag when you run multiple commands, otherwise the only first command will be run, and subsequent commands will be passed as arguments to the first one.
- Run multiple commands with arguments:
mill all bridges[2.11.11].publish bridges[2.12.4].publish -- --credentials foo --gpgPassphrase bar
Here --credentials foo --gpgPassphrase bar
arguments will be passed to both bridges[2.11.11].publish
and bridges[2.12.4].publish
command.
Note: arguments list should be separated with --
from command list.
Sometimes it is tedious to write multiple targets when you want to run same target in multiple modules, or multiple targets in one module. Here brace expansion from bash(or another shell that support brace expansion) comes to rescue. It allows you to make some "shortcuts" for multiple commands.
- Run same targets in multiple modules with brace expansion:
mill all {core,scalalib,scalajslib,integration}.test
will run test
target in core
, scalalib
, scalajslib
and integration
modules.
- Run multiple targets in one module with brace expansion:
mill all scalalib.{compile,test}
will run compile
and test
targets in scalalib
module.
- Run multiple targets in multiple modules:
mill all {core,scalalib}.{scalaVersion,scalaBinaryVersion}
will run scalaVersion
and scalaBinaryVersion
targets in both core
and scalalib
modules.
- Run targets in different cross build modules
mill all bridges[{2.11.11,2.12.4}].publish -- --credentials foo --gpgPassphrase bar
will run publish
command in both brides[2.11.11]
and bridges[2.12.4]
modules
You can also use the _
wildcard and __
recursive-wildcard to run groups of
tasks:
# Run the `test` command of all top-level modules
mill all _.test
# Run the `test` command of all modules, top-level or nested
mill all __.test
# Run `compile` in every cross-module of `bridges`
mill all bridges[_].compile
Note: When you run multiple targets with all
command, they are not
guaranteed to run in that exact order. Mill will build task evaluation graph and
run targets in correct order.
Mill provides a build REPL, which lets you explore the build interactively and
run Target
s from Scala code:
$ mill -i
Loading...
Compiling (synthetic)/ammonite/predef/interpBridge.sc
Compiling (synthetic)/ammonite/predef/replBridge.sc
Compiling (synthetic)/ammonite/predef/DefaultPredef.sc
Compiling /Users/lihaoyi/Dropbox/Workspace/mill/build.sc
Compiling /Users/lihaoyi/Dropbox/Workspace/mill/out/run.sc
Compiling (synthetic)/ammonite/predef/CodePredef.sc
@ build
res0: build.type = build
@ build.
!= core scalalib bridges getClass isInstanceOf |>
== MillModule asInstanceOf equals hashCode toString
@ build.core
res1: core = ammonite.predef.build#core:45
Children:
.test
Commands:
.console()()
.run(args: String*)()
.runMain(mainClass: String, args: String*)()
Targets:
.allSources()
.artifactId()
.artifactName()
.artifactScalaVersion()
.assembly()
.assemblyClasspath()
.classpath()
.compile()
.compileDepClasspath()
.compileIvyDeps()
.compilerBridge()
.crossFullScalaVersion()
.depClasspath()
.docJar()
.externalCompileDepClasspath()
.externalCompileDepSources()
...
@ core
res2: core.type = ammonite.predef.build#core:45
Children:
.test
Commands:
.console()()
.run(args: String*)()
.runMain(mainClass: String, args: String*)()
Targets:
.allSources()
.artifactId()
.artifactName()
.artifactScalaVersion()
.assembly()
.assemblyClasspath()
.classpath()
.compile()
.compileDepClasspath()
.compileIvyDeps()
.compilerBridge()
.crossFullScalaVersion()
.depClasspath()
.docJar()
.externalCompileDepClasspath()
.externalCompileDepSources()
...
@ core.scalaV
scalaVersion
@ core.scalaVersion
res3: mill.define.Target[String] = ammonite.predef.build#MillModule#scalaVersion:20
Inputs:
@ core.scalaVersion()
[1/1] core.scalaVersion
res4: String = "2.12.4"
@ core.ivyDeps()
Running core.ivyDeps
[1/1] core.ivyDeps
res5: Seq[mill.scalalib.Dep] = List(
Scala(Dependency(Module("com.lihaoyi", "sourcecode", Map()),
"0.1.4",
...
@ core.ivyDeps().foreach(println)
Scala(Dependency(com.lihaoyi:sourcecode,0.1.4,,Set(),Attributes(,),false,true))
Scala(Dependency(com.lihaoyi:pprint,0.5.3,,Set(),Attributes(,),false,true))
Point(Dependency(com.lihaoyi:ammonite,1.0.3,,Set(),Attributes(,),false,true))
Scala(Dependency(com.typesafe.play:play-json,2.6.6,,Set(),Attributes(,),false,true))
Scala(Dependency(org.scala-sbt:zinc,1.0.5,,Set(),Attributes(,),false,true))
Java(Dependency(org.scala-sbt:test-interface,1.0,,Set(),Attributes(,),false,true))
// run multiple tasks with `eval` function.
@ val (coreScala, bridge2106Scala) = eval(core.scalaVersion, bridges("2.10.6").scalaVersion)
coreScala: String = "2.12.4"
bridge2106Scala: String = "2.10.6"
Into a build.sc
file you can define separate Module
s (e.g. ScalaModule
).
Within each Module
you can define 3 type of task:
Target
: take no argument, output is cached and should be serializable; run frombash
(e.g.def foo = T{...}
)Command
: take serializable arguments, output is not cached; run frombash
(arguments withscopt
) (e.g.def foo = T.command{...}
)Task
: take arguments, output is not cached; do not run frombash
(e.g.def foo = T.task{...}
)
The out/
folder contains all the generated files & metadata for your build. It
is structured with one folder per Target
/Command
, that is run, e.g.:
out/core/compile/
out/main/test/compile/
out/main/test/forkTest/
out/scalalib/compile/
Each folder currently contains the following files:
-
dest/
: a path for theTask
to use either as a scratch space, or to place generated files that are returned usingPathRef
s.Task
s should only output files within their givendest/
folder (available asT.ctx().dest
) to avoid conflicting with otherTask
s, but files withindest/
can be named arbitrarily. -
log
: thestdout
/stderr
of theTask
. This is also streamed to the console during evaluation. -
meta.json
: the cache-key and JSON-serialized return-value of theTarget
/Command
. The return-value can also be retrieved viamill show core.compile
. Binary blobs are typically not included inmeta.json
, and instead stored as separate binary files indest/
which are then referenced bymeta.json
viaPathRef
s
In case of troubles with caching and/or incremental compilation, you can always
restart from scratch removing the out
directory:
rm -rf out/
-
Support for non-interactive (client/server) mode on Windows.
Mill requires an
sh
environment to run on Windows; it is recommended to use MSYS2 on Windows.Cygwin can also be used, but your mileage may vary when running mill on non-interactive (client/server) mode (it failed intermittently in mill's AppVeyor tests). On Cygwin, run the following after downloading mill:
sed -i '0,/-cp "\$0"/{s/-cp "\$0"/-cp `cygpath -w "\$0"`/}; 0,/-cp "\$0"/{s/-cp "\$0"/-cp `cygpath -w "\$0"`/}' <mill-path>
Mill also runs on Git-Bash and WSL but only on interactive mode.
- Fixes for non-interactive (client/server) mode on Java 9
-
Introduced the
mill plan foo.bar
command, which shows you what the execution plan of running thefoo.bar
task looks like without actually evaluating it. -
Mill now generates an
out/mill-profile.json
file containing task-timings, to make it easier to see where your mill evaluation time is going -
Introduced
ScalaModule#ivyDepsTree
command to show dependencies tree -
Rename
describe
toinspect
for consistency with SBT -
mill resolve
now prints results sorted alphabetically -
Node.js configuration can be customised with
ScalaJSModule#nodeJSConfig
-
Scala.js
fullOpt
now uses Google Closure Compiler after generating the optimized Javascript output -
Scala.js now supports
NoModule
andCommonJSModule
module kinds -
Include
compileIvyDeps
when generating IntelliJ projects -
Fixed invalid POM generation
-
Support for Java 9 (and 10)
-
Fixes for Windows support
-
Fixed test classes discovery by skipping interfaces
-
Include "optional" artifacts in dependency resolution if they exist
-
out/{module_name}
now added as a content root in generated IntelliJ project
-
Speed up Mill client initialization by another 50-100ms
-
Speed up incremental
assembly
s in the common case where upstream dependencies do not change. -
Make
ScalaJSModule#run
work with main-method discovery -
Make
ScalaWorkerModule
user-defineable, so you can use your own custom coursier resolvers when resolving Mill's own jars -
Simplify definitions of
SCM
strings -
Make the build REPL explicitly require
-i
/--interactive
to run -
Log a message when Mill is initializing the Zinc compiler interface
-
Greatly reduced the overhead of evaluating Mill tasks, with a warm already-cached
mill dev.launcher
now taking ~450ms instead of ~1000ms -
Mill now saves compiled build files in
~/.mill/ammonite
, which is configurable via the--home
CLI arg. -
Fixed linking of multi-module Scala.js projects
-
Mill now keeps a long-lived work-daemon around in between commands; this should improve performance of things like
compile
which benefit from the warm JVM. You can use-i
/--interactive
for interactive consoles/REPLs and for running commands without the daemon -
Implemented the
ScalaModule#launcher
target for easily creating command-line launchers you can run outside of Mill -
ScalaModule#docJar
no longer fails if you don't havescala-compiler
on classpath -
Support for multiple
testFrameworks
in a test module.
- Fixes for
foo.console
- Enable Ammonite REPL integration via
foo.repl
- First public release