-
Notifications
You must be signed in to change notification settings - Fork 26
Full Configuration
Wiki Maintenance Note: This page has been mostly replaced by Getting Started Full Def and other pages. It has some obsolete terminology:
- we now avoid referring to build definition as "configuration" to avoid confusion with compile configurations
- we now avoid referring to basic/light/quick vs. full configuration, in favor of ".sbt build definition files" and ".scala build definition files"
However, it may still be worth combing this page for examples or points that are not made in new pages. Some stuff that may not be elsewhere:
- discussion of cycles
- discussion of build-level settings
- discussion of omitting or augmenting defaults
Also, the discussion of configuration delegation which is teased here, needs to exist somewhere.
After extracting useful content, this page could simply be a redirect (delete the content, link to the new pages about build definition).
There is a related page Introduction to Full Configurations which could benefit from cleanup at the same time.
A full configuration consists of one or more Scala source files that define concrete Builds. A Build defines project relationships and configurations.
Create a file with extension .scala
in your project/
directory (such as <your-project>/project/Build.scala
).
A sample project/Build.scala
:
import sbt._
object MyBuild extends Build {
// Declare a project in the root directory of the build with ID "root".
// Declare an execution dependency on sub1.
lazy val root = Project("root", file(".")) aggregate(sub1)
// Declare a project with ID 'sub1' in directory 'a'.
// Declare a classpath dependency on sub2 in the 'test' configuration.
lazy val sub1: Project = Project("sub1", file("a")) dependsOn(sub2 % "test")
// Declare a project with ID 'sub2' in directory 'b'.
// Declare a configuration dependency on the root project.
lazy val sub2 = Project("sub2", file("b"), delegates = root :: Nil)
}
(It is probably best to skip this section and come back after reading about project relationships. It is near the example for easier reference.)
The configuration dependency sub2 -> root
is specified as an argument to the delegates
parameter of Project
, which is by-name and of type Seq[ProjectReference]
because by-name repeated parameters are not allowed in Scala.
There are also corresponding by-name parameters aggregate
and dependencies
for execution and classpath dependencies.
By-name parameters, being non-strict, are useful when there are cycles between the projects, as is the case for root
and sub2
.
In the example, there is a configuration dependency sub2 -> root
, a classpath dependency sub1 -> sub2
, and an execution dependency root -> sub1
.
This causes cycles at the Scala-level, but not within a particular dependency type, which is not allowed.
An internal project is defined by constructing an instance of Project
. The minimum information for a new project is its ID string and base directory. For example:
import sbt._
object MyBuild extends Build {
lazy val projectA = Project("a", file("subA"))
}
This constructs a project definition for a project with ID 'a' and located in the <project root>/subA
directory.
Here, file(...)
is equivalent to new File(...)
and is resolved relative to the build's base directory.
There are additional optional parameters to the Project constructor.
These parameters configure the project and declare project relationships, as discussed in the next sections.
A full build definition can configure settings for a project, just like a light configuration.
Unlike a light configuration, the default settings can be replaced or manipulated and sequences of settings can be manipulated.
In addition, a light configuration has default imports defined. A full definition needs to import these explicitly.
In particular, all keys (like name
and version
) need to be imported from sbt.Keys
.
For example, to define a build from scratch (with no default settings or tasks):
import sbt._
import Keys._
object MyBuild extends Build {
lazy val projectA = Project("a", file("subA"), settings = Seq(name := "From Scratch"))
}
To augment the default settings, the following Project definitions are equivalent:
lazy val a1 = Project("a", file("subA")) settings(name := "Additional", version := "1.0")
lazy val a2 = Project("a", file("subA"),
settings = Defaults.defaultSettings ++ Seq(name := "Additional", version := "1.0")
)
Web support is now split out into a plugin. With the plugin declared, its settings can be selected like:
import sbt_
import Keys._
object MyBuild extends Build {
lazy val projectA = Project("a", file("subA"), settings = Web.webSettings)
}
Settings defined in .sbt
files are appended to the settings for each Project
definition.
Lastly, settings can be defined for the entire build.
In general, these are used when a setting is not defined for a project.
These settings are declared either by augmenting Build.settings
or defining settings in the scope of the current build.
For example, to set the shell prompt to be the id for the current project, the following setting can be added to a .sbt
file:
shellPrompt in ThisBuild := { s => Project.extract(s).currentProject.id + "> " }
(The value is a function State => String
. State
contains everything about the build and will be discussed elsewhere.)
Alternatively, the setting can be defined in Build.settings
:
import sbt._
import Keys._
object MyBuild extends Build {
override lazy val settings = super.settings :+
(shellPrompt := { s => Project.extract(s).currentProject.id + "> " })
...
}
There are three kinds of project relationships in sbt. These are described by execution, classpath, and configuration dependencies.
When defining a dependency on another project, you provide a ProjectReference
.
In the simplest case, this is a Project
object. (Technically, there is an implicit conversion Project => ProjectReference
)
This indicates a dependency on a project within the same build.
It is possible to declare a dependency on a project in a directory separate from the current build, in a git repository, or in a project packaged into a jar and accessible via http/https.
These are referred to as external builds and projects. You can reference the root project in an external build with RootProject
:
RootProject( file("/home/user/a-project") )
RootProject( uri("git://github.com/dragos/dupcheck.git") )
or a specific project within the external build can be referenced using a ProjectRef
:
ProjectRef( uri("git://github.com/dragos/dupcheck.git"), "project-id")
The fragment part of the git URI can be used to specify a specific branch or tag. For example:
RootProject( uri("git://github.com/typesafehub/sbteclipse.git#v1.2") )
Ultimately, a RootProject
is resolved to a ProjectRef
once the external project is loaded.
Additionally, there are implicit conversions URI => RootProject
and File => RootProject
so that URIs and Files can be used directly.
External, remote builds are retrieved or checked out to a staging directory in the user's .sbt
directory so that they can be manipulated like local builds.
Examples of using project references follow in the next sections.
When using external projects, the sbt.boot.directory
should be set (see Setup) so that unnecessary recompilations do not occur (see #35).
If project A has an execution dependency on project B, then when you execute a task on project A, it will also be run on project B. No ordering of these tasks is implied.
An execution dependency is declared using the aggregate
method on Project
. For example:
lazy val root = Project(...) aggregate(sub1)
lazy val sub1 = Project(...) aggregate(sub2)
lazy val sub2 = Project(...) aggregate(ext)
lazy val ext = uri("git://github.com/dragos/dupcheck.git")
If 'clean' is executed on sub2
, it will also be executed on ext
(the locally checked out version).
If 'clean' is executed on root
, it will also be executed on sub1
, sub2
, and ext
.
Aggregation can be controlled more finely by configuring the aggregate
setting. This setting is of type Aggregation
:
sealed trait Aggregation
final case class Implicit(enabled: Boolean) extends Aggregation
final class Explicit(val deps: Seq[ProjectReference], val transitive: Boolean) extends Aggregation
This key can be set in any scope, including per-task scopes. By default, aggregation is disabled for run
, console-quick
, console
, and console-project
. Re-enabling it from the command line for the current project for run
would look like:
> set aggregate in run := true
(There is an implicit Boolean => Implicit
where true
translates to Implicit(true)
and false
translates to Implicit(false)
). Similarly, aggregation can be disabled for the current project using:
> set aggregate in clean := false
Explicit
allows finer control over the execution dependencies and transitivity. An instance is normally constructed using Aggregation.apply
. No new projects may be introduced here (that is, internal references have to be defined already in the Build's projects
and externals must be a dependency in the Build definition). For example, to declare that root/clean
aggregates sub1/clean
and sub2/clean
intransitively (that is, excluding ext
even though sub2
aggregates it):
> set aggregate in clean := Aggregation(Seq(sub1, sub2), transitive = false)
A classpath dependency declares that a project needs the full classpath of another project on its classpath. Typically, this implies that the dependency will ensure its classpath is up-to-date, such as by fetching dependencies and recompiling modified sources.
A classpath dependency declaration consists of a project reference and an optional configuration mapping.
For example, to use project b's compile
configuration from project a's test
configuration:
lazy val a = Project(...) dependsOn(b % "test->compile")
lazy val b = Project(...)
"test->compile"
may be shortened to "test"
in this case. The %
call may be omitted, in which case the mapping is "compile->compile"
by default.
A useful configuration declaration is test->test
. This means to use a dependency's test classes on the dependent's test classpath.
Multiple declarations may be separated by a semicolon. For example, the following says to use the main classes of b
for the compile classpath of a
as well as the test classes of b
for the test classpath of a
:
lazy val a = Project(...) dependsOn(b % "compile;test->test")
lazy val b = Project(...)
Suppose project A has a configuration dependency on project B. If a setting is not found on project A, it will be looked up in project B. This is one aspect of delegation and will be described in detail elsewhere.