Example usage of sbt build system

The sbt build system

This is a writeup explaining basic usage of the Simple Build Tool system for Scala. The complete build system is fairly comprehensive so I will only be focusing on aspects like setting up the project and using basic commands. A nice property of the build tool is its support for continuous compilation and testing. While you’re working on sourcefiles, the sbt tool will monitor changes then trigger recompilation and retesting whenever you modify code. By the time you need to check for errors in compilation or tests, a report might already be waiting for you.

For this example I’ll refer to a standard text editor and console. Alternatively, there exists sbt integration for IDEs like Eclipse for those that prefer that.

Getting started

Beginning, make sure you have a properly configured Java. Once that is in place you then need to download the sbt-launch.jar (0.13.1) library file. There are two options when using sbt. You can either install it system-wide, or include it with your project. I opted for the last one in this post.

With the download ready you can setup the project structure as follows:

project-name/ src/ main/scala/ -- your project sourcefiles goes here test/scala/ -- your project testfiles goes here lib/ -- manually handled library jars goes here build.sbt -- your project's sbt config file sbt -- sbt launchscript for unix sbt.cmd -- sbt launchscript for windows sbt-launch.jar -- the sbt library
Code language: Plaintext (plaintext)

Content of sbt file (if Unix mark it as executable using chmod u+x sbt):

#!/bin/bash java -Xmx512M -jar $(dirname $(readlink -f "$0"))/sbt-launch.jar run "$@"
Code language: Bash (bash)

Content of sbt.cmd file:

@echo off set SCRIPT_DIR=%~dp0 java -Xmx512M -jar "%SCRIPT_DIR%sbt-launch.jar" %*
Code language: Bash (bash)

Content of build.sbt file:

// Notes on syntax // - settings are initialized with := // - dependency paths given by % // - dependency paths against specific scala versions are given by %% name := "Project" version := "1.0" scalaVersion := "2.10.3" libraryDependencies += "org.scalatest" % "scalatest_2.10" % "2.0" % "test"
Code language: Scala (scala)

With that the project ready for use. All that remains is running sbt from console to pull in required library dependencies (or later manually with update):

D:\test-scala3>sbt Getting org.fusesource.jansi jansi 1.11 ... downloading http://repo1.maven.org/maven2/org/fusesource/jansi/jansi/1.11/jansi-1.11.jar ... [SUCCESSFUL ] org.fusesource.jansi#jansi;1.11!jansi.jar (639ms) :: retrieving :: org.scala-sbt#boot-jansi confs: [default] 1 artifacts copied, 0 already retrieved (111kB/26ms) Getting org.scala-sbt sbt 0.13.1 ... ...
Code language: Bash (bash)

Once started sbt will continue to accept commands (if you need to quit it, simply write exit).

Lets add the sourcefile /src/main/scala/demo/Demo.scala and compile it:

package demo object Demo { def clamp(value: Double, min: Double, max: Double): Double = { if (value < min) min else if (value > max) max else value } }
Code language: Scala (scala)
> compile [info] Updating {file:}... ... [info] Done updating. [info] Compiling 1 Scala source to target\scala-2.10\classes... [success] Total time: 2 s, completed 22.jan.2014 18:09:53
Code language: Bash (bash)

Lets also add a test /src/test/scala/demo/DemoSpec.scala and test our assumptions:

package demo import org.scalatest.FlatSpec import org.scalatest.matchers.ShouldMatchers import Demo.clamp class DemoSpec extends FlatSpec { "clamp" should "handle minimum cases" in { assert(clamp(-1.0, 0.0, 1.0) == 0.0) } }
Code language: Scala (scala)
> test [info] Compiling 1 Scala source to target\scala-2.10\test-classes... [info] DemoSpec: [info] clamp [info] - should handle minimum cases [info] Run completed in 339 milliseconds. [info] Total number of tests run: 1 [info] Suites: completed 1, aborted 0 [info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 [info] All tests passed. [success] Total time: 2 s, completed 22.jan.2014 18:12:13
Code language: Bash (bash)

Instead of issuing these commands manually each iteration, lets instead enable automatic testing with ~test (the tilde tells sbt to run the command continuously).

> ~test [info] DemoSpec: [info] clamp [info] - should handle minimum cases [info] Run completed in 488 milliseconds. [info] Total number of tests run: 1 [info] Suites: completed 1, aborted 0 [info] Tests: succeeded 1, failed 0, canceled 0, ignored 0, pending 0 [info] All tests passed. [success] Total time: 1 s, completed 22.jan.2014 18:14:15
Code language: Bash (bash)

Updating /src/test/scala/demo/DemoSpec.scala with additional test-cases now triggers recompile and retesting:

it should "handle maximum cases" in { assert(clamp(2.0, 0.0, 1.0) == 1.0) }
Code language: Scala (scala)
[info] Compiling 1 Scala source to target\scala-2.10\test-classes... [info] DemoSpec: [info] clamp [info] - should handle minimum cases [info] - should handle maximum cases [info] Run completed in 346 milliseconds. [info] Total number of tests run: 2 [info] Suites: completed 1, aborted 0 [info] Tests: succeeded 2, failed 0, canceled 0, ignored 0, pending 0 [info] All tests passed. [success] Total time: 2 s, completed 22.jan.2014 18:16:16
Code language: Bash (bash)
it should "handle inrange cases" in { assert(clamp(0.9, 0.0, 1.0) == 0.5) }
Code language: Scala (scala)
[info] Compiling 1 Scala source to target\scala-2.10\test-classes... [info] DemoSpec: [info] clamp [info] - should handle minimum cases [info] - should handle maximum cases [info] - should handle inrange cases *** FAILED *** [info] 0.9 did not equal 0.5 (DemoSpec.scala:15) [info] Run completed in 411 milliseconds. [info] Total number of tests run: 3 [info] Suites: completed 1, aborted 0 [info] Tests: succeeded 2, failed 1, canceled 0, ignored 0, pending 0 [info] *** 1 TEST FAILED *** [error] Failed tests: [error] demo.DemoSpec [error] (test:test) sbt.TestsFailedException: Tests unsuccessful [error] Total time: 2 s, completed 22.jan.2014 18:16:40
Code language: Bash (bash)

Here the tests did not complete. The error message points to where corrections are needed:

it should "handle inrange cases" in { assert(clamp(0.5, 0.0, 1.0) == 0.5) }
Code language: Scala (scala)
[info] Compiling 1 Scala source to target\scala-2.10\test-classes... [info] DemoSpec: [info] clamp [info] - should handle minimum cases [info] - should handle maximum cases [info] - should handle inrange cases [info] Run completed in 372 milliseconds. [info] Total number of tests run: 3 [info] Suites: completed 1, aborted 0 [info] Tests: succeeded 3, failed 0, canceled 0, ignored 0, pending 0 [info] All tests passed. [success] Total time: 2 s, completed 22.jan.2014 18:17:59 5. Waiting for source changes... (press enter to interrupt)
Code language: Bash (bash)

If you want API documentation generated, simply issue the doc command then locate them in /target/scala-version/api:

> doc [info] Main Scala API documentation to target\scala-2.10\api... model contains 3 documentable templates [info] Main Scala API documentation successful. [success] Total time: 1 s, completed 22.jan.2014 18:18:25
Code language: Bash (bash)

Once editing of sourcecode is done you can issue package commands to generate the final libraries:

> package [info] Packaging target\scala-2.10\project_2.10-1.0.jar ... [info] Done packaging. [success] Total time: 0 s, completed 22.jan.2014 18:18:39
Code language: Bash (bash)
> package-doc [info] Packaging target\scala-2.10\project_2.10-1.0-javadoc.jar ... [info] Done packaging. [success] Total time: 0 s, completed 22.jan.2014 18:18:52
Code language: Bash (bash)
> package-src [info] Packaging target\scala-2.10\project_2.10-1.0-sources.jar ... [info] Done packaging. [success] Total time: 0 s, completed 22.jan.2014 18:19:06
Code language: Bash (bash)

Further pointers

Presented here was just the absolute basics of sbt to get you started. There are tons of commands available within sbt (use tab-completion to show them all), additional configuration options, and plugins which extend it. If you want more in-depth information about sbt then visit its documentation page. Development on sbt is still ongoing so expect some changes in the future, but by including the sbt build system with your project you can largely ignore upgrades until you’re ready for them.

5 comments

  1. Thanks for this tutorial.

    There are a lot of old tutorials for prior versions that have sbt behaving *completely* differently than the current release, which e.g. doesn’t do any project set up for the user anymore. This tutorial helped me get started. Looks like a nice tool!

    Thanks again!

  2. Thanks. I wrote it since I felt the wiki for sbt had too many details for people who just wanted to get started. As of writing, its documentation is mostly developer notes and references, but that is probably because its author Mark Harrah is still improving its design.

  3. Instead of just:

    java -Xmx512M -jar `dirname $0`/sbt-launch.jar “$@”

    Use something like this to actually execute, to (hopefully) prevent spaces messing up the path, and to allow cleanly linking the script into a bin directory, for much simpler installation.

    #!/bin/bash
    java -Xmx512M -jar $(dirname $(readlink -f “$0”))/sbt-launch.jar “$@”

  4. Oh, actually, you want:

    #!/bin/bash
    java -Xmx512M -jar $(dirname $(readlink -f “$0″))/sbt-launch.jar run “$@”

Leave a comment

Your email address will not be published. Required fields are marked *