Is there a way to exclude fatal warnings from unit tests? - scala

I would like to turn fatal warnings on for my project but exclude fatal warnings for for my unit tests. I am currently cross compiling to Scala 2.12 and 2.13 (also ignoring deprecation warnings) and these are my current compiler flags within build.sbt
scalacOptions ++= Seq(
"-language:_",
"-target:11",
"-encoding",
"UTF-8",
"-feature",
"-Xfatal-warnings",
"-language:existentials",
"-language:higherKinds",
"-language:implicitConversions",
"-unchecked",
"-Ywarn-dead-code",
"-Ywarn-numeric-widen",
"-Ywarn-value-discard"
) ++ (CrossVersion.binaryScalaVersion(scalaVersion.value) match {
case "2.12" =>
Seq(
"-Xlint",
"-Yno-adapted-args",
"-Xfuture",
"-Ypartial-unification"
)
case "2.13" =>
Seq(
"-Wconf:cat=deprecation:w,any:e", // exclude deprecations in fatal warnings
"-Xlint:-byname-implicit" // needed for Doobie
)
case _ => Nil
})

All you need is:
Test / scalacOptions -= "-Xfatal-warnings"

Related

How to failed compilation if there are any dead code or unused imports, variables etc

I am trying to failed compilation if there are any unused imports, local or private variables or dead code in the codebase. So, I have added following scalacoptions.
scalacOptions ++= Seq(
"-encoding", "UTF-8",
"-Xfatal-warnings",
"-Xlint:-unused,_",
"-Ywarn-dead-code",
"-Ywarn-unused:imports", // Warn if an import selector is not referenced
"-Ywarn-unused:locals", // Warn if a local definition is unused
"-Ywarn-unused:patvars", // Warn if a variable bound in a pattern is unused
"-Ywarn-unused:privates", // Warn if a private member is unused
"-deprecation"
)
But whenever I compile my project, it fails compilation and gives the following error.
[error] 'imports' is not a valid choice for '-Ywarn-unused'
[error] bad option: '-Ywarn-unused:imports'
scala version: 2.11.11
I am not sure what mistake i am doing.
Below settings should work -
lazy val project_name = project.in(file(".")).settings(commonSettings)
lazy val commonSettings = reformatOnCompileSettings ++ Seq(
scalacOptions ++= Seq(
"-Ywarn-unused-import",
"-language:postfixOps",
"-Ypartial-unification"
)
)
Hope this helps.

sbt run Play as a sumodule

