Erstellen einer JAR-Datei aus einer Scala-Datei
Frage
Ich bin neu in Scala und weiß nicht, Java. Ich möchte aus einer einfachen Scala-Datei eine JAR-Datei erstellen. Also ich habe meine HelloWorld.scala, eine HelloWorld.jar erzeugen.
Manifest.mf:
Main-Class: HelloWorld
In der Konsole Ich betreiben:
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)
Lösung
Beispielverzeichnisstruktur:
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:
//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%\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
Um erfolgreich die verwenden -jar Schalter, müssen Sie zwei Einträge in der META-INF / MANIFEST.MF Datei: die Hauptklasse; relativer URLs auf irgendwelche Abhängigkeiten. Die Dokumentation Hinweise:
-jar
Führen Sie ein Programm in einem gekapselten JAR-Datei. Das erste Argument ist die Namen einer JAR-Datei anstelle eines Startklassennamen. Damit diese Option zu arbeiten, das Manifest der JAR-Datei muss eine Zeile der enthalten bilden Main-Klasse: Klassenname. Hier, Klassennamen identifiziert die Klasse mit die public static void main (String [] args) Methode, die als dient Anwendung Ausgangspunkt. siehe JAR-Tool Referenzseite und die Jar Spur des Java Tutorial für Informationen über die Arbeit mit Glas Dateien und Jar-Datei Manifeste.
Wenn Sie diese Option verwenden, die JAR-Datei ist die Quelle aller Benutzerklassen, und andere Benutzerklassenpfad-Einstellungen ignoriert werden.
(Hinweise:. JAR-Dateien können mit den meisten ZIP-Anwendungen überprüft werden; ich wahrscheinlich Vernachlässigung Umgang mit Leerzeichen im Verzeichnisnamen im Batch-Skript, Scala Code runner Version 2.7.4.final)
Für die Vollständigkeit, ein äquivalenter Bash-Skript:
#!/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
Andere Tipps
Da Scala Skripte die Scala Bibliotheken erfordern installiert werden, werden Sie die Scala Laufzeit zusammen mit Ihrem JAR enthalten müssen.
Es gibt viele Strategien, dies zu tun, wie zum Beispiel jar jar , aber letztlich die Frage Sie sehen, ist, dass die Java-Prozess Sie begonnen haben nicht die Scala JAR-Dateien finden.
Für einen einfachen Stand-alone-Skript, würde ich empfehlen, Jar Jar verwenden, andernfalls sollten Sie bei einem Abhängigkeits-Management-Tool suchen starten oder Benutzer benötigen Scala in dem JDK installieren .
Ich landete mit sbt Montage , es ist wirklich einfach zu bedienen. Ich habe eine Datei assembly.sbt
in das project/
im Stammverzeichnis des Projektes mit einem Einzeiler (Beachten Sie Ihre Version werden müssen möglicherweise geändert) genannt.
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
Dann einfach die assembly
Aufgabe in sbt
ausführen:
> assembly
oder nur 'sbt Assembly' in dem Projektstammverzeichnis
$ sbt assembly
Es wird zuerst die Tests ausgeführt und dann wird es das neue Glas in das target/
Verzeichnis (vorausgesetzt, dass meine build.sbt
bereits alle meine Abhängigkeiten Listen) erzeugen.
In meinem Fall habe ich nur, dass .jar
Datei ausführbar machen, benennen Sie die Erweiterung zu entfernen, und es ist bereit zu versenden!
Auch wenn Sie ein Kommandozeilen-Tool tun, vergessen Sie nicht, ein hinzufügen Mann (ich hasse Skripte ohne richtigen manpages oder mit mehrseitigen Klartext-Dokumentation, die nicht einmal in einen Pager für Sie geleitet wird).
Sie können auch Maven verwenden und die Maven-scala-Plugin. Sobald Sie Maven einstellen, können Sie mvn Paket nur tun, und es wird für Sie Ihr Glas erstellen.
Ich habe versucht MyDowell Methode zu reproduzieren. Schließlich konnte ich es funktioniert. Allerdings finde ich, dass die Antwort allerdings ein wenig zu kompliziert für einen Anfänger korrigieren (insbesondere die Verzeichnisstruktur ist unnötig kompliziert).
kann ich dieses Ergebnis mit sehr simplen Mitteln reproduzieren. Zunächst einmal gibt es nur ein Verzeichnis ist, die enthält drei Dateien:
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
zuerst kompilieren helloworld.scala:
scalac helloworld.scala
erstellen Sie dann das Glas:
\progra~1\java\jdk18~1.0_4\bin\jar -cfm helloworld.jar MANIFEST.MF .
Jetzt können Sie führen Sie es mit:
java -jar helloworld.jar
Ich fand diese einfache Lösung, weil das Original nicht funktionierte. Später fand ich heraus, dass nicht, weil es falsch ist, aber wegen eines trivialen Fehler: Wenn ich nicht die zweite Zeile in MANIFEST.MF mit einem Newline schließen tun, dann wird diese Zeile ignoriert. Dies dauerte eine Stunde, um herauszufinden, und ich versuchte, alle anderen Dinge vor, in dem Prozess diese sehr einfache Lösung zu finden.
Ich will nicht schreiben, warum und wie ist ziemlich einfach, die Lösung zeigen, was in meinem Fall war (über Linux Ubuntu Kommandozeile):
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
ich das Bash-Skript geändert einig Intelligence einschließlich Auto-Manifest Generation hinzufügen.
Dieses Skript geht davon aus, dass das Hauptziel die gleiche wie die Datei mit dem Namen wird es in dem (Groß- und Kleinschreibung). Auch entweder der aktuelle Verzeichnis-Name muss gleich mit dem Hauptobjektnamen oder das Hauptobjekt Name sollte als Befehlszeilenparameter zur Verfügung gestellt werden. Starten Sie dieses Skript aus dem Stammverzeichnis Ihres Projekts. Ändern Sie die Variablen an der Spitze, wie erforderlich.
Beachten Sie, dass das Skript die sind und dist Ordner erzeugen wird und alle vorhandenen Inhalte in Bin löschen.
#!/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 " "
Eine Sache, die ein ähnliches Problem verursachen kann (obwohl es oben nicht das Problem in der Ausgangsfrage ist) ist, dass der Java-VM zu fordern scheint, dass die Haupt-Methode gibt void
.
object MainProgram {
def main(args: Array[String]) = {
new GUI(args)
}
}
, wo Haupt tatsächlich ein GUI
-Objekt zurückgibt (das heißt, es ist nicht void
), aber das Programm wird gut laufen, wenn wir es mit dem scala-Befehl starten.
Wenn wir diesen Code in eine Jar-Datei verpacken, mit MainProgram
als Main-Klasse, wird die Java vm beklagen, dass es keine Hauptfunktion, da der Rückgabetyp unserer wichtigsten ist void
nicht (ich finde diese Beschwerde etwas seltsam da der Rückgabetyp nicht Teil der Signatur ist).
Wir würden keine Probleme haben, wenn wir das = -Zeichen in der Kopfzeile des Hauptes ausgelassen, oder wenn wir ausdrücklich als Unit
erklärt.
Wenn Sie nicht wünschen, SBT Einrichtungen nutzen ich die Verwendung einer Make-Datei empfehlen.
Hier ist ein Beispiel, in dem foo Paket wird ersetzt durch foo.bar.myApp auf Vollständigkeit.
Make-Datei
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