Cycles

(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.

Defining Projects

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:

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.

Project Settings

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.

No defaults

For example, to define a build from scratch (with no default settings or tasks):

Settings defined in .sbt files are appended to the settings for each Project definition.

Build-level Settings

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:

Project Relationships

There are three kinds of project relationships in sbt. These are described by execution, classpath, and configuration dependencies.

Project References

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:

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).

Execution Dependency

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:

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):

Classpath Dependencies

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:

"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:

Configuration Dependencies

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.