Multi project basics
While a simple program can start out as a single-project build, it's more common for a build to split into smaller, multiple subprojects.
Each subproject in a build has its own source directories, generates its own JAR file when you run packageBin
, and in general works like any other project.
A subproject is defined by declaring a lazy val
of type Project. For example, :
scalaVersion := "3.7.3"
LocalRootProject / publish / skip := true
lazy val core = (project in file("core"))
.settings(
name := "core",
)
lazy val util = (project in file("util"))
.dependsOn(core)
.settings(
name := "util",
)
The name of the val
is used as the subproject's ID, which is used to refer to the subproject in the sbt shell.
sbt will always define a root project, so in the above build definition, we will have total of three subprojects.
Subproject dependency
A subproject may depend on the code from another subproject. This is done by declaring dependsOn(...)
. For example, if util
needed util
on its classpath, you would define util
as:
lazy val util = (project in file("util"))
.dependsOn(core)
Task aggregation
Task aggregation means that running a task on the aggregate subproject will also run on the aggregated subprojects.
scalaVersion := "3.7.3"
lazy val root = (project in file("."))
.autoAggregate
.settings(
publish / skip := true
)
lazy val util = (project in file("util"))
lazy val core = (project in file("core"))
In the above example, the root subproject aggregates util
and core
. When you type compile
in the sbt shell, all tree subprojects are compiled in parallel.
Root project
The subproject at the root of the build is called a root project, and often plays a special role in the build. If a subproject is not defined at the root directory of the build, sbt automatically creates a default one that aggregates all other subprojects in the build.
Common settings
In sbt 2.x, bare settings that are written directly in build.sbt
without settings(...)
are common settings that are injected to all subprojects.
scalaVersion := "3.7.3"
lazy val core = (project in file("core"))
lazy val app = (project in file("app"))
.dependsOn(core)
In the above, the scalaVersion
setting is applied to the default root subproject, core
, and util
.
One exception to this rule is settings that are already scoped to a subproject.
scalaVersion := "3.7.3"
lazy val core = (project in file("core"))
lazy val app = (project in file("app"))
.dependsOn(core)
// This is applied only to app
app / name := "app1"
We can take advantage of this exception to add some settings that only apply to the default root project as follows:
scalaVersion := "3.7.3"
lazy val core = (project in file("core"))
lazy val app = (project in file("app"))
.dependsOn(core)
// These are applied only to root
LocalRootProject / name := "root"
LocalRootProject / publish / skip := true