sbt/src/sphinx/Getting-Started/Multi-Project.rst

182 lines
6.4 KiB
ReStructuredText
Raw Normal View History

2012-09-15 00:08:35 +02:00
====================
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>` and
:doc:`.scala build definition <Full-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 `src/main/scala`, generates
its own jar file when you run `package`, and in general works like any
2012-09-15 00:08:35 +02:00
other project.
Defining projects in a `.scala` file
2012-09-15 00:08:35 +02:00
--------------------------------------
To have multiple projects, you must declare each project and how they
relate in a `.scala` file; there's no way to do it in a `.sbt` file.
However, you can define settings for each project in `.sbt` files.
Here's an example of a `.scala` file which defines a root project
`hello`, where the root project aggregates two sub-projects,
`hello-foo` and `hello-bar`:
2012-09-15 00:08:35 +02:00
::
import sbt._
import Keys._
object HelloBuild extends Build {
lazy val root = Project(id = "hello",
base = file(".")) aggregate(foo, bar)
lazy val foo = Project(id = "hello-foo",
base = file("foo"))
lazy val bar = Project(id = "hello-bar",
base = file("bar"))
}
sbt finds the list of `Project` objects using reflection, looking for
fields with type `Project` in the `Build` object.
2012-09-15 00:08:35 +02:00
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.
2012-09-15 00:08:35 +02:00
Any `.sbt` files in `foo`, say `foo/build.sbt`, will be merged
2012-09-15 00:08:35 +02:00
with the build definition for the entire build, but scoped to the
`hello-foo` project.
2012-09-15 00:08:35 +02:00
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
2012-09-15 00:08:35 +02:00
prompt. You should get something like this (with whatever versions you
defined):
.. code-block:: console
2012-09-15 00:08:35 +02:00
> 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.
2012-09-15 00:08:35 +02:00
*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
2012-09-15 00:08:35 +02:00
shown above, listing the projects and base directories. *There is no
need to put settings in the `.scala` file.*
2012-09-15 00:08:35 +02:00
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.
2012-09-15 00:08:35 +02:00
You cannot have a `project` subdirectory or `project/*.scala` files
in the sub-projects. `foo/project/Build.scala` would be ignored.
2012-09-15 00:08:35 +02:00
Aggregation
-----------
Projects in the build can be completely independent of one another, if
you want.
In the above example, however, you can see the method call
`aggregate(foo, bar)`. This aggregates `hello-foo` and `hello-bar`
2012-09-15 00:08:35 +02:00
underneath the root project.
Aggregation means that running a task on the aggregate project will also
run it on the aggregated projects. Start up sbt with two subprojects as
in the example, and try `compile`. You should see that all three
2012-09-15 00:08:35 +02:00
projects are compiled.
*In the project doing the aggregating*, the root `hello` project in
2012-09-15 00:08:35 +02:00
this case, you can control aggregation per-task. So for example in
`hello/build.sbt` you could avoid aggregating the `update` task:
2012-09-15 00:08:35 +02:00
::
aggregate in update := false
`aggregate in update` is the `aggregate` key scoped to the
`update` task, see :doc:`scopes <Scopes>`.
2012-09-15 00:08:35 +02:00
Note: aggregation will run the aggregated tasks in parallel and with no
defined ordering.
Classpath dependencies
----------------------
A project may depend on code in another project. This is done by adding
a `dependsOn` method call. For example, if `hello-foo` needed
`hello-bar` on its classpath, you would write in your `Build.scala`:
2012-09-15 00:08:35 +02:00
::
lazy val foo = Project(id = "hello-foo",
base = file("foo")) dependsOn(bar)
Now code in `hello-foo` can use classes from `hello-bar`. This also
2012-09-15 00:08:35 +02:00
creates an ordering between the projects when compiling them;
`hello-bar` must be updated and compiled before `hello-foo` can be
2012-09-15 00:08:35 +02:00
compiled.
To depend on multiple projects, use multiple arguments to `dependsOn`,
like `dependsOn(bar, baz)`.
2012-09-15 00:08:35 +02:00
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")`.
2012-09-15 00:08:35 +02:00
The `->` in `"compile->compile"` means "depends on" so
`"test->compile"` means the `Test` configuration in `foo` would
depend on the `Compile` configuration in `bar`.
2012-09-15 00:08:35 +02:00
Omitting the `->config` part implies `->compile`, so
`dependsOn(bar % "test")` means that the `Test` configuration in
`foo` depends on the `Compile` configuration in `bar`.
2012-09-15 00:08:35 +02:00
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`,
2012-09-15 00:08:35 +02:00
for example.
You can have multiple configurations for a dependency, separated by
semicolons. For example,
`dependsOn(bar % "test->test;compile->compile")`.
2012-09-15 00:08:35 +02:00
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 `compile`, it runs on the current project. So you don't
2012-09-15 00:08:35 +02:00
necessarily have to compile the root project, you could compile only a
subproject.
Next
----
Move on to create :doc:`custom settings <Custom-Settings>`.