wake-up-neo.com

Erstellen einer JAR-Datei aus einer Scala-Datei

Ich bin neu bei Scala und kenne Java nicht. Ich möchte eine Jar-Datei aus einer einfachen Scala-Datei erstellen. Also ich habe meine HelloWorld.scala, generiere eine HelloWorld.jar. 

Manifest.mf:

Main-Class: HelloWorld

In der Konsole führe ich aus:

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)
48
nuriaion

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 den Schalter - jar erfolgreich zu verwenden, benötigen Sie zwei Einträge in der Datei META-INF/MANIFEST.MF : the Hauptklasse; relative URLs zu Abhängigkeiten. Die Dokumentation stellt fest:

- Glas

Führen Sie ein Programm aus, das in einer JAR-Datei enthalten ist. Das erste Argument ist der Name einer JAR-Datei anstelle eines Startklassennamens. Damit diese Option funktioniert, muss das Manifest der JAR-Datei eine Zeile der Form Hauptklasse: Klassenname enthalten. Hier kennzeichnet Klassenname die Klasse mit der öffentlichen statischen Methode void main (String [] args), die als Ausgangspunkt für Ihre Anwendung dient. Weitere Informationen zum Arbeiten mit Jar-Dateien und Jar-Dateimanifesten finden Sie auf der Jar-Tool-Referenzseite und im Jar-Trail des Lernprogramms Java.

Wenn Sie diese Option verwenden, ist die JAR-Datei die Quelle aller Benutzerklassen. und andere Benutzerklassenpfadeinstellungen werden ignoriert.

(Anmerkungen: JAR-Dateien können mit den meisten Zip-Anwendungen überprüft werden. Ich vernachlässige wahrscheinlich die Behandlung von Leerzeichen in Verzeichnisnamen im Stapelskript. Scala code runner version 2.7.4.final.)


Der Vollständigkeit halber ein gleichwertiges 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
54
McDowell

Da für Scala-Skripte die Installation der Scala-Bibliotheken erforderlich ist, müssen Sie die Scala-Laufzeitumgebung in Ihr JAR aufnehmen.

Dazu gibt es viele Strategien, z. B. jar jar . Das letzte Problem ist jedoch, dass der von Ihnen gestartete Java-Prozess die Scala-JARs nicht finden kann.

Für ein einfaches Stand-Alone-Skript würde ich die Verwendung von jar jar empfehlen. Andernfalls sollten Sie ein Abhängigkeits-Management-Tool suchen oder die Installation von Scala im JDK erfordern.

9
Matt Reynolds

Am Ende habe ich sbt Assembly benutzt, es ist wirklich einfach zu benutzen. Ich habe eine Datei namens Assembly.sbt im project/-Verzeichnis im Stammverzeichnis des Projekts mit einem Einzeiler hinzugefügt (Beachten Sie, dass Ihre Version möglicherweise geändert werden muss).

addSbtPlugin("com.eed3si9n" % "sbt-Assembly" % "0.11.2")

Dann führen Sie einfach die Assembly-Task in sbt aus:

> Assembly

Oder einfach 'sbt Assembly' im Projektstammverzeichnis

$ sbt Assembly

Es führt zuerst Ihre Tests aus und generiert dann die neue jar im target/-Verzeichnis (vorausgesetzt, mein build.sbt listet bereits alle meine Abhängigkeiten auf).

In meinem Fall mache ich die .jar-Datei einfach zu einer ausführbaren Datei, benenne sie um, um die Erweiterung zu entfernen, und sie ist versandfertig!

Wenn Sie ein Befehlszeilentool ausführen, vergessen Sie nicht, eine man-Seite hinzuzufügen (Ich hasse Skripts ohne richtige Manpages oder mit mehrseitiger Klartextdokumentation, die für Sie nicht in einen Pager geleitet wird.) .

5
DavidG

