Saturday, January 4, 2014

build.sbt vs Build.scala

http://www.scala-sbt.org/0.12.2/docs/Getting-Started/Full-Def.html
http://www.scala-sbt.org/release/docs/Getting-Started/Basic-Def.html#sbt-vs-scala-definition

To mix .sbt and .scala files in your build definition, you need to understand how they relate.
The following two files illustrate. First, if your project is in hello, create hello/project/Build.scala as follows:
import sbt._
import Keys._

object HelloBuild extends Build {

    val sampleKeyA = SettingKey[String]("sample-a", "demo key A")
    val sampleKeyB = SettingKey[String]("sample-b", "demo key B")
    val sampleKeyC = SettingKey[String]("sample-c", "demo key C")
    val sampleKeyD = SettingKey[String]("sample-d", "demo key D")

    override lazy val settings = super.settings ++
        Seq(sampleKeyA := "A: in Build.settings in Build.scala", resolvers := Seq())

    lazy val root = Project(id = "hello",
                            base = file("."),
                            settings = Project.defaultSettings ++ Seq(sampleKeyB := "B: in the root project settings in Build.scala"))
}
Now, create hello/build.sbt as follows:
sampleKeyC in ThisBuild := "C: in build.sbt scoped to ThisBuild"

sampleKeyD := "D: in build.sbt"
Start up the sbt interactive prompt. Type inspect sample-a and you should see (among other things):
[info] Setting: java.lang.String = A: in Build.settings in Build.scala
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}/*:sample-a
and then inspect sample-c and you should see:
[info] Setting: java.lang.String = C: in build.sbt scoped to ThisBuild
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}/*:sample-c
Note that the "Provided by" shows the same scope for the two values. That is, sampleKeyC in ThisBuild in a.sbt file is equivalent to placing a setting in the Build.settings list in a .scala file. sbt takes build-scoped settings from both places to create the build definition.
Now, inspect sample-b:
[info] Setting: java.lang.String = B: in the root project settings in Build.scala
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}hello/*:sample-b
Note that sample-b is scoped to the project ({file:/home/hp/checkout/hello/}hello) rather than the entire build ({file:/home/hp/checkout/hello/}).
As you've probably guessed, inspect sample-d matches sample-b:
[info] Setting: java.lang.String = D: in build.sbt
[info] Provided by:
[info]  {file:/home/hp/checkout/hello/}hello/*:sample-d
sbt appends the settings from .sbt files to the settings from Build.settings and Project.settings which means .sbt settings take precedence. Try changing Build.scala so it sets key sample-c or sample-d, which are also set in build.sbt. The setting in build.sbt should "win" over the one in Build.scala.
One other thing you may have noticed: sampleKeyC and sampleKeyD were available inside build.sbt. That's because sbt imports the contents of your Build object into your .sbt files. In this case import HelloBuild._was implicitly done for the build.sbt file.
In summary:
  • In .scala files, you can add settings to Build.settings for sbt to find, and they are automatically build-scoped.
  • In .scala files, you can add settings to Project.settings for sbt to find, and they are automatically project-scoped.
  • Any Build object you write in a .scala file will have its contents imported and available to .sbt files.
  • The settings in .sbt files are appended to the settings in .scala files.
  • The settings in .sbt files are project-scoped unless you explicitly specify another scope.

When to use .scala files

In .scala files, you are not limited to a series of settings expressions. You can write any Scala code includingvalobject, and method definitions.
One recommended approach is to define settings in ``.sbt`` files, using ``.scala`` files when you need to factor out a ``val`` or ``object`` or method definition.
Because the .sbt format allows only single expressions, it doesn't give you a way to share code among expressions. When you need to share code, you need a .scala file so you can set common variables or define methods.
There's one build definition, which is a nested project inside your main project. .sbt and .scala files are compiled together to create that single definition.
.scala files are also required to define multiple projects in a single build. More on that is coming up in Multi-Project Builds.
(A disadvantage of using .sbt files in a multi-project build is that they'll be spread around in different directories; for that reason, some people prefer to put settings in their .scala files if they have sub-projects. This will be clearer after you see how multi-project builds work.)

1 comment:

errd said...

Hi there!
Thank you for useful article.

Unfortunately, when I'm trying to deal with sample-b key, I've got the following error:
[hello] $ inspect sample-b
[error] Not a valid project ID: sample-b
[error] Expected ':' (if selecting a configuration)
[error] Not a valid key: sample-b (similar: sample-a, sample-d, sample-c)
[error] inspect sample-b
[error] ^

I'm not sure if this issue is connected with sbt/scala versions.
scalaVersion := "2.11.6"
sbt.version=0.13.8

Could you help me, please!