This page outlines the mechanisms by which sbt loads settings for a particular build, including the hooks where users can control the ordering of everything.
As stated elsewhere, sbt constructs its initialization graph and task
graph via Setting[_]
objects. A setting is something which can take
the values stored at other Keys in the build state, and generates a new
value for a particular build key. Sbt converts all registered
Setting[_]
objects into a giant linear sequence and compiles them
into the a task graph. This task graph is then used to execute your
build.
All of sbt’s loading semantics are contained within the Load.scala file. It is approximately the following:
The blue circles represent actions happening when sbt loads a project. We can see that sbt performs the following actions in load:
Compile the user-level project (~/.sbt/<version>/
)
a. Load any plugins defined by this project (~/.sbt/<version>/plugins/*.sbt
and ~/.sbt/<version>/plugins/project/*.scala
)
b. Load all settings defined (~/.sbt/<version>/*.sbt
and ~/.sbt/<version>/plugins/*.scala
)
Compile the current project (<working-directory/project
)
a. Load all defined plugins (project/plugins.sbt
and project/project/*.scala
)
b. Load/Compile the project (project/*.scala
)
*.sbt
files (build.sbt
and friends).
Each of these loads defines several sequences of settings. The diagram shows the two most important:
buildSettings
- These are settings defined to be in ThisBuild
or
directly against the Build
object. They are initialized once for
the build. You can add these, e.g. in project/build.scala
:
object MyBuild extends Build {
override val settings = Seq(foo := "hi")
}
or in a build.sbt
file :
foo in ThisBuild := "hi"
projectSettings
- These are settings specific to a project. They
are specific to a particular sub project in the build. A plugin
may be contributing its settings to more than on project, in which
case the values are duplicated for each project. You add project
specific settings, eg. in project/build.scala
:
object MyBuild extends Build {
val test = project.in(file(".")).settings(...)
}
After loading/compiling all the build definitions, sbt has a series of
Seq[Setting[_]]
that it must order. As shown in the diagram, the
default inclusion order for sbt is:
project/Build.scala
~/.sbt/<verison>/*.sbt
)
build.sbt
)
The order which sbt uses to load settings is configurable at a project
level. This means that we can’t control the order of settings added to
Build/Global namespace, but we can control how each project loads, e.g.
plugins and .sbt
files. To do so, use the AddSettings
class :
import sbt._
import Keys._
import AddSettings._
object MyOwnOrder extends Build {
// here we load config from a txt file.
lazy val root = project.in(file(".")).settingSets( autoPlugins, buildScalaFiles, sbtFiles(file("silly.txt")) )
}
In the above project, we’ve modified the order of settings to be:
project/Build.scala
file (shown
above).
silly.txt
file.
What we’ve excluded:
~/.sbt/<verison>
)
*.sbt
settings.
The AddSettings object provides the following “groups” of settings you can use for ordering:
autoPlugins
All the ordered settings of plugins after they’ve gone through
dependency resolution
buildScalaFiles
The full sequence of settings defined directly in project/*.scala
builds.
sbtFiles(*)
Specifies the exact setting DSL files to include (files must use the
.sbt
file format)
userSettings
All the settings defined in the user directory ~/.sbt/<version>/
.
defaultSbtFiles
Include all local *.sbt
file settings.
- Note: Be very careful when reordering settings. It’s easy to accidentally remove core functionality.*
For example, let’s see what happens if we move the build.sbt
files
before the buildScalaFile
.
Let’s create an example project the following defintiion. project/build.scala
:
object MyTestBuild extends Build {
val testProject = project.in(file(".")).settingSets(autoPlugins, defaultSbtFiles, buildScalaFile).settings(
version := scalaBinaryVersion.value match {
case "2.10" => "1.0-SNAPSHOT"
case v => "1.0-for-${v}-SNAPSHOT"
}
)
}
This build defines a version string which appends the scala version if
the current scala version is not the in the 2.10.x
series. Now, when
issuing a release we want to lock down the version. Most tools assume
this can happen by writing a version.sbt
file. version.sbt
:
version := "1.0.0"
However, when we load this new build, we find that the version
in
version.sbt
has been overriden by the one defined in
project/Build.scala
because of the order we defined for settings, so
the new version.sbt
file has no effect.