How to set main class from submodule to root in sbt - scala

I have multimodule setup, where root is just a project wrapper for submodules core and util.
Core is actually the application and contains main class(extending App) which I want to run.
Staying at project root(wrapper) I want to run sbt run and execute the main method from core submodule
lazy val root = project
.in(file("."))
.aggregate(util, core)
.settings(
mainClass in Compile := (mainClass in Compile in core).value
)
lazy val util = project
.in(file("util"))
lazy val core = project
.in(file("core"))
.settings(
mainClass in Compile := Some("com.iwaneez.scala.Hello"),
libraryDependencies ++= commonDependencies
)
.dependsOn(util)
I expect to run the application just by executing sbt run

addCommandAlias can be used to replace run in the root project.
lazy val root = project
.in(file("."))
.aggregate(util, core)
.settings(
addCommandAlias("run", "core/run")
)
sbt:root> run --test
[info] Running com.iwaneez.scala.Hello --test
Hello List(--test)

The following command will let you run the project
sbt "project core" run

Related

Scala, docker - how to set mainClass in multimodule application using sbt-native-packager?

I have created a multimodule, sbt project in Scala. For now it is:
main project (main-service) with build.sbt file
http module with Main class
In my sbt file I have:
lazy val root = (project in file("."))
.aggregate(http)
.settings(
dockerBaseImage := "openjdk:jre-alpine",
name := "main-service",
libraryDependencies ++= Seq(
)
)
.enablePlugins(JavaAppPackaging)
.enablePlugins(DockerPlugin)
.enablePlugins(AshScriptPlugin)
lazy val http = (project in file("http"))
.settings(
mainClass in Compile := Some("Main"),
name := "main-http",
libraryDependencies ++= Seq(
))
As you can see, I want to run it with docker. Image of this project is created well, but when I made docker run then I got an error:
docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"/opt/docker/bin/main-service\": stat /opt/docker/bin/main-service: no such file or directory": unknown.
I think the problem could be with mainClass line. I have my Main class in main-http/src/main/scala directory, but it looks like docker does not see it.
How should I move this Main class or change path to it and run it correctly?
If you want to keep the main class in http subproject, you need to move the plugins to this project too as following.
lazy val root = (project in file("."))
.aggregate(http)
.settings(name := "main-service")
lazy val http = (project in file("http"))
.settings(
mainClass in Compile := Some("Main"),
dockerBaseImage := "openjdk:jre-alpine",
name := "main-http",
libraryDependencies ++= Seq()
)
.enablePlugins(JavaAppPackaging)
.enablePlugins(DockerPlugin)
.enablePlugins(AshScriptPlugin)
The plugins must be enabled at the project where the Main class is located.
To build a docker image, do
sbt http/docker:publishLocal

no tests were executed for sbt integration test in system folder

I want to run sbt system:test to run the integration tests in system folder. Here's the build.sbt changes, but it the console log still says No tests were executed. any ideas why? thanks!!
lazy val `myproj` = (project in file(".")).enablePlugins(PlayScala).configs(SystemTest).settings(
inConfig(SystemTest)(Defaults.testSettings),
// other settings here
)
lazy val SystemTest = config("system") extend Test describedAs "System tests"
Try this
lazy val root = (project in file("."))
.enablePlugins(PlayScala)
.configs(SystemTest)
.settings( inConfig(SystemTest)(Defaults.testSettings) : _*)
lazy val SystemTest = config("system") extend(Test)
scalaSource in SystemTest := baseDirectory.value / "/system"
The last line is to give the path of the folder where yours test cases are.
You can see this blog as well.
Hope this helps!
Play/SBT offer You integration tests "out of the box" with command sbt it:test and below configuration:
lazy val root = (project in file("."))
.configs(IntegrationTest)
.settings(
Defaults.itSettings,
)

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)

Intertwined dependencies between sbt plugin and projects within multi-project build that uses the plugin itself

