Search code examples
scalasbtstack-overflowmulti-project

Scala build.sbt: Solving dependency recursions


I just started with SBT's Multi-Project-Builds and I ran into an interesting problem that I have not seen a good example for in the scala-sbt docs.

In my build.sbt, Project B and C are dependent on A, but B is also dependent on C (at least dependent on B's classes in C's testing scope):

(Common is referenced to an Object in root/project/Common.scala)

root/build.sbt:

lazy val prjA: Project = project.in(file("Project-A")).
settings(
    name := "Project-A",
    version := Common.prjVersion,
    scalaVersion := Common.scalaVersion,
    libraryDependencies ++= Common.Imports.compileDependencies,
    libraryDependencies ++= Common.Imports.testDependencies,
)

lazy val prjB: Project = project.in(file("Project-B")).
settings(
    name := "Project-B",
    version := Common.prjVersion,
    scalaVersion := Common.scalaVersion,
    libraryDependencies ++= Common.Imports.compileDependencies,
    libraryDependencies ++= Common.Imports.testDependencies,
).dependsOn(prjA)//.dependsOn(prjC % "test->compile")

lazy val prjC: Project = project.in(file("Project-C")).
settings(
    name := "Project-C",
    version := Common.prjVersion,
    scalaVersion := Common.scalaVersion,
    libraryDependencies ++= Common.Imports.compileDependencies,
    libraryDependencies ++= Common.Imports.testDependencies,
).dependsOn(prjA).dependsOn(prjB)        

This build.sbt, as it is written here, runs successful (via sbt clean update compile) but for sure, I cannot start the test-cases in prjB. Once I establish the .dependsOn(prjC % "test->compile") on prjB in my build.sbt, the output is a StackOverflowError - this makes perfectly sense to me, as the cross-dependency between prjB and prjC can not be solved.

However, is there a practical way to solve this endless recursion? I am thinking about one more step in the building process (1 & 2 are done by the actual build.sbt, as you can see), but I don't know how to do that.

  1. First compile prjB with prjA dependency,
  2. Then compile prjC with prjA and prjB dependency
  3. at last include the builded prjC's classes in prjB for testing purposes. <- is this a valid approach?

Best regards and thanks in advance!


Solution

  • This isn't really an SBT problem, but a module dependency problem. Ignoring A for a moment, since B needs C to compile and C needs B to compile, this cycle cannot be resolved by any build system.

    The only way to solve this is to change the structure of the modules themselves. For example, if possible, you could create a D project that contains the common classes and have them both rely on it. Or, use the Dependency Inversion Principle.