My favorites | Sign in
Project Home Downloads Wiki Issues
Search
for

sbt has moved

sbt has now completely moved to GitHub.

See https://github.com/harrah/xsbt/wiki.

Getting Started

SubProjects  
Updated Jul 27, 2011 by dmhar...@gmail.com

sbt has moved

sbt has now completely moved to GitHub.

See https://github.com/harrah/xsbt/wiki/FullConfiguration.

Overview

This page describes how to manage multiple projects within a project. The first section details the features of subproject support and the second section describes how to use this support.

Features

Summary

  • Declare dependencies between projects and between tasks in different projects
  • Execute tasks across multiple projects
  • Classpath of all dependencies is included when compiling a project
  • Proper inter-project source dependency handling
  • Can change to a project in an interactive session to work only on that project (and its dependencies)
  • Can run tasks in parallel

Details

With subproject support in sbt, you can specify project-level dependencies and execute tasks across multiple projects. For example, assume you have three projects A, B, and C and you specify that C depends on B and B depends on A. When you execute the compile action on project C, it will first execute compile on A, then B, and finally on C.

Additionally, the classpath of a project's dependency is available to that project. So, in the situation above, the classpath of C includes the A and B classpaths. This includes the compiled classes of A and B and all libraries of A and B in addition to those explicitly specified for C.

Source dependencies are tracked between projects, so that the right sources are recompiled when a dependency is updated. For example, assume each project has two classes. A has A1 and A2, B has B1 and B2, and C has C1 and C2. Let C1 depend on B1 and B1 depend on A1. Now, assume class A1 is modified. Executing compile on project C will recompile class A1 in project A, then class B1 in project B, and finally, class C1 in project C. A2 will only be recompiled if it depends on A1. B2 will only be recompiled if it depends on A1 or B1 (or A2 if A2 depends on A1). C2 will only be recompiled if it depends on A1 or B1 or C1 (or A2 or B2 if they required recompilation).

Finally, when working in an interactive session, you can switch to a specific project to only execute commands on that project and its dependencies. More details on this are given in the next section.

Usage

To demonstrate subprojects, consider a project for a simulator (flight simulator, circuit simulator, ...). It might have the backend as one project and the user interface as a separate project. Then, there might be some examples demonstrating using the backend as a library or some examples showing how to use the user interface. The following project definition (which goes in project/build as described in BuildConfiguration) would be one way of describing this project:

import sbt._

class SimulatorProject(info: ProjectInfo) extends ParentProject(info)
{
   lazy val core = project("core", "Simulator Core")
   lazy val ui = project("ui", "Simulator User Interface", core)
   
   lazy val examples = project("examples", "Simulator Examples", new Examples(_))
   
   class Examples(info: ProjectInfo) extends ParentProject(info)
   {
      lazy val embedded = project("embedded", "Embedded Simulator Example", core)
      lazy val standalone = project("standalone", "Standalone Simulator Example", ui)
   }
}

The first line

import sbt._

imports everything in the sbt package. All sbt classes for general use are currently in the sbt package. The next line

class SimulatorProject(info: ProjectInfo) extends ParentProject(info)

defines a class with the usual constructor signature and is a subclass of sbt.ParentProject, which is in turn a subclass of sbt.Project. ParentProject is the starting point for configuring a project that defines other projects. SimulatorProject will be the initial project on sbt startup and all actions will be performed on it.

The remaining lines define subprojects of the main project. The project method is defined in sbt.Project and has several variants. The main variant used here has the signature

def project(path: Path, name: String, deps: Project*): Project

A Path is described in Paths. The path provided to project specifies the directory containing the subproject and is relative to the project directory of the enclosing project (SimulatorProject in this case). name is the name of the project and is specified here to avoid the need to set the name property in every subproject directory like in a single project. The version of the new project is inherited from the enclosing project. The last argument is a list of dependencies of the project.

Implicit in using this method is that the project is of the default project type, sbt.DefaultProject. This can be changed by using the variant of project with another argument before the dependencies of type:

ProjectInfo => Project

As is shown for the definition of examples, this is usually just the constructor of a Project subclass.

The first note about Examples is that it is embedded in SimulatorProject. This is important so that sbt only finds one public, concrete, top-level subclass of Project and automatically loads it on startup. Alternatively, the class could be top-level but prefixed with protected. Next, Examples subclasses ParentProject and so it defines subprojects itself. These subprojects can depend on projects in the outer class, as is done in Examples.

Using a Project definition other than sbt.DefaultProject is also useful for projects containing code (usually subclasses of ScalaProject), although this is not shown here. This way, all project configuration can be done in the parent project if desired.

Build Definitions in Subprojects

