Mike Slinn

SBT Global Setup

— Draft —

Published 2013-09-14. Last modified 2019-10-17.
Time to read: 10 minutes.

Originally called the Simple Build Tool, sbt is misnamed – it is definitely not simple! I call it the Scala Build Tool. This lecture gives you an introduction to working with this important software tool and helps you set it up for use with all of your Scala projects.

Sbt (Scala Build Tool) is Scala’s interactive and automated build tool, which manages your project dependencies. Sbt behaves like an advanced version of Maven. While Maven can be used to build Scala projects, sbt is almost always used instead. sbt build setups for large projects can be complex, however small sbt projects are usually simple to understand.

sbt can build Scala and Java projects, run automated tests, generate documentation, run static code analysis, generate front-end web assets for web applications, and many other things.

Like Maven, it fetches project dependencies automatically – including the version of the Scala compiler. Sbt can guarantee projects will be built in a reproducible way because it offers total control of all dependencies. Sbt is unique in that an sbt project can specify the version of sbt used to build the project.

Because sbt considers itself to be a dependency, the sbt launcher will fetch the version of sbt specified for a project, if it has not already been fetched. Sbt launcher version X is bundled with sbt version X, and is therefore the default version of sbt if a version is not specified for a build. The SBT Project Setup lecture shows how to specify the sbt version for a project.

Sbt is frequently run in an interactive mode while doing development. You will learn how to use sbt’s commands interactively in this and the other lectures on sbt that follow.

Git is almost always used with sbt projects. I recommend that you get to know the git command line, if you are not already using it.

A Word About sbt’s Documentation

Search engines invariably provide links to out-of-date sbt documentation, and sbt has changed significantly in many ways over the years. Here is the current sbt documentation.

Reproducible Builds

The primary function of sbt is to support fully reproducible builds. This is possible if the sbt project declares all the information required to build a project within the project itself. The necessary information includes the version of the Scala compiler and sbt to use, any required sbt plug-ins, and any third-party libraries that the project depends on. If these dependencies are all declared, an sbt project should be able to be run by simply following these steps:

Shell
$ git clone your-project-URL
$ cd your-project
$ # Read the README
$ # Set any necessary environment variables
$ # Modify configuration files as required
$ # Start any necessary local databases
$ sbt run

Sbt provides per-project settings and global settings. Global settings should only specify plugins and settings that enrich your personal Scala development environment, but which are not required to build or run any specific project. It is bad practice to specify dependencies required to build and run specific projects in your global sbt settings.

For example, if you use a common logging library in all your projects, don’t specify the library in your sbt global configuration; instead, the specification of that dependency belongs in the project configuration of every project that uses it, or in a separate dependency that is referenced from the build specification. We’ll discuss popular sbt plugins later in this lecture.

The next lecture, SBT Project Setup, discusses project-specific settings.

sbt Directory Structure

Sbt uses a Maven-compatible directory structure for projects:

+---project
+---lib
+---src
   +---main
     +---java
     +---resources
     +---scala
   +---test
     +---java
     +---resources
     +---scala

As you can see, sbt can build projects containing both Scala and Java source code. Your program and its runtime resources are placed in the src/ directory according to the type of code (main or test) and language (java or scala).

sbt can also use Maven repositories of Scala and Java libraries. Starting with v1.3.0, released on Sep 4, 2019, sbt uses Coursier to implement dependency management. Older versions of sbt used Apache Ivy to manage dependencies, which are placed in ~/.ivy2 by default.

Sbt performs a two-stage build: first it compiles the project builder files (in effect, a meta-project), then it uses the project builder to compile and perhaps run the project code. The project builder meta-project consists of some or all of these files:

  • If you are using sbt 1.x, all .sbt files in ~/.sbt/1.0/ are processed; if that directory does not exist or you are using an older version of sbt, that directory is skipped.
  • If you are using sbt 0.13.x, all .sbt files in ~/.sbt/0.13/ are processed; if that directory does not exist or you are using an older version of sbt, that directory is skipped.
  • All .sbt files in the top-level directory of your project, plus Build.scala if it exists in that directory.
  • All files in your project’s project/ directory.

