SBT (Simple Build Tool) is the standard build tool for Scala projects, comparable to Maven or Gradle in the Java ecosystem. It offers several key advantages:
- Zero configuration reqiured for simple projects.
- Build logic defined using Scala source code.
- Precise incremental recompilation to save development time.
- Library management powered by Coursier.
- Seamless support for mixed Scala and Java projects.
Installation
SBT requires Java; ensure you have JDK 8 or newer installed. Download the installation package (e.g., version 1.5.5), extract it, and set the SBT_HOME environment variable. Add %SBT_HOME%\bin to your system PATH (installers often handle this automatically).
Basic Project Setup
Create a project directory and initialize the build file:
$ mkdir demo-project
$ cd demo-project
$ touch build.sbt
Launch the SBT interactive shell:
$ sbt
[info] Updated file /tmp/demo-project/project/build.properties: set sbt.version to 1.1.4
[info] Loading project definition from /tmp/demo-project/project
[info] Loading settings from build.sbt ...
[info] Set current project to demo-project (in build file:/tmp/demo-project/)
[info] sbt server started at local:///Users/user/.sbt/1.0/server/abc4fb6c89985a00fd95/sock
sbt:demo-project>
Note: The first initialization may take some time. Exit the shell with exit.
Common Shell Operations
compile: Compile the main sources.run: Execute the main class.~prefix (e.g.,~compile): Enter watch mode to automatically recompile on source changes. Press Enter to exit watch mode.help: View general help; usehelp <command>for specific command details.scalaVersion: Check the current Scala version.- Use Tab for command completion and arrow keys to navigate command history.
Adding Source Code
Keep ~compile running and create the standard source directory structure. Add a sample source file:
// src/main/scala/demo/Greeting.scala
package demo
object Greeting extends App {
println("Welcome")
}
SBT will detect the change and recompile automatically.
Configuring Build Settings
Modify the Scala version temporarily in the shell:
sbt:demo-project> set ThisBuild / scalaVersion := "2.13.6"
Save the configuration to build.sbt:
sbt:demo-project> session save
A typical build.sbt file looks like this:
ThisBuild / scalaVersion := "2.13.6"
ThisBuild / organization := "com.demo"
lazy val greeting = (project in file("."))
.settings(
name := "Greeting"
)
Reload the configuration with reload.
Testing with ScalaTest
Add ScalaTest as a test dependency in build.sbt:
ThisBuild / scalaVersion := "2.13.6"
ThisBuild / organization := "com.demo"
lazy val greeting = (project in file("."))
.settings(
name := "Greeting",
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.7" % Test
)
Create a test file:
// src/test/scala/GreetingSpec.scala
import org.scalatest.funsuite._
class GreetingSpec extends AnyFunSuite {
test("Greeting should start with G") {
assert("Greeting".startsWith("G"))
}
}
Run tests with test. Use ~testQuick to automatically run failed or updated tests on save.
Managing Dependencies
Add external libraries by appending to libraryDependencies:
lazy val greeting = (project in file("."))
.settings(
name := "Greeting",
libraryDependencies += "com.typesafe.play" %% "play-json" % "2.9.2",
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.7" % Test
)
Using the Scala REPL
Launch the REPL with console. Inside the REPL:
:paste: Enter paste mode to paste multi-line code.:q: Quit the REPL and return to the SBT shell.
Multi-Project Builds
Define subprojects in build.sbt:
ThisBuild / scalaVersion := "2.13.6"
ThisBuild / organization := "com.demo"
val scalaTest = "org.scalatest" %% "scalatest" % "3.2.7"
lazy val root = (project in file("."))
.aggregate(core)
.dependsOn(core)
.settings(
name := "RootProject",
libraryDependencies += scalaTest % Test
)
lazy val core = (project in file("core"))
.settings(
name := "CoreModule",
libraryDependencies += scalaTest % Test
)
After reload, the core/ directory is created. List projects with projects and compile the subproject with core/compile.
Packaging and Docker
Add the sbt-native-packager plugin in project/plugins.sbt:
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.4")
Enable packaging in build.sbt:
lazy val root = (project in file("."))
.aggregate(core)
.dependsOn(core)
.enablePlugins(JavaAppPackaging)
.settings(...)
Create a ZIP distribution with dist. For Docker, use Docker/publishLocal and run with docker run rootproject:version.
Project Structure
SBT follows a structure similar to Maven:
src/
main/
resources/ # Files included in the main JAR
scala/ # Main Scala sources
java/ # Main Java sources
test/
resources/ # Test resources
scala/ # Test Scala sources
java/ # Test Java sources
Build definitions reside in build.sbt. The project/ directory can contain Scala files for custom build logic. Generated files are output to the target/ directory (add to .gitignore).
Running Commands Directly
Execute SBT commands without entering the shell (slower startup):
sbt clean "testOnly GreetingSpec"
Scaffolding New Projects
Use the new command with Giter8 templates:
$ sbt new scala/scala-seed.g8
name [My Something Project]: greeting
Template applied in ./greeting
Common Commands Reference
| Command | Description |
|---|---|
| clean | Deletes all generated files in the target directory. |
| compile | Compiles main sources in src/main. |
| test | Compiles and runs all tests. |
| console | Starts the Scala REPL with project dependencies. |
| run [args] | Runs the project's main class with optional arguments. |
| package | Creates a JAR file of the main sources and resources. |
| reload | Reloads build.sbt and project definitions after changes. |
| ++2.12.14 | Temporarily switch the Scala version for the session. |