从Scala文件创建jar文件
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从Scala文件创建jar文件相关的知识,希望对你有一定的参考价值。
我是Scala的新手,不懂Java。我想用简单的Scala文件创建一个jar文件。所以我有我的HelloWorld.scala,生成一个HelloWorld.jar。
MANIFEST.MF:
Main-Class: HelloWorld
在我运行的控制台中:
fsc HelloWorld.scala
jar -cvfm HelloWorld.jar Manifest.mf HelloWorld$.class HelloWorld.class
java -jar HelloWorld.jar
=> "Exception in thread "main" java.lang.NoClassDefFoundError: HelloWorld/jar"
java -cp HelloWorld.jar HelloWorld
=> Exception in thread "main" java.lang.NoClassDefFoundError: scala/ScalaObject
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:675)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:124)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:260)
at java.net.URLClassLoader.access$100(URLClassLoader.java:56)
at java.net.URLClassLoader$1.run(URLClassLoader.java:195)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at java.lang.ClassLoader.loadClass(ClassLoader.java:316)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:280)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:374)
at hoppity.main(HelloWorld.scala)
示例目录结构:
X:scalain
X:scalauild.bat
X:scalaMANIFEST.MF
X:scalasrc
X:scalasrcfoo
X:scalasrcfooHelloWorld.scala
HelloWorld.scala:
//file: foo/HelloWorld.scala
package foo {
object HelloWorld {
def main(args: Array[String]) {
println("Hello, world!")
}
}
}
MANIFEST.MF:
Main-Class: foo.HelloWorld
Class-Path: scala-library.jar
运行build.bat:
@ECHO OFF
IF EXIST hellow.jar DEL hellow.jar
IF NOT EXIST scala-library.jar COPY %SCALA_HOME%libscala-library.jar .
CALL scalac -sourcepath src -d bin srcfooHelloWorld.scala
CD bin
jar -cfm ..hellow.jar ..MANIFEST.MF *.*
CD ..
java -jar hellow.jar
为了成功使用-jar开关,您需要在META-INF / MANIFEST.MF文件中有两个条目:主类;任何依赖项的相对URL。文档说明:
-罐
执行封装在JAR文件中的程序。第一个参数是JAR文件的名称而不是启动类名称。为了使此选项起作用,JAR文件的清单必须包含Main-Class:classname形式的一行。这里,classname标识具有public static void main(String [] args)方法的类,该方法充当应用程序的起点。有关使用Jar文件和Jar文件清单的信息,请参阅Jar工具参考页面和Java Tutorial的Jar跟踪。
使用此选项时,JAR文件是所有用户类的源,并忽略其他用户类路径设置。
(注意:大多数ZIP应用程序都可以检查JAR文件;我可能忽略了批处理脚本中目录名中的处理空格; Scala代码运行器版本2.7.4.final。)
为了完整性,一个等效的bash脚本:
#!/bin/bash
if [ ! $SCALA_HOME ]
then
echo ERROR: set a SCALA_HOME environment variable
exit
fi
if [ ! -f scala-library.jar ]
then
cp $SCALA_HOME/lib/scala-library.jar .
fi
scalac -sourcepath src -d bin src/foo/HelloWorld.scala
cd bin
jar -cfm ../hellow.jar ../MANIFEST.MF *
cd ..
java -jar hellow.jar
由于Scala脚本需要安装Scala库,因此您必须将Scala运行时与JAR一起包含在内。
有很多策略可以做到这一点,比如jar jar,但最终你看到的问题是你启动的Java进程无法找到Scala JAR。
对于一个简单的独立脚本,我建议使用jar jar,否则你应该开始查看依赖管理工具,或者要求用户在JDK中安装Scala。
我最终使用sbt assembly,它非常简单易用。我将一个名为assembly.sbt
的文件添加到项目根目录的project/
目录中,并附带一行内容(注意您的版本可能需要更改)。
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
然后在assembly
中运行sbt
任务:
> assembly
或者只是在项目根目录中'sbt assembly'
$ sbt assembly
它将首先运行您的测试然后它将生成新的jar到target/
目录(假设我的build.sbt
已经列出了我的所有依赖项)。
在我的情况下,我只是使.jar
文件可执行,重命名删除扩展名,它已准备好发货!
此外,如果您正在使用命令行工具,请不要忘记添加man page(我讨厌脚本没有正确的联机帮助页或多页纯文本文档,甚至没有管道传输到您的寻呼机)。
您还可以使用maven和maven-scala-plugin。一旦你设置了maven,你就可以做mvn包,它会为你创建你的jar。
我试图重现MyDowell的方法。最后我可以让它发挥作用。但是我发现答案虽然对新手来说有点过于复杂(特别是目录结构不必要地复杂化)。
我可以用非常简单的方法重现这个结果。首先,只有一个目录包含三个文件:
helloworld.scala
MANIFEST.MF
scala-library.jar
helloworld.scala
object HelloWorld
{
def main(args: Array[String])
{
println("Hello, world!")
}
}
MANIFEST.MF:
Main-Class: HelloWorld
Class-Path: scala-library.jar
首先编译helloworld.scala:
scalac helloworld.scala
然后创建jar:
progra~1javajdk18~1.0_4injar -cfm helloworld.jar MANIFEST.MF .
现在你可以运行它:
java -jar helloworld.jar
我找到了这个简单的解决方案,因为原来的一个没有用。后来我发现并不是因为它错了,而是因为一个微不足道的错误:如果我没有用换行符关闭MANIFEST.MF中的第二行,那么这一行将被忽略。这花了我一个小时才发现,之前我尝试了所有其他的东西,在这个过程中找到了这个非常简单的解决方案。
我不想写为什么,而是如何只显示在我的情况下工作的解决方案(通过Linux Ubuntu命令行):
1)
mkdir scala-jar-example
cd scala-jar-example
2)
nano Hello.scala
object Hello extends App { println("Hello, world") }
3)
nano build.sbt
import AssemblyKeys._
assemblySettings
name := "MyProject"
version := "1.0"
scalaVersion := "2.11.0"
3)
mkdir project
cd project
nano plugins.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.1")
4)
cd ../
sbt assembly
5)
java -jar target/target/scala-2.11/MyProject-assembly-1.0.jar
>> Hello, world
我修改了bash脚本,添加了一些智能,包括自动清单生成。
此脚本假定主对象的名称与其所在的文件相同(区分大小写)。此外,当前目录名称必须等于主对象名称,或者主对象名称应作为命令行参数提供。从项目的根目录启动此脚本。根据需要修改顶部的变量。
请注意,该脚本将生成bin和dist文件夹,并将删除bin中的任何现有内容。
#!/bin/bash
SC_DIST_PATH=dist
SC_SRC_PATH=src
SC_BIN_PATH=bin
SC_INCLUDE_LIB_JAR=scala-library.jar
SC_MANIFEST_PATH=MANIFEST.MF
SC_STARTING_PATH=$(pwd)
if [[ ! $SCALA_HOME ]] ; then
echo "ERROR: set a SCALA_HOME environment variable"
exit 1
fi
if [[ ! -f $SCALA_HOME/lib/$SC_INCLUDE_LIB_JAR ]] ; then
echo "ERROR: Cannot find Scala Libraries!"
exit 1
fi
if [[ -z "$1" ]] ; then
SC_APP=$(basename $SC_STARTING_PATH)
else
SC_APP=$1
fi
[[ ! -d $SC_DIST_PATH ]] && mkdir $SC_DIST_PATH
if [[ ! -d $SC_BIN_PATH ]] ; then
mkdir "$SC_BIN_PATH"
else
rm -r "$SC_BIN_PATH"
if [[ -d $SC_BIN_PATH ]] ; then
echo "ERROR: Cannot remove temp compile directory: $SC_BIN_PATH"
exit 1
fi
mkdir "$SC_BIN_PATH"
fi
if [[ ! -d $SC_SRC_PATH ]] || [[ ! -d $SC_DIST_PATH ]] || [[ ! -d $SC_BIN_PATH ]] ; then
echo "ERROR: Directory not found!: $SC_SRC_PATH or $SC_DIST_PATH or $SC_BIN_PATH"
exit 1
fi
if [[ ! -f $SC_DIST_PATH/$SC_INCLUDE_LIB_JAR ]] ; then
cp "$SCALA_HOME/lib/$SC_INCLUDE_LIB_JAR" "$SC_DIST_PATH"
fi
SCALA_MAIN=$(find ./$SC_SRC_PATH -name "$SC_APP.scala")
COMPILE_STATUS=$?
SCALA_MAIN_COUNT=$(echo "$SCALA_MAIN" | wc -l)
if [[ $SCALA_MAIN_COUNT != "1" ]] || [[ ! $COMPILE_STATUS == 0 ]] ; then
echo "Main source file not found or too many exist!: $SC_APP.scala"
exit 1
fi
if [[ -f $SC_DIST_PATH/$SC_APP.jar ]] ; then
rm "$SC_DIST_PATH/$SC_APP.jar"
if [[ -f $SC_DIST_PATH/$SC_APP.jar ]] ; then
echo "Unable to remove existing distribution!: $SC_DIST_PATH/$SC_APP.jar"
exit 1
fi
fi
if [[ ! -f $SC_MANIFEST_PATH ]] ; then
LEN_BASE=$(echo $(( $(echo "./$SC_SRC_PATH" |wc -c) - 0 )))
SC_MAIN_CLASS=$(echo $SCALA_MAIN |cut --complement -c1-$LEN_BASE)
SC_MAIN_CLASS=${SC_MAIN_CLASS%%.*}
SC_MAIN_CLASS=$(echo $SC_MAIN_CLASS |awk '{gsub( "/", "'"."'"); print}')
echo $(echo "Main-Class: "$SC_MAIN_CLASS) > $SC_MANIFEST_PATH
echo $(echo "Class-Path: "$SC_INCLUDE_LIB_JAR) >> $SC_MANIFEST_PATH
fi
scalac -sourcepath $SC_SRC_PATH -d $SC_BIN_PATH $SCALA_MAIN
COMPILE_STATUS=$?
if [[ $COMPILE_STATUS != "0" ]] ; then
echo "Compile Failed!"
exit 1
fi
cd "$SC_BIN_PATH"
jar -cfm ../$SC_DIST_PATH/$SC_APP.jar ../$SC_MANIFEST_PATH *
COMPILE_STATUS=$?
cd "$SC_STARTING_PATH"
if [[ $COMPILE_STATUS != "0" ]] || [[ ! -f $SC_DIST_PATH/$SC_APP.jar ]] ; then
echo "JAR Build Failed!"
exit 1
fi
echo " "
echo "BUILD COMPLETE!... TO LAUNCH: java -jar $SC_DIST_PATH/$SC_APP.jar"
echo " "
可能导致类似问题的一件事(尽管上面的初始问题不是问题)是Java vm似乎要求main方法返回void
。在Scala中我们可以编写类似的东西(在main的定义中观察= -sign):
object MainProgram {
def main(args: Array[String]) = {
new GUI(args)
}
}
main实际上返回一个GUI
对象(即它不是void
),但是当我们使用sca
以上是关于从Scala文件创建jar文件的主要内容,如果未能解决你的问题,请参考以下文章
scala 从 scala-io 生成 jar 文件以包含在 eclipse 中