==================== 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 ` and :doc:`.scala build definition ` 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 ` 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 `. 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 `. 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 ` 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 `.