How to include test classes and test dependencies in sbt asssembly - scala

I need to package my test classes, resources and also test dependencies with sbt assembly.
This question sbt-assembly : including test classes didn't help - test:assembly still didn't generate a jar with any of the desired classes included.
Note that my setup currently looks like this:
FooBuild.scala:
lazy val cucumberAssemblySettings = assemblySettings ++ Seq(
mainClass in assembly := Some("cucumber.api.cli.Main"),
jarName in assembly := "cucumber.jar",
mergeStrategy in assembly := {
case "application.conf" => MergeStrategy.concat
case "logback.xml" => MergeStrategy.last
case x => defaultMergeStrategy(x)
}
)
And it's about the subproject foo-cucumber:
lazy val foo_cucumber = Project("foo-cucumber", file("foo-cucumber"))
.settings(defaultSettings: _*)
.settings(cucumberAssemblySettings: _*)
.dependsOn(
foo_test_server % "compile->compile;test->test",
foo_test_utils % "compile->compile;test->test"
)
even if I append (Test, assembly) in the settings above, I only get a jar (whose name is not the one specified, but the full name, with version) that does not contain test classes or dependencies, no matter whether I invoke sbt foo-cucumber/test:assembly or sbt foo-cucumber/assembly
How do I get a jar that has everything in it (compile and test classes and dependencies)

The key for the multi-module project is to set the settings this way:
.settings(inConfig(Test)(cucumberAssemblySettings): _*)
and then not have any in (Test, assembly) in the settings

Related

sbt-assembly - can not find main class in jar