Prior to sbt 0.13.7, .sbt files had to be double-spaced. This is because the contents of .sbt files are parsed as a domain-specific language (DSL) which is then compiled as a Scala program. sbt combines all of the .sbt files together and loads them as one when it starts up, so you can put the project information into as few or as many files as you like. By convention, project/plugins.sbt defines the sbt plugins you want to make available to your project builder. We will look at some popular plugins in a minute.

project/build.properties contains the version of sbt to be used with the project. You should specify this value, so builds do not break as sbt and Scala continue to evolve. Here are typical contents:

sbt project/build.properties
sbt.version=1.3.0

You have only limited control of the version of Scala that is used by sbt to build the meta-project. For example, sbt 0.13.11 must use a version of Scala compatible with version 2.10. By default, sbt 0.13.11 meta-projects use Scala 2.10.6.

Installation

You have many choices for running Scala. You can choose all of them or only one of them:

  1. As discussed in the Installing Scala Only section of the Installing Scala lecture, Coursier provides the easiest way to install the latest Scala command-line tools. The lecture also discusses how to install a Java development kit to power Scala.
  2. The remainder of the Installing Scala Only lecture discusses how to install standalone Scala compiler without using Coursier.
  3. The remainder of this section is optional. It discusses how to install sbt, which can be used to compile, test and and run Scala projects. You could skip down to the Checking the sbt Version section if you don’t feel like running sbt from the command line, or you don’t want to spend a lot of time figuring out how to integrate editors with sbt. The non-optional SBT Project Setup and SBT Tasks and Settings lectures that follow explain sbt in more detail.
  4. The optional Setting Up IntelliJ IDEA lecture discusses how to install and configure IntelliJ IDEA by JetBrains for use with Scala and sbt. IDEA installs its own versions of the Scala compiler and sbt, based on the project settings you establish.

You can use the official sbt instructions to install the sbt launcher (which has a version of sbt bundled with it), or follow along below.

Mac

MacPorts and Homebrew both work.

Shell
$ brew install sbt

...or...

Shell
$ sudo port install sbt

Linux and Windows 10 Subsystem for Linux

For a Debian distro like Ubuntu, the instructions on scala-sbt.com work well. The warning message is fine, at least for now.

Shell
$ echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | sudo tee /etc/apt/sources.list.d/sbt.list
deb https://repo.scala-sbt.org/scalasbt/debian all main 
$ echo "deb https://repo.scala-sbt.org/scalasbt/debian /" | sudo tee /etc/apt/sources.list.d/sbt_old.list
deb https://repo.scala-sbt.org/scalasbt/debian /
$ curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | sudo apt-key add Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead (see apt-key(8)). OK
$ sudo apt update Hit:1 https://downloads.plex.tv/repo/deb public InRelease Hit:2 https://ca.archive.ubuntu.com/ubuntu mantic InRelease Hit:3 https://security.ubuntu.com/ubuntu mantic-security InRelease Hit:4 https://ca.archive.ubuntu.com/ubuntu mantic-updates InRelease Hit:5 https://ca.archive.ubuntu.com/ubuntu mantic-backports InRelease Hit:6 https://dl.google.com/linux/chrome/deb stable InRelease Get:7 https://scala.jfrog.io/artifactory/debian all InRelease [4,417 B] Ign:8 https://scala.jfrog.io/artifactory/debian InRelease Get:10 https://scala.jfrog.io/artifactory/debian all/main amd64 Packages [2,807 B] Get:9 https://scala.jfrog.io/artifactory/debian Release [815 B] Get:11 https://scala.jfrog.io/artifactory/debian all/main i386 Packages [2,807 B] Get:12 https://scala.jfrog.io/artifactory/debian Release.gpg [821 B] Get:13 https://scala.jfrog.io/artifactory/debian Packages [5,122 B] Fetched 16.8 kB in 2s (9,242 B/s) Reading package lists... Done Building dependency tree... Done Reading state information... Done 1 package can be upgraded. Run 'apt list --upgradable' to see it. W: https://repo.scala-sbt.org/scalasbt/debian/dists/all/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details. W: https://repo.scala-sbt.org/scalasbt/debian/Release.gpg: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details. $ sudo apt install sbt Reading package lists... Done Building dependency tree... Done Reading state information... Done The following NEW packages will be installed: sbt 0 upgraded, 1 newly installed, 0 to remove and 1 not upgraded. Need to get 20.0 kB of archives. After this operation, 50.2 kB of additional disk space will be used. Get:1 https://scala.jfrog.io/artifactory/debian all/main amd64 sbt all 1.10.1 [20.0 kB] Fetched 20.0 kB in 1s (19.0 kB/s) Selecting previously unselected package sbt. (Reading database ... 486042 files and directories currently installed.) Preparing to unpack .../archives/sbt_1.10.1_all.deb ... Unpacking sbt (1.10.1) ... Setting up sbt (1.10.1) ... Creating system group: sbt Creating system user: sbt in sbt with sbt daemon-user and shell /bin/false Processing triggers for man-db (2.11.2-3) ...

