Neil
Neil

Reputation: 25865

How to set up a multi-project SBT project with inheritance and shared dependencies

I'd like to create an SBT project with inheritance and shared dependencies.

With Maven's POM files, there is the idea of Project Inheritance where you can set a parent project. I'd like to do the same thing with SBT.

The xchange-stream library uses Maven's Project Inheritance to resolve subproject dependencies when compiled from the parent project.

Here is my idea of what the file structure would look like:

sbt-project/
  project/
    dependencies.scala    # Contains dependencies common to all projects
  build.sbt               # Contains definition of parent project with references
                          # to subprojects

  subproject1/
    build.sbt             # Contains `subproject3` as a dependency

  subproject2/
    build.sbt             # Contains `subproject3` as a dependency

  subproject3/
    build.sbt             # Is a dependency for `subproject1` and `subproject2`

Where project1 and project2 can include project3 in their dependencies lists like this:

libraryDependencies ++= "tld.organization" % "project3" % "1.0.0"

Such that when subproject1 or subproject2 are compiled by invoking sbt compile from within their subdirectories, or when the parent: sbt-project is compiled from the main sbt-project directory, then subproject3 will be compiled and published locally with SBT, or otherwise be made available to the projects that need it.

Also, how would shared dependencies be specified in sbt-project/build.sbt or anywhere in the sbt-project/project directory, such that they are useable within subproject1 and subproject2, when invoking sbt compile within those subdirectories?

The following examples don't help answer either of the above points:

  1. jbruggem/sbt-multiproject-example: Uses recursive build.sbt files, but doesn't share dependencies among child projects.

  2. Defining Multi-project Builds with sbt: pbassiner/sbt-multi-project-example: Uses a single build.sbt file for the projects in their subdirectories.

  3. sachabarber/SBT_MultiProject_Demo: Uses a single build.sbt file.

Upvotes: 6

Views: 1862

Answers (2)

Viktor Podzigun
Viktor Podzigun

Reputation: 59

You can use Mecha super-repo concept. Take a look on the setup and docs here: https://github.com/storm-enroute/mecha

The basic idea is that you can combine dependent sbt projects (with their own build.sbt) under single root super-repo sbt project:

/root
  /project/plugins.sbt
  repos.conf

  /project1
    /src/..
    /project/plugins.sbt
    build.sbt

  /project2
    /src/..
    /project/plugins.sbt
    build.sbt

Please, note that there is no build.sbt in the root folder! Instead there is repos.conf file. It contains definition of the sub-repos and looks like the folowing:

  root {
    dir = "."
    origin = ""
    mirrors = []
  }

  project1 {
    dir = "project1"
    origin = "[email protected]:some_user/project1.git"
    mirrors = []
  }

  project2 {
    dir = "project2"
    origin = "[email protected]:some_user/project2.git"
    mirrors = []
  }

Then you can specify the Inter-Project, source-level Dependencies within individual projects.

There are two approaches:

  • dependencies.conf file
  • or in the build source code

For more details, please, see the docs

Upvotes: 3

Eugene Yokota
Eugene Yokota

Reputation: 95654

Such that when subproject1 or subproject2 are compiled by invoking sbt compile from within their subdirectories...

Maybe Maven is meant to be used together with the shell environment and cd command, but that's not how sbt works at least as of sbt 1.x in 2019.

The sbt way is to use sbt as an interactive shell, and start it at the top level. You can then either invoke compilation as subproject1/compile, or switch into it using project subproject1, and call compile in there.

house plugin

A feature similar to parent POM would be achieved by creating a custom plugin.

package com.example

import sbt._
import Keys._

object FooProjectPlugin extends AutoPlugin {
  override def requires = plugins.JvmPlugin

  val commonsIo = "commons-io" % "commons-io" % "2.6"

  override def buildSettings: Seq[Def.Setting[_]] = Seq(
    organization := "com.example"
  )
  override def projectSettings: Seq[Def.Setting[_]] = Seq(
    libraryDependencies += commonsIo
  )
}

sbt-sriracha

It's not exactly what you are asking for, but I have an experimental plugin that allows you to switch between source dependency and binary dependency. See hot source dependencies using sbt-sriracha.

Using that you could create three individual sbt builds for project1, project2, and project3, all located inside $HOME/workspace directory.

ThisBuild / scalaVersion     := "2.12.8"
ThisBuild / version          := "0.1.1-SNAPSHOT"

lazy val project3Ref = ProjectRef(workspaceDirectory / "project3", "project3")
lazy val project3Lib = "tld.organization" %% "project3" % "0.1.0"

lazy val project1 = (project in file("."))
  .enablePlugins(FooProjectPlugin)
  .sourceDependency(project3Ref, project3Lib)
  .settings(
    name := "project1"
  )

With this setup, you can launch sbt -Dsbt.sourcemode=true and it will pick up project3 as a subproject.

Upvotes: 3

Related Questions