I’m currently working on a small Scala project, so I thought I’d try Li Haoyi’s Mill build tool – currently at version 0.6.1 — for about a week and see how things go. In this tutorial I’ll share an example of how to use Mill on a little “Hello, world” example.
My Github project
If you want to follow along with the discussion that follows, I put my Mill “Hello, world” Github project here:
Installing Mill
To install Mill, see this page.
I initially installed Mill with Homebrew, but that involved Homebrew installing OpenJDK 13, which seemed like overkill for what I want to do, so I uninstalled that and then reinstalled Mill with the “Manual” approach.
Creating a Mill project
I’m used to working with the common/standard directory structures that Maven and SBT use, but Mill is a little different. Mill seems to promote the concept of potentially having multiple modules or sub-projects inside a main project, so its directory structure is different.
For a little Scala project, creating a Mill directory structure looks like this:
mkdir MillTest1
cd MillTest1
mkdir HelloWorld
mkdir -p HelloWorld/src/main/scala
touch build.sc
Visually that directory structure looks like this:
> tree .
.
├── HelloWorld
│ └── src
│ └── main
│ └── scala
└── build.sc
For the rest of this tutorial I’ll assume that you’re in the MillTest1 directory, with build.sc in the current directory and HelloWorld as a subdirectory.
Mill’s build.sc file
Where SBT uses a build.sbt build file, Mill’s configuration file is named build.sc file. With our project going under the HelloWorld directory, the simplest possible build.sc file looks like this:
import mill._, scalalib._
object HelloWorld extends ScalaModule {
def scalaVersion = "2.12.11"
}
Notice that the name of the object here — HelloWorld
— must match your subdirectory name. This makes sense when you think about having multiple modules. For instance, if you had modules named Foo
and Bar
in directories named Foo and Bar, your build file would look like this:
import mill._, scalalib._
object Foo extends ScalaModule {
def scalaVersion = "2.12.11"
}
object Bar extends ScalaModule {
def scalaVersion = "2.12.11"
}
Creating a HelloWorld example
To create a “Hello, world” example, I want to put my source code in a package named hello
, so I first create a subdirectory under the src tree:
mkdir HelloWorld/src/main/scala/hello
Then I put these contents in a file at HelloWorld/src/main/scala/hello/Hello.scala:
package hello
object Hello extends App {
println("Hello, world")
}
Running the example
To compile and run this example, run this mill
command at your command line:
mill HelloWorld
The output of that command looks like this:
[27/37] HelloWorld.compile
Compiling compiler interface...
warning: there were three deprecation warnings (since 2.12.0); re-run with -deprecation for details
warning: there were three feature warnings; re-run with -feature for details
two warnings found
[info] Compiling 1 Scala source to MillTest1/out/HelloWorld/compile/dest/classes ...
[info] Done compiling.
[37/37] HelloWorld.run
Hello, world
If that worked, congratulations, you just compiled and ran your first Scala project with Mill.
It’s cool to see the “Hello, world” output, but ...
Deprecation and feature warnings?
It’s a little weird to see deprecation and feature warnings on a one-class project like this, so let’s see if we can figure out what’s going on.
From this example it looks like I should be able to specify the scalac
deprecation and feature options like this:
import mill._, scalalib._
// HelloWorld must match the subdirectory name
object HelloWorld extends ScalaModule {
def scalaVersion = "2.12.11"
def scalacOptions = Seq(
"-deprecation",
"-feature"
)
}
However, after running these commands:
mill clean
mill HelloWorld
I still see the same output. I also tried this and it didn’t work:
def scalacOptions = super.scalacOptions ++ Seq( "-deprecation", "-feature" )
so ... this item is TBD.
scalac works fine
Note: Just in case I missed something obvious, I came back and compiled my class manually using scalac
2.13.1, and did not see any warnings:
scalac HelloWorld/src/main/scala/hello/Hello.scala
So whatever those feature and deprecation warnings are about, they have nothing to do with my class.
Update: Day 3
For the first few days I worked on my project with a plain text editor, but today I started using VS Code. An interesting side effect of doing this is that VS Code recognizes that I’m working in a Mill workspace, and fires up the Metals and Bloop tooling, and some part of this eliminates the warning messages I write about here (and later in this tutorial).
This is what you see when you start VS Code inside my Mill project:
I’m not sure why this makes those initial warning messages go away, but I’ll try to look into that.
Other Mill commands
Skipping over that problem, it’s worth mentioning that there are several other Mill commands you can run. For instance, you compile your project like this:
mill HelloWorld.compile
You can also “continuously compile” your project — recompiling the project any time a file changes — with either of these “watch” commands:
mill --watch HelloWorld.compile
mill -w HelloWorld.compile
You can run your project with either of these commands:
mill HelloWorld
mill HelloWorld.run
I’ll show test-related commands in a future tutorial.
One other note: You can also run similar commands from inside the Mill REPL. The commands are a little different there, and I’ll show those in a future tutorial as well.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
Mill output
Whenever you compile or run your project, Mill writes its output to the out directory inside your project. A potentially nice thing about this is that it writes a meta.json file to most of the directories inside that directory with information about the build. For instance, if you run a command like this on the “Hello, world” project:
find out | grep meta.json
you’ll see that there are 69 meta.json files under the out directory. I looked at a few of those files and didn’t see anything interesting, but the Mill documentation mentions that these files can be helpful when debugging problems.