سؤال

أنا جديد في Scala ولا أعرف Java.أريد إنشاء ملف jar من ملف Scala البسيط.لذلك لدي HelloWorld.scala الخاص بي، وقم بإنشاء HelloWorld.jar.

البيان.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)
هل كانت مفيدة؟

المحلول

بنية الدليل النموذجية:

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.سكالا:

//file: foo/HelloWorld.scala
package foo {
  object HelloWorld {
    def main(args: Array[String]) {
      println("Hello, world!")
    }
  }
}

البيان.MF:

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

بناء.بات:

@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

من أجل استخدام بنجاح -إناء التبديل، تحتاج إلى إدخالين في ميتا-INF/MANIFEST.MF ملف:الطبقة الرئيسيةعناوين URL النسبية لأي تبعيات.تلاحظ الوثائق:

-إناء

تنفيذ برنامج مغلف في ملف جرة.الوسيطة الأولى هي اسم ملف جرة بدلاً من اسم فئة بدء التشغيل.لكي يعمل هذا الخيار ، يجب أن يحتوي بيان ملف الجرة على سطر من الفئة الرئيسية النموذج:اسم الفئة.هنا ، يحدد ClassName الفصل الذي يحتوي على طريقة الفراغ الثابتة (سلسلة [] args) التي تعمل كنقطة انطلاق للتطبيق.راجع صفحة مرجع الأداة JAR ودرب JAR of the Java Tutorial للحصول على معلومات حول العمل مع ملفات JAR و AMAR-LIFT.

عند استخدام هذا الخيار ، فإن ملف الجرة هو مصدر جميع فئات المستخدمين ، ويتم تجاهل إعدادات مسار فئة المستخدم الأخرى.

(ملحوظات:يمكن فحص ملفات JAR باستخدام معظم تطبيقات ZIP؛ربما أهمل التعامل مع المسافات في أسماء الأدلة في البرنامج النصي الدفعي؛إصدار Scala Code Runner 2.7.4.نهائي.)


للاكتمال، نص برمجي 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

نصائح أخرى

ولأن النصوص سكالا تتطلب مكتبات سكالا ليتم تثبيتها، سيكون لديك لتشمل وقت سكالا جنبا إلى جنب مع JAR الخاص بك.

وهناك العديد من الاستراتيجيات للقيام بذلك، مثل جرة جرة ، ولكن في النهاية القضية ترونه هو أن عملية جافا كنت قد بدأت لا يمكن العثور على سكالا الجرار.

لسيناريو بسيط مستقل، أنصح باستخدام جرة جرة، وإلا يجب أن تبدأ في النظر في أداة إدارة التبعية، أو تطلب من المستخدمين لتثبيت سكالا <م> في JDK .

وانتهى بي الأمر باستخدام SBT التجمع أو أنها بسيطة جدا للاستخدام. أضفت ملف يسمى assembly.sbt إلى الدليل project/ في جذور المشروع مع بطانة واحدة (لاحظ الإصدار الخاص بك قد تحتاج إلى تغيير).

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

وبعد ذلك فقط تشغيل المهمة assembly في sbt:

> assembly

وأو مجرد "تجميع التون" في الدليل الجذر المشروع

$ sbt assembly

وسيكون أول تشغيل الاختبارات الخاصة بك ومن ثم فإنه سيتم إنشاء جرة جديدة إلى الدليل target/ (بالنظر إلى أن بلدي build.sbt يسرد بالفعل كل ما عندي من تبعيات).

في حالتي، أنا فقط جعل ذلك تنفيذ ملف .jar، إعادة تسمية لإزالة الملحق وأنه على استعداد للسفينة!

وبالإضافة إلى ذلك، إذا كنت تفعل أداة سطر الأوامر، لا تنس أن تضيف رجل الصفحة (أنا أكره البرامج النصية دون manpages المناسبة أو مع عدة صفحات وثائق النص العادي التي لم يتم حتى تضخ إلى النداء لك).

ويمكنك أيضا استخدام مخضرم و-مخضرم سكالا البرنامج المساعد. مرة واحدة قمت بإعداد مخضرم، يمكنك القيام به للتو حزمة mvn وأنه سيتم إنشاء جرة لك.

وحاولت استنساخ طريقة 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 مع سطر جديد، ثم سيتم تجاهل هذا الخط. هذا وقد كلفني ساعة لمعرفة وحاولت كل أمور أخرى من قبل، في عملية إيجاد هذا الحل بسيط جدا.

وأنا لا أريد أن أكتب لماذا وكيف انها بدلا تظهر فقط الحل الذي يعمل في حالتي (عن طريق لينكس أوبونتو سطر الأوامر):

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

ولقد عدلت النصي باش إضافة بعض المعلومات الاستخبارية بما في ذلك الجيل لصناعة السيارات واضح.

وهذا السيناريو يفترض أن الهدف الرئيسي يدعى نفس الملف هو في (حالة الأحرف). أيضا، إما يجب أن يساوي اسم الدليل الحالي إلى اسم الكائن الرئيسي أو ينبغي توفير اسم الكائن الرئيسي كمعلمة سطر الأوامر. إطلاق هذا البرنامج النصي من الدليل الجذر من المشروع الخاص بك. تعديل المتغيرات في أعلى كما هو مطلوب.

وأن ندرك أن السيناريو سوف تولد بن والمجلدات حي وسوف يمحو أي محتويات موجودة في سلة المهملات.


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

والشيء الوحيد الذي قد يسبب مشكلة مماثلة (على الرغم من انها ليست المشكلة في السؤال الأول أعلاه) هو أن VM جافا يبدو أن نطالب بأن الأسلوب الرئيسي يعود void. في سكالا يمكننا أن أكتب شيئا مثل (<م> مراقبة =، وقع في تعريف الرئيسية ):

object MainProgram {

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

وحيث الرئيسي يعود في الواقع GUI وجوه (أي انها ليست void)، ولكن سيتم تشغيل البرنامج بشكل جيد عندما نبدأ باستخدام الأمر سكالا.

إذا نحن مجموعة هذه التعليمات البرمجية في ملف جرة، مع MainProgram باسم الرئيسية كلاس، فإن VM جافا يشكون من أن ليس هناك وظيفة رئيسية، لأن نوع الإرجاع الرئيسية لدينا لا void (أجد هذه الشكوى غريب نوعا ما منذ نوع الإرجاع ليس جزءا من التوقيع).

و

ونحن لن يكون لها أي مشاكل إذا تركنا خارج =، وقع في رأس الرئيسي، أو إذا أعلنا صراحة أنها Unit.

إذا كنت لا ترغب في استخدام مرافق sbt أوصي باستخدام ملف makefile.

هنا مثال حيث foo يتم استبدال الحزمة بـ foo.bar.myApp للأكتمال.

com.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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top