Sie können auch Maven und das Maven-Scala-Plugin verwenden. Wenn Sie maven eingerichtet haben, können Sie einfach mvn package machen und es wird Ihr Jar für Sie erstellen. 

4
tommy chheng

Ich habe versucht, die Methode von MyDowell zu reproduzieren. Endlich konnte ich es schaffen. Allerdings finde ich, dass die Antwort für einen Anfänger zwar zu kompliziert ist (insbesondere die Verzeichnisstruktur ist unnötig kompliziert).

Ich kann dieses Ergebnis mit sehr einfachen Mitteln reproduzieren. Zunächst gibt es nur ein Verzeichnis, das drei Dateien enthält:

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

erstes Kompilieren von helloworld.scala:

scalac helloworld.scala

dann erstelle das Glas:

\progra~1\Java\jdk18~1.0_4\bin\jar -cfm helloworld.jar MANIFEST.MF .

jetzt können Sie es ausführen mit:

Java -jar helloworld.jar

Ich habe diese einfache Lösung gefunden, weil die ursprüngliche nicht funktioniert hat. Später habe ich herausgefunden, dass es nicht falsch ist, sondern aufgrund eines trivialen Fehlers: Wenn ich die zweite Zeile in MANIFEST.MF nicht mit einem Zeilenumbruch schließe, wird diese Zeile ignoriert. Ich brauchte eine Stunde, um das herauszufinden, und ich habe alle anderen Dinge zuvor ausprobiert. Dabei fand ich diese sehr einfache Lösung.

3
javachessgui

Ich möchte nicht schreiben, warum und wie, sondern nur die Lösung zeigen, die in meinem Fall funktionierte (über Linux Ubuntu-Befehlszeile):

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
3
Humoyun

Eine Sache, die ein ähnliches Problem verursachen kann (obwohl dies nicht das Problem in der ursprünglichen Frage oben ist) ist, dass der Java-VM scheinbar verlangt, dass die Hauptmethode void zurückgibt. In Scala können wir etwas schreiben ( Beachten Sie das = -Zeichen in der Definition von main ):

object MainProgram {

  def main(args: Array[String]) = {
    new GUI(args)
  }
}

dabei gibt main tatsächlich ein GUI- Objekt zurück (d. h. es ist nicht void), aber das Programm wird gut ausgeführt, wenn wir es mit dem Befehl scala starten.

Wenn wir diesen Code in eine jar-Datei mit MainProgram als Main-Klasse packen, wird der Java-VM-Server sich darüber beschweren, dass es keine Hauptfunktion gibt, da der Rückgabetyp unseres main nicht void ist (ich finde diese Beschwerde etwas seltsam, da die Rückgabetyp ist nicht Teil der Signatur).

Wir hätten keine Probleme, wenn wir das = -sign im Header von main weglassen oder es explizit als Unit deklarieren.

2
user403090

Ich habe das Bash-Skript geändert und einige Intelligenz hinzugefügt, einschließlich der Erstellung von automatischen Manifestationen.

Bei diesem Skript wird davon ausgegangen, dass das Hauptobjekt genauso benannt ist wie die Datei, in der es sich befindet (case sensitive). Außerdem muss entweder der aktuelle Verzeichnisname dem Hauptobjektnamen entsprechen oder der Hauptobjektname sollte als Befehlszeilenparameter angegeben werden. Starten Sie dieses Skript im Stammverzeichnis Ihres Projekts. Ändern Sie die Variablen oben nach Bedarf.

Beachten Sie, dass das Skript die Ordner bin und dist generiert und alle vorhandenen Inhalte in bin löscht.


#!/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 " "
2
xhalarin

Wenn Sie keine sbt-Funktionen verwenden möchten, empfehle ich die Verwendung eines Makefiles.

Hier ist ein Beispiel, in dem foo package zur Vollständigkeit durch foo.bar.myApp ersetzt wird.

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)" > [email protected]
    @echo "Class-Path: scala-library.jar" >> [email protected]

$(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)[email protected]

.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
0
AMFVargas