how to run scala sbt-native-packager for a appJS/appJVM cross-build project - scala

The sbt-native-packager can make a zip file with all dependencies and a script to run_
$ sbt universal:packageBin
I have a scala web application, using cross-build (appJS for front-end and appJVM for back-end).
How do I run this packager for the appJVM?
I've tried as follows, but it does not accept the command:
$ sbt appJVM/universal:packageBin
Here it is the build.sbt project, from https://www.scala-js.org/doc/project/cross-build.html
...
lazy val foo = crossProject.in(file(".")).
settings(
name := "foo",
version := "0.1-SNAPSHOT"
).
jvmSettings(
// Add JVM-specific settings here
).
jsSettings(
// Add JS-specific settings here
)
lazy val fooJVM = foo.jvm
lazy val fooJS = foo.js
How do I run this packager for the appJVM?
And how I include the file generated by sbt appJS/fullOptJS?
And some other static files?
Update with Ivan response
build.sbt:
import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType}
val sharedSettings = Seq(
scalaVersion := "2.12.8",
)
lazy val app =
crossProject(JSPlatform, JVMPlatform)
.in(file("."))
.settings(sharedSettings)
.jsSettings(
)
.jvmSettings(
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-http" % "10.1.9"
),
)
lazy val backend = project
.enablePlugins(UniversalPlugin)
.enablePlugins(JavaAppPackaging)
.dependsOn(app.jvm)
.settings(
mainClass in Compile := Some("com.example.EchoServer")
)
lazy val frontend = project
.enablePlugins(ScalaJSPlugin)
.dependsOn(app.js)
backend
.settings(
Seq(
resourceGenerators in Compile += Def.task {
Seq(
(fullOptJS in Compile in frontend).value,
(fastOptJS in Compile in frontend).value
).map { js =>
val resource = (resourceManaged in Compile).value / "public" / "assets" / js.data.name
IO.write(resource, IO.read(js.data))
resource
}
}.taskValue
)
)
and run:
$ sbt backend/universal:packageBin
34: error: type mismatch;
found : Seq[sbt.Def.Setting[Seq[sbt.Task[Seq[java.io.File]]]]]
required: Int
Seq(
^
[error] Type error in expression

I used the following structure.
Define a shared project that needs to be cross-compiled for JS and Scala.
lazy val shared = CrossPlugin.autoImport
.crossProject(JSPlatform, JVMPlatform)
.crossType(CrossType.Pure)
.jvmSettings(???)
.jsSettings(???)
lazy val sharedJvm = shared.jvm
lazy val sharedJs = shared.js
Add project that contains a Main class.
lazy val backend = project
.enablePlugins(UniversalPlugin)
.enablePlugins(JavaAppPackaging)
.dependsOn(sharedJvm)
Add web project containing web related code.
lazy val web = project
.enablePlugins(ScalaJSPlugin)
.dependsOn(sharedJs)
And finally, attach resources from web compiled into JS to backend.
backend
.settings(
Seq(
resourceGenerators in Compile += Def.task {
Seq(
(fullOptJS in Compile in web).value,
(fastOptJS in Compile in web).value
).map { js =>
val resource = (resourceManaged in Compile).value / "public" / "assets" / js.data.name
IO.write(resource, IO.read(js.data))
resource
}
}.taskValue
)
Main class needs to service compiled JS from public/assets, as configured in sbt, and any other web resources from its class path.

Related

How to run main project with all sub-project in scala SBT

I'm using multi-project build in SBT to create a simple microservice architecture. I defined build.sbt as:
addCommandAlias("rebuild", ";clean; compile; package")
lazy val oc = Project(id = "oc",
base = file("."),
settings = commonSettings).aggregate(
persistence,
core,
configuration,
integration).dependsOn(
persistence,
core,
configuration,
integration)
.configs (MultiJvm)
lazy val persistence = Project(...)
lazy val core = Project(...)
lazy val log = Project(...)
lazy val configuration = Project(...)
lazy val integration = Project(...)
lazy val commonSettings = Defaults.coreDefaultSettings ++
basicSettings ++
net.virtualvoid.sbt.graph.Plugin.graphSettings
lazy val basicSettings = Seq(
version := PROJECT_VERSION,
organization := ORGANIZATION,
scalaVersion := SCALA_VERSION,
scalacOptions in Compile ++= Seq("-deprecation", "-feature", "-unchecked", "-Xlog-reflective-calls", "-Xlint" , "-encoding", "utf8"),
javacOptions in Compile ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),
javaOptions in run ++= Seq("-Xms128m", "-Xmx1024m"),
libraryDependencies ++= Seq( ...
),
fork in run := true,
// disable parallel tests
parallelExecution in Test := false,
licenses := Seq(("CC0", url("http://creativecommons.org/publicdomain/zero/1.0"))),
fork in Test := true
)
For single sub-project I can easily build the project. But I cannot build all projects at once to check the working as a whole system. Any help from the experts on building the system would be appreciable

How can I cross compile only 1 subproject in sbt?

I have a multi-project sbt build using Build.scala.
I want to cross-compile one subproject against Scala 2.12 and 2.11, but the other subprojects have dependencies that don't support 2.12 yet, so I just want 2.11.
How can I do this in sbt?
I've tried this:
val scalaVer = "2.11.8"
val scalaVer12 = "2.12.0"
lazy val basicSettings = Seq(
scalaVersion := scalaVer
)
lazy val root = (project in file("."))
.settings(basicSettings: _*)
.aggregate(scalajack, scalajack_dynamodb, scalajack_mongo)
lazy val scalajack = project.in(file("core"))
.settings(basicSettings: _*)
.settings(Seq(crossScalaVersions := Seq(scalaVer, scalaVer12)))
lazy val scalajack_dynamodb = project.in(file("dynamodb"))
.settings(basicSettings: _*)
.dependsOn( scalajack )
lazy val scalajack_mongo = project.in(file("mongo"))
.settings(basicSettings: _*)
.dependsOn( scalajack )
This will build everything correctly but ignores my wish to have a 2.12 version of 'core' subproject.
I build packages with:
sbt> + package

sbt multi project undefined settings

I have a multi project setup in SBT. In our build process there's a file in the project that is automatically updated by our CI. It contains the app version.
However, whenever I try to load the app settings, I get an error similar to the following:
[error] References to undefined settings:
[error]
[error] module1/*:appProperties from module1/*:version (/Users/jespeno/workspace/multi-module/build.sbt:10)
[error]
[error] module2/*:appProperties from module2/*:version (/Users/jespeno/workspace/multi-module/build.sbt:10)
This is what my sbt file looks like:
val appProperties = settingKey[Properties]("app version")
appProperties := {
val prop = new Properties()
IO.load(prop, new File("version.properties"))
prop
}
val commonSettings = Seq(
version := appProperties.value.getProperty("project.version"),
scalaVersion := "2.11.7"
)
lazy val root = (project in file(".")).settings(commonSettings: _*)
.aggregate(module1, module2)
.settings(
name := appProperties.value.getProperty("project.name")
)
lazy val module1 = (project in file("./modules/module1"))
.settings(commonSettings: _*)
.settings(
name := "module1"
)
lazy val module2 = (project in file("./modules/module2"))
.settings(commonSettings: _*)
.settings(
name := "module2"
)
Here's my version.properties:
project.name="multi-module"
project.version="0.0.1"
The interesting thing is, the root project is able to load the settings correctly: if I remove the sub-modules, the build starts correctly. I'm using SBT version 0.13.8.
This is caused by appProperties not being visible to submodules(module1, module2), you can change it to:
appProperties in Global := {
val prop = new Properties()
IO.load(prop, new File("version.properties"))
prop
}
sbt scopes

How to have sbt multi-project builds configure setting for subprojects?

I have an sbt (0.13.1) project with a bunch of subprojects. I am generating eclipse project configurations using sbteclipse. My projects only have scala source files, so I want to remove the generated src/java folders.
I can achieve that by (redundantly) adding the following to the build.sbt of each subproject:
unmanagedSourceDirectories in Compile := (scalaSource in Compile).value :: Nil
unmanagedSourceDirectories in Test := (scalaSource in Test).value :: Nil
I tried just adding the above configuration to the root build.sbt but the eclipse command still generated the java source folders.
Is there any way to specify a configuration like this once (in the root build.sbt) and have it flow down to each subproject?
You could define the settings unscoped and then reuse them
val onlyScalaSources = Seq(
unmanagedSourceDirectories in Compile := Seq((scalaSource in Compile).value),
unmanagedSourceDirectories in Test := Seq((scalaSource in Test).value)
)
val project1 = project.in( file( "project1" )
.settings(onlyScalaSources: _*)
val project2 = project.in( file( "project2" )
.settings(onlyScalaSources: _*)
You could also create a simple plugin (untested code)
object OnlyScalaSources extends AutoPlugin {
override def trigger = allRequirements
override lazy val projectSettings = Seq(
unmanagedSourceDirectories in Compile := Seq((scalaSource in Compile).value),
unmanagedSourceDirectories in Test := Seq((scalaSource in Test).value)
)
}
More details about creating plugins in the plugins documentation

How to use custom plugin in multi-module project?

In a multi-module project, with one module implementing a custom SBT plugin with custom TaskKey, how this plugin can be imported for the project settings of another submodule.
If the submodule using the plugin is define in submodule1/build.sbt, then submodule1/project/plugins.sbt is not loaded.
If plugin is registered in project/plugins.sbt it will fails when loading top/aggregate project as plugin is not necessarily already built.
Is there any other way to define a custom task requiring custom dependency so that it can be used by a submodule?
Here is how I finally make it works:
import sbt._
import Keys._
object MyBuild extends Build {
private lazy val myGenerator =
// Private project with generator code and its specific dependencies
// (e.g. Javassist)
Project(id = "my-generator",
base = file("project") / "my-generator").settings(
name := "my-generator",
javacOptions in Test ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),
autoScalaLibrary := false,
scalacOptions += "-feature",
resolvers +=
"Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/releases/",
libraryDependencies ++= Seq( // Dependencies required to generate classes
"org.javassist" % "javassist" % "3.18.2-GA")
)
// Some custom setting & task
lazy val generatedClassDirectory = settingKey[File](
"Directory where classes get generated")
lazy val generatedClasses = taskKey[Seq[(File, String)]]("Generated classes")
lazy val myProject =
Project(id = "my-project", base = file("my-project")).settings(
name := "my-project",
javacOptions in Test ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"),
autoScalaLibrary := false,
scalacOptions += "-feature",
libraryDependencies ++= Seq(/* ... */),
generatedClassDirectory := {
// Defines setting for path to generated classes
val dir = target.value / "generated_classes"
if (!dir.exists) dir.mkdirs()
dir
},
generatedClasses <<= Def.task { // Define task generating .class files
// first get classloader including generator and its dependencies
val cp = (fullClasspath in (myGenerator, Compile)).value
val cl = classpath.ClasspathUtilities.toLoader(cp.files)
// then loaded generator class, and instantiate with structural type
val genClass = cl loadClass "my.custom.GeneratorClass"
val generator = genClass.newInstance.
asInstanceOf[{def writeTo(out: File): File}]
// finally we can call the
val outdir = generatedClassDirectory.value
val generated = generator writeTo outdir
val path = generated.getAbsolutePath
// Mappings describing generated classes
Seq[(File, String)](generated -> path.
drop(outdir.getAbsolutePath.length+1))
} dependsOn(compile in (myGenerator, Compile))/* awkward? */,
managedClasspath in Compile := {
// Add generated classes to compilation classpath,
// so it can be used in my-project sources
val cp = (managedClasspath in Compile).value
cp :+ Attributed.blank(generatedClassDirectory.value)
},
// Make sure custom class generation is done before compile
compile in Compile <<= (compile in Compile) dependsOn generatedClasses,
mappings in (Compile, packageBin) := {
val ms = mappings.in(Compile, packageBin).value
ms ++ generatedClasses.value // add generated classes to package
}
).dependsOn(myGenerator/* required even if there dependsOn(compile in (myGenerator, Compile)) */)
}
Not sure there is better solution, especially about the redondant dependsOn(compile in (myGenerator, Compile)) and .dependsOn(myGenerator).