Sbt multi-project dependencies

In an sbt configuration, I have a project T that depends on a project S, but some code in T would be handy to write the tests in S. I tried this:

lazy val S = project.dependsOn(T % "test")
lazy val T = project.dependsOn(S % "compile")

but that fails with an infinite recursion.

Is there a way to achieve what I want in sbt?

I believe you will need to make three projects, not two. At least, I don’t see any other way forward here.

(The third project would hold only the tests for S, or only the ones that need T.)

1 Like

It “works” if you define your projects in a .scala file, e.g. project/Build.scala

import sbt._

object Projects {
  val S = project
  val T = project.dependsOn(S % "compile")
  S.dependsOn(T % "test")
}

build.sbt:

lazy val S = Projects.S
lazy val T = Projects.T

Not sure if that really solves your specific problem though because I’m not sure I understand " some code in T would be handy to write the tests in S".

Using a Scala build, I get no complaints from sbt, but it still doesn’t do what I need.
I’ve created a small repository as an illustration: https://github.com/charpov/TestDependencies.
I want S/Test to depend on T/Compile, which itself depends on S/Compile.
Maybe that’s asking too much…

I almost went that route right from the beginning, then thought that maybe it wasn’t necessary if sbt can handle “main” and “test” dependencies separately.

Yeah, I guess that’s really asking too much :slight_smile:
I tried S.dependsOn(T % "compile->test"), but that doesn’t help either.

Using three projects will also save you from trouble when trying to import it into Intellij and potentially other IDEs/tools.

To my understanding, dependencies reside at project level and per-configuration dependencies only further constrain the dependency to subsets of the classpath.

It may be possible to force a cyclic dependency at classpath level, but I’d definitely steer clear of this. What remains of the concept of two separate projects if they are interdependent? Moving the shared parts to a dedicated upstream project seems to be the right choice here.

(I’d go even further and apply this rationale at the package level, as well, according to the Acyclic Dependencies Principle, which used to have some traction in the Java/OO space but doesn’t really seem to be a thing in the Scala camp - or maybe it is, but everybody just takes it for granted, so nobody talks about it much.)

Modularity? It’s being done with mixins, even if there are cycles in the dependencies.

But the only reason I was hoping to do it here is that there is no real cycle. Project S is independent from T and can be packaged and exported by itself. It’s only some test code, not part of the final jar, that depends on T.

I’ll use 3 projects, or see if I really need the dependency from S to T. I may only need to move a few things from T to S to get rid of it.

Acyclic dependencies are often considered a requirement for a good module breakdown. I can see why you’d want to assign test code a special role in this picture, but I’ve found it helps in the long run to consider test code an integral part of the code in basically all respects. sbt seems to take the same view (and having special rules for configuration dependencies would likely complicate the sbt model even further).

If you actually can shift stuff between projects to break the dependencies, that’s very likely the best solution, and this move might actually benefit your module breakdown in other ways, but at least you’ll have reduced coupling somewhat.

sbt lets you specify test-only dependencies, using all this % jazz, e.g., Scalatest % Test and dependsOn(util % "test"), hence my initial hope.