SBT Cheatsheet¶
Überblick¶
SBT (Scala Build Tool) ist ein interaktives Build-Tool für Scala, Java und andere JVM-Sprachen. Es verwendet Scala für Build-Definitionen und bietet inkrementelle Zusammenstellung, Tests und Bereitstellung.
Installation¶
Paketmanager¶
# 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
```_
### Überprüfung
```bash
sbt --version
```_
## Projektstruktur
### Standard Layout
Grundaufbau. Sbt¶
Minimales Beispiel¶
```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 ) ) ```_
Komplettes Beispiel¶
```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" ) ) ```_
Abhängigkeiten¶
Abhängigkeit 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 Configuration¶
```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") ) ```_
Resolver¶
```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" ) ```_
Aufgaben und Befehle¶
Grundlegende Befehle¶
```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 ```_
Interaktive 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-Projekt Befehle¶
```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 ```_
Einstellungen und Aufgaben¶
Individuelle Einstellungen¶
```scala // Define custom setting lazy val myCustomSetting = settingKeyString
// Define custom task lazy val myCustomTask = taskKeyUnit
lazy val root = (project in file(".")) .settings( myCustomSetting := "Hello World", myCustomTask := \\{ val value = myCustomSetting.value println(s"Custom setting value: $value") \\} ) ```_
Aufgabenabhängigkeiten¶
```scala lazy val generateSources = taskKeySeq[File] lazy val processResources = taskKeyUnit
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
) ```_
Umfangreiche Einstellungen¶
```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¶
Gemeinsame 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 Konfiguration¶
```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 ```_
Prüfung¶
Testkonfiguration¶
```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"
)
) ```_
Integrationstests¶
```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"))) ) ```_
Konfiguration erstellen¶
Erstellungseinstellungen¶
```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"
) ```_
Ressourcenmanagement¶
```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"
) ```_
Veröffentlichungen¶
Veröffentlichung¶
```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
) ```_
Komplette Veröffentlichung¶
```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 ```_
Erweiterte Funktionen¶
Benutzerdefinierte Befehle¶
```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 ```_
Informationen zum Thema¶
```scala // project/plugins.sbt addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0")
// build.sbt lazy val root = (project in file(".")) .enablePlugins(BuildInfoPlugin) .settings( buildInfoKeys := SeqBuildInfoKey \\{ System.currentTimeMillis \\} ), buildInfoPackage := "com.example.buildinfo" ) ```_
Dynamische Ausführung¶
```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¶
Projektorganisation¶
```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 \\} ```_
Leistungsoptimierung¶
```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 ```_
Fehlerbehebung¶
Gemeinsame Themen¶
```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 ```_
Ressourcen¶
- Amtsdokumentation: scala-sbt.org
- **Getting gestartet*: scala-sbt.org/1.x/docs/Getting-Started.html
- **Plugin Index*: scala-sbt.org/release/docs/Community-Plugins.html
- **Beste Praktiken*: github.com/sbt/wiki/Best-Practices