Création d’un fichier jar à partir d’un fichier Scala

Je suis nouveau à 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ère un HelloWorld.jar.

Manifest.mf:

Main-Class: HelloWorld 

Dans la console je cours:

 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) 

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[Ssortingng]) { 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 le commutateur -jar avec succès, vous avez besoin de deux entrées dans le fichier META-INF / MANIFEST.MF : la classe principale; URL relatives à toutes les dépendances. La documentation note:

-pot

Exécutez un programme encapsulé dans un fichier JAR. Le premier argument est le nom d’un fichier JAR au lieu d’un nom de classe de démarrage. Pour que cette option fonctionne, le manifeste du fichier JAR doit contenir une ligne de la forme Main-Class: classname. Ici, nom_classe identifie la classe ayant la méthode publique statique principale vide (Ssortingng [] args) qui sert de sharepoint départ à votre application. Reportez-vous à la page de référence de l’outil Jar et à la piste Jar du didacticiel Java pour obtenir des informations sur l’utilisation des fichiers Jar et des manifestes de fichier Jar.

Lorsque vous utilisez cette option, le fichier JAR est la source de toutes les classes d’utilisateurs et les autres parameters du chemin d’access aux classes d’utilisateurs sont ignorés.

  • utilisation de la ligne de commande java
  • spec manifeste

(Notes: les fichiers JAR peuvent être inspectés avec la plupart des applications ZIP; je néglige probablement la gestion des espaces dans les noms de répertoires dans le script de traitement par lots;


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 

Les scripts Scala nécessitant l’installation des bibliothèques Scala, vous devrez inclure le moteur d’exécution Scala avec votre JAR.

Il existe de nombreuses stratégies pour ce faire, comme jar jar , mais le problème que vous rencontrez en fin de compte est que le processus Java que vous avez démarré ne trouve pas les JAR Scala.

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

J’ai fini par utiliser sbt assembly , c’est très simple à utiliser. J’ai ajouté un fichier appelé assembly.sbt dans le répertoire project/ à la racine du projet avec un seul liner (notez que votre version doit être modifiée).

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

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

 > assembly 

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

 $ sbt assembly 

Il lancera d’abord vos tests puis générera le nouveau build.sbt jar dans le répertoire target/ (étant donné que ma build.sbt répertorie déjà toutes mes dépendances).

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

En outre, si vous utilisez un outil de ligne de commande, n’oubliez pas d’append une 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 n’est même pas insérée dans un pageur).

Vous pouvez également utiliser maven et le plugin maven-scala. Une fois que vous avez configuré maven, vous pouvez simplement faire un paquet mvn et cela va créer votre pot 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 corrige un peu trop compliqué 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’y a qu’un seul répertoire qui contient trois fichiers:

 helloworld.scala MANIFEST.MF scala-library.jar 

helloworld.scala

 object HelloWorld { def main(args: Array[Ssortingng]) { println("Hello, world!") } } 

MANIFEST.MF:

 Main-Class: HelloWorld Class-Path: scala-library.jar 

comstackr 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 l’exécuter avec:

 java -jar helloworld.jar 

J’ai trouvé cette solution simple car l’original ne fonctionnait pas. Plus tard, j’ai découvert que non parce que c’est faux, mais à cause d’une erreur sortingviale: 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 essayé toutes les autres choses avant, en train de trouver cette solution très simple.

Je ne veux pas écrire pourquoi et comment montrer plutôt 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 de l’intelligence, y compris la génération automatique de manifestes.

Ce script suppose que l’object principal est nommé comme le fichier dans lequel il se trouve (sensible à la casse). De même, le nom du répertoire en cours doit être égal au nom de l’object principal ou le nom de l’object principal 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 comstack 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 dissortingbution!: $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 "Comstack 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 dans la question initiale ci-dessus), c’est que Java vm semble exiger que la méthode principale retourne void . En Scala, on peut écrire quelque chose comme ( observer le signe = dans la définition de main ):

 object MainProgram { def main(args: Array[Ssortingng]) = { new GUI(args) } } 

où main retourne en fait un object GUI (c.-à-d. qu’il n’est pas void ), mais le programme s’exécutera correctement lorsque nous le lancerons à l’aide de la commande scala.

Si nous empaquetons ce code dans un fichier jar, avec MainProgram tant que classe principale, la machine virtuelle Java se plaindra de l’absence de fonction principale, car le type de retour de notre main n’est pas void (cette plainte est quelque peu étrange, car type de retour ne fait pas partie de la signature).

Nous n’aurions aucun problème si nous omettions le = -sign dans l’en-tête de main, ou si nous le déclarions explicitement comme Unit .

Si vous ne souhaitez pas utiliser les fonctionnalités de sbt, je vous recommande d’utiliser un fichier makefile.

Voici un exemple où foo package est remplacé par foo.bar.myApp pour être complet.

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