Excluding dependency transitively for assembly only? [duplicate] - sbt

Here's an example build.sbt:
import AssemblyKeys._
assemblySettings
buildInfoSettings
net.virtualvoid.sbt.graph.Plugin.graphSettings
name := "scala-app-template"
version := "0.1"
scalaVersion := "2.9.3"
val FunnyRuntime = config("funnyruntime") extend(Compile)
libraryDependencies += "org.spark-project" %% "spark-core" % "0.7.3" % "provided"
sourceGenerators in Compile <+= buildInfo
buildInfoPackage := "com.psnively"
buildInfoKeys := Seq[BuildInfoKey](name, version, scalaVersion, target)
assembleArtifact in packageScala := false
val root = project.in(file(".")).
configs(FunnyRuntime).
settings(inConfig(FunnyRuntime)(Classpaths.configSettings ++ baseAssemblySettings ++ Seq(
libraryDependencies += "org.spark-project" %% "spark-core" % "0.7.3" % "funnyruntime"
)): _*)
The goal is to have spark-core "provided" so it and its dependencies are not included in the assembly artifact, but to reinclude them on the runtime classpath for the run- and test-related tasks.
It seems that using a custom scope will ultimately be helpful, but I'm stymied on how to actually cause the default/global run/test tasks to use the custom libraryDependencies and hopefully override the default. I've tried things including:
(run in Global) := (run in FunnyRuntime)
and the like to no avail.
To summarize: this feels essentially a generalization of the web case, where the servlet-api is in "provided" scope, and run/test tasks generally fork a servlet container that really does provide the servlet-api to the running code. The only difference here is that I'm not forking off a separate JVM/environment; I just want to manually augment those tasks' classpaths, effectively "undoing" the "provided" scope, but in a way that continues to exclude the dependency from the assembly artifact.

For a similar case I used in assembly.sbt:
run in Compile <<= Defaults.runTask(fullClasspath in Compile, mainClass in (Compile, run), runner in (Compile, run))
and now the 'run' task uses all the libraries, including the ones marked with "provided". No further change was necessary.
Update:
#rob solution seems to be the only one working on latest SBT version, just add to settings in build.sbt:
run in Compile := Defaults.runTask(fullClasspath in Compile, mainClass in (Compile, run), runner in (Compile, run)).evaluated,
runMain in Compile := Defaults.runMainTask(fullClasspath in Compile, runner in(Compile, run)).evaluated

Adding to #douglaz' answer,
runMain in Compile <<= Defaults.runMainTask(fullClasspath in Compile, runner in (Compile, run))
is the corresponding fix for the runMain task.

Another option is to create separate sbt projects for assembly vs run/test. This allows you to run sbt assemblyProj/assembly to build a fat jar for deploying with spark-submit, as well as sbt runTestProj/run for running directly via sbt with Spark embedded. As added benefits, runTestProj will work without modification in IntelliJ, and a separate main class can be defined for each project in order to e.g. specify the spark master in code when running with sbt.
val sparkDep = "org.apache.spark" %% "spark-core" % sparkVersion
val commonSettings = Seq(
name := "Project",
libraryDependencies ++= Seq(...) // Common deps
)
// Project for running via spark-submit
lazy val assemblyProj = (project in file("proj-dir"))
.settings(
commonSettings,
assembly / mainClass := Some("com.example.Main"),
libraryDependencies += sparkDep % "provided"
)
// Project for running via sbt with embedded spark
lazy val runTestProj = (project in file("proj-dir"))
.settings(
// Projects' target dirs can't overlap
target := target.value.toPath.resolveSibling("target-runtest").toFile,
commonSettings,
// If separate main file needed, e.g. for specifying spark master in code
Compile / run / mainClass := Some("com.example.RunMain"),
libraryDependencies += sparkDep
)

If you use sbt-revolver plugin, here is a solution for its "reStart" task:
fullClasspath in Revolver.reStart <<= fullClasspath in Compile
UPD: for sbt-1.0 you may use the new assignment form:
fullClasspath in Revolver.reStart := (fullClasspath in Compile).value

