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 and Netbeans for those that require 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.11.2) 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 libraryContent of sbt file (if Unix mark it as executable using chmod u+x sbt):
java -Xmx512M -jar `dirname $0`/sbt-launch.jar "$@"
Content of sbt.cmd file:
@echo off set SCRIPT_DIR=%~dp0 java -Xmx512M -jar "%SCRIPT_DIR%sbt-launch.jar" %*
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.9.1" libraryDependencies += "org.scalatest" %% "scalatest" % "1.6.1"
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:\project-sbt-demo>sbt
Getting net.java.dev.jna jna 3.2.3 ...
:: retrieving :: org.scala-tools.sbt#boot-app
confs: [default]
1 artifacts copied, 0 already retrieved (838kB/17ms)
Getting Scala 2.8.1 (for sbt)...
:: retrieving :: org.scala-tools.sbt#boot-scala
confs: [default]
3 artifacts copied, 0 already retrieved (15178kB/39ms)
Getting org.scala-tools.sbt sbt_2.8.1 0.10.1 ...
:: retrieving :: org.scala-tools.sbt#boot-app
confs: [default]
36 artifacts copied, 0 already retrieved (6414kB/235ms)
Getting Scala 2.9.1 ...
:: retrieving :: org.scala-tools.sbt#boot-scala
confs: [default]
4 artifacts copied, 0 already retrieved (19939kB/50ms)
[info] Set current project to default-086a22 (in build file:/D:/project-sbt-demo/)
>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 } }
> compile
[info] Updating {file:/D:/project-sbt-demo/}default-086a22...
[info] Done updating.
[info] Compiling 1 Scala source to D:\project-sbt-demo\target\scala-2.9.1.final\classes...
[success] Total time: 3 sLets 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 with ShouldMatchers { "clamp" should "handle minimum cases" in { assert(clamp(-1.0, 0.0, 1.0) == 0.0) } }
> test [info] Compiling 1 Scala source to D:\project-sbt-demo\target\scala-2.9.1.final\test-classes... [info] DemoSpec: [info] clamp [info] - should handle minimum cases [info] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0 [success] Total time: 3 s
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] Passed: : Total 1, Failed 0, Errors 0, Passed 1, Skipped 0 [success] Total time: 0 s 1. Waiting for source changes... (press enter to interrupt)
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) }
[info] Compiling 1 Scala source to D:\project-sbt-demo\target\scala-2.9.1.final\test-classes... [info] DemoSpec: [info] clamp [info] - should handle minimum cases [info] - should handle maximum cases [info] Passed: : Total 2, Failed 0, Errors 0, Passed 2, Skipped 0 [success] Total time: 3 s 2. Waiting for source changes... (press enter to interrupt)
it should "handle inrange cases" in { assert(clamp(0.9, 0.0, 1.0) == 0.5) }
[info] Compiling 1 Scala source to D:\project-sbt-demo\target\scala-2.9.1.final\test-classes...
[info] DemoSpec:
[info] clamp
[info] - should handle minimum cases
[info] - should handle maximum cases
[info] - should handle inrange cases *** FAILED ***
[info] org.scalatest.TestFailedException was thrown. (DemoSpec.scala:16)
[error] Failed: : Total 3, Failed 1, Errors 0, Passed 2, Skipped 0
[error] Failed tests:
[error] demo.DemoSpec
[error] {file:/D:/project-sbt-demo/}default-086a22/test:test: Tests unsuccessful
[error] Total time: 3 s
3. Waiting for source changes... (press enter to interrupt)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) }
[info] Compiling 1 Scala source to D:\project-sbt-demo\target\scala-2.9.1.final\test-classes... [info] DemoSpec: [info] clamp [info] - should handle minimum cases [info] - should handle maximum cases [info] - should handle inrange cases [info] Passed: : Total 3, Failed 0, Errors 0, Passed 3, Skipped 0 [success] Total time: 3 s 4. Waiting for source changes... (press enter to interrupt)
If you want API documentation generated, simply issue the doc command then locate them in /target/scala-version/api:
> doc
[info] Updating {file:/D:/project-sbt-demo/}default-086a22...
[info] Done updating.
[info] Generating API documentation for main sources...
model contains 3 documentable templates
[info] API documentation generation successful.
[success] Total time: 4 sOnce editing of sourcecode is done you can issue package commands to generate the final libraries:
> package [info] Packaging D:\project-sbt-demo\target\scala-2.9.1.final\project_2.9.1-1.0.jar ... [info] Done packaging. [success] Total time: 1 s
> package-doc [info] Generating API documentation for main sources... model contains 3 documentable templates [info] API documentation generation successful. [info] Packaging D:\project-sbt-demo\target\scala-2.9.1.final\project_2.9.1-1.0-javadoc.jar ... [info] Done packaging. [success] Total time: 3 s
> package-src [info] Packaging D:\project-sbt-demo\target\scala-2.9.1.final\project_2.9.1-1.0-sources.jar ... [info] Done packaging. [success] Total time: 0 s
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 wiki 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.
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!
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.