I was wondering if I can run Play in development as a submodule?
Here is the project structure:
Root
submodule1
submodule2(Play)
submodule3
......
There is only one build.sbt file under the root level, which specified the structure of the project.
Previously, I had the Play module as a separate project, which has its own build.sbt, and I could simply run it in development mode with sbt run.
However, after I combined the Play module with the other modules into one project, sbt "project projectName" run can still start the Play module in development mode, but when I tried to hit any endpoint, I got injection error, such as No implementation for models.services.UserService was bound.
Here is a portion of the build.sbt. emberconflagration is the Play project.
lazy val root = project
.in(file("."))
.aggregate(embercore)
.aggregate(emberdataset)
.aggregate(emberconflagration)
.aggregate(emberservice)
.aggregate(embersession)
.aggregate(emberetl)
.aggregate(embermodel)
.aggregate(emberclient)
.aggregate(emberserver)
.aggregate(emberservermongo)
.aggregate(emberstreaming)
.settings(commonSettings: _*)
.settings(name := "ember-conflagration-root")
lazy val embercore = (project in file("./embercore"))
.settings(commonSettings: _*)
.settings(testSettings: _*)
.settings(
name := "embercore",
libraryDependencies ++= Seq(
Libraries.scalatest,
Libraries.play_json
),
resolvers ++= Seq(
"Apache Repository" at "https://repository.apache.org/content/repositories/releases/",
"Cloudera Repository" at "https://repository.cloudera.com/artifactory/cloudera-repos/",
Resolver.sonatypeRepo("public")
),
javaOptions in assembly += "-xmx6g"
)
lazy val emberservice = (project in file("./emberservice"))
.settings(commonSettings: _*)
.settings(testSettings: _*)
.settings(
name := "emberservice",
libraryDependencies ++= Seq(
Libraries.scalatest,
Libraries.play_json,
Libraries.scala_j
),
resolvers ++= Seq(
"Apache Repository" at "https://repository.apache.org/content/repositories/releases/",
"Cloudera Repository" at "https://repository.cloudera.com/artifactory/cloudera-repos/",
Resolver.sonatypeRepo("public")
),
javaOptions in assembly += "-xmx6g"
)
.dependsOn(embercore)
lazy val emberdataset = (project in file("./emberdataset"))
.settings(commonSettings: _*)
.settings(testSettings: _*)
.settings(
name := "emberdataset",
libraryDependencies ++= Seq(
Libraries.scalatest,
Libraries.spark_core_conflP,
Libraries.spark_mllib_conflP,
Libraries.spark_sql_conflP,
Libraries.mysql_connector,
Libraries.spark_xml
),
resolvers ++= Seq(
"Apache Repository" at "https://repository.apache.org/content/repositories/releases/",
"Cloudera Repository" at "https://repository.cloudera.com/artifactory/cloudera-repos/",
Resolver.sonatypeRepo("public")
),
javaOptions in assembly += "-xmx6g"
)
.dependsOn(embercore)
lazy val embersession = (project in file("./embersession"))
.enablePlugins(LauncherJarPlugin)
.settings(commonSettings: _*)
.settings(testSettings: _*)
.settings(
name := "embersession",
libraryDependencies ++= Seq(
Libraries.scalatest,
Libraries.h2o_sparkling_water_core
exclude ("com.fasterxml.jackson.core", "jackson-core")
exclude ("com.fasterxml.jackson.core", "jackson-databind")
exclude ("javax.servlet", "servlet-api"),
// exclude ("org.apache.spark", "spark-sql_2.11") // Comment out for standard build. Uncomment for EmberSession assembly.
// exclude ("org.apache.spark", "spark-core_2.11") // Comment out for standard build. Uncomment for EmberSession assembly.
// exclude ("org.apache.spark", "spark-mllib_2.11") // Comment out for standard build. Uncomment for EmberSession assembly.
// exclude ("org.apache.hadoop", "hadoop-common"), // Comment out for standard build. Uncomment for EmberSession assembly.
Libraries.jackson_scala,
Libraries.duke,
Libraries.scala_library,
Libraries.spark_core_conflP,
Libraries.spark_mllib_conflP,
Libraries.spark_sql_conflP
),
mainClass in assembly := Some("com.metistream.ember.embersession.Test"),
javaOptions in assembly += "-xmx6g",
resolvers ++= Seq(
"Apache Repository" at "https://repository.apache.org/content/repositories/releases/",
"Cloudera Repository" at "https://repository.cloudera.com/artifactory/cloudera-repos/",
Resolver.sonatypeRepo("public")
),
assemblyShadeRules in assembly ++= Seq(
ShadeRule.rename("com.esotericsoftware.kryo.**" -> "emberkryo.#1").inAll,
ShadeRule.rename("com.fasterxml.jackson.**" -> "emberjackson.#1").inAll,
ShadeRule.rename("play.api.**" -> "emberplay.play.api.#1").inAll
),
assemblyMergeStrategy in assembly := {
case PathList("META-INF", "services", "org.apache.hadoop.fs.FileSystem") => MergeStrategy.filterDistinctLines
case PathList("reference.conf") => MergeStrategy.concat
case PathList("META-INF", xs # _*) => MergeStrategy.discard
case x => MergeStrategy.first
}
)
.dependsOn(embercore, emberdataset, emberservice)
lazy val emberconflagration = (project in file("security-layer"))
.enablePlugins(PlayScala)
.settings(commonSettings: _*)
.settings(testSettings: _*)
.settings(
name := "ember-conflagration",
libraryDependencies ++= Seq(
jdbc,
cache,
ws,
filters,
Libraries.play2_reactivemongo,
Libraries.mockito,
Libraries.embed_mongo,
Libraries.play_silhouette,
Libraries.silhouette_password,
Libraries.silhouette_persistence,
Libraries.silhouette_crypto,
Libraries.silhouette_testkit,
Libraries.scala_guice,
Libraries.ficus,
Libraries.mleap_runtime,
Libraries.google_api_dataproc,
Libraries.scalaj_http,
Libraries.google_core,
Libraries.google_client,
Libraries.google_sqladmin,
Libraries.google_cloud_compute,
Libraries.google_api_services,
Libraries.google_cloud_storage,
Libraries.postgresql_connector,
Libraries.jersey_media_glass,
Libraries.jersey_core_glass,
Libraries.jackson_xml,
Libraries.jackson_scala,
Libraries.janino,
Libraries.play_json_extensions,
Libraries.hapi_base,
Libraries.hapi_structures_v21,
Libraries.hapi_structures_v22,
Libraries.hapi_structures_v23,
Libraries.hapi_structures_v231,
Libraries.hapi_structures_v24,
Libraries.hapi_structures_v25,
Libraries.hapi_structures_v251,
Libraries.hapi_structures_v26,
Libraries.play_quartz_extension,
Libraries.elastic4s_core,
Libraries.elastic4s_http,
Libraries.scalatest,
specs2 % Test,
Libraries.uimaj_core,
Libraries.spark_core_confl,
Libraries.spark_mllib_confl,
Libraries.spark_sql_confl,
Libraries.unbound_id,
Libraries.swagger
),
coverageExcludedPackages := "<empty>;Reverse.*;router\\..*",
// Assembly Settings
assemblyShadeRules in assembly ++= Seq(
ShadeRule
.rename("com.google.common.**" -> "my_guava.#1")
.inLibrary("com.google.api-client" % "google-api-client" % "1.22.0")
),
mainClass in assembly := Some("play.core.server.ProdServerStart"),
fullClasspath in assembly += Attributed.blank(PlayKeys.playPackageAssets.value),
assemblyMergeStrategy in assembly := {
case PathList("META-INF", "MANIFEST.MF") => MergeStrategy.discard
case PathList("META-INF", xs # _*) => MergeStrategy.first
case x => MergeStrategy.first
},
projectDependencies := {
Seq(
(projectID in emberdataset).value.excludeAll(ExclusionRule(organization = "org.slf4j"),
ExclusionRule(organization = "io.netty"))
)
},
fork in run := false,
resolvers ++= Seq(
"elasticsearch-releases" at "https://artifacts.elastic.co/maven",
"sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/",
Resolver.bintrayRepo("iheartradio", "maven"),
"Atlassian Releases" at "https://maven.atlassian.com/public/",
Resolver.url("Typesafe Ivy releases", url("https://repo.typesafe.com/typesafe/ivy-releases"))(
Resolver.ivyStylePatterns)
),
javaOptions in assembly += "-xmx6g"
)
.dependsOn(embercore, emberdataset, emberetl)
I think it is just your particular app that doesn't work. This is possibly because you have multiple packages which are colliding with each other. Here's an example that works.
Directory structure
./build.sbt
./mod1
./mod1/src
./mod1/src/main
./mod1/src/main/scala
./mod1/src/main/scala/Hello.scala
./mod2
./mod2/app
./mod2/app/controllers
./mod2/app/controllers/HomeController.scala
./mod2/app/modules/MainModule.scala
./mod2/conf
./mod2/conf/application.conf
./mod2/conf/logback.xml
./mod2/conf/messages
./mod2/conf/routes
./mod3
./mod3/src
./mod3/src/main
./mod3/src/main/scala
./mod3/src/main/scala/Hello.scala
./project/plugins.sbt
Important files:
build.sbt
name := """root"""
organization := "com.example"
version := "1.0-SNAPSHOT"
lazy val mod1 = project in file("mod1")
lazy val mod2 = (project in file("mod2"))
.enablePlugins(PlayScala)
.settings(
name := """myplayproject""",
organization := "com.example",
version := "1.0-SNAPSHOT",
scalaVersion := "2.12.6",
libraryDependencies += guice,
libraryDependencies += "org.scalatestplus.play" %% "scalatestplus-play" % "3.1.2" % Test
)
lazy val mod3 = project in file("mod3")
lazy val root = (project in file("."))
.aggregate(mod1)
.aggregate(mod2)
.aggregate(mod3)
HomeController.scala
package controllers
import javax.inject._
import play.api._
import play.api.mvc._
trait MyServiceLike {
def getString: String
}
class MyService extends MyServiceLike {
override def getString: String = "hello"
}
#Singleton
class HomeController #Inject()(cc: ControllerComponents, myService: MyServiceLike) extends AbstractController(cc) {
def index() = Action { implicit request: Request[AnyContent] =>
Ok(myService.getString)
}
}
project/plugins.sbt
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.16")
mod2/conf/application.conf
play.modules.enabled += "modules.MainModule"
mod2/app/modules/MainModule.scala
package modules
import controllers.MyService
import controllers.MyServiceLike
import play.api.Configuration
import play.api.Environment
import play.api.inject.Binding
import play.api.inject.Module
class MainModule extends Module {
override def bindings(environment: Environment, configuration: Configuration): Seq[Binding[_]] = Seq(
bind[MyServiceLike].to[MyService]
)
}
Run
Then, you can run by starting sbt:
project mod2
run

Using .value in shared setting definition in SBT / Sane way to organize settings based on Scala version

I want to provide a multiple settings based on Scala binary version.
Those settings would be shared between several projects.
Something like:
lazy val akaneSettings = Def.settings(
organization := "ws.kotonoha",
moduleName := "akane",
crossScalaVersions := Seq("2.11.12", "2.12.4"),
scalaVersion := "2.11.12",
version := "0.2-SNAPSHOT",
javacOptions ++= Seq("-encoding", "utf8"),
scalacOptions ++= Seq(
"-feature",
"-deprecation"
),
scalaBinaryVersion.value match {
case "2.11" =>
Def.settings(
scalacOptions ++= Seq(
"-Ybackend:GenBCode",
"-Yopt:l:classpath",
"-Yopt-warnings",
"-target:jvm-1.8"
),
libraryDependencies ++= Seq("org.scala-lang.modules" % "scala-java8-compat_2.11" % "0.8.0")
)
case "2.12" =>
Def.settings(
scalacOptions ++= Seq(
"-opt:l:classpath"
)
)
case _ => throw new Exception("Not supported yet")
}
}
)
Unfortunately, the pattern match on .value does not work: it requires that I use it within a macro context.
Of course I can do the branching logic for each individual setting and use := / ++=, but that will leave a mess.
Is there a way to sanely organize groups of settings based on Scala version?
You need to move your conditional checks to inside the settings definitions, and not generate multiple settings from outside.
SBT's syntax might give the impression that you're updating values in a mutable fashion, such as by using the := operator, but you're not. Every single setting transformation is stored to be composed and applied later. At the point where akaneSettings is defined the value of scalaBinaryVersion is not known (and may actually be different depending on the context being evaluated).
Your example should look somewhat like:
// No need to use Def.Setting in recent SBT versions
lazy val akaneSettings = Seq(
organization := "ws.kotonoha",
// ...,
scalacOptions ++= {
scalaBinaryVersion.value match {
case "2.11" =>
Seq("-some-2.11-setting")
case "2.12" =>
Seq("-some-2.12-setting")
case _ =>
sys.error("Only Scala 2.11 and 2.12 are supported")
}
},
libraryDependencies ++= {
scalaBinaryVersion.value match {
case "2.11" =>
Seq("org.scala-lang.modules" % "scala-java8-compat_2.11" % "0.8.0")
case "2.12" =>
Seq.empty
case _ =>
sys.error("Only Scala 2.11 and 2.12 are supported")
}
}
}
You can create functions to generate your settings. For example:
def akaneSettings(scalaBinaryVersion: String) = Def.settings(
...
scalaBinaryVersion match {
...
}
)
... and then using it as akaneSettings(scalaBinaryVersion.value).

