czr_RR
czr_RR

Reputation: 591

Build sbt project in uber jar instead of individual submodule jars

Been recently trying to change the way my Scala project does the assembly so that only a single jar is generated out of it instead of one per module.

main-project
| - inner-module-one [inner_module_one]
| - inner-module-two [inner_module_two]

What I've currently done is the following for the main module (the one I want its uber jar containing other modules content).

project
  .in(file("."))
  .settings(
    name := "main-project",
    assemblyName,
    settings
  )
  .aggregate(
    inner_module_one,
    inner_module_two
  )

Having the other two modules declared as follows.

lazy val inner_module_one = project
  .in(file("inner-module-one"))
  .settings(
    name := "inner-module-one",
    assemblyName,
    settings,
    dependencies and such (...)
  )
  .dependsOn(
    inner_module_two
  )

The jar file generated for the main-project is really, really small (no more than 5mbs in size) and only contains Scala related stuff, no project classes or such. However, other modules jars are complete and contains everything they require.

I've already tried adding the following setting to the main-project.

aggregate in assembly := false

But still no luck so far. Jars for submodules aren't generated but the main-project jar still doesn't contain the contents of the submodules.

Any clue where the issue could be?

EDIT

Tried what @LuisMiguelMejíaSuárez suggested and seems to be wanting to build, however, some errors arise that were already solved within their respective modules. In a given module there are some conflicts which are solved due to some overrides, but now they are appearing again.

[error] deduplicate: different file contents found in the following:

Having the dependsOn instead of aggregate affects the way dependencies are added, overridden and such?

Upvotes: 1

Views: 768

Answers (1)

Tomer Shetah
Tomer Shetah

Reputation: 8539

Let's divide your question into two sections, how to include the submodules into the main-project, and you want to know how to not package assembly the 2 submodules.

Let's start from the easier, the second. sbt-assembly, is a plugin for sbt. Just like any other plugin, you cann disable it using the disablePlugins command. Therefore, if you define:

lazy val inner_module_one = project.disablePlugins(AssemblyPlugin)
  .in(file("inner-module-one"))
  .settings(
    name := "inner-module-one"
)
.dependsOn(
  inner_module_two
)

then inner_module_one won't build a jar.

To solve the first question, we need to understand the difference between aggregate and dependsOn. For that I'll quote from Jacek Laskowski great explanation:

aggregate causes the tasks to be executed in the aggregating module and all aggregated one while dependsOn sets a CLASSPATH dependency so the libraries are visible to the aggregateing module (depending on the configuration that's compile aka default in the example).

Therefore, when you aggregate inner_module_one, and inner_module_two, you just cause then to assembly as well. In order to get those to be part of main-project all you need to do is declare:

project
  .in(file("."))
  .settings(
    name := "main-project",
    assemblyName,
    settings
  )
  .dependsOn(
    inner_module_one,
    inner_module_two
  )

In order to test it, I created the following structue:

main-project
|-inner_module_one
 |-main
  |-scala
   |One.scala
|-inner_module_two
 |-main
  |-scala
   |Two.scala

In order to check whether the class One was in a jar I used the command:

jar tfv inner-module-one/target/scala-2.12/inner-module-one-assembly-0.1.0-SNAPSHOT.jar | grep One 

Then, when running sbt assembly with the original aggregate, the command above provided empty results on the output jar. And when running sbt assembly with dependsOn, the command above provided one result:

480 Fri Oct 09 01:48:14 IDT 2020 One.class

If this causes conflicts, you can read about it at sbt-assembly: deduplication found error

Upvotes: 2

Related Questions