Related

Sbt native packager doesn't create service script file

I've used native packager to deploy a play api as a RPM package. The package created lacks the /etc/init.d/ folder and script.
How can I debug this situation ?
Here is an extract from the build.sbt :
lazy val myApp = (project in file("api"))
.enablePlugins(PlayScala, RpmDeployPlugin, SonarPlugin)
.configs(IntegrationTest)
.settings(
organization := "com.organization",
scalaVersion := "2.11.12"
...)
libraryDependencies ++= Seq( etc...)
The problem I had was that I was missing SystemVplugin and RpmDeployPlugin (I used RpmPlugin only)
lazy val `my-api` = (project in file("my-api"))
.enablePlugins(PlayScala, RpmDeployPlugin, SystemVPlugin)
...

sbt aspectj with native packager

I'm attempting to use the sbt-aspectj plugin with the sbt native packager and am running into an issue where the associated -javaagent path to the aspectj load time weaver jar references an ivy cache location rather than something packaged.
That is, after running sbt stage, executing the staged application via bash -x target/universal/stage/bin/myapp/ results in this javaagent:
exec java -javaagent:/home/myuser/.ivy2/cache/org.aspectj/aspectjweaver/jars/aspectjweaver-1.8.10.jar -cp /home/myuser/myproject/target/universal/stage/lib/org.aspectj.aspectjweaver-1.8.10.jar:/home/myuser/myproject/target/universal/stage/lib/otherlibs.jar myorg.MyMainApp args
My target platform is Heroku where the artifacts are built before being effectively 'pushed' out to individual 'dynos' (very analogous to a docker setup). The issue here is that the resulting -javaagent path was valid on the machine in which the 'staged' deployable was built, but will not exist where it's ultimately run.
How can one configure the sbt-aspectj plugin to reference a packaged lib rather than one from the ivy cache?
Current configuration:
project/plugins.sbt:
addSbtPlugin("com.typesafe.sbt" % "sbt-aspectj" % "0.10.6")
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.1.5")
build.sbt (selected parts):
import com.typesafe.sbt.SbtAspectj._
lazy val root = (project in file(".")).settings(
aspectjSettings,
javaOptions in Runtime ++= { AspectjKeys.weaverOptions in Aspectj }.value,
// see: https://github.com/sbt/sbt-native-packager/issues/598#issuecomment-111584866
javaOptions in Universal ++= { AspectjKeys.weaverOptions in Aspectj }.value
.map { "-J" + _ },
fork in run := true
)
Update
I've tried several approaches including pulling the relevant output for javaOptions from existing mappings, but the result is a cyclical dependency error thrown by sbt.
I have something that technically solves my problem but feels unsatisfactory. As of now, I'm including an aspectjweaver dependency directly and using the sbt-native-packager concept of bashScriptExtraDefines to append an appropriate javaagent:
updated build.sbt:
import com.typesafe.sbt.SbtAspectj._
lazy val root = (project in file(".")).settings(
aspectjSettings,
bashScriptExtraDefines += scriptClasspath.value
.filter(_.contains("aspectjweaver"))
.headOption
.map("addJava -javaagent:${lib_dir}/" + _)
.getOrElse(""),
fork in run := true
)
You can add the following settings in your sbt config:
.settings(
retrieveManaged := true,
libraryDependencies += "org.aspectj" % "aspectjweaver" % aspectJWeaverV)
AspectJ weaver JAR will be copied to ./lib_managed/jars/org.aspectj/aspectjweaver/aspectjweaver-[aspectJWeaverV].jar in your project root.
I actually solved this by using the sbt-javaagent plugin to adding agents to the runtime

How to successfully import a CrossProject sbt build into eclipse using sbt-eclipse