Alternatively, each subproject can configured with its project directory as is done with a single project. In this case, the form of project used would be:

def project(path: Path, deps: Project*): Project

There are performance and type-checking disadvantages to this, however.

Project definitions in subprojects are built after the parent project. When you call the project method to define a subproject, the subproject is built and loaded. The consequence is that building and loading of subprojects is done separately and serially. There is a substantial performance overhead to running multiple, small compilations and this translates to a performance disadvantage when recompiling multiple subprojects. The disadvantage is likely less noticeable when reloading a single subproject build definition.

Second, when all project definitions at top-level, there is more static type information. If builds are defined in multiple locations, you can only refer to other projects by the Project type.

class Top(info: ProjectInfo) extends ParentProject(info)
{
    // create a subproject, with some custom information
    lazy val subA = project("a", "A", info => new SubProject("something", info))

    // define a subproject type that provides custom information
    class SubProject(val customInfo: String, info: ProjectInfo)
      extends DefaultProject(info)

    // couldn't do this if SubProject was separate
    lazy val printCustom =
        task { println(subA.customInfo); None }
}

Finally, any code shared between builds must be defined as plugins because the classpaths of subprojects are not visible to each other.

Parallel Builds

sbt can execute tasks in parallel. Add

  override def parallelExecution = true

to your root project definition to enable this.

sbt and Scala Versions

All projects in a multi-project build must use the same version of sbt and Scala for building. If a different version of sbt or Scala is set on a subproject, it is ignored. In a future release, this may generate a warning or an error.

Dependency Management

A ParentProject by default does not publish an artifact other than an Ivy file or a pom.

Project dependencies declared using the project method are automatically added to a generated ivy.xml or pom for each project. That is, for:

import sbt._
class SimulatorProject(info: ProjectInfo) extends ParentProject(info)
{
   lazy val core = project("core", "Simulator Core")
   lazy val ui = project("ui", "Simulator User Interface", core)
}

the generated ivy.xml for Simulator User Interface will declare a dependency on Simulator Core in the compile configuration. To change the default behavior, override deliverProjectDependencies in the depending project (here, Simulator User Interface). deliverProjectDependencies returns the module IDs (the type of object created by "org.example" % "name" % "1.0") for each immediate project dependency. You can add or remove projects from this list or modify the configuration mappings.

To remove core from ui:

  class UIProject(info: ProjectInfo) extends DefaultProject(info) {
    override def deliverProjectDependencies =
     super.deliverProjectDependencies.toList - core.projectID
  }

To declare core as being a dependency of ui in the test configuration:

  class UIProject(info: ProjectInfo) extends DefaultProject(info) {
    override def deliverProjectDependencies =
      super.deliverProjectDependencies - core.projectID ++ Seq(core.projectID % "test->default")
  }

To declare no dependencies on other subprojects:

  override def deliverProjectDependencies = Nil

If you don't want to publish a subproject, you can do the following steps:

  • Override the relevant actions to do nothing:
  •   def doNothing = task { None }
      override def publishLocalAction = doNothing
      override def deliverLocalAction = doNothing
      override def publishAction = doNothing
      override def deliverAction = doNothing
  • For each project that depends on that subproject, remove it from deliverProjectDependencies.:
  •     override def deliverProjectDependencies =
         super.deliverProjectDependencies.toList - subproject.projectID
Comment by paul.bridger, Sep 21, 2009

It's not clear how to perform a build action on a specific subproject from the command-line. For example, how would I do sbt run on the ui subproject?

Comment by project member dmhar...@gmail.com, Sep 28, 2009

You couldn't until now. sbt 0.5.5 will let you run the same commands from the command-line that you could interactively. Then you can do, for example:

  sbt "project ui" compile

-Mark

Comment by dustin.w...@gmail.com, Oct 23, 2009

Noting for others: took me a while to figure out that if I wanted to use the project defs in the subdirectories where my subprojects are, I should use either

project (path : Path, deps : Project) : Project or project (path : Path, name : java.lang.String, deps : Project) : Project

I found this in the ScalaDocs? for the Project class

Comment by hoegqvist@gmail.com, Jan 6, 2010

I have a project with the same structure as the SimulatorProject? in the example above. The sub-projects both have a number of dependencies defined inline in a config-file under project/build/Config.scala, but these are not resolved when running update in the main project.

It works when defining the dependencies in ivy.xml and ivysettings.xml, but I prefer to do it inline. Can I use inline dependency management with sub-projects?

Mikael

Comment by project member dmhar...@gmail.com, Jan 6, 2010

See the part about using configurations defined in subprojects instead of in the main project configuration. It is right about the Parallel Builds section and is mentioned in the comment above yours.

