How to use sbt packageBin with multiple modules in order - sbt

I have a sbt project with multiple sub modules which look like this:
--\ root
-- module 1
-- module 2
Using packageBin, I can get two zip files: module1.zip and module2.zip.
This is my build.sbt:
import Dependencies._
import NativePackagerHelper._
lazy val commonSettings = Seq(
organization := "com.zhyea.sbt",
version := "0.1-SNAPSHOT",
scalaVersion := "2.11.12",
exportJars := true,
artifactName := {
(sv: ScalaVersion, module: ModuleID, artifact: Artifact) => artifact.name + "." + artifact.extension
}
)
lazy val module2 = project.settings(commonSettings).settings()
.enablePlugins(JavaAppPackaging, UniversalPlugin)
.settings(libraryDependencies ++= module2Dependencies)
lazy val module1 = project.settings(commonSettings)
.enablePlugins(JavaAppPackaging, UniversalPlugin)
.settings(libraryDependencies ++= module1Dependencies)
lazy val root = project.in(file("."))
.settings(commonSettings)
.aggregate(module2, module1)
.enablePlugins(JavaAppPackaging, UniversalPlugin)
.dependsOn(module2, module1).configs()
mappings in Universal ++= directory("module2/target/universal")
The mappings in Universal ++= directory("module1/target/universal")
Now I want to execute the packageBin task at root and add sub module zips into root.zip.
The problem is that when the root module executes packageBin task, the sub modules' packageBin tasks haven't finished, and the root cannot get module1.zip and mudule2.zip.
How can I tell sbt to execute the packageBin task in order?

i just pack all sub modules' files into one zip by adding a new module named pack.

Related

When using a Scala compiler plugin in sbt, how do you set a library dependency for the plugin?

I'm using a compiler plugin I wrote that depends on the Kyro serialization library. When attempting to use my plugin I set this up in build.sbt (top-level) like this:
lazy val dependencies =
new {
val munit = "org.scalameta" %% "munit" % "0.7.12" % Test
val kyro = "com.esotericsoftware" % "kryo" % "5.0.0-RC9"
}
lazy val commonDependencies = Seq(
dependencies.kyro,
dependencies.munit
)
lazy val root = (project in file("."))
.settings(
libraryDependencies ++= commonDependencies,
Test / parallelExecution := false
)
addCompilerPlugin("co.blocke" %% "dotty-reflection" % reflectionLibVersion)
But when I compile my target project, I get a java.lang.NoClassDefFoundError that it can't find Kyro. I've added kyro to my dependencies, but since this is for the compiler, not my app, it's not picking that up.
How can I properly tell sbt about a dependency my plugin needs?

Unresolved dependency in published assembly package in SBT

