SBT: How to get started using the Build.scala file (instead of build.sbt)

This is an excerpt from the Scala Cookbook (partially modified for the internet). This is Recipe 18.16, “Using Build.scala Instead of build.sbt.”

Problem

In an SBT project, you want to use the project/Build.scala file instead of build.sbt to define your Scala project, or you need some examples of how to use Build.scala to solve build problems that can’t be handled in build.sbt.

Solution

The recommended approach when using SBT is to define all your simple settings (key/value pairs) in the build.sbt file, and handle all other work, such as build logic, in the project/Build.scala file. However, it can be useful to use only the project/Build.scala file to learn more about how it works.

To demonstrate this, don’t create a build.sbt file in your project, and then do create a Build.scala file in the project subdirectory by extending the SBT Build object:

import sbt._
import Keys._

object ExampleBuild extends Build {

  val dependencies = Seq(
      "org.scalatest" %% "scalatest" % "1.9.1" % "test"
  )

  lazy val exampleProject = Project("SbtExample", file(".")) settings(
      version       := "0.2",
      scalaVersion  := "2.10.0",
      scalacOptions := Seq("-deprecation"),
      libraryDependencies ++= dependencies
  )

}

With just this Build.scala file, you can now run all the usual SBT commands in your project, including compile, run, package, and so on.

Discussion

The Build.scala file shown in the Solution is equivalent to the following build.sbt file:

name := "SbtExample"

version := "0.2"

scalaVersion := "2.10.0"

scalacOptions += "-deprecation"

libraryDependencies += "org.scalatest" %% "scalatest" % "1.9.1" % "test"

As mentioned, the recommended approach when working with SBT is to define your basic settings in the build.sbt file, and perform all other work in a Build.scala file, so creating a Build.scala file with only settings in it is not a best practice. However, when you first start working with a Build.scala file, it’s helpful to see a “getting started” example like this.

Also, although the convention is to name this file Build.scala, this is only a convention, which I use here for simplicity. You can give your build file any legal Scala filename, as long as you place the file in the project directory with a .scala suffix. Another convention is to name this file after the name of your project, so the Scalaz project uses the name ScalazBuild.scala (as shown at this URL).

The “Full Configuration Example” in the SBT documentation

The Full Configuration Example in the SBT documentation and the ScalazBuild.scala build file both show many more examples of what can be put in a Build.scala file. For instance, the Full Configuration Example shows how to add a series of resolvers to a project:

// build 'oracleResolvers'
object Resolvers {
  val sunrepo    = "Sun Maven2 Repo" at "http://download.java.net/maven/2"
  val sunrepoGF  = "Sun GF Maven2 Repo" at
                   "http://download.java.net/maven/glassfish" 
  val oraclerepo = "Oracle Maven2 Repo" at "http://download.oracle.com/maven"
  val oracleResolvers = Seq (sunrepo, sunrepoGF, oraclerepo)
}

object CDAP2Build extends Build {
  import Resolvers._
  // more code here ...

  // use 'oracleResolvers' here
  lazy val server = Project (
    "server",
    file ("cdap2-server"),
    settings = buildSettings ++ Seq (resolvers := oracleResolvers, 
                                     libraryDependencies ++= serverDeps)
  ) dependsOn (common)

This code is similar to the example shown in Recipe 18.11, “Telling SBT How to Find a Repository,” where the following configuration line is added to a build.sbt file:

resolvers += "Java.net Maven2 Repository" at "http://download.java.net/maven/2/"

The ScalazBuild.scala file also shows many examples of using TaskKey and SettingKey, which are different types of keys that can be used in SBT project definition files.

See Also