How can sbt generate several jars with different classifiers and different main classes?

325 views Asked by At

I am using sbt to package my project. However there are two main classes in my project based on the same source. And I want to create two jars with different classifiers for these two main classes. Since these two main classes shares most source code in the project, I cannot separate them into two projects. Therefore, could anyone help me modify the build.sbt to achieve such a goal?

1

There are 1 answers

0
Tomer Shetah On

TL;DR: All you need to do is define the mainClass in sbt.

Now let's describe how to do it.

At the root, we need to create sbt.build. Its content is going to be:

lazy val root = project.settings(
    version := "0.0.1-SNAPSHOT",
    scalaVersion := "2.12.12"
).aggregate(moduleA, moduleB)

lazy val commons = project
lazy val moduleA = project.settings(mainClass in (Compile, packageBin) := Some("MainClassA")).dependsOn(commons)
lazy val moduleB = project.settings(mainClass in (Compile, packageBin) := Some("MainClassB")).dependsOn(commons)

You can read more about sbt aggregation, and about How to specify a main method/class to run. The project is going to be structured as follows:

/
|-build.sbt
|-project
    |-plugins.sbt
|-commons
    |-src
        |-main
            |scala
                |-SomeClass.scala
|-moduleA
    |-src
        |-main
            |-scala
                |-MainClassA.scala
|-moduleB
    |-src
        |-main
            |-scala
                |-MainClassB.scala

The content of SomeClass.scala is:

object SomeClass {
  def doPrint() = {
    println("I am in commons")
  }
}

The content of MainClassA.scala is(assuming scala version is up to 2.12 . In scala 2.13 you do not need to extend App annymore.):

object MainClassA extends App {

  override def main(args: Array[String]): Unit = {
      SomeClass.doPrint()
      println("Hello MainClassA")
  }
}

The content of MainClassB.scala is(same comment here):

object MainClassB extends App {

  override def main(args: Array[String]): Unit = {
      SomeClass.doPrint()
      println("Hello MainClassB")
  }
}

The content of plugins.sbt will be:

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")

Now, we have everything ready, and we need to execute. We installed the sbt-assembly plugin, hence we can run: sbt assembly.

This will create some files, but we will be interested in 2:

./moduleA/target/scala-2.12/moduleA-assembly-0.1.0-SNAPSHOT.jar 
./moduleB/target/scala-2.12/moduleB-assembly-0.1.0-SNAPSHOT.jar

Then, when running java -jar ./moduleA/target/scala-2.12/moduleA-assembly-0.1.0-SNAPSHOT.jar we get:

I am in commons
Hello MainClassA

And, when running java -jar ./moduleB/target/scala-2.12/moduleB-assembly-0.1.0-SNAPSHOT.jar we get:

I am in commons
Hello MainClassB

P.S. It is really not recommended to create multiple jars from the same build. It creates a huge risk to miss dependencies and get a runtime exception of missing methods/classes.