문제

나는 스칼라를 처음 사용하고 자바를 모른다. 간단한 Scala 파일에서 Jar 파일을 만들고 싶습니다. 그래서 나는 내 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)
도움이 되었습니까?

해결책

샘플 디렉토리 구조 :

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

성공적으로 사용하려면 -항아리 스위치, 당신은 두 개의 항목이 필요합니다 Meta-Inf/Manifest.mf 파일 : 메인 클래스; 모든 종속성에 대한 상대 URL. 문서 참고 사항 :

-항아리

JAR 파일에 캡슐화 된 프로그램을 실행합니다. 첫 번째 인수는 시작 클래스 이름 대신 JAR 파일의 이름입니다. 이 옵션이 작동하려면 JAR 파일의 매니페스트에는 Main-Class : ClassName 양식의 줄이 포함되어야합니다. 여기에서 ClassName은 응용 프로그램의 시작점 역할을하는 공개 정적 무효 기본 (String [] args) 메소드가있는 클래스를 식별합니다. JAR 파일 및 JAR 파일 매니페스트 작업에 대한 정보는 JAR 도구 참조 페이지와 Java 튜토리얼의 JAR 트레일을 참조하십시오.

이 옵션을 사용하면 JAR 파일은 모든 사용자 클래스의 소스입니다. 다른 사용자 클래스 경로 설정은 무시됩니다.

(참고 : JAR 파일은 대부분의 ZIP 응용 프로그램으로 검사 할 수 있습니다. 배치 스크립트의 디렉토리 이름의 처리 공간을 무시하는 것을 무시할 수 있습니다. Scala Code Runner 버전 2.7.4.final.)


완전성을 위해 동등한 배쉬 스크립트 :

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

다른 팁

Scala Scripts에는 Scala 라이브러리를 설치해야하므로 Scala 런타임과 JAR과 함께 포함해야합니다.

다음과 같은 많은 전략이 있습니다. 항아리 항아리, 그러나 궁극적으로 당신이보고있는 문제는 당신이 시작한 Java 프로세스가 Scala Jars를 찾을 수 없다는 것입니다.

간단한 독립형 스크립트의 경우 Jar Jar를 사용하는 것이 좋습니다. 그렇지 않으면 종속성 관리 도구를 보거나 사용자가 Scala를 설치해야합니다. JDK에서.

나는 결국 사용했다 SBT 어셈블리, 사용하기가 정말 간단합니다. 나는라는 파일을 추가했다 assembly.sbtproject/ 하나의 라이너가있는 프로젝트의 루트에있는 디렉토리 (버전을 변경해야 할 수도 있음).

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

그런 다음 그냥 실행하십시오 assembly 작업 sbt:

> assembly

또는 프로젝트 루트 디렉토리의 'SBT 어셈블리'

$ sbt assembly

먼저 테스트를 실행 한 다음 새 병을 생성합니다. target/ 디렉토리 (내 build.sbt 이미 내 모든 종속성을 나열합니다).

제 경우에는 그냥 만듭니다 .jar 파일 실행 파일, 확장을 제거하기 위해 이름 바꾸면 배송 준비가되었습니다!

또한 명령 줄 도구를 수행하는 경우 추가를 잊지 마십시오. 남자 페이지 (나는 적절한 맨 페지가없는 스크립트를 싫어하거나 당신을 위해 호출기에 파이프되지 않은 다중 페이지 일반 텍스트 문서가있는 것을 싫어합니다).

Maven과 Maven-Scala-Plugin을 사용할 수도 있습니다. Maven을 설치하면 MVN 패키지 만 수행하면 항아리를 만들 수 있습니다.

MyDowell의 방법을 재현하려고했습니다. 마침내 나는 그것을 작동시킬 수있었습니다. 그러나 대답은 초보자에 대해 너무 복잡하지만 (특히 디렉토리 구조는 불필요하게 복잡합니다).

이 결과를 매우 단순한 수단으로 재현 할 수 있습니다. 우선 3 개의 파일을 포함하는 디렉토리는 하나뿐입니다.

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

원래 솔루션이 작동하지 않았기 때문에이 간단한 솔루션을 찾았습니다. 나중에 나는 그것이 잘못 되었기 때문에가 아니라 사소한 오류 때문에 : Newline이있는 Manifest.mf에서 두 번째 줄을 닫지 않으면이 줄은 무시됩니다. 이것은 나를 알아내는 데 한 시간이 걸렸고,이 매우 간단한 솔루션을 찾는 과정에서 다른 모든 것을 시도했습니다.

나는 이유와 방법을 쓰고 싶지 않습니다 (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

Bash 스크립트를 수정하여 자동 관리 생성을 포함한 일부 지능을 추가했습니다.

이 스크립트는 기본 객체의 이름이있는 파일과 동일하다고 가정합니다 (Case Sensitive). 또한 현재 디렉토리 이름은 기본 오브젝트 이름과 같아야하거나 기본 개체 이름은 명령 줄 매개 변수로 제공되어야합니다. 프로젝트의 루트 디렉토리 에서이 스크립트를 시작하십시오. 필요에 따라 상단의 변수를 수정하십시오.

스크립트가 빈과 거리 폴더를 생성하고 기존 내용물이 빈으로 지울 것입니다.


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

비슷한 문제를 일으킬 수있는 한 가지는 (위의 초기 질문에서 문제는 아니지만) Java VM이 기본 메소드가 반환 될 것을 요구하는 것 같습니다. void. 스칼라에서 우리는 다음과 같은 것을 쓸 수 있습니다.메인의 정의에서 = -Sign을 관찰하십시오):

object MainProgram {

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

Main이 실제로 반환하는 곳 GUI-Object (예 : 그렇지 않습니다 void), 그러나 스칼라 명령을 사용하여 시작하면 프로그램이 잘 실행됩니다.

이 코드를 jar 파일로 포장하면 MainProgram 메인 클래스로서, Java VM은 메인의 반환 유형이 아니기 때문에 기본 기능이 없다고 불평합니다. void (반환 유형이 서명의 일부가 아니기 때문에이 불만이 다소 이상하다고 생각합니다).

우리가 메인 헤더에 = -sign을 제외하고, 또는 명시 적으로 그것을 Unit.

SBT 시설을 사용하지 않으려면 makefile을 사용하는 것이 좋습니다.

다음은 어디에 있습니다 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
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top