Native Windows

For native Windows, just use the .msi installer provided on the sbt download site.

Checking the Currently Available sbt Version

You can check the currently installed (default) version of sbt, type the following at an OS prompt:

Shell
$ sbt --script-version
downloading sbt launcher 1.10.1
[info] [launcher] getting org.scala-sbt sbt 1.10.1  (this may take some time)...
[info] [launcher] getting Scala 2.12.19 (for sbt)...
1.10.1 

To check the version of sbt used in an sbt project, and the version of Scala used by sbt:

Shell
$ sbt about
[info] Loading settings for project global-plugins from idea.sbt ...
[info] Loading global plugins from /home/mslinn/.sbt/1.0/plugins
[info] Loading settings for project cadenzaclient-build-build from plugins.sbt ...
[info] Loading project definition from /mnt/c/work/cadenzaHome/cadenzaCode/cadenzaClient/project/project
[warn] There may be incompatibilities among your library dependencies; run ’evicted’ to see detailed eviction warnings.
[info] Loading settings for project cadenzaclient-build from build.sbt,plugins.sbt ...
[info] Loading project definition from /mnt/c/work/cadenzaHome/cadenzaCode/cadenzaClient/project
[info] Loading settings for project cadenzaclient from assembly.sbt,build.sbt ...
[info] Set current project to cadenzaClient (in build file:/mnt/c/work/cadenzaHome/cadenzaCode/cadenzaClient/)
[info] This is sbt 1.2.8
[info] The current project is ProjectRef(uri("file:/mnt/c/work/cadenzaHome/cadenzaCode/cadenzaClient/"), "cadenzaclient") 0.1.0
[info] The current project is built against Scala 2.12.10
[info] Available Plugins
[info]  - coursier.sbtcoursier.CoursierPlugin
[info]  - coursier.sbtcoursiershared.SbtCoursierShared
[info]  - sbt.ScriptedPlugin
[info]  - sbt.plugins.CorePlugin
[info]  - sbt.plugins.Giter8TemplatePlugin
[info]  - sbt.plugins.IvyPlugin
[info]  - sbt.plugins.JUnitXmlReportPlugin
[info]  - sbt.plugins.JvmPlugin
[info]  - sbt.plugins.SbtPlugin
[info]  - sbt.plugins.SemanticdbPlugin
[info]  - sbtassembly.AssemblyPlugin
[info] sbt, sbt plugins, and build definitions are using Scala 2.12.10 

If you want to know the version of sbt used in an existing sbt project, you can just examine project/build.properties, where that value should be specified. You can also type the following:

