web-dev-qa-db-fra.com

Création d'un fichier JAR à partir d'un fichier Scala

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

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

Il existe plusieurs stratégies pour ce faire, telles que jar jar , mais le problème que vous constatez est que le processus Java que vous avez lancé ne parvient pas à 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.

9
Matt Reynolds

J'ai fini par utiliser sbt Assembly , c'est vraiment simple à utiliser. J'ai ajouté un fichier appelé Assembly.sbt dans le répertoire project/ à 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 simplement la tâche Assembly 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 fichier .jar exécutable, renommer pour supprimer l'extension et il est prêt à être expédié!

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

5
DavidG

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. 

4
tommy chheng

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

première compilation 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.

3
javachessgui

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

Une chose qui peut poser 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 main renvoie void. En Scala, nous pouvons écrire quelque chose comme ( observe 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 void), mais le programme fonctionnera correctement lorsque nous le lancerons à l'aide de la commande scala.

Si nous empaquetons ce code dans un fichier jar, avec MainProgram comme classe principale, Java vm se plaindra de l'absence de fonction principale, car le type de retour de notre classe principale n'est pas void (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 = -sign dans l'en-tête de main ou si nous le déclarions explicitement comme étant Unit.

2
user403090

J'ai modifié le script bash en y ajoutant une certaine intelligence, y compris la génération de manifeste automatique.

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 " "
2
xhalarin

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

Voici un exemple où foo package est remplacé par foo.bar.myApp pour des informations complètes.

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