Pergunta

Eu sou novo para Scala e não sei Java. Eu quero criar um arquivo jar fora de um arquivo Scala simples. Então, eu tenho o meu HelloWorld.scala, gerar um HelloWorld.jar.

Manifest.mf:

Main-Class: HelloWorld

No console eu corro:

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)
Foi útil?

Solução

estrutura de diretórios de amostra:

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

Para poder usar com sucesso o jar switch, você precisa de duas entradas no META-INF / MANIFEST.MF arquivo: a classe principal; URLs relativos a todas as dependências. As notas de documentação:

jar

Executar um programa encapsulado em um arquivo JAR. O primeiro argumento é o nome de um arquivo JAR em vez de um nome da classe de inicialização. Para que isso opção de trabalho, o manifesto do arquivo JAR deve conter uma linha do formar Main-Classe: classname. Aqui, identifica a classe classname possuindo o public static void main (String [] args) método que serve como seu aplicação é o ponto de partida. Veja o página de referência ferramenta do frasco e do frasco Trail of the Tutorial Java para informações sobre como trabalhar com Jar arquivos e manifesta-arquivo JAR.

Quando você usa esta opção, o arquivo JAR é a fonte de todas as classes de usuário, configurações de caminho de classe de usuário e outros são ignorados.

(Notas:. Arquivos JAR pode ser inspecionado com a maioria ZIP aplicações; eu provavelmente negligência lidar com espaços em nomes de diretório no script lote; Scala versão corredor código 2.7.4.final)


Para completar, um script bash equivalentes:

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

Outras dicas

Como os scripts Scala requerem as bibliotecas Scala a serem instalados, você terá que incluir o tempo de execução Scala juntamente com o seu JAR.

Há muitas estratégias para fazer isso, como Jar Jar , mas em última análise, a questão você está vendo é que o processo de Java você começou a não conseguir encontrar o Scala JARs.

Para um script autônomo simples, eu recomendo usar Jar Jar, caso contrário, você deve começar a olhar para uma ferramenta de gerenciamento de dependência, ou exigir que os usuários instalem Scala no JDK .

Acabei usando sbt montagem , é realmente simples de usar. Eu adicionei um arquivo chamado assembly.sbt no diretório project/ na raiz do projeto, com um um forro (Nota a sua versão pode precisar ser alterado).

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

Em seguida, basta executar a tarefa assembly em sbt:

> assembly

Ou apenas 'sbt assembly' no diretório raiz do projeto

$ sbt assembly

Ele vai primeiro executar os testes e, em seguida, ele irá gerar o novo jar para o diretório target/ (dado que a minha build.sbt já lista todas as minhas dependências).

No meu caso, eu só fazer essa arquivo executável .jar, mudar o nome para remover a extensão e está pronto para enviar!

Além disso, se você está fazendo uma ferramenta de linha de comando, não se esqueça de adicionar um homem página (eu odeio scripts sem manpages adequadas ou com várias páginas de documentação de texto simples que nem sequer é canalizada para um pager para você).

Você também pode utilizar o Maven e o maven-scala-plugin. Depois de configurar o Maven, você pode apenas fazer pacote mvn e ele irá criar o seu jar para você.

Eu tentei reproduzir o método de MyDowell. Finalmente, eu poderia fazê-lo funcionar. No entanto eu acho que a resposta que corrigir um pouco complicado para um novato (em particular a estrutura do diretório é desnecessariamente complicado).

Eu posso reproduzir este resultado com meios muito simplistas. Para começar com há apenas um diretório que contém três arquivos:

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

primeira HelloWorld.scala compilação:

scalac helloworld.scala

, em seguida, criar o jar:

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

Agora você pode executá-lo com:

java -jar helloworld.jar

Eu encontrei esta solução simples, porque o original não funcionou. Mais tarde eu descobri que não porque é errado, mas devido a um erro trivial: se eu não fecham a segunda linha no MANIFEST.MF com uma nova linha, a seguir esta linha será ignorado. Isso levou-me uma hora para descobrir e eu tentei todas as outras coisas antes, no processo de encontrar essa solução muito simples.

Eu não quero escrever porquê e como é bastante apenas mostrar a solução que funcionou no meu caso (via Linux Ubuntu linha de comando):

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

Eu modifiquei o script bash adicionando alguma inteligência incluindo geração de auto-manifesto.

Este script assume que o objeto principal é o mesmo nome que o arquivo está em (case sensitive). Além disso, o nome do diretório atual deve ser igual ao nome do objeto principal ou o nome do objeto principal deve ser fornecida como um parâmetro de linha de comando. Lançar este script a partir do diretório raiz do seu projeto. Modificar as variáveis ??no topo, conforme necessário.

Esteja ciente de que o script irá gerar o bin e pastas dist e apagar qualquer conteúdo existente no 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 " "

Uma coisa que pode causar um problema semelhante (embora não é o problema em questão inicial acima) é que o Java VM parece exigir que o principal método retorna void. Em Scala podemos escrever algo como ( observar a = -sign na definição do principal ):

object MainProgram {

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

onde principal realmente retorna um GUI-objeto (ou seja, não é void), mas o programa será executado muito bem quando iniciá-lo usando o comando scala.

Se empacotar esse código em um arquivo jar, com MainProgram como o Main-Class, o Java vm se queixam de que não há nenhuma função principal, uma vez que o tipo de retorno do nosso principal não é void (acho que esta queixa um pouco estranho , uma vez que o tipo de retorno não é parte da assinatura).

Nós não teria problemas se deixou de fora a = -sign no cabeçalho do principal, ou se declarou explicitamente como Unit.

Se você não quiser usar instalações sbt eu recomendo o uso de um makefile.

Aqui está um exemplo onde foo pacote é substituído por foo.bar.myApp para ser completo.

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top