In my project there are 3 sub projects under root. build.sbt is as below.
proj_C depends on proj_A and proj_B.
If I created the assembly proj_C package with below command. It success and the assembly package could be imported in other projects.
sbt "project proj_C" assembly
If I publish with "sbt publish", as I defined addArtifact in proj_C settings, an assembly jar package is also generated and then published. But when I try to compile another project which imports this assembly jar, it will below error
[error] unresolved dependency: proj_A;1.0.0: not found
part of build.sbt is as below. Could anyone point me what I made wrong in my way?
Many thanks!
artifact in (Compile, assembly) := {
val art = (artifact in (Compile, assembly)).value
art.withClassifier(Some("assembly"))
}
lazy val assemblySettings = Seq(
assemblyMergeStrategy in assembly := {
{
case PathList("META-INF", xs # _*) => MergeStrategy.discard
case _ => MergeStrategy.first
}
}
)
lazy val root = Project(base = file("."))
.disablePlugins(sbtassembly.AssemblyPlugin)
.aggregate(proj_A, proj_B, proj_C)
.settings(
commonSettings,
skip in publish := true,
name := "proj_root"
)
lazy val proj_A= (project in file("proj_A"))
.disablePlugins(sbtassembly.AssemblyPlugin)
.settings(
commonSettings,
skip in publish := true,
name := "proj_A"
)
lazy val proj_B= (project in file("proj_B"))
.disablePlugins(sbtassembly.AssemblyPlugin)
.settings(
commonSettings,
skip in publish := true,
name := "proj_B"
)
lazy val proj_C= (project in file("proj_C"))
.settings(
commonSettings,
assemblySettings,
addArtifact(artifact in (Compile, assembly), assembly),
name := "proj_C"
) dependsOn(proj_A, proj_B)
First of all, I hope you know that the publishing of the fat jar is not recommended. And to be honest, in your case I really see no benefit in doing so.
If you simply publish A, B, C separately and then add the dependency in your other project it will all be automatically downloaded (along with dependencies of those projects). And the dependency management will be much easier...
But, since you want to add the A-assembly dependency, by the error I guess that you are actually adding the wrong jar. My guess would be that you publish both C.jar and C-assembly.jar, and you added the dependency like:
"your.organisation" %% "C" % "version"
but you should have:
"your.organisation" %% "C" % "version" classifier "assembly"

scalaFX standalone execute jar file

Good day! Help me, please. I startup this example
sbt> run
It's okey all play, after
sbt> package
Will build jar file, after double click messge:
Error: A JNI error has occured, please check your installation and try again.
Scala version: 2.12.4. JVM:1.8.0_152. ScalaFX:8.0.102-R11
hello.scala: `
package hello
import scalafx.Includes._
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.scene.Scene
import scalafx.scene.paint.Color._
import scalafx.scene.shape.Rectangle
object HelloStage extends JFXApp {
stage = new JFXApp.PrimaryStage {
title.value = "Hello Stage"
width = 600
height = 450
scene = new Scene {
fill = LightGreen
content = new Rectangle {
x = 25
y = 40
width = 100
height = 100
fill <== when(hover) choose Green otherwise Red
}
}
}
}
build.sbt:
name := "Scala"
organization := "scalafx.org"
version := "1.0.5"
scalaVersion := "2.12.4"
scalacOptions ++= Seq("-unchecked", "-deprecation", "-Xcheckinit", "-encoding", "utf8")
resourceDirectory in Compile := (scalaSource in Compile).value
libraryDependencies ++= Seq(
"org.scalafx" %% "scalafx" % "8.0.102-R11",)
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
fork := true
This is a Java classpath issue. When you try to execute the resulting JAR file, it cannot find the jar files that it needs to run.
Try the following:
Firstly, copy & paste the following to project/plugins.sbt:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.5")
This loads the sbt-assembly plugin, which will create a fat JAR file, containing all of the dependencies.
Secondly, change your build.sbt file to the following:
name := "Scala"
organization := "scalafx.org"
version := "1.0.5"
scalaVersion := "2.12.4"
scalacOptions ++= Seq("-unchecked", "-deprecation", "-Xcheckinit", "-encoding", "utf8")
libraryDependencies += "org.scalafx" %% "scalafx" % "8.0.102-R11"
fork := true
mainClass in assembly := Some("hello.HelloStage")
This simplifies what you originally had. The macro paradise compiler plugin is not required, and I also removed the slightly odd resourceDirectory setting.
To create the fat JAR, run the command:
sbt
sbt> assembly
The JAR file you're looking for is most likely located at target/scala-2.12/Scala-assembly-1.0.5.jar. You should now be good to go...
Alternatively, you can add all the necessary JAR files to your classpath. Another plugin that can help with that (you probably shouldn't use it with sbt-assembly) - is sbt-native-packager, which creates installers for you. You can then install your app and run it like a regular application.

how to define multi-project build with sbt-native-packager that creates an RPM for each sub-project

In all of the examples I've seen regarding multi-module builds and sbt-native-packager, they all aggregate the sub-projects into a single package. I have sub-projects that each provide a micro-service. I believe that each of these should have it's own native package, but I don't see how to do that and have a one command build for all of the sub-projects.
This turns out be straightforward. Simply provide native-packager settings for each of the sub-projects that you want to package and don't provide any on the aggregating project.
I tested by modifying https://github.com/muuki88/sbt-native-packager-examples/tree/master/multi-module-build accordingly:
import NativePackagerKeys._
name := "mukis-fullstack"
// used like the groupId in maven
organization in ThisBuild := "de.mukis"
// all sub projects have the same version
version in ThisBuild := "1.0"
scalaVersion in ThisBuild := "2.11.2"
// common dependencies
libraryDependencies in ThisBuild ++= Seq(
"com.typesafe" % "config" % "1.2.0"
)
// this is the root project, aggregating all sub projects
lazy val root = Project(
id = "root",
base = file("."),
// configure your native packaging settings here
// settings = packageArchetype.java_server++ Seq(
// maintainer := "John Smith <john.smith#example.com>",
// packageDescription := "Fullstack Application",
// packageSummary := "Fullstack Application",
// entrypoint
// mainClass in Compile := Some("de.mukis.frontend.ProductionServer")
// ),
// always run all commands on each sub project
aggregate = Seq(frontend, backend, api)
) dependsOn(frontend, backend, api) // this does the actual aggregation
// --------- Project Frontend ------------------
lazy val frontend = Project(
id = "frontend",
base = file("frontend"),
settings = packageArchetype.java_server++ Seq(
maintainer := "John Smith <john.smith#example.com>",
packageDescription := "Frontend appplication",
mainClass in Compile := Some("de.mukis.frontend.ProductionServer")
)
) dependsOn(api)
// --------- Project Backend ----------------
lazy val backend = Project(
id = "backend",
base = file("backend"),
settings = packageArchetype.java_server++ Seq(
maintainer := "John Smith <john.smith#example.com>",
packageDescription := "Fullstack Application",
packageSummary := "Fullstack Application",
// entrypoint
mainClass in Compile := Some("de.mukis.frontend.ProductionServer")
)
) dependsOn(api)
// --------- Project API ------------------
lazy val api = Project(
id = "api",
base = file("api")
Results:
debian:packageBin
...misc messages elided...
[info] dpkg-deb: building package `frontend' in `../frontend_1.0_all.deb'.
[info] dpkg-deb: building package `backend' in `../backend_1.0_all.deb'.
For whom just ended up here, a more up-to-date answer could look like:
lazy val root = (project in file("."))
.aggregate(common, frontend, backend)
lazy val common = (project in file("common"))
lazy val frontend = (project in file("frontend"))
.enablePlugins(JavaServerAppPackaging)
lazy val backend = (project in file("backend"))
.dependsOn(common)
.enablePlugins(JavaAppPackaging)
.settings(javaPackagingSettings)
lazy val javaPackagingSettings = Seq(
// follow sbt-native-packager to identify settings you need
)
Description
Here is the scenario supporting the above configuration
Project root is the parent and we don't want to package it. It aggregates other subprojects.
Project common is a sort of library and also we don't want to package it
Project backend depends on common for the libraries.
Project frontend is a standalone project packaged as a Java server app with default configuration

sbt modify the lib output directory when using sbt-native-packager

How can I modify the output of the final packaged zip to move the "lib" directory contents up one level. Basically I output a zip and the contents are like so:
ZIP FILE CONTENT:
-- my-plugin-1.0.jar
-- /lib
-- /lib/mydependency1.jar
-- /lib/mydependency2.jar
ZIP FILE CONTENT I WISH TO HAVE:
-- my-plugin-1.0.jar
-- mydependency1.jar
-- mydependency2.jar
I want to move everything in "lib" up one level to the root output.
sbt version 0.13.0
Here is my build.sbt:
import NativePackagerHelper._
organization := "com.company.product"
name := "my-plugin"
version := "1.0"
enablePlugins(UniversalPlugin)
packageName in Universal:= "deployment"
publishArtifact in (Compile, packageDoc) := false
artifactName := {
(sv: ScalaVersion, module: ModuleID, artifact: Artifact) =>
artifact.name + "-" + module.revision + "." + artifact.extension
}
javacOptions ++= Seq("-source", "1.8")
mappings in Universal <+= packageBin in Compile map { jar => jar -> (jar.getName()) }
topLevelDirectory := None
plugins.sbt
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.0")
command line:
sbt universal:packageBin
Looks like your requirement is a first class citizen in sbt-native-packager:
mappings in Universal ++= contentOf("src/main/resources/cache")

Resources