A quick tour of build tools in Scala

  • Update 20.04.2018: added Polyglot Maven

Scala has a rich ecosystem and active community producing a lot of useful libraries. So much so that, sometimes it is not easy as a newcomer to decide which library to pick for a given task (this is the case for example when it comes to database access and JSON handling). In this article we are going to cover another domain in which there is an increasing number of alternatives: build tools. At the time of writing this article in April 2018 we have at our disposal:

  • sbt
  • cbt
  • mill
  • fury
  • maven
  • polyglot maven
  • gradle
  • ant (to be written)
  • bazel (to be written)
  • pants (to be written)
  • make (to be written)

Let’s take a bit of time and walk through each of these options and see how they work


sbt, shorthand for sbt, is Scala’s first (or so I think) and most well-known build tool. It is, by now, solid, reliable and has a large set of plugins and integrations.


sbt is available via Homebrew on on Mac, MSI on Windows and packages on linux (Debian or RedHat). On Mac, you’d install it using

Creating a new project

New projects can be created using Giter8 templates. For a simple project you can use the scala-seed template:

At this point, the project structure is:

Defining the build

sbt requires a build.sbt file located at the root of the project to work. Additional build definitions can be defined in Scala files in the project directory. This is what we get from the template:

Running the project

There is two ways to working with sbt. You can run a task directly on the command-line shell, or enter the sbt shell. Typically you’ll want to run the tasks from within the sbt shell, because that’s the fastest (starting the shell takes a while). You can start the shell simply by running sbt:

At this point you can execute tasks (press tab for auto-completion):

Adding dependencies

Dependencies are expressed using sbt’s DSL. As projects grow, it is a good idea to keep the dependencies structured, as the template hints at:

The %% notation is used for Scala dependencies and makes sure to select the correct Scala version of a dependency. For normal maven / ivy dependencies, a single % is used.

You can then add the dependency using the libraryDependencies setting:

The last % in this example defines the dependency scope – in the case of ScalaTest, we have a test dependency. For most dependencies you’ll want to leave this blank.

Creating custom tasks

sbt defines its own task definition, resolution and execution layer. Rather than attempting at explaining this I’ll redirect you to James Roper’s excellent article on sbt.

To define a task in sbt, we first need to define a task key:

This is a bit like an interface – it tells us the name of the task, its type (this task will return a String) and also lets us define a description.

We then need to implement this task:

This is a short example and only gives you a short glimpse at how things work. For more background I invite you to check out the documentation.

Documentation, plugins, community

sbt has a fairly complete documentation, a strong community and a myriad of plugins (the previous link only shows the plugins that are hosted in the general sbt Github organization, but there are many more out there).


cbt, shorthand for Chris’ Build Tool, is the result of Christopher Vogt having had enough of using sbt, but not quite enough so for choosing an entirely new name. It is a build orchestration tool aiming at using the Scala language and only a few concepts in order to create builds. Unlike sbt, it maps task execution to JVM invocations instead of adding its own layer in between.


To install cbt you have to clone the cbt repository and can then add the directory to your path:

At the first execution, it compiles itself and also nags about being faster when installing nailgun. To install nailgun, run:

Creating a new project

Now that we’re all set, we can let cbt create a new project for us:

Defining the build

Without a build file, cbt will use default build settings. It is possible to let it create a new build file:

At this point, this is the structure of our project:

Running the project

Unlike sbt, cbt doesn’t have a console. You simply run the tasks from the common shell:

Adding dependencies

Dependencies are expressed using the ScalaDependency or MavenDependency constructs like shown in the generated build file above. Those dependencies need to be nested under a Resolver:

What’s interesting is that the sbt DSL is also supported which makes for easy copy-pasting of dependencies from many README files out there.

Creating custom tasks

Creating new taks is rather straight-forward, you only need to create a new function in the build file, like for example:

And then call your task with cbt:

Documentation, plugins, community

cbt has a documentation that helps you get started with it, a few plugins and a small community (check out the Gitter channel).


Mill is the only Scala build tool written from the ground up in x86 assembly. It’s fast. You can read Li Haoyi’s introductory post about Mill here.

Edit 23.04.2018: okay, I’m getting too many comments on this. So just to be clear: Mill is not written in x86 assembly. The comment came to me after trying it out, given it has a fast feel to it.


Mill has a brew package on OS X, an AUR package for arch linux and can be downloaded directly as bat file for Windows. For OS X we’ll just run:

Creating a new project

Mill doesn’t have (yet) a mechanism to generate a new project, instead you can download a sample project.

Defining the build

The build is defined in a file at the root of the
This gives you the following build definition:

The project structure is:

What you might notice right away is that this structure is not following the now almost-standard maven project structure. Mill doesn’t impose a particular structure on the projects, instead it allows for quite some flexibility in regards to how modules are layed out. There’s a few common project layouts documented, amongst which an sbt-compatible layout.

Running the project

The common syntax for running project tasks folllows the pattern module.task:

Adding dependencies

Mill uses a DSL for adding dependencies to a project:

