mirror of https://github.com/sbt/sbt.git
221 lines
7.0 KiB
ReStructuredText
221 lines
7.0 KiB
ReStructuredText
====================
|
|
Multi-Project Builds
|
|
====================
|
|
|
|
This page introduces multiple projects in a single build.
|
|
|
|
Please read the earlier pages in the Getting Started Guide first, in
|
|
particular you need to understand :doc:`build.sbt <Basic-Def>` before
|
|
reading this page.
|
|
|
|
Multiple projects
|
|
-----------------
|
|
|
|
It can be useful to keep multiple related projects in a single build,
|
|
especially if they depend on one another and you tend to modify them
|
|
together.
|
|
|
|
Each sub-project in a build has its own source directories, generates
|
|
its own jar file when you run :key:`package`, and in general works like any
|
|
other project.
|
|
|
|
A project is defined by declaring a `lazy val` of type `Project <../../api/sbt/Project.html>`_.
|
|
For example, ::
|
|
|
|
lazy val util = project
|
|
|
|
lazy val core = project
|
|
|
|
The name of the val is used as the project's ID and base directory name.
|
|
The ID is used to refer to the project at the command line.
|
|
The base directory may be changed from the default using the `in` method.
|
|
For example, the following is a more explicit way to write the previous example: ::
|
|
|
|
lazy val util = project.in( file("util") )
|
|
|
|
lazy val core = project in file("core")
|
|
|
|
Dependencies
|
|
------------
|
|
|
|
Projects in the build can be completely independent of one another, but usually
|
|
they will be related to one another by some kind of dependency. There are two
|
|
types of dependencies: aggregate and classpath.
|
|
|
|
|
|
Aggregation
|
|
~~~~~~~~~~~
|
|
|
|
Aggregation means that running a task on the aggregate project will also
|
|
run it on the aggregated projects. For example, ::
|
|
|
|
lazy val root =
|
|
project.in( file(".") )
|
|
.aggregate(util, core)
|
|
|
|
lazy val util = project
|
|
|
|
lazy val core = project
|
|
|
|
|
|
In the above example, the root project aggregates `util` and `core`.
|
|
Start up sbt with two subprojects as in the example, and try :key:`compile`.
|
|
You should see that all three projects are compiled.
|
|
|
|
*In the project doing the aggregating*, the `root` project in
|
|
this case, you can control aggregation per-task.
|
|
For example, to avoid aggregating the :key:`update` task:
|
|
|
|
::
|
|
|
|
lazy val root =
|
|
project.in( file(".") )
|
|
.aggregate(util, core)
|
|
.settings(
|
|
aggregate in update := false
|
|
)
|
|
|
|
[...]
|
|
|
|
`aggregate in update` is the :key:`aggregate` key scoped to the :key:`update` task.
|
|
(See :doc:`scopes <Scopes>`.)
|
|
|
|
Note: aggregation will run the aggregated tasks in parallel and with no
|
|
defined ordering between them.
|
|
|
|
Classpath dependencies
|
|
~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
A project may depend on code in another project. This is done by adding
|
|
a `dependsOn` method call. For example, if `core` needed
|
|
`util` on its classpath, you would define `core` as:
|
|
|
|
::
|
|
|
|
lazy val core = project.dependsOn(util)
|
|
|
|
Now code in `core` can use classes from `util`.
|
|
This also creates an ordering between the projects when compiling them;
|
|
`util` must be updated and compiled before `core` can be compiled.
|
|
|
|
To depend on multiple projects, use multiple arguments to `dependsOn`,
|
|
like `dependsOn(bar, baz)`.
|
|
|
|
Per-configuration classpath dependencies
|
|
++++++++++++++++++++++++++++++++++++++++
|
|
|
|
`foo dependsOn(bar)` means that the `compile` configuration in
|
|
`foo` depends on the `compile` configuration in `bar`. You could
|
|
write this explicitly as `dependsOn(bar % "compile->compile")`.
|
|
|
|
The `->` in `"compile->compile"` means "depends on" so
|
|
`"test->compile"` means the `test` configuration in `foo` would
|
|
depend on the `compile` configuration in `bar`.
|
|
|
|
Omitting the `->config` part implies `->compile`, so
|
|
`dependsOn(bar % "test")` means that the `test` configuration in
|
|
`foo` depends on the `Compile` configuration in `bar`.
|
|
|
|
A useful declaration is `"test->test"` which means `test` depends on `test`.
|
|
This allows you to put utility code for testing in `bar/src/test/scala` and then use that code in `foo/src/test/scala`,
|
|
for example.
|
|
|
|
You can have multiple configurations for a dependency, separated by
|
|
semicolons. For example,
|
|
`dependsOn(bar % "test->test;compile->compile")`.
|
|
|
|
|
|
Default root project
|
|
--------------------
|
|
|
|
If a project is not defined for the root directory in the build, sbt creates a default
|
|
one that aggregates all other projects in the build.
|
|
|
|
|
|
Because project `hello-foo` is defined with `base = file("foo")`, it
|
|
will be contained in the subdirectory `foo`. Its sources could be
|
|
directly under `foo`, like `foo/Foo.scala`, or in
|
|
`foo/src/main/scala`. The usual sbt :doc:`directory structure <Directories>`
|
|
applies underneath `foo` with the exception of build definition files.
|
|
|
|
Any `.sbt` files in `foo`, say `foo/build.sbt`, will be merged
|
|
with the build definition for the entire build, but scoped to the
|
|
`hello-foo` project.
|
|
|
|
If your whole project is in `hello`, try defining a different version
|
|
(`version := "0.6"`) in `hello/build.sbt`, `hello/foo/build.sbt`,
|
|
and `hello/bar/build.sbt`. Now `show version` at the sbt interactive
|
|
prompt. You should get something like this (with whatever versions you
|
|
defined):
|
|
|
|
.. code-block:: console
|
|
|
|
> show version
|
|
[info] hello-foo/*:version
|
|
[info] 0.7
|
|
[info] hello-bar/*:version
|
|
[info] 0.9
|
|
[info] hello/*:version
|
|
[info] 0.5
|
|
|
|
`hello-foo/*:version` was defined in `hello/foo/build.sbt`,
|
|
`hello-bar/*:version` was defined in `hello/bar/build.sbt`, and
|
|
`hello/*:version` was defined in `hello/build.sbt`. Remember the
|
|
:doc:`syntax for scoped keys <Scopes>`. Each `version` key
|
|
is scoped to a project, based on the location of the `build.sbt`. But
|
|
all three `build.sbt` are part of the same build definition.
|
|
|
|
*Each project's settings can go in `.sbt` files in the base directory
|
|
of that project*, while the `.scala` file can be as simple as the one
|
|
shown above, listing the projects and base directories. *There is no
|
|
need to put settings in the `.scala` file.*
|
|
|
|
You may find it cleaner to put everything including settings in
|
|
`.scala` files in order to keep all build definition under a single
|
|
`project` directory, however. It's up to you.
|
|
|
|
You cannot have a `project` subdirectory or `project/*.scala` files
|
|
in the sub-projects. `foo/project/Build.scala` would be ignored.
|
|
|
|
Navigating projects interactively
|
|
---------------------------------
|
|
|
|
At the sbt interactive prompt, type `projects` to list your projects
|
|
and `project <projectname>` to select a current project. When you run
|
|
a task like :key:`compile`, it runs on the current project. So you don't
|
|
necessarily have to compile the root project, you could compile only a
|
|
subproject.
|
|
|
|
You can run a task in another project by explicitly specifying the
|
|
project ID, such as `subProjectID/compile`.
|
|
|
|
Common code
|
|
-----------
|
|
|
|
The definitions in `.sbt` files are not visible in other `.sbt` files.
|
|
In order to share code between `.sbt` files, define one or more Scala
|
|
files in the `project/` directory of the build root. This directory
|
|
is also an sbt project, but for your build.
|
|
|
|
For example:
|
|
|
|
`<root>/project/Common.scala` ::
|
|
|
|
import sbt._
|
|
import Keys._
|
|
|
|
object Common {
|
|
def text = "org.example"
|
|
}
|
|
|
|
`<root>`/build.sbt ::
|
|
|
|
organization := Common.text
|
|
|
|
See :doc:`Full-Def` for details.
|
|
|
|
Next
|
|
----
|
|
|
|
Move on to :doc:`using plugins <Using-Plugins>`.
|