Scala SBT Build Tool

SBT (Scala Build Tool) is the standard build tool for Scala projects. It compiles your code, runs tests, manages library dependencies, packages your application, and provides an interactive shell for running tasks. Every real Scala project uses SBT or a compatible alternative like Mill.

Project Structure


my-project/
├── build.sbt                ← project definition (name, version, deps)
├── project/
│   ├── build.properties     ← SBT version
│   └── plugins.sbt          ← SBT plugins
└── src/
    ├── main/
    │   ├── scala/           ← your Scala source files
    │   └── resources/       ← config files, assets
    └── test/
        ├── scala/           ← test files
        └── resources/       ← test resources

build.sbt — The Project File

// build.sbt

name := "my-scala-app"
version := "1.0.0"
scalaVersion := "3.3.1"

// Add library dependencies
libraryDependencies ++= Seq(
  "org.typelevel" %% "cats-core"  % "2.10.0",
  "io.circe"      %% "circe-core" % "0.14.6",
  "org.scalatest" %% "scalatest"  % "3.2.17" % Test
)

// Compiler options
scalacOptions ++= Seq(
  "-deprecation",    // warn on deprecated APIs
  "-feature",        // warn on advanced features
  "-unchecked"       // warn on unchecked type patterns
)

Common SBT Commands


Command          What it does
───────────────  ─────────────────────────────────────────────
compile          Compile all source files
run              Compile and run the main class
test             Compile and run all tests
clean            Delete compiled files
reload           Reload build.sbt after changes
console          Open Scala REPL with project classpath loaded
package          Create a JAR file
assembly         Create a fat JAR with all dependencies
update           Download all declared dependencies
~compile         Watch for changes and auto-recompile
~run             Watch and auto-run on every change
~test            Watch and auto-test on every change

Running SBT

// Start SBT interactive shell
$ sbt

// In the SBT shell:
sbt> compile
sbt> run
sbt> test
sbt> exit

// Or run a single command
$ sbt compile
$ sbt run
$ sbt "runMain com.example.MyApp"

Multiple Modules (Multi-project Build)

// build.sbt for a multi-project setup

lazy val common = project
  .in(file("common"))
  .settings(
    name := "common",
    scalaVersion := "3.3.1"
  )

lazy val api = project
  .in(file("api"))
  .dependsOn(common)
  .settings(
    name := "api",
    scalaVersion := "3.3.1",
    libraryDependencies += "com.typesafe.akka" %% "akka-http" % "10.5.0"
  )

lazy val root = project
  .in(file("."))
  .aggregate(common, api)

root (aggregates all)
├── common  (shared utilities)
│     shared by
└── api     (depends on common)

project/build.properties

// Set the SBT version your project requires
sbt.version=1.9.7

project/plugins.sbt — Adding Plugins

// Common plugins

// Create a fat JAR with all dependencies
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.5")

// Generate code coverage reports
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.9")

// Format Scala code automatically
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2")

// Native Scala packaging
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.16")

Dependency Scopes

libraryDependencies ++= Seq(
  "org.typelevel"  %% "cats-core"  % "2.10.0",          // all scopes
  "org.scalatest"  %% "scalatest"  % "3.2.17" % Test,   // test only
  "ch.qos.logback" % "logback-classic" % "1.4.7" % Runtime // runtime only
)

No scope    → available in compile, test, run
% Test      → only in test compilation and execution
% Runtime   → only at runtime (not at compile time)
% Provided  → available at compile time, NOT bundled in JAR

Running a Specific Main Class

// In build.sbt — set the default main class
mainClass := Some("com.example.Main")

// Or specify when running
$ sbt "runMain com.example.ServerApp"
$ sbt "runMain com.example.BatchJob"

SBT Console — REPL with Dependencies

$ sbt console

// Inside SBT console — all project classes and dependencies available
scala> import cats.syntax.all._
scala> import com.example.MyService
scala> val result = MyService.compute(42)

Useful SBT Settings

// build.sbt

// Fork a new JVM for run (useful for server apps)
fork := true

// Pass JVM arguments
javaOptions ++= Seq("-Xmx2g", "-Xms512m")

// Show full stack traces in test failures
testOptions += Tests.Argument("-oF")

// Aggregate test results across sub-projects
Test / aggregate := true

The %% vs % Difference

// %% appends Scala version automatically
"org.typelevel" %% "cats-core" % "2.10.0"
// becomes: "org.typelevel" % "cats-core_3" % "2.10.0" (for Scala 3)
// or:      "org.typelevel" % "cats-core_2.13" % "2.10.0" (for Scala 2.13)

// % uses the exact artifact name (for Java libraries)
"com.google.guava" % "guava" % "32.1.3-jre"
// Java libraries have no Scala version suffix

SBT is more than a build tool — it is the foundation of the entire Scala development workflow. Every plugin, deployment pipeline, and CI system integrates through SBT. Understanding its project structure and basic commands is essential before working on any real Scala project.

Leave a Comment

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