After creating a useful application in Scala with dependencies, how do I deploy (create a binary) for it?
I would like to know the most idiomatic way which hopefully is the simplest way.
For me that would be the usual sbt compile
, then look for the main class:
./target/scala-2.12/classes/scala_pandoc/Main.class
Then execute it:
$ CLASSPATH="$CLASSPATH:./target/scala-2.12/classes/" scala scala_pandoc.Main --unwrap-explain
Picked up _JAVA_OPTIONS: -Xms256m -Xmx300m
java.lang.ClassNotFoundException: ujson.Value
at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at scala_pandoc.Main$.main(Main.scala:51)
at scala_pandoc.Main.main(Main.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at scala.reflect.internal.util.ScalaClassLoader.$anonfun$run$2(ScalaClassLoader.scala:106)
at scala.reflect.internal.util.ScalaClassLoader.asContext(ScalaClassLoader.scala:41)
at scala.reflect.internal.util.ScalaClassLoader.asContext$(ScalaClassLoader.scala:37)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:132)
at scala.reflect.internal.util.ScalaClassLoader.run(ScalaClassLoader.scala:106)
at scala.reflect.internal.util.ScalaClassLoader.run$(ScalaClassLoader.scala:98)
at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:132)
at scala.tools.nsc.CommonRunner.run(ObjectRunner.scala:28)
at scala.tools.nsc.CommonRunner.run$(ObjectRunner.scala:27)
at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:45)
at scala.tools.nsc.CommonRunner.runAndCatch(ObjectRunner.scala:35)
at scala.tools.nsc.CommonRunner.runAndCatch$(ObjectRunner.scala:34)
at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:45)
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:73)
at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:92)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:103)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:108)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
But as we can see it somehow does not find the dependencies. When I compile the project a bunch of files are downloaded/created at ~/.sbt/
and ~/.ivy2
but neither adding those (or all sub folders to CLASSPATH
) solves the issue.
The forementioned procedure works for projects without external dependencies.
Workaround:
Use https://github.com/sbt/sbt-assembly which is great (creates an executable .jar
) which I can run with java -jar myjar.jar
but feels hackish/non official/fragile and besides, it also puts more dependencies on my project.
build.sbt
:lazy val scalatest = "org.scalatest" %% "scalatest" % "3.0.5" lazy val ujson = "com.lihaoyi" %% "ujson" % "0.7.1" name := "scala_pandoc" organization := "org.fmv1992" licenses += "GPLv2" -> url("https://www.gnu.org/licenses/gpl-2.0.html") lazy val commonSettings = Seq( version := "0.0.1-SNAPSHOT", scalaVersion := "2.12.8", pollInterval := scala.concurrent.duration.FiniteDuration(50L, "ms"), maxErrors := 10, // This final part makes test artifacts being only importable by the test files // libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % Test, // ↑↑↑↑↑ // Removed on commit 'cd9d482' to enable 'trait ScalaInitiativesTest' define // 'namedTest'. libraryDependencies ++= Seq(scalatest, ujson), scalacOptions ++= Seq("-feature", "-deprecation", "-Xfatal-warnings") ) lazy val root = (project in file(".")).settings(commonSettings).settings(assemblyJarName in assembly := "scala_pandoc.jar")
project/build.properties
:sbt.version=1.2.8
project/plugins.sbt
:addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.9")
Related question: Deploy Scala binaries without dependencies