James Davies James Davies - 1 year ago 154
Scala Question

Using DependsOn between two ScalaJS SBT projects

(Long question ahead. Simplified tl;dr at the bottom).

I have two ScalaJS projects built with SBT - "myapp" and "mylib", in the following directory structure




exports an artifact named "com.example:mylib:0.1", which as used as a libraryDependency for

myapp and mylib are in separate repositories, contain their own build files, and should be able to be build completely separately (i.e. they must contain their own individual build config).

In production, they will be built separately with
being first published as a maven artifact before building

In development however, I want to be able to merge these into a parent SBT project so that both can be developed in parallel without needing to use
after each change.

In a traditional (not scalajs) project this would be quite easy


lazy val mylib = project
lazy val myapp = project.dependsOn(mylib)

However in ScalaJS, we actually have two projects inside each module -
. As such, the above configuration only finds the aggregate root project and does not correctly apply the
configuration to the actual JVM and JS projects.

(i.e. myapp and mylib build.sbt each contains two projects, and an aggregate root project)

Ideally I'd like to be able to do something like the following

lazy val mylibJVM = project
lazy val myappJVM = project.dependsOn(mylibJVM)

lazy val mylibJS = project
lazy val myappJS = project.dependsOn(myappJS)

Unfortunately this just creates new projects within the root instead of importing the subprojects themselves.

I've also tried various combinations of paths (such as)

lazy val mylibJVM = project.in(file("mylib/jvm"))

But this doesn't see configuration in build.sbt file in

Ultimately I keep running up against the same problem - when importing an existing multi-project SBT project into a parent sbt file, it imports the root project, but does not seem to provide a way to import a subproject from an existing multimodule SBT file in a way that lets me add
configuration to it.


If I have

  • root/mylib/build.sbt
    with multiple projects defined and

  • root/myapp/build.sbt
    with multiple projects defined

Is it possible to import individual subprojects into
instead of the root project from the submodule?

i.e. Can I have two layers of multiproject builds.

Answer Source

After spending a lot of time digging through SBT source code, I managed to figure out a solution. This isn't clean, but it works. (For bonus points, it imports correctly into IntelliJ).

// Add this function to your root build.sbt file. 
// It can be used to define a dependency between any
// `ProjectRef` without needing a full project definition.
def addDep(from:String, to:String) = {
  buildDependencies in Global <<= (
  buildDependencies in Global, 
  thisProjectRef in from, 
  thisProjectRef in to) { 
    (deps, fromref, toref) => 
      deps.addClasspath(fromref, ResolvedClasspathDependency(toref, None))

// `project` will import the `build.sbt` file
// in the subdirectory of the same name as the `lazy val`
// (performed by an SBT macro). i.e. `./mylib/build.sbt`
// This won't reference the actual subprojects directly, 
// will but import them into the namespace such that they 
// can be referenced as "ProjectRefs", which are implicitly
// converted to from strings.
// We then aggregate the JVM and JS ScalaJS projects 
// into the new root project we've defined. (Which unfortunately
// won't inherit anything from the child build.sbt)

lazy val mylib = project.aggregate("mylibJVM","mylibJS")
lazy val myapp = project.aggregate("myappJVM","myappJS")

// Define a root project to aggregate everything
lazy val root = project.in(file(".")).aggregate(mylib,myapp)

// We now call our custom function to define a ClassPath dependency
// between `myapp` -> `mylib` for both JVM and JS subprojects.
// In particular, this will correctly find exported artifacts
// so that `myapp` can refer to `mylib` in libraryDependencies
// without needing to use `publishLocal`. 
addDep("myappJVM", "mylibJVM")