Similar to sbt’s %% notation, Scala dependencies are expressed using the :: notation. It’d be nice, that is, if like cbt, Mill did also have a compatible way of accepting the sbt format for the sake of copy-paste.

One thing I’d like to point out here is that mill uses coursier to fetch dependencies. Coursier is quite a bit faster than sbt’s dependency resolution which has personally been somwhat painful to use over the years (it has gotten much better, but used to drive me insane). For example, it supports downloading multiple artifacts in parallel and has no global lock (the infamous Waiting for ~/.ivy2/.sbt.ivy.lock to be available which you are guaranteed to get if you are working with both sbt and IntelliJ at the same time.

Creating custom tasks

Mill defines its own task graph abstraction to handle task definition, ordering and cache. Calling one task from another establishes a dependency, which makes it quite natural to understand. There are 3 types of tasks:

  • targets: to define where outputs get generated / compiled / assembled
  • sources: to define where code comes from
  • commands: to define things to do

Let’s define a task:

Which yields:

Documentation, plugins, community

Mill has a complete documentation site and an active community.

Plugins aren’t called plugins, but modules. There doesn’t seem to be a central module repository yet at the time of writing this article or maybe I missed it.


There is a new build tool in the making:

Having not attended ScalaSphere I can only speculate where the name comes from, so I’ll be so free as to hypothetize that it has to something to do with Marvel’s Nick Fury.

This seems to be further supported by the following tweet:

I’m looking forward to adding the review of the tool here once it is available!


Maven is… well, what do you want me to say. It’s maven.

If you have to, you can use maven to build scala projects using the scala-maven-plugin (previously maven-scala-plugin):

From there on, it is plain maven, therefore I won’t go into any details as to how to work with the project.

Polyglot Maven

If you really need to be using maven but would like to use Scala for your build definition, Polyglot for Maven allows you to do so. Here’s an example of how it looks like:

import org.sonatype.maven.polyglot.scala.model._
import scala.collection.immutable.Seq


Just to mention this possibility as well, there’s a Gradle Scala plugin if you want to use Gradle for your Scala project build.

Great, now which one to pick?

I find this one more difficult to answer than for the database and JSON topics.

Clearly, sbt is well-established and has a large amount of plugins to integrate with many, many things out there. But if you are a newcomer and need to do something for which there isn’t a well-documented plugin or just a documented way of doing things, things can become quite frustrating. And this isn’t only true for newcomers – you’ll find quite a few experienced Scala developers that are frustrated with sbt. See, to some extent it is possible to just get by using sbt without really understanding it and relying on copy-pasting things from Stack Overflow or from other build definitions. It’s when you need to do this one thing that’s a bit different and not so well documented that it gets frustrating. Or maybe things are well-documented, but you don’t have the conceptual model of sbt in your mind (and don’t find a good way to acquire it), so you don’t know how to proceed. Now, I’m not saying “don’t use sbt”. What I’m trying to say is that sbt is hard and that, as a newcomer, you should approach it as something that is hard, which will help a lot with regards to your personal frustration and willingness to learn (because, if it is simple, you shouldn’t need to have to invest time in learning it and it should just work, right?).

And therefore I completely understand that there are a few new alternatives coming up, which is something I feel happens in the Scala community a bit too easily and often – rather than trying to improve existing tools, people go ahead and create new ones. Except that, well, in this case and with its current design, I don’t see any easy way of making sbt radically easier to grasp.

Now, all that rambling doesn’t help. What to pick? Well, here’s what I do:

  • for projects at clients, unless there’s a strong drive from the client, I’d stick to sbt, because that’s a safe choice
  • for my own projects, should I one day have time for them, I think I’ll opt for Mill at the moment. It uses Coursier (remember, I wear the scars of waiting about 4 minutes simply for dependencies to resolve), is well-documented, under active development and even if at the moment there isn’t a clear plugin repository I do get the impression that this is just a matter of time

Happy building!

Comments 13

  1. Interesting article. Scala build tools seem on the rise at the moment. Just wanted to mention that you can also use Bazel to build Scala projects.

    1. Post
    1. Post
  2. Mill is the only Scala build tool written from the ground up in x86 assembly.

    Say what?

    1. Post
  3. Don’t forget there’s a good sbt plugin for coursier and sbt had made a ton of progress toward simplicity in the recent releases.

    1. Post

      Good to know that there is a plugin, thanks! Yes I’m not arguing that SBT hasn’t made progress – it is quite amazing how it has evolved recently. And once you invest time in learning SBT it becomes manageable – I just think there’s quite a few people that don’t want to spend that time or don’t expect it to be necessary. And at the end of the day I think it is quite normal to have several build tool alternatives in Scala. I mean just looking at other languages (Java, Clojure, etc.) it seems normal that multiple approaches co-exist.

    1. Post
  4. Pingback: ? ??? ??? ?? - 2018? 21?(5? 21?) - ?????

  5. Pingback: 2018: year in review - manuel bernhardt

Leave a Reply

Your email address will not be published.