Создание файла JAR из файла Scala - PullRequest
48 голосов
/ 01 мая 2009

Я новичок в Scala и не знаю Java. Я хочу создать файл jar из простого файла Scala. Итак, у меня есть HelloWorld.scala, сгенерируйте HelloWorld.jar.

Manifest.mf:

Main-Class: HelloWorld

В консоли я запускаю:

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)

Ответы [ 9 ]

54 голосов
/ 01 мая 2009

Пример структуры каталогов:

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

Чтобы успешно использовать ключ -jar , вам нужно две записи в файле META-INF / MANIFEST.MF : основной класс; относительные URL-адреса для любых зависимостей. Примечания к документации:

-jar

Выполнить программу, инкапсулированную в Файл JAR. Первый аргумент имя файла JAR вместо имя класса запуска. Для этого возможность работать, манифест JAR-файл должен содержать строку Форма Main-Class: имя класса. Вот, имя класса идентифицирует класс, имеющий открытый статический void main (String [] args) метод, который служит вашим отправная точка приложения. Увидеть Справочная страница инструмента Jar и банка след Java-учебник для информация о работе с Jar файлы и Jar-файлы манифестов.

Когда вы используете эту опцию, файл JAR является источником всех пользовательских классов, и другие параметры пути класса пользователя игнорируются.

(Примечания: файлы JAR можно проверять в большинстве приложений ZIP; я, вероятно, пренебрегаю обработкой пробелов в именах каталогов в пакетном скрипте; версия бегуна кода Scala 2.7.4.final.)


Для полноты, эквивалентный скрипт bash:

#!/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
9 голосов
/ 01 мая 2009

Поскольку сценарии Scala требуют установки библиотек Scala, вы должны будете включить среду выполнения Scala вместе с вашим JAR.

Существует множество стратегий для этого, например jar jar , но в конечном итоге проблема, с которой вы столкнулись, заключается в том, что запущенный вами процесс Java не может найти JAR-файлы Scala.

Для простого автономного скрипта я бы порекомендовал использовать jar jar, в противном случае вам следует начать искать инструмент управления зависимостями или попросить пользователей установить Scala в JDK .

5 голосов
/ 29 декабря 2014

Я использовал sbt Assembly , это действительно просто в использовании. Я добавил файл с именем assembly.sbt в каталог project/ в корне проекта с одним вкладышем (обратите внимание, что ваша версия может потребоваться изменить).

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

Затем просто запустите задачу assembly в sbt:

> assembly

Или просто 'сборка sbt' в корневом каталоге проекта

$ sbt assembly

Сначала он запустит ваши тесты, а затем сгенерирует новый jar-файл в каталог target/ (учитывая, что в моем build.sbt уже перечислены все мои зависимости).

В моем случае я просто делаю этот .jar исполняемый файл, переименовываем, чтобы удалить расширение, и он готов к отправке!

Кроме того, если вы используете инструмент командной строки, не забудьте добавить справочную страницу (я ненавижу скрипты без надлежащих man-страниц или с многостраничной текстовой документацией, которая даже не передается пейджер для вас).

4 голосов
/ 10 марта 2010

Вы также можете использовать maven и maven-scala-plugin. После того, как вы настроите maven, вы можете просто сделать пакет mvn, и он создаст ваш jar для вас.

3 голосов
/ 22 апреля 2016

Я не хочу писать почему и как, а просто показать решение, которое работало в моем случае (через командную строку 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 голосов
/ 25 ноября 2015

Я пытался воспроизвести метод MyDowell. Наконец-то я смог заставить это работать. Однако я нахожу, что ответ, хотя и правильный, слишком сложен для новичка (в частности, структура каталогов излишне сложна).

Я могу воспроизвести этот результат очень упрощенно. Для начала есть только один каталог, который содержит три файла:

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

первая компиляция helloworld.scala:

scalac helloworld.scala

затем создайте банку:

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

теперь вы можете запустить его с:

java -jar helloworld.jar

Я нашел это простое решение, потому что оригинал не работал. Позже я узнал, что это не потому, что это неправильно, а из-за тривиальной ошибки: если я не закрою вторую строку в MANIFEST.MF новой строкой, то эта строка будет проигнорирована. Мне потребовался час, чтобы выяснить это, и я попробовал все остальное, находясь в процессе поиска этого очень простого решения.

2 голосов
/ 27 июля 2010

Одна вещь, которая может вызвать подобную проблему (хотя это не проблема в начальном вопросе выше), заключается в том, что Java vm, по-видимому, требует, чтобы метод main возвращал void. В Scala мы можем написать что-то вроде ( соблюдать знак = в определении main ):

object MainProgram {

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

, где main на самом деле возвращает GUI -объект (т.е. это не void), но программа будет работать хорошо, когда мы запустим ее с помощью команды scala.

Если мы упакуем этот код в jar-файл с MainProgram в качестве Main-Class, Java vm будет жаловаться на отсутствие основной функции, так как тип возврата нашего main не void (я нахожу эта жалоба несколько странная, так как тип возвращаемого значения не является частью подписи).

У нас не было бы проблем, если бы мы пропустили = -sign в заголовке main или явно объявили его как Unit.

2 голосов
/ 25 марта 2010

Я изменил скрипт bash, добавив немного интеллекта, включая автоматическое создание манифеста.

Этот сценарий предполагает, что основной объект назван так же, как и файл, в котором он находится (с учетом регистра). Кроме того, либо имя текущего каталога должно совпадать с именем основного объекта, либо имя основного объекта должно быть указано в качестве параметра командной строки. Запустите этот скрипт из корневого каталога вашего проекта. Измените переменные вверху, как требуется.

Помните, что скрипт сгенерирует папки bin и dist и удалит любое существующее содержимое в 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 " "
0 голосов
/ 15 декабря 2016

Если вы не хотите использовать средства sbt, я рекомендую использовать make-файл.

Вот пример, где foo пакет заменен на foo.bar.myApp для полноты.

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...