Appearance
SBT Cheatsheet
Overview
SBT (Scala Build Tool) is an interactive build tool for Scala, Java, and other JVM languages. It uses Scala for build definitions and provides incremental compilation, testing, and deployment.
Installation
Package Managers
bash
# macOS
brew install sbt
# Ubuntu/Debian
echo "deb https://repo.scala-sbt.org/scalasbt/debian all main" | sudo tee /etc/apt/sources.list.d/sbt.list
curl -sL "https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x2EE0EA64E40A89B84B2DF73499E82A75642AC823" | sudo apt-key add
sudo apt update && sudo apt install sbt
# Windows (Chocolatey)
choco install sbt
# Manual installation
wget https://github.com/sbt/sbt/releases/download/v1.8.0/sbt-1.8.0.tgz
tar -xzf sbt-1.8.0.tgz
export PATH=$PATH:/path/to/sbt/bin
Verification
bash
sbt --version
Project Structure
Standard Layout
project/
├── build.sbt
├── project/
│ ├── build.properties
│ ├── plugins.sbt
│ └── Dependencies.scala
├── src/
│ ├── main/
│ │ ├── scala/
│ │ ├── java/
│ │ └── resources/
│ └── test/
│ ├── scala/
│ ├── java/
│ └── resources/
├── target/
└── README.md
Basic build.sbt
Minimal Example
scala
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / scalaVersion := "2.13.10"
lazy val root = (project in file("."))
.settings(
name := "my-project",
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.2.15" % Test
)
)
Complete Example
scala
ThisBuild / version := "1.0.0"
ThisBuild / scalaVersion := "2.13.10"
ThisBuild / organization := "com.example"
// Global settings
ThisBuild / scalacOptions ++= Seq(
"-deprecation",
"-feature",
"-unchecked",
"-Xlint",
"-Ywarn-dead-code",
"-Ywarn-numeric-widen"
)
ThisBuild / javacOptions ++= Seq(
"-source", "11",
"-target", "11",
"-Xlint:unchecked",
"-Xlint:deprecation"
)
// Dependencies
val catsVersion = "2.9.0"
val akkaVersion = "2.7.0"
val scalaTestVersion = "3.2.15"
lazy val commonDependencies = Seq(
"org.typelevel" %% "cats-core" % catsVersion,
"ch.qos.logback" % "logback-classic" % "1.4.5",
"org.scalatest" %% "scalatest" % scalaTestVersion % Test,
"org.scalatestplus" %% "mockito-4-6" % "3.2.15.0" % Test
)
// Root project
lazy val root = (project in file("."))
.aggregate(core, web, api)
.settings(
name := "my-multi-project",
publish / skip := true
)
// Core module
lazy val core = (project in file("core"))
.settings(
name := "my-project-core",
libraryDependencies ++= commonDependencies ++ Seq(
"com.typesafe.akka" %% "akka-actor-typed" % akkaVersion,
"com.typesafe.akka" %% "akka-stream" % akkaVersion
)
)
// Web module
lazy val web = (project in file("web"))
.dependsOn(core)
.enablePlugins(PlayScala)
.settings(
name := "my-project-web",
libraryDependencies ++= commonDependencies ++ Seq(
"com.typesafe.play" %% "play" % "2.8.18",
"com.typesafe.play" %% "play-json" % "2.9.4"
)
)
// API module
lazy val api = (project in file("api"))
.dependsOn(core)
.settings(
name := "my-project-api",
libraryDependencies ++= commonDependencies ++ Seq(
"com.typesafe.akka" %% "akka-http" % "10.4.0",
"com.typesafe.akka" %% "akka-http-spray-json" % "10.4.0"
)
)
Dependencies
Dependency Syntax
scala
libraryDependencies ++= Seq(
// Scala library (cross-compiled)
"org.typelevel" %% "cats-core" % "2.9.0",
// Java library
"com.google.guava" % "guava" % "31.1-jre",
// Test dependency
"org.scalatest" %% "scalatest" % "3.2.15" % Test,
// Provided dependency
"javax.servlet" % "javax.servlet-api" % "4.0.1" % Provided,
// Runtime dependency
"mysql" % "mysql-connector-java" % "8.0.33" % Runtime,
// Optional dependency
"org.slf4j" % "slf4j-api" % "2.0.6" % Optional,
// Classifier
"org.apache.spark" %% "spark-core" % "3.3.2" classifier "tests",
// Exclude transitive dependencies
"com.typesafe.akka" %% "akka-actor" % "2.7.0" exclude("org.scala-lang", "scala-library")
)
Dependency Configurations
scala
// Custom configurations
lazy val IntegrationTest = config("it") extend Test
lazy val myProject = (project in file("."))
.configs(IntegrationTest)
.settings(
Defaults.itSettings,
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.2.15" % "it,test"
)
)
// Dependency overrides
dependencyOverrides ++= Seq(
"org.scala-lang" % "scala-library" % scalaVersion.value,
"org.scala-lang" % "scala-compiler" % scalaVersion.value
)
// Exclude specific dependencies
excludeDependencies ++= Seq(
ExclusionRule("commons-logging", "commons-logging"),
ExclusionRule("org.slf4j", "slf4j-log4j12")
)
Resolvers
scala
resolvers ++= Seq(
"Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots",
"Typesafe Repository" at "https://repo.typesafe.com/typesafe/releases/",
"Maven Central" at "https://repo1.maven.org/maven2/",
Resolver.mavenLocal
)
// Custom resolver
resolvers += "My Nexus" at "https://nexus.example.com/repository/maven-public/"
// Resolver with credentials
resolvers += "Private Repo" at "https://private.example.com/maven/" withCredentials Credentials(
"Sonatype Nexus Repository Manager",
"private.example.com",
"username",
"password"
)
Tasks and Commands
Basic Commands
bash
# Start SBT shell
sbt
# Compile
sbt compile
# Test
sbt test
# Run
sbt run
# Package
sbt package
# Clean
sbt clean
# Show dependencies
sbt dependencyTree
# Show project info
sbt projects
sbt show name
sbt show version
Interactive Shell
scala
// In SBT shell
compile
test
run
package
clean
// Run specific main class
runMain com.example.MyApp
// Run with arguments
run arg1 arg2
// Test specific class
testOnly com.example.MySpec
// Test with pattern
testOnly *MySpec
// Continuous compilation
~compile
~test
~run
Multi-Project Commands
bash
# Compile all projects
sbt compile
# Compile specific project
sbt core/compile
sbt web/test
sbt api/run
# Run command on all projects
sbt "project core" compile "project web" compile
# Aggregate commands
sbt test # runs test on all projects
Settings and Tasks
Custom Settings
scala
// Define custom setting
lazy val myCustomSetting = settingKey[String]("A custom setting")
// Define custom task
lazy val myCustomTask = taskKey[Unit]("A custom task")
lazy val root = (project in file("."))
.settings(
myCustomSetting := "Hello World",
myCustomTask := {
val value = myCustomSetting.value
println(s"Custom setting value: $value")
}
)
Task Dependencies
scala
lazy val generateSources = taskKey[Seq[File]]("Generate source files")
lazy val processResources = taskKey[Unit]("Process resources")
lazy val root = (project in file("."))
.settings(
generateSources := {
val dir = (Compile / sourceManaged).value
val file = dir / "Generated.scala"
IO.write(file, "object Generated { val value = 42 }")
Seq(file)
},
Compile / sourceGenerators += generateSources.taskValue,
processResources := {
val log = streams.value.log
log.info("Processing resources...")
// Resource processing logic
},
Compile / compile := (Compile / compile).dependsOn(processResources).value
)
Scoped Settings
scala
lazy val root = (project in file("."))
.settings(
// Global setting
version := "1.0.0",
// Compile scope
Compile / scalacOptions ++= Seq("-deprecation", "-feature"),
// Test scope
Test / scalacOptions ++= Seq("-Yrangepos"),
// Runtime scope
Runtime / unmanagedClasspath += baseDirectory.value / "config",
// Custom configuration
IntegrationTest / testOptions += Tests.Argument("-oD")
)
Plugins
Common Plugins
scala
// project/plugins.sbt
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.18")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.10.4")
addSbtPlugin("com.github.sbt" % "sbt-native-packager" % "1.9.11")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.6")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.0")
Plugin Configuration
scala
// Enable plugins
lazy val root = (project in file("."))
.enablePlugins(JavaAppPackaging, DockerPlugin)
.settings(
// Native packager settings
packageName := "my-app",
maintainer := "user@example.com",
// Docker settings
dockerBaseImage := "openjdk:11-jre-slim",
dockerExposedPorts := Seq(8080),
// Assembly settings
assembly / assemblyMergeStrategy := {
case PathList("META-INF", xs @ _*) => MergeStrategy.discard
case x => MergeStrategy.first
}
)
// Scalafmt configuration
scalafmtOnCompile := true
// Scoverage configuration
coverageMinimumStmtTotal := 80
coverageFailOnMinimum := true
Testing
Test Configuration
scala
lazy val root = (project in file("."))
.settings(
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "3.2.15" % Test,
"org.scalatestplus" %% "mockito-4-6" % "3.2.15.0" % Test,
"org.scalacheck" %% "scalacheck" % "1.17.0" % Test
),
// Test options
Test / testOptions ++= Seq(
Tests.Argument(TestFrameworks.ScalaTest, "-oD"),
Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/test-reports")
),
// Parallel execution
Test / parallelExecution := true,
Test / fork := true,
// JVM options for tests
Test / javaOptions ++= Seq(
"-Xmx1G",
"-XX:+CMSClassUnloadingEnabled"
)
)
Integration Tests
scala
lazy val IntegrationTest = config("it") extend Test
lazy val root = (project in file("."))
.configs(IntegrationTest)
.settings(
Defaults.itSettings,
IntegrationTest / testOptions := Seq(Tests.Filter(s => s.endsWith("IntegrationSpec")))
)
Build Configuration
Compilation Settings
scala
lazy val root = (project in file("."))
.settings(
// Scala compiler options
scalacOptions ++= Seq(
"-deprecation",
"-encoding", "UTF-8",
"-feature",
"-unchecked",
"-Xlint",
"-Ywarn-dead-code",
"-Ywarn-numeric-widen",
"-Ywarn-value-discard"
),
// Java compiler options
javacOptions ++= Seq(
"-source", "11",
"-target", "11",
"-Xlint:unchecked",
"-Xlint:deprecation"
),
// Incremental compilation
incOptions := incOptions.value.withNameHashing(true),
// Source directories
Compile / scalaSource := baseDirectory.value / "src" / "main" / "scala",
Test / scalaSource := baseDirectory.value / "src" / "test" / "scala"
)
Resource Management
scala
lazy val root = (project in file("."))
.settings(
// Resource directories
Compile / resourceDirectory := baseDirectory.value / "src" / "main" / "resources",
Test / resourceDirectory := baseDirectory.value / "src" / "test" / "resources",
// Resource filtering
Compile / resourceGenerators += Def.task {
val file = (Compile / resourceManaged).value / "version.properties"
val contents = s"version=${version.value}\nbuildTime=${System.currentTimeMillis}"
IO.write(file, contents)
Seq(file)
}.taskValue,
// Include/exclude resources
Compile / unmanagedResources / includeFilter := "*.conf" || "*.properties",
Compile / unmanagedResources / excludeFilter := "*.tmp"
)
Publishing
Basic Publishing
scala
lazy val root = (project in file("."))
.settings(
// Publishing settings
publishTo := {
val nexus = "https://oss.sonatype.org/"
if (isSnapshot.value)
Some("snapshots" at nexus + "content/repositories/snapshots")
else
Some("releases" at nexus + "service/local/staging/deploy/maven2")
},
// Credentials
credentials += Credentials(Path.userHome / ".sbt" / "sonatype_credentials"),
// POM settings
pomIncludeRepository := { _ => false },
publishMavenStyle := true,
publishArtifact in Test := false
)
Complete Publishing Setup
scala
ThisBuild / organization := "com.example"
ThisBuild / organizationName := "Example Corp"
ThisBuild / organizationHomepage := Some(url("https://example.com/"))
ThisBuild / scmInfo := Some(
ScmInfo(
url("https://github.com/example/my-project"),
"scm:git@github.com:example/my-project.git"
)
)
ThisBuild / developers := List(
Developer(
id = "johndoe",
name = "John Doe",
email = "john@example.com",
url = url("https://github.com/johndoe")
)
)
ThisBuild / description := "My awesome Scala library"
ThisBuild / licenses := List("Apache 2" -> new URL("http://www.apache.org/licenses/LICENSE-2.0.txt"))
ThisBuild / homepage := Some(url("https://github.com/example/my-project"))
// Remove all additional repository other than Maven Central from POM
ThisBuild / pomIncludeRepository := { _ => false }
ThisBuild / publishTo := {
val nexus = "https://oss.sonatype.org/"
if (isSnapshot.value) Some("snapshots" at nexus + "content/repositories/snapshots")
else Some("releases" at nexus + "service/local/staging/deploy/maven2")
}
ThisBuild / publishMavenStyle := true
Advanced Features
Custom Commands
scala
// Define custom command
lazy val hello = Command.command("hello") { state =>
println("Hello from custom command!")
state
}
// Add command to project
commands += hello
// Parameterized command
lazy val greet = Command.single("greet") { (state, name) =>
println(s"Hello, $name!")
state
}
commands += greet
Build Info
scala
// project/plugins.sbt
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0")
// build.sbt
lazy val root = (project in file("."))
.enablePlugins(BuildInfoPlugin)
.settings(
buildInfoKeys := Seq[BuildInfoKey](
name,
version,
scalaVersion,
sbtVersion,
BuildInfoKey.action("buildTime") {
System.currentTimeMillis
}
),
buildInfoPackage := "com.example.buildinfo"
)
Dynamic Versioning
scala
// Version from Git
version := {
import scala.sys.process._
"git describe --tags --always --dirty".!!.trim
}
// Snapshot versions
version := {
val base = "1.0.0"
if (isSnapshot.value) s"$base-SNAPSHOT"
else base
}
Best Practices
Project Organization
scala
// Use consistent naming
lazy val commonSettings = Seq(
organization := "com.example",
scalaVersion := "2.13.10",
scalacOptions ++= Seq("-deprecation", "-feature")
)
// Separate concerns
lazy val core = (project in file("core"))
.settings(commonSettings)
.settings(
name := "my-project-core",
libraryDependencies ++= coreDependencies
)
// Use dependency management
lazy val Dependencies = new {
val catsVersion = "2.9.0"
val akkaVersion = "2.7.0"
val cats = "org.typelevel" %% "cats-core" % catsVersion
val akkaActor = "com.typesafe.akka" %% "akka-actor-typed" % akkaVersion
}
Performance Optimization
scala
// Parallel compilation
Global / concurrentRestrictions := Seq(
Tags.limitAll(4)
)
// Incremental compilation
ThisBuild / incOptions := incOptions.value.withNameHashing(true)
// Fork JVM for tests
Test / fork := true
Test / javaOptions += "-Xmx2G"
// Disable documentation generation in development
Compile / doc / sources := Seq.empty
Compile / packageDoc / publishArtifact := false
Troubleshooting
Common Issues
bash
# Clear cache and reload
sbt clean reload
# Show dependency conflicts
sbt evicted
# Debug dependency resolution
sbt "show dependencyTree"
# Check for updates
sbt dependencyUpdates
# Inspect settings
sbt "inspect compile"
sbt "inspect tree clean"
# Show classpath
sbt "show Runtime/fullClasspath"
Debug Mode
bash
# Enable debug logging
sbt -Dsbt.log.level=debug
# JVM debug options
sbt -J-Xdebug -J-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
# Memory settings
sbt -J-Xmx2G -J-XX:+CMSClassUnloadingEnabled
Resources
- Official Documentation: scala-sbt.org
- Getting Started: scala-sbt.org/1.x/docs/Getting-Started.html
- Plugin Index: scala-sbt.org/release/docs/Community-Plugins.html
- Best Practices: github.com/sbt/sbt/wiki/Best-Practices