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.
Run unit test suite:
sbt main/testBuild a standalone executable jar:
sbt bin/test:assemblyNow you can re-build this very same project using the build.sc file, e.g. re-run core unit tests
e.g.:
./target/bin/mill core.compile
./target/bin/mill main.test.compile
./target/bin/mill main.test
./target/bin/mill scalalib.assemblyThere is already a watch option that looks for changes on files, e.g.:
./target/bin/mill --watch core.compileYou can get Mill to show the JSON-structured output for a particular Target or
Command using the show flag:
./target/bin/mill show core.scalaVersion
./target/bin/mill show core.compile
./target/bin/mill show core.assemblyClasspath
./target/bin/mill show main.testOutput 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:
sbt "~bin/test:run main.test"
sbt "~bin/test:run"Lastly, you can generate IntelliJ Scala project files using Mill via
./target/bin/mill mill.scalalib.GenIdeaModule/ideaAllowing 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}.testwill 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 barwill 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[_].compileNote: When you run multiple targets with --all flag, 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 Targets from Scala code:
lihaoyi mill$ target/bin/mill
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()
.docsJar()
.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()
.docsJar()
.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 Modules (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 theTaskto use either as a scratch space, or to place generated files that are returned usingPathRefs.Tasks should only output files within their givendest/folder (available asT.ctx().dest) to avoid conflicting with otherTasks, but files withindest/can be named arbitrarily. -
log: thestdout/stderrof 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.jsonviaPathRefs
You can use SBT to build a Mill executable, which itself is able to build more Mill executables that can you can use to run Mill commands:
git clean -xdf
# Build Mill executable using SBT
sbt bin/test:assembly
# Build Mill executable using the Mill executable generated by SBT
target/bin/mill devAssembly
# Build Mill executable using the Mill executable generated by Mill itself
out/devAssembly/dest/out.jar devAssemblyEventually, as Mill stabilizes, we will get rid of the SBT build entirely and rely on previous versions of Mill to build itself.
In case of troubles with caching and/or incremental compilation, you can always
restart from scratch removing the out directory:
rm -rf out/The end goal of the Mill project is to develop a new Scala build tool to replace SBT. Mill should satisfy most of the current use cases for SBT's functionality, but hopefully needing much fewer features and much less complexity to do so. We take inspiration from SBT, Make, Bazel, and many other existing build tools and libraries.
The immediate goal of Mill is to be feature-complete enough to:
- Sustain its own development, without needing SBT
- Start porting over existing open-source Scala library builds from SBT to Mill
com-lihaoyi#2 would kick off the process porting
com.lihaoyi:acyclic's build to Mill, and from there we can flesh out the
missing features needed to port other builds: Scala.js support, Scala-Native
support, etc..
As the maintainer of many open-source libraries, all of the com.lihaoyi
libraries are fair game to be ported.
Once a fair number of libraries have been ported, Mill should be in good enough shape to release to the public, and we can try getting more people in the community-at-large on board trying out Mill. This should hopefully happen by the end of 2017.
Until then, let's keep Mill private. If someone wants to poke their nose in and see what's going on, we should expect them to contribute code!
The final milestone before a public release, is for Mill to be able to fully substitute SBT in developing the following projects:
-
https://github.com/lihaoyi/mill: a single-scala-version, single-platform Scala project
-
https://github.com/lihaoyi/acyclic: a Scala project cross-published against Scala {2.10, 2.11, 2.12}
-
https://github.com/playframework/play-json: a Scala project with multiple submodules partially-cross-published against Scala {2.10, 2.11, 2.12} X {JVM, JS}
-
https://github.com/lihaoyi/utest: a Scala project fully-cross-published against Scala {2.10, 2.11, 2.12} X {JVM, JS, Native}
-
https://github.com/gitbucket/gitbucket: a standalone application build using Scala (v.s. all the libraries listed above) with a simple build
Each of these are relatively simple projects. Satisfying all of their requirements (codegen, building, testing, publishing, etc.) and being happy with the Mill code necessary to do so (both in their build code, as well as Mill's own implementation). Meeting that baseline is the minimum we should support before advertising Mill to the general community