Suppress main class in sbt-assembly

build, maven-assembly-plugin, sbt, sbt-assembly, scala

Solution

`Main-Class` and all other attributes in jar are ultimately determined by `packageOptions in assembly`, so all you could remove `Package.MainClass` from it as follows:

lazy val root = Project(id = "test-assembly",
  base = file("."),
  settings = buildSettings) settings(
  packageOptions in assembly ~= { os => os filterNot {_.isInstanceOf[Package.MainClass]} }
)

This should work for multi-project builds too.

Note: sbt-assembly caches the output jar based on the metadata of the source input, so testing settings around `assembly` requires clearing the cache by calling `clean`.

Problem

In a multi-project SBT build, how should one explicitly suppress the mainClass attribute in SBT-assembly? I've searched extensively but cannot seem to find out how to prevent a main class from being set in the jar built by sbt-assembly in a multi-project build. In a single-project build, this seems to work as long as there are at least two classes that might potentially be invoked by command line: ``` import sbt._ import Keys._ import sbtassembly.Plugin._ import sbtassembly.AssemblyUtils._ import AssemblyKeys._ object TestBuild extends Build { lazy val buildSettings = Defaults.defaultSettings ++ assemblySettings ++ Seq( version := "0.1-SNAPSHOT", organization := "com.organization", scalaVersion := "2.10.2", mergeStrategy in assembly := mergeFirst ) lazy val root = Project(id = "test-assembly", base = file("."), settings = buildSettings) settings( mainClass in assembly := None ) lazy val mergeFirst: String => MergeStrategy = { case "reference.conf" | "rootdoc.txt" => MergeStrategy.concat case PathList("META-INF", xs @ _*) => (xs map {_.toLowerCase}) match { case ("manifest.mf" :: Nil) | ("index.list" :: Nil) | ("dependencies" :: Nil) => MergeStrategy.discard case ps @ (x :: xs) if ps.last.endsWith(".sf") || ps.last.endsWith(".dsa") => MergeStrategy.discard case "plexus" :: xs => MergeStrategy.discard case "services" :: xs => MergeStrategy.filterDistinctLines case ("spring.schemas" :: Nil) | ("spring.handlers" :: Nil) => MergeStrategy.filterDistinctLines case _ => MergeStrategy.first } case _ => MergeStrategy.first } } ``` However, it appears that the `mainClass := None` isn't even necessary. In fact, even if it is left there, a main class will still be set in the manifest if there is only one candidate class. Unforunately, in a multi-project build, I could not prevent a main class from being set even by including an additional class as a dummy entry-point. After a lot of fiddling around, I found that I could prevent a main class from being set by setting `mainClass` to `None` in several scopes independently. In particular, this does the trick: ``` mainClass in assembly := None mainClass in packageBin := None nainClass in Compile := None mainClass in run := None ``` I could not find this requirement stated in the documentation and cannot figure out why this is necessary. Setting `mainClass in (Compile, run) := None` does not work. They really need to be scoped separately. Is this the proper way to manually suppress a main class or am I missing something? If not a bug, I think this should be documented somewhere, especially given that the behavior is not consistent between single- and multi-project builds.

Original source