Java 从 Scala 文件创建 jar 文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/809138/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating a jar file from a Scala file
提问by nuriaion
I'm new to Scala and don't know Java. I want to create a jar file out of a simple Scala file. So I have my HelloWorld.scala, generate a HelloWorld.jar.
我是 Scala 的新手,不知道 Java。我想从一个简单的 Scala 文件中创建一个 jar 文件。所以我有我的HelloWorld.scala,生成一个HelloWorld.jar。
Manifest.mf:
清单.mf:
Main-Class: HelloWorld
In the console I run:
在控制台中我运行:
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.access0(URLClassLoader.java:56)
at java.net.URLClassLoader.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)
采纳答案by McDowell
Sample directory structure:
示例目录结构:
X:\scala\bin
X:\scala\build.bat
X:\scala\MANIFEST.MF
X:\scala\src
X:\scala\src\foo
X:\scala\src\foo\HelloWorld.scala
HelloWorld.scala:
HelloWorld.scala:
//file: foo/HelloWorld.scala
package foo {
object HelloWorld {
def main(args: Array[String]) {
println("Hello, world!")
}
}
}
MANIFEST.MF:
清单.MF:
Main-Class: foo.HelloWorld
Class-Path: scala-library.jar
build.bat:
构建.bat:
@ECHO OFF
IF EXIST hellow.jar DEL hellow.jar
IF NOT EXIST scala-library.jar COPY %SCALA_HOME%\lib\scala-library.jar .
CALL scalac -sourcepath src -d bin src\foo\HelloWorld.scala
CD bin
jar -cfm ..\hellow.jar ..\MANIFEST.MF *.*
CD ..
java -jar hellow.jar
In order to successfully use the -jarswitch, you need two entries in the META-INF/MANIFEST.MFfile: the main class; relative URLs to any dependencies. The documentation notes:
为了成功使用-jar开关,您需要META-INF/MANIFEST.MF文件中的两个条目:主类;任何依赖项的相对 URL。文档说明:
-jar
Execute a program encapsulated in a JAR file. The first argument is the name of a JAR file instead of a startup class name. In order for this option to work, the manifest of the JAR file must contain a line of the form Main-Class: classname. Here, classname identifies the class having the public static void main(String[] args) method that serves as your application's starting point. See the Jar tool reference page and the Jar trail of the Java Tutorial for information about working with Jar files and Jar-file manifests.
When you use this option, the JAR file is the source of all user classes, and other user class path settings are ignored.
-罐
执行封装在 JAR 文件中的程序。第一个参数是 JAR 文件的名称,而不是启动类名称。为了使该选项起作用,JAR 文件的清单必须包含 Main-Class: classname 形式的一行。在这里, classname 标识具有 public static void main(String[] args) 方法的类,该方法用作应用程序的起点。有关使用 Jar 文件和 Jar 文件清单的信息,请参阅 Jar 工具参考页面和 Java 教程的 Jar 跟踪。
使用此选项时,JAR 文件是所有用户类的来源, 其他用户类路径设置将被忽略。
(Notes: JAR files can be inspected with most ZIP applications; I probably neglect handling spaces in directory names in the batch script; Scala code runner version 2.7.4.final .)
(注意:大多数 ZIP 应用程序都可以检查 JAR 文件;我可能忽略了批处理脚本中目录名称中的空格的处理;Scala 代码运行器版本 2.7.4.final 。)
For completeness, an equivalent bash script:
为了完整起见,一个等效的 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
回答by Matt Reynolds
Because Scala scripts require the Scala libraries to be installed, you will have to include the Scala runtime along with your JAR.
因为 Scala 脚本需要安装 Scala 库,所以您必须在 JAR 中包含 Scala 运行时。
There are many strategies for doing this, such as jar jar, but ultimately the issue you're seeing is that the Java process you've started can't find the Scala JARs.
执行此操作的策略有很多,例如jar jar,但最终您看到的问题是您启动的 Java 进程找不到 Scala JAR。
For a simple stand-alone script, I'd recommend using jar jar, otherwise you should start looking at a dependency management tool, or require users to install Scala in the JDK.
对于简单的独立脚本,我建议使用 jar jar,否则你应该开始寻找依赖管理工具,或者要求用户在 JDK 中安装 Scala 。
回答by tommy chheng
You can also use maven and the maven-scala-plugin. Once you set up maven, you can just do mvn package and it will create your jar for you.
您还可以使用 maven 和 maven-scala-plugin。一旦你设置了 maven,你就可以做 mvn package 了,它会为你创建你的 jar。
回答by xhalarin
I modified the bash script adding some intelligence including auto-manifest generation.
我修改了 bash 脚本,添加了一些智能,包括自动清单生成。
This script assumes that the main object is named the same as the file it is in (case sensitive). Also, either the current directory name must equal to the main object name or the main object name should be provided as a command line parameter. Launch this script from the root directory of your project. Modify the variables at the top as required.
此脚本假定主对象的名称与其所在的文件相同(区分大小写)。此外,当前目录名称必须等于主对象名称,或者主对象名称应作为命令行参数提供。从项目的根目录启动此脚本。根据需要修改顶部的变量。
Be aware that the script will generate the bin and dist folders and will ERASE any existing contents in bin.
请注意,该脚本将生成 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 "" ]] ; then
SC_APP=$(basename $SC_STARTING_PATH)
else
SC_APP=
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 " "
回答by user403090
One thing which may cause a similar problem (although it's not the problem in the initial question above) is that the Java vm seems to demand that the main method returns void
. In Scala we can write something like (observe the =-sign in the definition of main):
可能导致类似问题的一件事(尽管它不是上面最初问题中的问题)是 Java vm 似乎要求 main 方法返回void
. 在 Scala 中,我们可以这样写(观察 main 定义中的 = 符号):
object MainProgram {
def main(args: Array[String]) = {
new GUI(args)
}
}
where main actually returns a GUI
-object (i.e. it's not void
), but the program will run nicely when we start it using the scala command.
其中 main 实际上返回一个GUI
-object (即它不是void
),但是当我们使用 scala 命令启动它时,程序将运行良好。
If we package this code into a jar-file, with MainProgram
as the Main-Class, the Java vm will complain that there's no main function, since the return type of our main is not void
(I find this complaint somewhat strange, since the return type is not part of the signature).
如果我们将此代码打包到一个 jar 文件中,MainProgram
作为 Main-Class,Java vm 会抱怨没有 main 函数,因为我们的 main 的返回类型不是void
(我觉得这个抱怨有点奇怪,因为返回类型不是签名的一部分)。
We would have no problems if we left out the =-sign in the header of main, or if we explicitly declared it as Unit
.
如果我们在 main 的头文件中省略 = 符号,或者如果我们明确地将它声明为Unit
.
回答by DavidG
I ended up using sbt assembly, it is really simple to use. I added a file called assembly.sbt
into the project/
directory at the root of the project with a one liner (Note your version might need to be changed).
我最终使用了sbt assembly,它使用起来非常简单。我添加了一个名为assembly.sbt
进入project/
目录的项目的根用一个衬垫(注意你的版本可能需要改变)。
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
Then just run the assembly
task in sbt
:
然后只需assembly
在sbt
以下位置运行任务:
> assembly
Or just 'sbt assembly' in project root directory
或者只是项目根目录中的“sbt assembly”
$ sbt assembly
It will first run your tests and then it will generate the new jar into the target/
directory (given that my build.sbt
already lists all my dependencies).
它将首先运行您的测试,然后将新的 jar 生成到target/
目录中(假设我build.sbt
已经列出了我的所有依赖项)。
In my case, I just make that .jar
file executable, rename to remove the extension and it is ready to ship!
就我而言,我只是使该.jar
文件可执行,重命名以删除扩展名,然后就可以发货了!
Also, if you are doing a command line tool, don't forget to add a man page(I hate scripts without proper manpages or with multi-page plain text documentation that is not even piped into a pager for you).
此外,如果您正在使用命令行工具,请不要忘记添加手册页(我讨厌没有适当手册页的脚本或多页纯文本文档,这些文档甚至没有通过管道传输到您的寻呼机中)。
回答by javachessgui
I tried to reproduce MyDowell's method. Finally I could make it work. However I find that the answer though correct a bit too complicated for a novice ( in particular the directory structure is unnecessarily complicated ).
我试图重现 MyDowell 的方法。最后我可以让它工作。但是我发现答案虽然对新手来说有点太复杂了(特别是目录结构不必要地复杂)。
I can reproduce this result with very simplistic means. To start with there is only one directory which contains three files:
我可以用非常简单的方法重现这个结果。首先,只有一个目录包含三个文件:
helloworld.scala
MANIFEST.MF
scala-library.jar
helloworld.scala
helloworld.scala
object HelloWorld
{
def main(args: Array[String])
{
println("Hello, world!")
}
}
MANIFEST.MF:
清单.MF:
Main-Class: HelloWorld
Class-Path: scala-library.jar
first compile helloworld.scala:
首先编译helloworld.scala:
scalac helloworld.scala
then create the jar:
然后创建罐子:
\progra~1\java\jdk18~1.0_4\bin\jar -cfm helloworld.jar MANIFEST.MF .
now you can run it with:
现在你可以运行它:
java -jar helloworld.jar
I found this simple solution because the original one did not work. Later I found out that not because it is wrong, but because of a trivial error: if I don't close the second line in MANIFEST.MF with a newline, then this line will be ignored. This took me an hour to find out and I tried all other things before, in the process finding this very simple solution.
我找到了这个简单的解决方案,因为原来的解决方案不起作用。后来我发现不是因为它错了,而是因为一个小错误:如果我没有用换行符关闭MANIFEST.MF中的第二行,那么这一行将被忽略。这花了我一个小时才找到答案,我之前尝试了所有其他方法,在此过程中找到了这个非常简单的解决方案。
回答by Humoyun Ahmad
I don't want to write why's and how's rather just show the solution which worked in my case (via Linux Ubuntu command line):
我不想写为什么和如何,而只是展示在我的情况下有效的解决方案(通过 Linux Ubuntu 命令行):
1)
1)
mkdir scala-jar-example
cd scala-jar-example
2)
2)
nano Hello.scala
object Hello extends App { println("Hello, world") }
3)
3)
nano build.sbt
import AssemblyKeys._
assemblySettings
name := "MyProject"
version := "1.0"
scalaVersion := "2.11.0"
3)
3)
mkdir project
cd project
nano plugins.sbt
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.9.1")
4)
4)
cd ../
sbt assembly
5)
5)
java -jar target/target/scala-2.11/MyProject-assembly-1.0.jar
>> Hello, world
回答by AMFVargas
If you do not wish to use sbt facilities I recommend the use of a makefile.
如果您不想使用 sbt 工具,我建议您使用 makefile。
Here is an example where foopackage is replaced by foo.bar.myAppfor completeness.
这是一个示例,其中foo包被foo.bar.myApp替换以确保完整性。
makefile
生成文件
NAME=HelloWorld
JARNAME=helloworld
PACKAGE=foo.bar.myApp
PATHPACK=$(subst .,/,$(PACKAGE))
.DUMMY: default
default: $(NAME)
.DUMMY: help
help:
@echo "make [$(NAME)]"
@echo "make [jar|runJar]"
@echo "make [clean|distClean|cleanAllJars|cleanScalaJar|cleanAppJar]"
.PRECIOUS: bin/$(PATHPACK)/%.class
bin/$(PATHPACK)/%.class: src/$(PATHPACK)/%.scala
scalac -sourcepath src -d bin $<
scala-library.jar:
cp $(SCALA_HOME)/lib/scala-library.jar .
.DUMMY: runjar
runJar: jar
java -jar $(JARNAME).jar
.DUMMY: jar
jar: $(JARNAME).jar
MANIFEST.MF:
@echo "Main-Class: $(PACKAGE).$(NAME)" > $@
@echo "Class-Path: scala-library.jar" >> $@
$(JARNAME).jar: scala-library.jar bin/$(PATHPACK)/$(NAME).class \
MANIFEST.MF
(cd bin && jar -cfm ../$(JARNAME).jar ../MANIFEST.MF *)
%: bin/$(PATHPACK)/%.class
scala -cp bin $(PACKAGE).$@
.DUMMY: clean
clean:
rm -R -f bin/* MANIFEST.MF
cleanAppJar:
rm -f $(JARNAME).jar
cleanScalaJar:
rm -f scala-library.jar
cleanAllJars: cleanAppJar cleanScalaJar
distClean cleanDist: clean cleanAllJars