How to set different scalacOptions per Scala version when cross-compiling using Build.scala?

When building with Scala 2.10 and SBT 0.13.2, I want to have -language:_, but this isn't recognized by Scala 2.9. There's a question about settings different scalacOptions for cross-compilation (Conditional scalacOptions with SBT), but it is about build.sbt. I'm using Build.scala because I'm doing a multi-project build.
I have tried this:
def scalacOptionsVersion(v: String) = {
Seq(
"-unchecked",
"-deprecation",
"-Xlint",
"-Xfatal-warnings",
"-Ywarn-dead-code",
"-target:jvm-1.7",
"-encoding", "UTF-8") ++ (
if (v.startsWith("2.9")) Seq() else Seq("-language:_"))
}
override val settings = super.settings ++ Seq(
...,
scalaVersion := "2.10.4",
scalacOptions <++= scalaVersion(scalacOptionsVersion),
crossScalaVersions := Seq("2.9.2", "2.10.4", "2.11.4"),
...
)
but I get an error:
[error] /Users/benwing/devel/lemkit/scala/project/build.scala:29: type mismatch;
[error] found : sbt.Def.Initialize[Equals]
[error] required: sbt.Def.Initialize[sbt.Task[?]]
[error] Note: Equals >: sbt.Task[?], but trait Initialize is invariant in type T.
[error] You may wish to define T as -T instead. (SLS 4.5)
[error] scalacOptions <++= scalaVersion(scalacOptionsVersion),
[error] ^
[error] one error found
Help?
In SBT 0.13+ this will work:
def scalacOptionsVersion(scalaVersion: String) = {
Seq(
"-unchecked",
"-deprecation",
"-Xlint",
"-Xfatal-warnings",
"-Ywarn-dead-code",
"-target:jvm-1.7",
"-encoding", "UTF-8"
) ++ CrossVersion.partialVersion(scalaVersion) match {
case Some((2, scalaMajor)) if scalaMajor == 9 => Nil
case _ => Seq("-language:_")
}
}
val appSettings = Seq(
scalacOptions := scalacOptionsVersion(scalaVersion.value)
// other settings...
)

