How to manage project dependencies with SBT (Simple Build Tool)

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 18.4, “How to manage dependencies with SBT (Simple Build Tool).”

Problem

You want to use one or more external libraries (dependencies) in your Scala/SBT projects.

Solution

You can use both managed and unmanaged dependencies in your SBT projects.

If you have JAR files (unmanaged dependencies) that you want to use in your project, simply copy them to the lib folder in the root directory of your SBT project, and SBT will find them automatically. If those JARs depend on other JAR files, you’ll have to download those other JAR files and copy them to the lib directory as well.

If you have a single managed dependency, such as wanting to use the Java HtmlCleaner library in your project, add a libraryDependencies line like this to your build.sbt file:

libraryDependencies += "net.sourceforge.htmlcleaner" % "htmlcleaner" % "2.4"

Because configuration lines in build.sbt must be separated by blank lines, a simple but complete file with one dependency looks like this:

name := "BasicProjectWithScalaTest"

version := "1.0"

scalaVersion := "2.10.0"

libraryDependencies += "org.scalatest" %% "scalatest" % "1.9.1" % "test"

To add multiple managed dependencies to your project, define them as a Seq in your build.sbt file:

libraryDependencies ++= Seq(
    "net.sourceforge.htmlcleaner" % "htmlcleaner" % "2.4",
    "org.scalatest" % "scalatest_2.10" % "1.9.1" % "test",
    "org.foobar" %% "foobar" % "1.8"
)

Or, if you prefer, you can add them one line at a time to the file, separating each line by a blank line:

libraryDependencies += "net.sourceforge.htmlcleaner" % "htmlcleaner" % "2.4"

libraryDependencies += "org.scalatest" % "scalatest_2.10" % "1.9.1" % "test"

libraryDependencies += "org.foobar" %% "foobar" % "1.6"

As you might infer from these examples, entries in build.sbt are simple key/value pairs.

SBT works by creating a large map of key/value pairs that describe the build, and when it parses this file, it adds the pairs you define to its map. The fields in this file named version, name, scalaVersion, and libraryDependencies are all SBT keys (and in fact are probably the most common keys).

Discussion

A managed dependency is a dependency that’s managed by your build tool, in this case, SBT. In this situation, if library a.jar depends on b.jar, and that library depends on c.jar, and those JAR files are kept in an Ivy/Maven repository along with this relationship information, then all you have to do is add a line to your build.sbt file stating that you want to use a.jar. The other JAR files will be downloaded and included into your project automatically.

When using a library as an unmanaged dependency, you have to manage this situation yourself. Given the same situation as the previous paragraph, if you want to use the library a.jar in your project, you must manually download a.jar, and then know about the dependency on b.jar, and the transitive dependency on c.jar, then download all those files yourself, and place them in your project’s lib directory.

I’ve found that manually managing JAR files in the lib directory works fine for small projects, but as shown in Recipe 16.2, “Connecting to a Database with the Spring Framework”, a few lines of managed dependency declarations can quickly explode into a large number of JAR files you’ll need to manually track down and add to your lib folder.

Under the covers, SBT uses Apache Ivy as its dependency manager. Ivy is also used by Ant and Maven, and as a result, you can easily use the wealth of Java libraries that have been created over the years in your Scala projects.

There are two general forms for adding a managed dependency to a build.sbt file. In the first form, you specify the groupID, artifactID, and revision:

libraryDependencies += groupID % artifactID % revision

In the second form, you add an optional configuration parameter:

libraryDependencies += groupID % artifactID % revision % configuration

The groupID, artifactID, revision, and configuration strings correspond to what Ivy requires to retrieve the module you want. Typically, the module developer will give you the information you need. For instance, the specs2 website provides this string:

libraryDependencies += "org.specs2" %% "specs2" % "1.14" % "test"

It also provides this information, which shows how to use the same library with Maven:

<dependency>
    <groupId>org.specs2</groupId>
    <artifactId>specs2_2.10</artifactId>
    <version>1.14</version>
    <scope>test</scope>
</dependency>

Common build.sbt methods

The symbols +=, %, and %% used in build.sbt are part of the DSL defined by SBT. They’re described in Table 18-2.

Table 18-2. Common methods used in a build.sbt file

Method Description
+= Appends to the key’s value. The build.sbt file works with settings defined as key/value pairs. In the examples shown, libraryDependencies is a key, and it’s shown with several different values.
% A method used to construct an Ivy Module ID from the strings you supply.
%% When used after the groupID, it automatically adds your project’s Scala version (such as _2.10) to the end of the artifact name.

As shown in the examples, you can use % or %% after the groupID. This example shows the % method:

libraryDependencies += "org.scalatest" % "scalatest_2.10" % "1.9.1" % "test"

This example shows the %% method:

libraryDependencies += "org.scalatest" %% "scalatest" % "1.9.1" % "test"

When using Scala 2.10, these two lines are equivalent. The %% method adds your project’s Scala version to the end of the artifact name. The practice of adding the Scala version (in the format _2.10.0) to the artifactID is used because modules may be compiled for different Scala versions.

Note that in some of the examples, the string test is added after the revision:

"org.scalatest" % "scalatest_2.10" % "1.9.1" % "test"

This demonstrates the use of the “configuration” form for adding a dependency that was shown earlier:

libraryDependencies += groupID % artifactID % revision % configuration

As the SBT documentation states, this means that the dependency you’re defining “will be added to the classpath only for the ‘Test’ configuration, and won’t be added in the Compile configuration.” This is useful for adding dependencies like ScalaTest, specs2, Mockito, etc., that will be used when you want to test your application, but not when you want to compile and run the application.

If you’re not familiar with Apache Ivy, it can be helpful to know that managed dependencies are downloaded beneath a .ivy2 directory in your home directory (~/.ivy2/) on your filesystem. See the Ivy documentation for more information.

Repositories

SBT uses the standard Maven2 repository by default, so it can locate most libraries when you add a libraryDependencies line to a build.sbt file. In these cases, there’s no need for you to tell SBT where to look for the file. However, when a library is not in a standard repository, you can tell SBT where to look for it. This process is referred to as adding a resolver, and it’s covered in Recipe 18.11, “Telling SBT How to Find a Repository (Working with Resolvers)”.

See Also