I've used sbt-eclipse in the past to successfully import a simple sbt project into eclipse. I'm now trying to leverage the CrossProject mechanism of sbt to use the Scala-JS environment (makes 2 subprojects in sbt--one for Javascript and one for JVM code). The recommendation (see SBT docs link here) is to add the setting 'EclipseKeys.useProjectId := true' in the build.sbt file to support importing (now) 2 projects into one eclipse project. I then give the 'eclipse' command in a running SBT session to create my eclipse project and then launch eclipse and attempt to import this new project. When I do this, the import dialog wizard in eclipse does show me two sub-projects, but when I try to finish the import, eclipse complains that the project already exists and I get two strange looking links in my eclipse project that seem to do nothing.
What is the correct procedure for getting a CrossProject sbt build into eclipse?
Ok, so it seems eclipse did not like that I had only one 'name' for the project that was in the shared settings area of the build.sbt I had this:
lazy val sp = crossProject.in(file(".")).
settings(
version := "0.1",
name := "SJSTut",
scalaVersion := "2.11.7"
).
jvmSettings(
// Add JVM-specific settings here
libraryDependencies ++= Seq(...)
).
jsSettings(
// Add JS-specific settings here
libraryDependencies ++= Seq(...)
)
and what I should have done was this:
lazy val sp = crossProject.in(file(".")).
settings(
version := "0.1",
scalaVersion := "2.11.7"
).
jvmSettings(
// Add JVM-specific settings here
name := "SJSTutJVM",
libraryDependencies ++= Seq(...)
).
jsSettings(
// Add JS-specific settings here
name := "SJSTutJS",
libraryDependencies ++= Seq(...)
)
Note the removal of the 'name' assignment from settings and instead, placements into both the jvmSettings and jsSettings area with uniquely different names.
Now I'm able to pull this into eclipse (as 2 separate projects). If anyone else has a better setup, I'd love to hear about it.

Include the contents of a dependency in (mappings in packageBin)

We assemble multiple projects into one jar:
val asmDep = "org.scala-lang.modules" % "scala-asm" % "5.0.3-scala-3"
lazy val compiler = (project in file(".") / "src" / "compiler")
.settings(
libraryDependencies ++= Seq(asmDep),
mappings in Compile in packageBin :=
(mappings in Compile in packageBin).value ++
(mappings in Compile in packageBin in LocalProject("interactive")).value ++
[...])
I'd like to also include the content of the asmDep dependency (it's a single JAR) into the project package. Is there an easy way to get that?
You want to use sbt-assembly.
I'm not sure if asmDep is the only dependency out of many in your project you want to add to your package or not, but there's a few different configuration options available in sbt-assembly that might suit your needs.

Missing classes from the assembly file created by sbt assembly

I have a project that uses sbt 0.13.6 with the assembly 0.12.0 plugin to create the farJar. My build.sbt is:
name := "test"
version := "0.0.1"
scalaVersion := "2.10.4"
libraryDependencies ++= Seq(
("org.apache.kafka" % "kafka_2.10" % "0.8.0" % "provided").
exclude("javax.jms", "jms").
exclude("com.sun.jdmk", "jmxtools").
exclude("com.sun.jmx", "jmxri").
exclude("org.slf4j", "slf4j-simple")
)
When I run sbt assembly I get a file called target/scala-2.10/test-assembly-0.0.1.jar but it is missing some kafka classes, included the one that I need at runtime:
> diff <(jar -tf /home/rief/.ivy2/cache/org.apache.kafka/kafka_2.10/jars/kafka_2.10-0.8.0.jar) <(jar -tf target/scala-2.10/test-assembly-0.0.1.jar) | grep "^<"
...
kafka/consumer/ZookeeperConsumerConnector$ZKRebalancerListener$$anonfun$kafka$consumer$ZookeeperConsumerConnector$ZKRebalancerListener$$closeFetchersForQueues$1.class
...
Is this a correct behaviour? How can I include kafka in my fatJar?
That's the intended behavior. % "provided" is skipped, since it's intention is to provide those classes from the container like Apache Spark, Kafka etc.
If you want everything in it here's what you can do:
fullClasspath in assembly := (fullClasspath in Compile).value

Resources