I'm developing a library that includes an sbt plugin. Naturally, I'm using sbt to build this (multi-project) library. My (simplified) project looks as follows:
myProject/ # Top level of library
-> models # One project in the multi-project sbt build.
-> src/main/scala/... # Defines common models for both sbt-plugin and framework
-> sbt-plugin # The sbt plugin build
-> src/main/scala/...
-> framework # The framework. Ideally, the sbt plugin is run as part of
-> src/main/scala/... # compiling this directory.
-> project/ # Multi-project build configuration
Is there a way to have the sbt-plugin defined in myProject/sbt-plugin be hooked into the build for myProject/framework all in a unified build?
Note: similar (but simpler) question: How to develop sbt plugin in multi-project build with projects that use it?
Is there a way to have the sbt-plugin defined in myProject/sbt-plugin be hooked into the build for myProject/framework all in a unified build?
I have a working example on Github eed3si9n/plugin-bootstrap. It's not super pretty, but it kind of works. We can take advantage of the fact that sbt is recursive.
The project directory is another build inside your build, which knows how to build your build. To distinguish the builds, we sometimes use the term proper build to refer to your build, and meta-build to refer to the build in project. The projects inside the metabuild can do anything any other project can do. Your build definition is an sbt project.
By extension, we can think of the sbt plugins to be library- or inter-project dependencies to the root project of your metabuild.
meta build definition (project/plugins.sbt)
In this example, think of the metabuild as a parallel universe or shadow world that has parallel multi-build structure as the proper build (root, model, sbt-plugin).
To reuse the source code from model and sbt-plugin subprojects in the proper build, we can re-create the multi-project build in the metabuild. This way we don't need to get into the circular dependency.
addSbtPlugin("com.eed3si9n" % "sbt-doge" % "0.1.5")
lazy val metaroot = (project in file(".")).
dependsOn(metaSbtSomething)
lazy val metaModel = (project in file("model")).
settings(
sbtPlugin := true,
scalaVersion := "2.10.6",
unmanagedSourceDirectories in Compile :=
mirrorScalaSource((baseDirectory in ThisBuild).value.getParentFile / "model")
)
lazy val metaSbtSomething = (project in file("sbt-plugin")).
dependsOn(metaModel).
settings(
sbtPlugin := true,
scalaVersion := "2.10.6",
unmanagedSourceDirectories in Compile :=
mirrorScalaSource((baseDirectory in ThisBuild).value.getParentFile / "sbt-plugin")
)
def mirrorScalaSource(baseDirectory: File): Seq[File] = {
val scalaSourceDir = baseDirectory / "src" / "main" / "scala"
if (scalaSourceDir.exists) scalaSourceDir :: Nil
else sys.error(s"Missing source directory: $scalaSourceDir")
}
When sbt loads up, it will build metaModel and metaSbtSomething first, and use metaSbtSomething as a plugin to your proper build.
If you have any other plugins you need you can just add it to project/plugins.sbt normally as I've added sbt-doge.
proper build (build.sbt)
The proper build looks like a normal multi-project build.
As you can see framework subproject uses SomethingPlugin. Important thing is that they share the source code, but the target directory is completely separated, so there are no interference once the proper build is loaded, and you are changing code around.
import Dependencies._
lazy val root = (project in file(".")).
aggregate(model, framework, sbtSomething).
settings(inThisBuild(List(
scalaVersion := scala210,
organization := "com.example"
)),
name := "Something Root"
)
// Defines common models for both sbt-plugin and framework
lazy val model = (project in file("model")).
settings(
name := "Something Model",
crossScalaVersions := Seq(scala211, scala210)
)
// The framework. Ideally, the sbt plugin is run as part of building this.
lazy val framework = (project in file("framework")).
enablePlugins(SomethingPlugin).
dependsOn(model).
settings(
name := "Something Framework",
crossScalaVersions := Seq(scala211, scala210),
// using sbt-something
somethingX := "a"
)
lazy val sbtSomething = (project in file("sbt-plugin")).
dependsOn(model).
settings(
sbtPlugin := true,
name := "sbt-something",
crossScalaVersions := Seq(scala210)
)
demo
In the SomethingPlugin example, I'm defining something task that uses foo.Model.x.
package foo
import sbt._
object SomethingPlugin extends AutoPlugin {
def requries = sbt.plugins.JvmPlugin
object autoImport {
lazy val something = taskKey[Unit]("")
lazy val somethingX = settingKey[String]("")
}
import autoImport._
override def projectSettings = Seq(
something := { println(s"something! ${Model.x}") }
)
}
Here's how we can invoke something task from the build:
Something Root> framework/something
something! 1
[success] Total time: 0 s, completed May 29, 2016 3:01:07 PM
1 comes from foo.Model.x, so this demonstrates that we are using the sbt-something plugin in framework subproject, and that the plugin is using metaModel.

How to set up multi-module build with reference to external local sbt project?

I just started with Scala and Play and I'm trying to set up a multi build with sbt 0.13.5
My project structure is the following:
/AnormCypher
-> /src
->/main
->/scala
->org.anormcypher[package]
->[Some classes]
-> [other dirs/files]
-> build.sbt
/sample
-> /src
->/main
->/scala
->/controllers[package]
->Application.scala
->[Some classes]
-> [other dirs/files]
-> build.sbt
The sample project depends on the AnormCypher project. I tried to set up the dependency following this SO post. My build.sbt in sample looks like this:
name := """sample"""
version := "1.0-SNAPSHOT"
lazy val root = (project in file(".")).enablePlugins(PlayScala)
scalaVersion := "2.11.1"
libraryDependencies ++= Seq(
jdbc,
anorm,
cache,
ws
)
lazy val core = ProjectRef(file("../AnormCypher"), "anormcypher")
val main = root.dependsOn(core)
When I go into my console and type
activator
sbt is able to load the project. But when I try to compile the sources and try to use classes from the org.anormcypher package, they can't be resolved:
object anormcypher is not a member of package org
[error] import org.anormcypher._
[error] ^
Running a clean compile also brought no results.
Change
lazy val root = (project in file(".")).enablePlugins(PlayScala)
to
lazy val root = (project in file(".")).enablePlugins(PlayScala).dependsOn(core)
and remove
val main = root.dependsOn(core)
reload and the project should work fine.