I have a project where I am trying to create a fat jar using the sbt-assembly plugin. When I attempt to run my main class from the jar using the java -jar command, I get the error message: Error: Could not find or load main class com.gordon.timeshare.apps.TimeShareLauncher.
I only have one main class in my project (I use the extends App syntax to accomplish this), so I do not specify the path to the main class explicitly, although I have tried that and it did not help.
Below are all the settings I have in my build.sbt file.
ThisBuild / version := "0.1.0-SNAPSHOT"
ThisBuild / organization := "com.gordon.timeshare.apps"
ThisBuild / scalaVersion := "2.13.5"
lazy val app = (project in file("app"))
.settings(
assembly / mainClass := Some("com.gordon.timeshare.apps.TimeShareLauncher"),
assembly / assemblyJarName := "TimeShareLauncher.jar"
)
assemblyMergeStrategy in assembly := {
case PathList("META-INF", xs # _*) => MergeStrategy.discard
case x => MergeStrategy.first
}
I have also tried other strategies like deduplicate, but that would give me an error when trying to make the .jar.
Additionally, when making the .jar, I get a warning:
[warn] Could not create directory C:\Users\dgord\workspace\new-timeshare\timeshare\target\streams\_global\assembly\_global\streams\assembly\88fbe735ce5abc6987fbc59b072404628cdc94b4_a99f2fe2a42747ed9809d4f62f51a9e1b336dde8_da39a3ee5e6b4b0d3255bfef95601890afd80709\META-INF\versions\9: java.nio.file.FileAlreadyExistsException: C:\Users\dgord\workspace\new-timeshare\timeshare\target\streams\_global\assembly\_global\streams\assembly\88fbe735ce5abc6987fbc59b072404628cdc94b4_a99f2fe2a42747ed9809d4f62f51a9e1b336dde8_da39a3ee5e6b4b0d3255bfef95601890afd80709\META-INF\versions\9
And in case you want to know what my main class looks like:
package com.gordon.timeshare.apps
object TimeShareLauncher extends App
sbt: 1.4.7 (also tried 1.5.5)
sbt-assembly: 1.1.0
scala 2.13.5
I have also tried this on WSL and had the same result.
The issue is with lazy val app = (project in file("app")). Assuming a single module project with no module named app, sbt-assembly will create a directory named app and attempt to stuff the build in there. However, since the main class is not in the app bundle, the class will not be added to the jar file.
The correct way to do this is:
lazy val app = (project in file(".")), which specifies the current directory as the one to look for the main class. So this was not really an issue with knowing how to use the sbt-assembly plugin, but a more general issue with specifying projects in an sbt build.

Rename assembly-generated uberjar in SBT

How to rename and move an uberjar generated with SBT assembly plugin?
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")
My assemblyMergeStrategy(for META-INF removal):
assemblyMergeStrategy in assembly := {
case PathList("META-INF", xs # _*) => MergeStrategy.discard
case x => MergeStrategy.first
}
It generates something like :
target/scala-2.12/my-project-assembly-0.1.jar
which I would like to able to automatically rename (and generate in another directory) with a consistent name (without the need of a separate script).
You can find a bit of documentation in project's page. There, you can find the keys you can rewrite for the assembly task.
The ones you are searching for are assemblyJarName and assemblyOutputPath. Then, your project build should look something like:
lazy val myProject = (project in file(".")).
settings(
...
assemblyJarName in assembly := "myName.jar",
assemblyOutputPath in assembly := "...",
...
)

Multiple main classes with SBT assembly

I'm looking to create jars for AWS Lambda to run job tasks. Currently my build.sbt file looks something like this:
lazy val commonSettings = Seq(...)
lazy val core = project
.settings(commonSettings: _*)
lazy val job = project
.settings(commonSettings: _*)
.dependsOn(core)
lazy val service = project
.settings(commonSettings: _*)
.settings(
mainClass in assembly := Some("io.example.service.Lambda"),
assemblyJarName in assembly := "lambda.jar"
)
.dependsOn(core)
Running sbt assembly assembles the service module into a jar for my API and that works fine. The module job however will have multiple Main classes (one pr. job) and when I run sbt assembly job the service module is also assembled (even through its not depended on).
How can I configure my setup to only assemble the job module when needed, and specify individual mainClasses as separately assembled jars?
Set mainClass in assembly in job to define which main class to use, and run job/assembly to just assemble the job assembly jar.
You will need to override the default main class at build time by setting the property explicitly.
sbt "; set mainClass in assembly := Some("another/class"); job/assembly"
Not sure it's good practice but alternatively you can define a sub-project for each job with the correct main class set.
lazy val job1 = project
.settings(commonSettings: _*)
.settings(
mainClass in assembly := Some("io.example.service.Lambda"),
assemblyJarName in assembly := "lambda.jar"
)
.dependsOn(core)
lazy val job2 = project
.settings(commonSettings: _*)
.settings(
mainClass in assembly := Some("io.example.service.Lambda2"),
assemblyJarName in assembly := "lambda2.jar"
)
.dependsOn(core)

sbt-assembly: How do I exclude the configuration files in src/main/resources

I am using sbtassembly from https://github.com/sbt/sbt-assembly to package my project.
I'm wondering is there anyway to exclude the resource files?
You can specify files (and paths) to exclude by customizing the mergeStrategy:
https://github.com/sbt/sbt-assembly#excluding-specific-files
So for discarding specific file you can do something like this:
// build.sbt
assemblyMergeStrategy in assembly := {
case PathList("about.html") => MergeStrategy.discard
case x =>
val oldStrategy = (assemblyMergeStrategy in assembly).value
oldStrategy(x)
}
Here's the documentaion for all available strategies:
https://github.com/sbt/sbt-assembly#merge-strategy
Using Dani's approach with sbt 0.13.13, the config files were still included in my jar. This worked, though:
excludeFilter in Compile := "myconfig.conf",
In my case, all the files have the same name, myconfig.conf, but exist within a tree structure under src/main/resources/config. I tried:
unmanagedResourceDirectories in Compile += { baseDirectory.value / "src/main/resources/config" },
But it removed the directories from the jar, leaving the files.
It's documented here: http://www.scala-sbt.org/0.13/docs/Howto-Customizing-Paths.html

sbt-assembly include test classes

I follow sbt-assembly : including test classes from a config described in https://github.com/sbt/sbt-assembly that work ok doing assembly
When I load sbt I get
assembly.sbt:5: error: reference to jarName is ambiguous;
it is imported twice in the same scope by
import sbtassembly.AssemblyKeys._
and import _root_.sbtassembly.AssemblyPlugin.autoImport._
jarName in (Test, assembly) := s"${name.value}-test-${version.value}.jar"
^
So, I comment import line and run sbt:assembly but that begin the test but dont generate any -test-.jar.
Any one know how to generate the jar that include the test classes?
Thanks
I had to remove this line (I think it is now autoimported based on https://github.com/sbt/sbt-assembly/blob/546d200477b64e2602beeb65bfa04306122cd9f5/Migration.md)
import sbtassembly.AssemblyKeys._
And I added the rest (i.e. the two lines below) to build.sbt instead of assembly.sbt:
Project.inConfig(Test)(baseAssemblySettings)
jarName in (Test, assembly) := s"${name.value}-test-${version.value}.jar"
After taking those steps, test:assembly does produce a test jar for me however I expected the jar to only include test classes (similar to test:package), but it seems to include non-test classes as well. In other words, if I have src/main/scala/Foo.scala and src/test/scala/FooTest.scala then I thought that the jar produced by test:assembly would only include FooTest.class but it seems to also include Foo.class. Hopefully that's not an issue for you as I'm not yet sure how to workaround that.
EDIT: If you want the jar to only include classes from src/test (like I did), then you can add the following to your build.sbt to filter out everything else that may be on your classpath:
fullClasspath in (Test, assembly) := {
val cp = (fullClasspath in (Test, assembly)).value
cp.filter({x => x.data.getPath.contains("test-classes")})
}
This works for me:
lazy val root = project.settings(
assembly / fullClasspath := (assembly / fullClasspath).value ++ (Test / fullClasspath).value
)