Question

Je suis nouveau sur Scala et je ne connais pas Java. Je veux créer un fichier jar à partir d'un simple fichier Scala. J'ai donc mon HelloWorld.scala, générer un HelloWorld.jar.

Manifest.mf:

Main-Class: HelloWorld

Dans la console, je lance:

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)
Était-ce utile?

La solution

Exemple de structure de répertoire:

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

Pour utiliser avec succès le commutateur -jar , vous devez disposer de deux entrées dans le fichier META-INF / MANIFEST.MF : la classe principale; URL relatives à toutes les dépendances. Les notes de documentation:

  

-jar

     

Exécuter un programme encapsulé dans un   Fichier JAR. Le premier argument est le   nom d'un fichier JAR au lieu d'un   nom de la classe de démarrage. Pour cela   option de travail, le manifeste de la   Le fichier JAR doit contenir une ligne du   formulaire Main-Class: nom de classe. Ici,   nom de classe identifie la classe ayant   public static void main (String []   args) qui sert de votre   point de départ de l'application. Voir le   Page de référence de l'outil Jar et le Jar   piste du tutoriel Java pour   informations sur travailler avec Jar   des fichiers et des manifestes de fichiers Jar.

     

Lorsque vous utilisez cette option, le fichier JAR   est la source de toutes les classes d'utilisateurs,    et les autres paramètres de chemin d'accès aux classes d'utilisateurs sont ignorés.

(Remarques: les fichiers JAR peuvent être inspectés avec la plupart des applications ZIP; je néglige probablement de gérer les espaces dans les noms de répertoire dans le script batch; Scala code runner version 2.7.4.final.)

Pour être complet, un script bash équivalent:

#!/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

Autres conseils

Les scripts Scala nécessitant l’installation des bibliothèques Scala, vous devez donc inclure le runtime Scala avec votre fichier JAR.

Il existe de nombreuses stratégies pour ce faire, telles que jar jar , mais finalement le problème vous constatez que le processus Java que vous avez lancé ne permet pas de trouver les fichiers JAR Scala.

Pour un script autonome simple, je vous recommande d'utiliser jar jar, sinon vous devriez commencer à rechercher un outil de gestion des dépendances ou demander aux utilisateurs d'installer Scala dans le JDK .

J'ai utilisé sbt assembly , son utilisation est vraiment simple. J'ai ajouté un fichier appelé assembly.sbt dans le répertoire project/ situé à la racine du projet avec une ligne (notez que votre version devra peut-être être modifiée).

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

Ensuite, exécutez la assembly tâche dans sbt:

> assembly

Ou simplement 'sbt assembly' dans le répertoire racine du projet

$ sbt assembly

Il exécutera d'abord vos tests, puis générera le nouveau fichier jar dans le répertoire target/ (étant donné que mon build.sbt répertorie déjà toutes mes dépendances).

Dans mon cas, je viens de rendre ce .jar fichier exécutable, le renommer pour supprimer l'extension et il est prêt à être expédié!

De même, si vous utilisez un outil de ligne de commande, n'oubliez pas d'ajouter un page de manuel (je déteste les scripts sans pages de manuel appropriées ou avec une documentation en texte brut de plusieurs pages qui ne figure même pas dans un pager pour vous).

Vous pouvez également utiliser maven et le plugin maven-scala-plugin. Une fois que vous avez configuré Maven, vous pouvez créer un package MVN qui créera votre jarre pour vous.

J'ai essayé de reproduire la méthode de MyDowell. Enfin, je pourrais le faire fonctionner. Cependant, je trouve que la réponse, bien que correcte, est un peu trop compliquée pour un novice (en particulier, la structure des répertoires est inutilement compliquée).

Je peux reproduire ce résultat avec des moyens très simplistes. Pour commencer, il n’existe qu’un seul répertoire contenant trois fichiers:

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

compilez d'abord helloworld.scala:

scalac helloworld.scala

puis créez le pot:

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

maintenant vous pouvez le lancer avec:

java -jar helloworld.jar

J'ai trouvé cette solution simple car l'original ne fonctionnait pas. Plus tard, j'ai découvert que ce n'était pas parce que c'était faux, mais à cause d'une erreur triviale: si je ne ferme pas la deuxième ligne dans MANIFEST.MF avec une nouvelle ligne, cette ligne sera ignorée. Cela m'a pris une heure pour le découvrir et j'ai déjà essayé toutes les autres choses avant de trouver cette solution très simple.

Je ne veux pas écrire pourquoi et comment montrer simplement la solution qui a fonctionné dans mon cas (via la ligne de commande 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

J'ai modifié le script bash en ajoutant une intelligence, notamment la génération de manifestes automatiques.

Ce script suppose que l'objet principal porte le même nom que le fichier dans lequel il se trouve (sensible à la casse). De plus, le nom du répertoire actuel doit être égal au nom de l'objet principal ou doit être fourni en tant que paramètre de ligne de commande. Lancez ce script à partir du répertoire racine de votre projet. Modifiez les variables en haut si nécessaire.

Sachez que le script générera les dossiers bin et dist et effacera tout contenu existant dans 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 " "

Une chose qui peut causer un problème similaire (bien que ce ne soit pas le problème de la question initiale ci-dessus) est que Java vm semble exiger que la méthode principale retourne void. En Scala, nous pouvons écrire quelque chose comme ( observez le signe = dans la définition de main ):

object MainProgram {

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

où main renvoie en fait un objet GUI (c'est-à-dire que ce n'est pas MainProgram), mais le programme s'exécute correctement lorsque nous le démarrons à l'aide de la commande scala.

Si nous empaquetons ce code dans un fichier jar, avec Unit en tant que classe principale, Java vm se plaint de l'absence de fonction principale, car le type de retour de notre classe principale n'est pas <=> (je trouve cette plainte quelque peu étrange, car le type de retour ne fait pas partie de la signature).

Nous n'aurions aucun problème si nous omettions le signe = dans l'en-tête de main ou si nous le déclarions explicitement comme <=>.

Si vous ne souhaitez pas utiliser les installations sbt, je vous recommande d'utiliser un fichier makefile.

Voici un exemple où le package toto est remplacé par toto.bar.myApp pour plus de détails.

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top