سؤال
أنا جديد في 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