Как работает sbt? Что такое .sbt и .scala конфигурации?
sbt (Simple Build Tool, теперь просто "Scala Build Tool") — это основной инструмент сборки для проектов на Scala. Он управляет компиляцией, зависимостями, тестами, запуском и публикацией. sbt тесно интегрирован с экосистемой Scala и предоставляет мощную систему конфигурации через .sbt и .scala файлы.
Как работает sbt
sbt запускается в интерактивном режиме: он загружает проект, поднимает виртуальную машину (JVM), парсит конфигурации и отслеживает изменения в файлах. Благодаря инкрементальной компиляции, он перекомпилирует только те части, которые были изменены.
Основные этапы:
-
Парсит конфигурационные файлы (build.sbt, project/*.scala, project/*.sbt).
-
Загружает зависимости из Maven/ivy репозиториев.
-
Выполняет указанные команды (compile, test, run, clean и т.д.).
-
Мониторит изменения (если в интерактивном режиме — автоматически перекомпилирует при сохранении).
Структура проекта
project/
build.properties
plugins.sbt
MyBuild.scala (опционально)
src/
main/scala/
test/scala/
build.sbt
build.sbt
Это основной файл конфигурации сборки. Он используется для задания настроек проекта в декларативной форме.
Пример:
name := "my-awesome-app"
version := "0.1.0"
scalaVersion := "2.13.13"
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-core" % "2.10.0",
"org.scalatest" %% "scalatest" % "3.2.17" % Test
)
Особенности:
-
.sbt — это Scala-выражения, но в специфическом DSL-стиле (однострочные выражения, без def, val, import).
-
Можно использовать := для установки значения, += / ++= для добавления.
-
Не поддерживает полноценных многострочных def или class — для них используется .scala.
project/plugins.sbt
Файл, в котором задаются плагины, используемые в проекте.
Пример:
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.10.0")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2")
Этот файл — тоже DSL в виде .sbt, но обрабатывается отдельно (для загрузки плагинов).
project/build.properties
Определяет, какая версия sbt используется:
sbt.version=1.9.8
Этот файл критичен для совместимости между разработчиками и CI/CD.
.scala файлы в project/
Если нужно более гибкое поведение, используют .scala файлы внутри project/.
Они содержат полноценный код Scala, например:
import sbt._
import Keys._
object MyBuild extends Build {
lazy val myProject = Project(
id = "my-awesome-app",
base = file("."),
settings = Seq(
name := "My App",
scalaVersion := "2.13.13"
)
)
}
Сегодня такой стиль считается устаревшим в пользу build.sbt и AutoPlugin, но в сложных проектах ещё встречается.
Основные команды sbt
-
compile — компиляция src/main/scala
-
test — запуск тестов
-
run — запуск приложения
-
console — REPL со всеми зависимостями
-
update — загрузка зависимостей
-
reload — перезагрузка конфигурации
-
clean — удаление артефактов сборки
-
~compile — "watch mode": перекомпиляция при изменении файлов
Зависимости и конфигурации
sbt использует механизм конфигураций:
-
Compile — основная сборка
-
Test — код тестов
-
Runtime, Provided, IntegrationTest и т.д.
Можно указать зависимости только для тестов:
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.17" % Test
Или для разных подконфигураций:
configs += IntegrationTest
Defaults.itSettings
libraryDependencies += "com.h2database" % "h2" % "2.2.224" % IntegrationTest
Multi-project build
sbt позволяет организовывать несколько модулей в одном репозитории:
lazy val core = project.in(file("core"))
lazy val api = project.in(file("api")).dependsOn(core)
Это позволяет задавать общие зависимости, кэшировать сборку модулей и управлять иерархией.
Task'и и Settings
sbt устроен вокруг понятий SettingKey, TaskKey, InputKey.
-
SettingKey[T] — значение, определённое на этапе конфигурации (например, version).
-
TaskKey[T] — отложенное значение, вычисляется при вызове (например, compile, run).
-
InputKey[T] — задача с аргументами от пользователя (реже используется).
Пример:
val myTask = taskKey\[Unit\]("Prints Hello")
myTask := {
println("Hello from sbt task!")
}
Дополнительные фишки
-
sbt-shell: запоминает контекст, что позволяет быстро вызывать команды без перезапуска JVM.
-
autoImport: плагины и настройки, доступные в build.sbt.
-
crossScalaVersions: для сборки под разными версиями Scala.
-
scalacOptions: тонкая настройка компиляции.
scalacOptions ++= Seq("-deprecation", "-feature", "-unchecked", "-Xfatal-warnings")
Инкрементальная сборка
sbt использует собственный механизм кэширования и анализа зависимостей между файлами, что делает повторные сборки быстрыми. Он отслеживает:
-
изменения в файлах,
-
API изменений (например, сигнатуры методов),
-
изменения зависимостей.
sbt — мощный, но сложный инструмент, особенно при переходе от простых .sbt файлов к полным .scala-проектам. Он предоставляет масштабируемый способ управления сборкой, конфигурацией и зависимостями в Scala-проектах.