Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

sbt

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.

like image 368
Neil Avatar asked Aug 15 '18 23:08

Neil


People also ask

Which is the correct way to add dependencies in sbt file?

If you have JAR files (unmanaged dependencies) that you want to use in your project, simply copy them to the lib folder in the root directory of your SBT project, and SBT will find them automatically.

How do we specify library dependencies in sbt?

The libraryDependencies key Most of the time, you can simply list your dependencies in the setting libraryDependencies . It's also possible to write a Maven POM file or Ivy configuration file to externally configure your dependencies, and have sbt use those external configuration files.

What is sbt dependency management?

These tools support automatic dependency management for your project. Like in almost every build system, SBT allows you to define library dependencies which are resolved automatically, so you don't have to download and package required libraries by yourself.

What is project in file in sbt?

A project is defined by declaring a lazy val of type Project. For example, : lazy val util = (project in file("util")) lazy val core = (project in file("core")) The name of the val is used as the subproject's ID, which is used to refer to the subproject at the sbt shell.


2 Answers

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.

like image 96
Eugene Yokota Avatar answered Oct 23 '22 04:10

Eugene Yokota


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

like image 27
Viktor Podzigun Avatar answered Oct 23 '22 05:10

Viktor Podzigun