How to package Scala 3 application using the "scopt" library with "Proguard"? - scala

The moment I add a dependency on the scopt library to my Scala 3 application, I am no longer able to successfully package it using Proguard. Upon attempting to run an executable jar, I am only seeing the following error message:
Exception in thread "main" java.lang.VerifyError: Bad invokespecial instruction: interface method reference is in an indirect superinterface.
Exception Details:
Location:
scala/collection/SeqFactory$Delegate.tabulate(ILscala/Function1;)Ljava/lang/Object; #9: invokespecial
Reason:
Error exists in the bytecode
Bytecode:
0000000: 2a1b 2c4e 3d59 4c1c 2db7 000a b0
at scopt.OptionDef.<init>(OptionDef.scala:52)
at scopt.OptionDef.<init>(OptionDef.scala:63)
at HelloWorld$.<clinit>(HelloWorld.scala:1082)
at HelloWorld.main(HelloWorld.scala)
Here is the minimal example to reproduce the issue:
$ cat build.sbt
lazy val root = (project in file(".")).
settings(
name := "helloworld",
scalaVersion := "3.1.1"
)
libraryDependencies ++= Seq(
"com.github.scopt" %% "scopt" % "4.0.1"
)
enablePlugins(SbtProguard)
Proguard / proguardFilteredInputs ++= ProguardOptions.noFilter((Compile / packageBin).value)
Proguard / proguardInputs := (Compile / dependencyClasspath).value.files
Proguard / proguardOptions += ProguardOptions.keepMain("HelloWorld")
Proguard / proguardOptions += ProguardConf.helloWorld
Proguard / proguardVersion := "7.2.0"
$ cat project/ProguardConf.scala
object ProguardConf {
val helloWorld =
"""
-dontnote
-dontobfuscate
-dontwarn java.lang.invoke.MethodHandle
-dontwarn scala.AnyKind
-libraryjars <java.home>/jmods
"""
}
$ cat project/build.properties
sbt.version=1.6.2
$ cat project/plugins.sbt
addSbtPlugin("com.github.sbt" % "sbt-proguard" % "0.5.0")
$ cat src/main/scala/HelloWorld.scala
import scopt.OParser
case class Config(name: String)
object HelloWorld {
private val parser = OParser.sequence(
OParser.builder[Config]
.opt[String]("name")
.action((value: String, option: Config) => option.copy(name = value)))
def main(args: Array[String]) = {
OParser.parse(parser, args, Config("World")) match {
case Some(config) => {
println(s"Hello ${config.name}")
}
case None =>
}
}
}
I am packaging and running it like this:
$ sbt
sbt:helloworld> proguard:proguard
Ctrl-D
$ java -jar target/scala-3.1.1/proguard/helloworld_3-0.1.0-SNAPSHOT.jar
Any clues what might I have been missing? Many thanks!

Related

Issue in testing using scalatest

I just set up a new scala project with sbt in IntelliJ, and wrote the following basic class:
Person.scala:
package learning.functional
case class Person(
name: String
)
Main.scala:
package learning.functional
import learning.functional.person
object Main{
val p = Person("John")
}
PersonTest.scala:
import learning.functional.Person
import org.scalatest.FunSuite
class PersonTest extends FunSuite {
test("test person") {
val p = Person("John")
assert(p.name == "John")
}
}
When I try to run sbt test, it throws the following error:
## Exception when compiling 1 sources to /Users/johndooley/Desktop/Scala/scala-learning/target/scala-2.13/test-classes
[error] java.lang.AssertionError: assertion failed:
[error] unexpected value engine in trait FunSuite final <expandedname> private[this]
[error] while compiling: /Users/johndooley/Desktop/Scala/scala-learning/src/test/scala/PersonTest.scala
What could be the reason for this? My build.sbt file:
ThisBuild / scalaVersion := "2.13.10"
libraryDependencies += "org.scalatest" % "scalatest_2.10" % "1.9.1"
lazy val root = (project in file("."))
.settings(
name := "scala-learning"
)
Project structure:
I have tried invalidating cache and restarting, doing clean, update, compile, but nothing works.
I solved this by modifying my dependency to the following:
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.14"
and completely reloading the sbt shell

Specs2 test within plays gives me "could not find implicit value for evidence parameter of type org.specs2.main.CommandLineAsResult

