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. As Mill is under active development, stable releases may not be able to build the current development branch of Mill. It is recommended to install the latest unstable release manually.
To run unit test suite:
mill main.testBuild a standalone executable jar:
mill dev.assemblyNow 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.assemblyThere is already a watch option that looks for changes on files, e.g.:
./out/dev/assembly/dest/mill --watch core.compileYou 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.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:
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.runLastly, you can generate IntelliJ Scala project files using Mill via
./out/dev/assembly/dest/mill mill.scalalib.GenIdea/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 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 Targets 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 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
In case of troubles with caching and/or incremental compilation, you can always
restart from scratch removing the out directory:
rm -rf out/-
Universal (combined batch/sh) script generation for launcher, assembly, and release (#264)
-
Windows client/server improvements (#262)
-
Windows repl support (note: MSYS2 subsystem/shell will be supported when jline3 v3.6.3 is released)
-
Fixed Java 9 support
-
Remove need for running
publishAllusing--interactivewhen on OSX and your GPG key has a passphrase -
First-class support for
JavaModules -
Properly pass compiler plugins to Scaladoc (#282)
-
Support for ivy version-pinning via
ivy"...".forceVersion() -
Support for ivy excludes via
ivy"...".exclude()(#254) -
Make
ivyDepsTreeproperly handle transitive dependencies (#226) -
Fix handling of
runtime-scoped ivy dependencies (#173) -
Make environment variables available to Mill builds (#257)
-
Support ScalaCheck test runner (#286)
-
Support for using Typelevel Scala (#275)
-
If a module depends on multiple submodules with different versions of an ivy dependency, only one version is resolved (#273)
-
Support for non-interactive (client/server) mode on Windows.
-
More fixes for Java 9
-
Bumped the Mill daemon timeout from 1 minute to 5 minutes of inactivity before it shuts down.
-
Avoid leaking Node.js subprocesses when running
ScalaJSModuletests -
Passing command-line arguments with spaces in them to tests no longer parses wrongly
-
ScalaModule#repositories,scalacPluginIvyDeps,scalacOptions,javacOptionsare now automatically propagated toTestsmodules -
ScalaJSModulelinking errors no longer show a useless stack trace -
ScalaModule#docJarnow properly uses the compileClasspath rather than runClasspath -
Bumped underlying Ammonite version to 1.1.0, which provides the improved Windows and Java 9 support
-
Fixes for non-interactive (client/server) mode on Java 9
-
Windows batch (.bat) generation for launcher, assembly, and release
-
Introduced the
mill plan foo.barcommand, which shows you what the execution plan of running thefoo.bartask looks like without actually evaluating it. -
Mill now generates an
out/mill-profile.jsonfile containing task-timings, to make it easier to see where your mill evaluation time is going -
Introduced
ScalaModule#ivyDepsTreecommand to show dependencies tree -
Rename
describetoinspectfor consistency with SBT -
mill resolvenow prints results sorted alphabetically -
Node.js configuration can be customised with
ScalaJSModule#nodeJSConfig -
Scala.js
fullOptnow uses Google Closure Compiler after generating the optimized Javascript output -
Scala.js now supports
NoModuleandCommonJSModulemodule kinds -
Include
compileIvyDepswhen 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
assemblys in the common case where upstream dependencies do not change. -
Make
ScalaJSModule#runwork with main-method discovery -
Make
ScalaWorkerModuleuser-defineable, so you can use your own custom coursier resolvers when resolving Mill's own jars -
Simplify definitions of
SCMstrings -
Make the build REPL explicitly require
-i/--interactiveto 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.launchernow taking ~450ms instead of ~1000ms -
Mill now saves compiled build files in
~/.mill/ammonite, which is configurable via the--homeCLI 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
compilewhich benefit from the warm JVM. You can use-i/--interactivefor interactive consoles/REPLs and for running commands without the daemon -
Implemented the
ScalaModule#launchertarget for easily creating command-line launchers you can run outside of Mill -
ScalaModule#docJarno longer fails if you don't havescala-compileron classpath -
Support for multiple
testFrameworksin a test module.
- Fixes for
foo.console - Enable Ammonite REPL integration via
foo.repl
- First public release