Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SBT create sub-projects using a collection

I've been searching if this is possible for a while with little success.

Using SBT, can you create a sub-project programmatically, without explicitly assigning each project to it's own val?

My current project structure looks something like this:

root/
    common/ <--- This is another sub-project that others dependOn
    project/
        build.scala
    src/main/scala
    apps/ <--- sub-projects live here
        Sub1/
        Sub2/

Sub1 and Sub2 are both their own SBT projects.

My first attempt to link these projects together looked like this:

// root/project/build.scala
import sbt._
import Keys._
object build extends Build {
  lazy val common = project /* Pseudo-code */
  val names = List("Sub1", "Sub2")
  lazy val deps = names map { name =>
    Project(id = name, base = file(s"apps/$name")).dependsOn(common)
  }

  lazy val finalDeps = common :: deps
  lazy val root = project.in(file(".")).aggregate(finalDeps.map(sbt.Project.projectToRef) :_*)
                 .dependsOn(finalDeps.map(ClassPathDependency(_, None)) :_*)
}

However, because SBT uses reflection to build it's projects and sub-projects, this doesn't work.

It only works if each sub-project is stated explicitly:

lazy val Sub1 = project.in(file("apps/Sub1"))

So the question:

Is there a way to programmatically build sub-project dependencies in SBT?

like image 677
Snnappie Avatar asked Nov 02 '22 01:11

Snnappie


1 Answers

Sbt allows for making a build definition for the build itself:

http://www.scala-sbt.org/release/docs/Getting-Started/Full-Def.html

You can try creating a project/project/build.scala file that contains a source generator, something like this:

// project/project/build.scala
sourceGenerators in Compile <+= sourceManaged in Compile map { out =>
    Generator.generate(out / "generated")
}

EDIT: You should implement the Generator object yourself.

This source generator will in turn scan the topmost apps folder and create a source for an object that contains all the subprojects.

// project/subprojects.scala
// This is autogenerated from the source generator
object Subprojects{
  lazy val Sub1 = project.in(file("apps/Sub1"))
  lazy val Sub2 = project.in(file("apps/Sub2"))
  lazy val all = Seq(Sub1,Sub2)
}

Now in your main build.scala just write:

// project/build.scala
lazy val root = project.in(file("."))
  .aggregate(Subprojects.all.map(sbt.Project.projectToRef) :_*)
  .dependsOn(Subprojects.all.map(ClassPathDependency(_, None)) :_*)

I didn't run all this through a compiler so some errors are possible but the principle should work.

EDIT: I created a repo on Github where I implemented the solution. Go there and see how it is done.

https://github.com/darkocerdic/sbt-auto-subprojects

like image 200
Darko Cerdic Avatar answered Nov 15 '22 05:11

Darko Cerdic