Shell
$ sbt sbtVersion
[info] Loading settings for project global-plugins from idea.sbt ...
[info] Loading global plugins from /home/mslinn/.sbt/1.0/plugins
[info] Loading settings for project cadenzaclient-build-build from plugins.sbt ...
[info] Loading project definition from /mnt/c/work/cadenzaHome/cadenzaCode/cadenzaClient/project/project
[info] Loading settings for project cadenzaclient-build from build.sbt,plugins.sbt ...
[info] Loading project definition from /mnt/c/work/cadenzaHome/cadenzaCode/cadenzaClient/project
[info] Loading settings for project cadenzaclient from assembly.sbt,build.sbt ...
[info] Set current project to cadenzaClient (in build file:/mnt/c/work/cadenzaHome/cadenzaCode/cadenzaClient/)
[info] 1.3.3

Unfortunately, if your sbt project fails to build, the sbt sbtVersion command will fail.

Unversioned Setup

Prior to sbt 0.13, there was only one global sbt setup, in ~/.sbt/. Globally installed plugins were located in ~/.sbt/plugins/. For Windows, the directories are best referred to as %USERPROFILE%\.sbt\ and %USERPROFILE%\.sbt\project\.

Here is my ~/.sbt/plugins/build.sbt, set up for sbt 0.12.3 and Scala 2.10 (this is old!) This file provides plugins for all my legacy sbt projects. It contains two resolvers, which I have named artifactory releases and sbt-idea-repo. The name of a resolver is arbitrary; it is only there for documentation purposes. The URL that follows tells sbt where to look for dependencies, and the optional resolver style indicates the format of the repository. The default resolver style is Maven, however you can see that the artifactory releases repository uses the Ivy style.

The URL for the artifactory releases resolver was commonly used back when sbt 0.12.3 was current. Around that time sbt was starting to build in standard repository URLs, but I explicitly mentioned it because some older versions of sbt did not know to look there. The last release of the sbt-idea plugin was Aug 1, 2014, and it is no longer required or maintained. However, this project continued to build properly as other dependencies continued to change, because of versioned setups, discussed next.

Now that artifactoryonline.com is gone, however, builds that reference the site will fail.

sbt Program Fragment
resolvers += Resolver.url("artifactory releases",
  url("https://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-releases"))(Resolver.ivyStylePatterns)

resolvers += "sbt-idea-repo" at "https://mpeltonen.github.com/maven/"

sbt Versioned Setup

Globally installed plugins (those installed in ~/.sbt/plugins/) might work with one version of sbt but might not work with another version of sbt. Because sbt is able to automatically install any older version of sbt as required for the project you are currently working with, the plugins did not behave in a predictable manner.

sbt 0.13 introduced the capability for you to configure sbt plugins differently for each version of sbt called for by your projects.

This means that a project that requires sbt 0.12.3, for example, can invoke an older version of a plugin, while a newer project that uses sbt 0.13.1 can use a newer version of that same plugin – with different settings, if desired. To support this, versioned sbt setup was introduced.

Two versions can be maintained.

sbt 0.13 Versioned Setup

Global settings specific to sbt 0.13.x go in ~/.sbt/0.13/ and global plugins specific to that version of sbt go in ~/.sbt/0.13/plugins/. For Windows, the directories should be specified as %USERPROFILE%/.sbt/0.13/ and %USERPROFILE%/.sbt/0.13/project/.

If you do not create a ~/.sbt/0.13/ directory, sbt 0.13+ runs in compatibility mode, and issues this warning that suggests you should create the directory:

[warn] The global sbt directory is now versioned and is located at /Users/mslinn/.sbt/0.13.
[warn]   You are seeing this warning because there is global configuration in
         /Users/mslinn/.sbt but not in /Users/mslinn/.sbt/0.13.

The sentence actually says the opposite of what was intended. A better wording might be:

Your computer has no versioned global sbt directory.
If you want one, create it at ~/.sbt/0.13/

Creating a Versioned Setup

If you already have ~/.sbt/ set up, and you want to create an equivalent versioned setup for sbt 0.13, the easiest way to proceed is as follows:

Shell
$ cp -a ~/.sbt ~/.sbtTemp
$ mv ~/.sbtTemp ~/.sbt/0.13

Now edit the contents of ~/.sbt/0.13/ to suit.

My Versioned Setup

Here is my ~/.sbt/0.13/plugins/plugins.sbt (for Windows, this file is found at %USERPROFILE%/.sbt/0.13/plugins/plugins.sbt):

~/.sbt/0.13/plugins/plugins.sbt
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "4.0.0")

addSbtPlugin("com.github.mpeltonen"    % "sbt-idea"          % "1.6.0")

addSbtPlugin("com.timushev.sbt"        % "sbt-updates"       % "0.1.8")

addSbtPlugin("com.softwaremill.clippy" % "plugin-sbt"        % "0.2.5")

These plugins are described later in this lecture.

sbt 1.0 Versioned Setup

The sbt 1.0 Versioned Setup is quite similar:

Global settings specific to sbt 1.x go in ~/.sbt/1.0/ and global plugins specific to that version of sbt go in ~/.sbt/1.0/plugins/. For Windows, use %USERPROFILE%/.sbt/1.0/ and %USERPROFILE%/.sbt/1.0/project/. The sbt-idea plugin is no longer mentioned in this versioned setup.

Plugins

As I mentioned earlier, the sbt launcher automatically downloads and runs an instance of the specified version of sbt in order to compile a project.

Plug-ins extend the functionality of sbt. There are hundreds of plug-ins. Plugins are versioned and managed by sbt in a manner similar to library dependencies. To install a plug-in, you need to provide sbt with:

  1. The fully qualified name and version of the plug-in you want to use.
  2. The repository that the plug-in can be found in; this is called the resolver.

For example, Scalastyle is a popular static analysis code checker for Scala. To use the Scalastyle sbt plugin simply add the following to your project’s project/plugins.sbt file:

project/plugins.sbt
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.6.0")

The above specifies that:

  • The plugin’s maven group is org.scalastyle.
  • The maven id (name) of the plug-in is scalastyle-sbt-plugin
  • The version of the plugin is 0.6.0
  • This plugin was written in Scala and the default name mangling scheme should be used (%%)

sbt comes with some basic resolvers preconfigured, so many plugins do not need to have a resolver specified. You do not need to add a resolver for this plugin because it resides in a well-known repository. If you did want to add the resolver, you could specify it like this:

project/plugins.sbt
resolvers += "sonatype-releases" at https://oss.sonatype.org/content/repositories/releases/

Popular sbt plugins

All of these plugins could be globally specified. I do not show the installation instructions for these plugins, since they frequently change. Check the links that I provide for installation instructions.

  • plugin-sbt – also known as Scala Clippy, is a plugin that enhances the Scala compiler’s error messages.
  • scalastyle-sbt-plugin – static code analysis for Scala.
  • sbt-assembly – Build executable jar containing all dependencies.
  • sbt-dependency-graph-sugar – Displays an SVG graph of hierarchical direct and transitive dependencies. If using Mac, install GraphViz first. If you are using Ubuntu, do the following before trying to run it:
    Shell
    $ sudo apt-get install graphviz
    $ mkdir -p  ~/.sbt/gilt
    $ echo ’chromium-browser $1’ > ~/.sbt/gilt/sbt-dependency-graph-sugar-cmd
  • sbt-idea Converts sbt projects for use with IntelliJ IDEA; this plugin has not been required since IDEA 15.
  • sbt-stats – displays basic statistics about your project’s source code.
  • sbt-updates – Shows dependencies which have updates. Invoke sbt-update like this:
    Shell
    $ sbt dependencyUpdates

Colorized Scala REPL Output

As of Scala 2.11.3+, the Scala REPL output can be colorized. This is helpful.

  • Bold blue for vals
  • Bold green for types
  • Magenta for the shell prompt

If your terminal has a light-colored background, output looks like this:

If your terminal has a dark-colored background, output looks like this:

