Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sbt/sbt/llms.txt

Use this file to discover all available pages before exploring further.

Overview

An sbt build is defined by one or more .sbt files in the project’s base directory, optionally combined with Scala source files in the project/ directory for more complex configuration.

build.sbt Files

The build.sbt file in the project root is the primary build definition file. It uses a Scala-based DSL to configure your build.

Basic Structure

name := "my-project"

version := "1.0.0"

scalaVersion := "2.13.12"

libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "2.3.0"
Each line in build.sbt is a setting expression. Settings are separated by blank lines (not semicolons).

Real-World Example

From the sbt source code itself (build.sbt:1-50):
import Dependencies.*

// ThisBuild settings take lower precedence,
// but can be shared across the multi projects.
ThisBuild / version := {
  val v = "2.0.0-RC9-bin-SNAPSHOT"
  nightlyVersion.getOrElse(v)
}

ThisBuild / organization := "org.scala-sbt"
ThisBuild / description := "sbt is an interactive build tool"
ThisBuild / licenses := List(
  "Apache-2.0" -> url("https://github.com/sbt/sbt/blob/develop/LICENSE")
)

ThisBuild / javacOptions ++= Seq("-source", "1.8", "-target", "1.8")
ThisBuild / Compile / doc / javacOptions := Nil
Settings defined with ThisBuild / apply to all subprojects in a multi-project build.

Project Directory

For more complex build logic, you can write Scala code in project/*.scala files. This is where you define project definitions, custom tasks, and build-level utilities.

Project Definition Example

From sbt’s source (main-settings/src/main/scala/sbt/Project.scala:129-154):
sealed trait Project extends ProjectDefinition[ProjectReference] {
  /** Adds new configurations directly to this project. */
  def configs(cs: Configuration*): Project = 
    copy(configurations = configurations ++ cs)

  /** Adds classpath dependencies on internal or external projects. */
  def dependsOn(deps: ClasspathDep[ProjectReference]*): Project =
    copy(dependencies = dependencies ++ deps)

  /** Adds projects to be aggregated. */
  def aggregate(refs: ProjectReference*): Project =
    copy(aggregate = (aggregate: Seq[ProjectReference]) ++ refs)

  /** Appends settings to the current settings sequence. */
  def settings(ss: Def.SettingsDefinition*): Project =
    copy(settings = (settings: Seq[Def.Setting[?]]) ++ Def.settings(ss*))

  /** Sets the base directory for this project. */
  infix def in(dir: File): Project = copy(base = dir)
}

Creating Projects in Scala

From the sbt build (build.sbt:164-217):
lazy val sbtRoot: Project = (project in file("."))
  .aggregate(
    (allProjects diff Seq(lmCoursierShaded))
      .map(p => LocalProject(p.id))*
  )
  .settings(
    minimalSettings,
    onLoadMessage := """Welcome to the build for sbt.""",
    publishLocal := {},
    mimaPreviousArtifacts := Set.empty
  )

lazy val collectionProj = project
  .in(file("util-collection"))
  .dependsOn(utilPosition, utilCore)
  .settings(
    name := "Collections",
    testedBaseSettings,
    libraryDependencies ++= Seq(sjsonNewScalaJson.value),
    mimaSettings
  )

Build Definition Components

Settings vs Tasks

Settings are evaluated once at project load time:
name := "my-project"  // Setting
scalaVersion := "3.3.1"  // Setting
Tasks are evaluated on demand:
compile := { /* compilation logic */ }  // Task
packageBin := { /* packaging logic */ }  // Task

Common Settings

From sbt’s Keys.scala, here are commonly used settings:
// Project identification (Keys.scala:78-86)
val thisProject = settingKey[ResolvedProject](
  "Provides the current project for the referencing scope."
)
val thisProjectRef = settingKey[ProjectRef](
  "Provides a fully-resolved reference to the current project."
)

// Paths (Keys.scala:147-154)
val baseDirectory = settingKey[File](
  "The base directory. Depending on the scope, this is the base directory for the build, project, configuration, or task."
)
val target = settingKey[File](
  "Main directory for files generated by the build."
)
val sourceDirectory = settingKey[File](
  "Default directory containing sources."
)

Build Structure

A typical sbt project structure:
project-root/
├── build.sbt              # Primary build definition
├── project/
│   ├── build.properties   # sbt version
│   ├── plugins.sbt        # sbt plugin dependencies
│   └── Build.scala        # (optional) Complex build logic
├── src/
│   ├── main/
│   │   ├── scala/        # Main Scala sources
│   │   └── resources/    # Main resources
│   └── test/
│       ├── scala/        # Test Scala sources
│       └── resources/    # Test resources
└── target/               # Generated files
Do not edit files in the target/ directory - they are generated by the build and will be deleted by clean.

Loading Process

When sbt loads a build:
  1. Reads project/build.properties to determine sbt version
  2. Loads plugin definitions from project/*.sbt and project/*.scala
  3. Compiles project/*.scala files
  4. Evaluates build.sbt using the compiled project definition
  5. Resolves all settings and creates the build structure

Best Practices

Keep build.sbt simple: For most projects, build.sbt alone is sufficient. Only use project/*.scala when you need complex logic, custom tasks, or shared build utilities.
Use blank lines as separators: In .sbt files, settings must be separated by at least one blank line. This is a syntactic requirement, not just style.
// Correct
name := "project"

version := "1.0"

// Incorrect - missing blank lines
name := "project"
version := "1.0"

References