Confused how to set up a multi-project sbt project

Blankman picture Blankman · Apr 21, 2014 · Viewed 11.9k times · Source

I'm using sbt .13 here.

I have this so far:

import sbt._
import Keys._
import play.Project._

object ApplicationBuild extends Build {

  val appVersion = "1.0"

  resolvers += "local maven" at "/Users/blankman/.m2/repository/"

  val commonDependencies = Seq()
  val modelDependencies = Seq(
    "com.typesafe.slick" %% "slick" % "2.0.1",
    "org.slf4j" % "slf4j-nop" % "1.6.4"
  )

  val serviceDependencies = Seq(
    "com.typesafe.slick" %% "slick" % "2.0.1",
    "org.slf4j" % "slf4j-nop" % "1.6.4"
  )

  val webDependencies = Seq(
    //"org.apache.tomcat" % "tomcat-jdbc" % "8.0.3",
    "mysql" % "mysql-connector-java" % "5.1.30",
    "com.typesafe.slick" %% "slick" % "2.0.1"
  )


  lazy val common = Project(
    id = "app-common",
    base = file("app-common"),
    dependencies = commonDependencies
  )

  lazy val models = Project(
    id = "app-models",
    base = file("app-models"),
    settings(modelDependencies: _*)
    )
  ).dependsOn(common)

  lazy val services = Project(
    id = "app-services",
    base = file("app-services"),
    settings = Seq(
      libraryDependencies ++= serviceDependencies
    )
  ).dependsOn(models, common)


  lazy val web = play.Project("app-web", appVersion, webDependencies,
                        path = file("app-web"))
                      .settings(playScalaSettings: _*)
                      .dependsOn(services)

}

This doesn't work. For example, if I go into:

project app-models

and try and compile, it says compile isn't valid or something.

I'm really confused how to set up a project. What is the correct way?

Looking at this slide #10 here http://jsuereth.com/scala/2013/06/11/effective-sbt.html it says I can do:

lazy val web = (
  Project("app-models", file("app-models"))
  settings(
     libraryDependencies += modelDependencies
  )
)

But when I do this I get an error also.

I basically have a few projects inside of sbt:

common
models
services
web (which is play)
  • models depends on commons
  • services depends on commons + models
  • web depends on services

Can someone help me get this to work?

Answer

Eugene Yokota picture Eugene Yokota · Apr 21, 2014

There are a few issues I found in your build definition, but since you bought up Josh's Effective sbt talk, I think we should go whole hog on the style.

Effective sbt

Here are the files.

project/build.properties

sbt.version=0.13.2

project/play.sbt

val playVersion = "2.2.2"

resolvers += Resolver.typesafeRepo("releases")

addSbtPlugin("com.typesafe.play" % "sbt-plugin" % playVersion) 

project/commons.scala

import sbt._
import Keys._

object Commons {
  val appVersion = "1.0"

  val settings: Seq[Def.Setting[_]] = Seq(
    version := appVersion,
    resolvers += Opts.resolver.mavenLocalFile
  )
}

project/dependencies.scala

import sbt._
import Keys._

object Dependencies {
  val slickVersion = "2.0.1"
  val slick = "com.typesafe.slick" %% "slick" % slickVersion
  val slf4jVersion = "1.6.4"
  val slf4jNop = "org.slf4j" % "slf4j-nop" % slf4jVersion
  val mysqlConnectorVersion = "5.1.30"
  val mysqlConnector = "mysql" % "mysql-connector-java" % mysqlConnectorVersion

  val commonDependencies: Seq[ModuleID] = Seq(
    slick,
    slf4jNop
  )
  val modelDependencies: Seq[ModuleID] = Seq()
  val serviceDependencies: Seq[ModuleID] = Seq()
  val webDependencies: Seq[ModuleID] = Seq(
    mysqlConnector
  )
}

build.sbt

import play.Project._
import Dependencies._

lazy val appCommon = (project in file("app-common")).
  settings(Commons.settings: _*).
  settings(
    libraryDependencies ++= commonDependencies
  )

lazy val appModels = (project in file("app-models")).
  settings(Commons.settings: _*).
  settings(
    libraryDependencies ++= modelDependencies
  ).
  dependsOn(appCommon)

lazy val appServices = (project in file("app-services")).
  settings(Commons.settings: _*).
  settings(
    libraryDependencies ++= serviceDependencies
  ).
  dependsOn(appModels, appCommon)

lazy val appWeb = (project in file("app-web")).
  settings(Commons.settings: _*).
  settings(playScalaSettings: _*).
  dependsOn(appServices)

notes

settings parameter vs settings method

For models and services, you're passing in settings sequence into Project(...) constructor, so the default settings are likely not loaded. You can pass in the default settings manually, or use settings(...) method on Project, which I would recommend.

lazy val appModels = (project in file("app-models")).
  settings(
    libraryDependencies ++= modelDependencies
  ).
  dependsOn(appCommon)

Josh uses postfix notation using parenthesis, but I prefer using dot notation for this, so that's a slight deviation from the talk.

libraryDependencies ++=

As the above example, you have to pass modelDependencies to libraryDependencies. You had it calling directly into settings.

resolvers

The resolvers setting is not passed into anything, which likely is not correct.