In particular, you want to use the following method:

project(path: Path, deps: Project*): Project

instead of:

project(path: Path, name: String, deps: Project*): Project

which tells sbt to use the default configuration.

-Mark

Comment by toni.latev@gmail.com, Mar 2, 2010

I want to call sbt from a separate scala file. Can I just import sbt. and call xsbt.boot methods to start compiling a project?

Comment by heiko.se...@gmail.com, May 1, 2010

Small fix: "... important so that sbt only finds one public, concrete, top-level subclass of Project and automatically loads it on startup ..." => It should say ".. subclass of ParentProject? ...", right?

Comment by project member dmhar...@gmail.com, May 1, 2010

ParentProject is never required. Your top project could be a DefaultProject or your own Project type.

Comment by kris.nuttycombe, May 4, 2010

How does one go about specifying the version for a subproject artifact such that it may vary independently of the version of the parent artifact?

Comment by project member dmhar...@gmail.com, May 4, 2010

You can set it in

<sub>/project/build.properties
by direct edit or
project Sub set project.version x.y.z
or with
override def version = ...

version has type Version, a neglected attempt to provide structure to versions. OpaqueVersion(anyString) is the simplest constructor or there is Version.fromString.

Comment by project member dmhar...@gmail.com, May 4, 2010

didn't display right:

project Sub
and then
set project.version x.y.z

Comment by oleg.mir...@gmail.com, May 15, 2010

Hello! Have spent a few hours reading this wiki and experimenting with sbt; many things are still far from being clear, so maybe I point them out in hope it helps to improve the documentation.

1) What exactly is a subproject by definition? What is a typical folder structure? Who depends on whom: parent on child (would be unusual, wouldn't it?) or child on parent?

2) Is it only ParentProject? that one must inherit from if one is to have subprojects? Can DefaultWebProject?'s descendants have subprojects?

3) Suppose I'm working on a LiftWeb? project, and I want to have many main classes in it for utility purposes (e.g. various data transformation tasks). Each task has a separate folder in src for that, next to main and test. Can one use the subproject mechanism for that, or it is irrelevant? Would be inconvenient to create a separate project for every utility task. This problem is solved in Eclipse by creating separate run configurations for every main class. What is sbt's analogue?

Thanks a lot for your work on sbt!

/Oleg.

Comment by remi.del...@gmail.com, Jul 19, 2010

Hello,

I would like to have the following multi project structure :

Project1
--> project
--> src
    --> main
       --> scala
Project2
--> project
--> src
    --> main
       --> scala

And tell sbt that project2 depends on project1 so I made this configuration class in project2/project/build

import sbt._

class Project2(info: ProjectInfo) extends ParentProject(info)
{
   lazy val proj1 = project( ".."/"project1", "project1" )
   lazy val proj2 = project( path("."), "project2", proj1 )
}

and sbt yelled that the ".." and "." not allowed in paths. my problem is that project1 is really a backend to be shared by several other projects, that needs to exist on its own and be tagged independently, so I want these two projects to be on their own at the root of the folder hierarchy of the version management system. How can I do that?

Comment by Soren...@gmail.com, Aug 2, 2010

I have the same issue as remi.delmas.3000

Comment by Soren...@gmail.com, Aug 2, 2010

Using Path.fromFile("../Project1") works !

Comment by razvanc99@gmail.com, Aug 7, 2010

I would really appreciate a clear example on how to do multiproject dependencies in the case described above by remi.delmas.3000, where there are multiple projects in the same workspace and they depend on each-other. Anyone here used Eclipse?

That structure is very common in development and I'm not sure who and why should be a parent of whom and why?

Comment by regis.jean-gilles%mimesis-republic.com@gtempaccount.com, Nov 4, 2010

Thank you for this great project. I too have the same problem as remi.delmas.3000. It is really unfortunate that project dependencies somewhat dictates their layout on disk. This seems like an arbitrary limitation. Is there any rationale behind this, and do you plan to enhance this situation? Regards.

Comment by dirk.lou...@gtempaccount.com, Dec 2, 2010

Even though I do have the servlet api jar in my <root>/<subproject>/lib_managed/test directory (due to automatic dependency management), the compiler is unable to find the javax.servlet package! It used to work when my DeafultWebProject? was my root project, but since I have changed it to be a subproject it stopped working. Are DefaultWebProjects? allowed to be subprojects? Any other possibilities?

Comment by JimI...@gmail.com, May 27, 2011

Here is a skeleton project I created that demonstrates sub module dependencies as well as maven support.

https://github.com/jiminoc/SBT-Maven-Example-Project

Powered by Google Project Hosting