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

182 lines
6.6 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
other project.
Defining projects in a ``.scala`` file
--------------------------------------
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``:
::
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.
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
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.
*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.
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``
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
projects are compiled.
*In the project doing the aggregating*, the root ``hello`` project in
this case, you can control aggregation per-task. So for example in
``hello/build.sbt`` you could avoid aggregating the ``update`` task:
::
aggregate in update := false
``aggregate in update`` is the ``aggregate`` key scoped to the
``update`` task, see :doc:`scopes <Scopes>`.
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``:
::
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
creates an ordering between the projects when compiling them;
``hello-bar`` must be updated and compiled before ``hello-foo`` 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")``.
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
necessarily have to compile the root project, you could compile only a
subproject.
Next
----
Move on to create :doc:`custom settings <Custom-Settings>`.