To enable colorized Scala REPL output, define the scala.color Java system property when invoking the scala compiler driver, like this:

Shell
$ scala -Dscala.color=true

For convenience, you could define a shell alias (place this in ~/.bash_profile for Mac; and in ~/.bashrc for Linux):

Shell alias
alias scala="scala -Dscala.color=true"

To enable colorized REPL output when using sbt console, create the following file as ~/.sbt/0.13/repl.sbt:

~/.sbt/0.13/repl.sbt
initialize ~= { _ =>
  val ansi = System.getProperty("sbt.log.noformat", "false") != "true"
  if (ansi) System.setProperty("scala.color", "true")
}

.sbtrc

Each line in an .sbtrc file is evaluated before a project is loaded. Each sbt project can have its own .sbtrc file, and you can define a global ~/.sbtrc file in your home directory.

Examples – Clear Console Then Compile, Run or Test Affected Code

The commands from these examples cause sbt to continually monitor your project’s source code tree, and whenever a file changes, clear the console and then either compile, run the project or run the unit tests affected by the change you made. These examples share a few tricks:

  1. Tilde (~) causes whatever follows it to be performed each time a file in the project changes.
  2. A semicolon (;) is equivalent to a newline.
  3. ANSI escape codes are output.

Create a global ~/.sbtrc containing three sbt aliases, which are rather like C preprocessor macros:

~/.sbtrc
# Clear the screen and recompile the project each time a change is made
alias cc = ~; eval "\u001B[2J\u001B[0\u003B0H"; compile

# Clear the screen and rerun the project each time a change is made
alias rc = ~; eval "\u001B[2J\u001B[0\u003B0H"; run

# Clear the screen and only (re)run unit tests specific to the modified code
alias tc = ~; eval "\u001B[2J\u001B[0\u003B0H"; testQuick

Use as follows:

Shell
$ sbt cc

Now open the courseNotes project in an editor and make a small change to src/main/scala/Hello.scala. When you save that file, sbt will recompile the changed project. sbt remains running in the background, keeping the compiler ’warm’.

Exit sbt by typing Ctrl-D and then restart with the rc alias:

Shell
$ sbt rc

Hello.scala should run. Make another small change to the file, and the program will recompile and rerun.

Exit sbt by typing Ctrl-D and then restart with the tc alias:

Shell
$ sbt tc

The unit tests affected by the changes you made to Hello.scala should run. Make another small change to the file, and the program will recompile and rerun the necessary tests.

A Better Way

These examples show useful techniques, however recent versions of sbt provide this functionality better via the watched.clearWhenTriggered setting, which will be discussed in the SBT Project Setup lecture that follows this one. I showed these examples to you anyway so you can understand what .sbtrc files are for and how to work with them.

Coursier – An Alternative Maven / Ivy Fetcher

Prior to v1.3.0, sbt was rather slow when fetching dependencies, because it used Ivy, and Ivy is slow. Coursier is much faster, and it has better offline support.

Coursier became standard June 18, 2019 when sbt 1.3.0 was released.

Compared to the default dependency resolution of sbt, Coursier adds:

  • Downloading of artifacts in parallel.
  • Better offline mode - one can safely work with snapshot dependencies if these are cached (sbt fails if it cannot check for updates).
  • Non-obfuscated cache (cache structure just mimics the URL it caches).
  • No global lock (no more "Waiting for ~/.ivy2/.sbt.ivy.lock to be available").

From the command-line, Coursier also has:

  • A launcher, which can launch applications distributed via Maven / Ivy repositories.
  • A bootstrap generator, which can generate stripped launchers of these applications.

* indicates a required field.

Please select the following to receive Mike Slinn’s newsletter:

You can unsubscribe at any time by clicking the link in the footer of emails.

Mike Slinn uses Mailchimp as his marketing platform. By clicking below to subscribe, you acknowledge that your information will be transferred to Mailchimp for processing. Learn more about Mailchimp’s privacy practices.