Conditional scalacOptions with SBT

I am using a project with cross-build for Scala 2.8, 2.9 and (hopefully) 2.10, using SBT. I would like to add the -feature option when compiling with 2.10 only.
In other words, when I compile with a version smaller than 2.10.0, I would like to set the compiler options as:
scalacOptions ++= Seq( "-deprecation", "-unchecked" )
and when compiling with a version greater or equal than 2.10.0:
scalacOptions ++= Seq( "-deprecation", "-unchecked", "-feature" )
Is there a way to achieve this ?
I found this was quick and concise way of doing it:
scalaVersion := "2.10.0"
crossScalaVersions := "2.9.2" :: "2.10.0" :: Nil
scalacOptions <<= scalaVersion map { v: String =>
val default = "-deprecation" :: "-unchecked" :: Nil
if (v.startsWith("2.9.")) default else default :+ "-feature"
}
When cross-building, scalaVersion reflects the version your project is currently built against. So depending on scalaVersion should do the trick:
val scalaVersionRegex = "(\\d+)\\.(\\d+).*".r
...
scalacOptions <++= scalaVersion { sv =>
sv match {
case scalaVersionRegex(major, minor) if major.toInt > 2 || (major == "2" && minor.toInt >= 10) =>
Seq( "-deprecation", "-unchecked", "-feature" )
case _ => Seq( "-deprecation", "-unchecked" )
}
There is CrossVersion.partialVersion now which can be used for this. I am not sure which SBT it was introduced in, but it seems to work fine even in 0.13.8:
scalacOptions ++= {
if (CrossVersion.partialVersion(scalaVersion.value).exists(_ >= (2, 10))) {
Seq("-deprecation", "-unchecked", "-feature")
} else {
Seq("-deprecation", "-unchecked")
}
}
Note: you need to import scala.math.Ordering.Implicits._ to be able to use >= operator on tuples.