I'm trying to write a test case for a simple REST API in Play2/Scala that send/receives JSON. My test looks like the following:
import org.junit.runner.RunWith
import org.specs2.matcher.JsonMatchers
import org.specs2.mutable._
import org.specs2.runner.JUnitRunner
import play.api.libs.json.{Json, JsArray, JsValue}
import play.api.test.Helpers._
import play.api.test._
import play.test.WithApplication
/**
* Add your spec here.
* You can mock out a whole application including requests, plugins etc.
* For more information, consult the wiki.
*/
#RunWith(classOf[JUnitRunner])
class APIv1Spec extends Specification with JsonMatchers {
val registrationJson = Json.parse("""{"device":"576b9cdc-d3c3-4a3d-9689-8cd2a3e84442", |
"firstName":"", "lastName":"Johnny", "email":"justjohnny#test.com", |
"pass":"myPassword", "acceptTermsOfService":true}
""")
def dropJsonElement(json : JsValue, element : String) = (json \ element).get match {
case JsArray(items) => util.dropAt(items, 1)
}
def invalidRegistrationData(remove : String) = {
dropJsonElement(registrationJson,remove)
}
"API" should {
"Return Error on missing first name" in new WithApplication {
val result= route(
FakeRequest(
POST,
"/api/v1/security/register",
FakeHeaders(Seq( ("Content-Type", "application/json") )),
invalidRegistrationData("firstName").toString()
)
).get
status(result) must equalTo(BAD_REQUEST)
contentType(result) must beSome("application/json")
}
...
However when I attempt to run sbt test, I get the following error:
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=384M; support was removed in 8.0
[info] Loading project definition from /home/cassius/brentspace/esalestracker/project
[info] Set current project to eSalesTracker (in build file:/home/cassius/brentspace/esalestracker/)
[info] Compiling 3 Scala sources to /home/cassius/brentspace/esalestracker/target/scala-2.11/test-classes...
[error] /home/cassius/brentspace/esalestracker/test/APIv1Spec.scala:34: could not find implicit value for evidence parameter of type org.specs2.main.CommandLineAsResult[play.test.WithApplication{val result: scala.concurrent.Future[play.api.mvc.Result]}]
[error] "Return Error on missing first name" in new WithApplication {
[error] ^
[error] one error found
[error] (test:compileIncremental) Compilation failed
[error] Total time: 3 s, completed 18/01/2016 9:30:42 PM
I have similar tests in other applications, but it looks like the new version of specs adds a lot of support for Futures and other things that invalidate previous tutorials. I'm on Scala 2.11.6, Activator 1.3.6 and my build.sbt looks like the following:
name := """eSalesTracker"""
version := "1.0-SNAPSHOT"
lazy val root = (project in file(".")).enablePlugins(PlayScala)
scalaVersion := "2.11.6"
libraryDependencies ++= Seq(
jdbc,
cache,
ws,
"com.typesafe.slick" %% "slick" % "3.1.0",
"org.postgresql" % "postgresql" % "9.4-1206-jdbc42",
"org.slf4j" % "slf4j-api" % "1.7.13",
"ch.qos.logback" % "logback-classic" % "1.1.3",
"ch.qos.logback" % "logback-core" % "1.1.3",
evolutions,
specs2 % Test,
"org.specs2" %% "specs2-matcher-extra" % "3.7" % Test
)
resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases"
resolvers += Resolver.url("Typesafe Ivy releases", url("https://repo.typesafe.com/typesafe/ivy-releases"))(Resolver.ivyStylePatterns)
// Play provides two styles of routers, one expects its actions to be injected, the
// other, legacy style, accesses its actions statically.
routesGenerator := InjectedRoutesGenerator
I think you are using the wrong WithApplication import.
Use this one:
import play.api.test.WithApplication
The last line of the testcase should be the assertion/evaluation statement.
e.g. before the last } of the failing testcase method put the statement false must beEqualTo(true) and run it again.

Scala deserialization of custom scala object with play-json library

My ScalaTest breaks while trying to parse a JSON string into a custom Scala object. I'm using Play-Json library for [de]serialization. Serialization works fine but the deserialization breaks while running a unit test on the Blah class. The test invokes the fromJsonString() method and Im using ScalaTest library for unit testing. Appreciate some help here.
Exception trace: (Full trace - http://pasted.co/e627b1ee)
An exception or error caused a run to abort: scala.collection.immutable.$colon$colon.hd$1()Ljava/lang/Object;
java.lang.NoSuchMethodError: scala.collection.immutable.$colon$colon.hd$1()Ljava/lang/Object;
at play.api.libs.json.jackson.JsValueDeserializer.deserialize(JacksonJson.scala:144)
at play.api.libs.json.jackson.JsValueDeserializer.deserialize(JacksonJson.scala:108)
at play.api.libs.json.jackson.JsValueDeserializer.deserialize(JacksonJson.scala:103)
at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:3536)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1872)
at play.api.libs.json.jackson.JacksonJson$.parseJsValue(JacksonJson.scala:226)
at play.api.libs.json.Json$.parse(Json.scala:21)
at com.project.gateway.model.Blah.fromJsonString(Blah.scala:98)
Scala Object definition:
case class Blah(name: String, id: String) {
implicit val BlahWrites: Writes[Blah] = (
(JsPath \ "name").write[String] and
(JsPath \ "id").write[String]
)(unlift(Blah.unapply))
implicit val BlahReads: Reads[Blah] = (
(JsPath \ "name").read[String] and
(JsPath \ "id").read[String]
)(Blah)
def toJsonString(): String = {
Json.toJson(this).toString()
}
def fromJsonString(jsonString: String): Blah = {
val value = Json.parse(jsonString)
value.as[Blah]
//Json.fromJson[Blah](value).get
}
}
My SBT file:
name := "Project"
version := "1.0"
scalaVersion := "2.11.7"
libraryDependencies += "com.typesafe.scala-logging" % "scala-logging_2.11" % "3.1.0"
libraryDependencies += "org.scalatest" % "scalatest_2.11" % "2.2.4" % "test"
libraryDependencies += "com.typesafe.play" % "play-json_2.10" % "2.4.2"
I guess there is difference between the version of scala-library at runtime/test, and the one used to build the dependency which is raising the error.
If using SBT or Maven, you can check the used libraries (including the transitive one), so check there is no incompatibility regarding the scala-library (pull in different versions by different dependencies).

Using Specs2 in a Typesafe activator play application

I have used specs2 many times successfully in vanilla SBT projects. now I am starting to learn typesafe activator platform.
I did the following steps
activator new Shop just-play-scala
this is my build.sbt file
name := """Shop"""
version := "1.0-SNAPSHOT"
// Read here for optional jars and dependencies
libraryDependencies ++= Seq("org.specs2" %% "specs2-core" % "3.6.1" % "test")
resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases"
scalacOptions in Test ++= Seq("-Yrangepos")
lazy val root = project.in(file(".")).enablePlugins(PlayScala)
I created a file Shop/app/test/models/ShopSpec.scala
import org.specs2.mutable.Specification
class ShopSpec extends Specification {
def foo = s2"""
| This is a specification to check the 'Hello world' string
| The 'Hello world' string should
| contain 11 characters $e1
| start with 'Hello' $e2
| end with 'world' $e3
| """.stripMargin
def e1 = "Hello world" must haveSize(11)
def e2 = "Hello world" must startWith("Hello")
def e3 = "Hello world" must endWith("world")
}
When I run activator test I get an error
[success] Total time: 0 s, completed Jun 24, 2015 12:21:32 AM
Mohitas-MBP:Shop abhi$ activator test
[info] Loading project definition from /Users/abhi/ScalaProjects/Shop/project
[info] Set current project to Shop (in build file:/Users/abhi/ScalaProjects/Shop/)
**cannot create a JUnit XML printer. Please check that specs2-junit.jar is on the classpath**
org.specs2.reporter.JUnitXmlPrinter$
java.net.URLClassLoader.findClass(URLClassLoader.java:381)
java.lang.ClassLoader.loadClass(ClassLoader.java:424)
sun.misc.Launcher$AppClassLoader.loadClass(Launcher.jav
I have previously written spec2 test cases successfully when I was using SBT projects. but only when I use the typesafe activator that I get this issue with test cases.
I even changed the code of my test to something as simple as
import org.specs2.mutable.Specification
class ShopSpec extends Specification {
"A shop " should {
"create item" in {
failure
}
}
}
But still the same problem.
Wait .. I think I resolved it.
The activator play platform already has specs2 included so there is no need for me to tweak the built.sbt file for specs 2.
So I removed everything I had added to build.sbt file and left the file as
name := """Shop"""
version := "1.0-SNAPSHOT"
lazy val root = project.in(file(".")).enablePlugins(PlayScala)
Now it works fine. So basically, I don't need to add anything in a activator project for specs2.
I could have deleted the question... but leaving it here so that it can be of help to someone.
What worked for me was adding the following to build.sbt:
libraryDependencies ++= Seq("org.specs2" %% "specs2-core" % "3.6.2" % "test",
"org.specs2" %% "specs2-junit" % "3.6.2" % "test")

Using unboundid ldap in scala ... strange compile error

I am trying to use LDAP via unboundid in scala but the compiler keeps crashing.
I just created an object that looks like this:
package utils
import com.unboundid.ldap.sdk._
object LdapHelper {
val ldap = LDAPConnection("ldap.example.com", 389)
}
I added this: "com.unboundid" % "unboundid-ldapsdk" % "2.3.1" to my appDependencies in Build.scala. I use Play 2.1 and Scala Version 2.10.1.
I get a very strange error message (see below):
The error message is so strange that i really dont know where to begin to look for hints.
Not sure if the problem is in unboundid, play, scala, sbt?
How can i successfully integrate unboundid in my scala project?
Thanks
Error in Scala compiler: assertion failed: while compiling: C:\play\todolist\app\utils\LdapHelper.scala during phase: global=typer, atPhase=parser library version: version 2.10.2 compiler version: version 2.10.2 reconstructed args: -classpath C:\play\todolist.target;C:\eclipse\scala-SDK-3.0.1-vfinal-2.10-win32.win32.x86_64\configuration\org.eclipse.
...
last tree to typer: Ident(LDAPConnection)
symbol: (flags: )
symbol definition:
symbol owners:
context owners: value ldap -> object LdapHelper -> package utils
== Enclosing template or block ==
Template( // val : in object LdapHelper
"java.lang.Object" // parents
ValDef(
private
"_"
)
// 3 statements
DefDef( // def : in object LdapHelper
""
[]
List(Nil)
Block(
Apply(
super.""
Nil
)
()
)
)
DefDef( // def x: in object LdapHelper
"x"
[]
Nil
()
)
ValDef( // private[this] val ldap: in object LdapHelper
private
"ldap"
Apply(
"LDAPConnection"
// 2 arguments
"ldap.example.com"
389
)
)
)
There was a warning that turned into an assert in Scala 2.10.2 causing this.
There is a bug open here:
https://issues.scala-lang.org/browse/SI-7014
And a fix staged for 2.10.4:
https://github.com/scala/scala/pull/2829
You can ask Play to use Scala 2.10.4-SNAPSHOT by using the following Build.scala:
import sbt._
import Keys._
import play.Project._
object ApplicationBuild extends Build {
val appName = "AppName"
val appVersion = "1.0-SNAPSHOT"
val mainDeps = Seq(
jdbc,
anorm,
cache
)
lazy val main = play.Project(appName, appVersion, mainDeps).settings(
scalaVersion := "2.10.4-SNAPSHOT"
)
}
If you are using build.sbt the file would look like:
import play.Project._
playScalaSettings
name := "AppName"
version := "1.0-SNAPSHOT"
scalaVersion := "2.10.4-SNAPSHOT"
libraryDependencies ++= Seq(jdbc, anorm, cache)
Note: if building from sbt (instead of play) you may have to add a repository resolver under the scalaVersion line such as:
resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/repo/"
The answer from #jeckhart works.
Firstly I use Scala 2.10.4-RC1 to build the Play 2.3 SNAPSHOT. Then use the output to compile with UnboundID.
Finally everything compiles with no assertion or error.
To build Play 2.3 SNAPSHOT using Scala 2.10.4-RC1, I modified the file framework/project/Build.scala.
Change these two section from
val buildScalaVersion = propOr("scala.version", "2.10.3")
val buildScalaVersionForSbt = propOr("play.sbt.scala.version", "2.10.3")
to
val buildScalaVersion = propOr("scala.version", "2.10.4-RC1")
val buildScalaVersionForSbt = propOr("play.sbt.scala.version", "2.10.4-RC1")