Creating a jar file from a Scala file

前端 未结 9 1998
忘掉有多难
忘掉有多难 2020-12-02 04:56

I\'m new to Scala and don\'t know Java. I want to create a jar file out of a simple Scala file. So I have my HelloWorld.scala, generate a HelloWorld.jar.

Manifest.m

相关标签:
9条回答
  • 2020-12-02 05:31

    Sample directory structure:

    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
    

    In order to successfully use the -jar switch, you need two entries in the META-INF/MANIFEST.MF file: the main class; relative URLs to any dependencies. The documentation notes:

    -jar

    Execute a program encapsulated in a JAR file. The first argument is the name of a JAR file instead of a startup class name. In order for this option to work, the manifest of the JAR file must contain a line of the form Main-Class: classname. Here, classname identifies the class having the public static void main(String[] args) method that serves as your application's starting point. See the Jar tool reference page and the Jar trail of the Java Tutorial for information about working with Jar files and Jar-file manifests.

    When you use this option, the JAR file is the source of all user classes, and other user class path settings are ignored.

    • java command line usage
    • manifest spec

    (Notes: JAR files can be inspected with most ZIP applications; I probably neglect handling spaces in directory names in the batch script; Scala code runner version 2.7.4.final .)


    For completeness, an equivalent bash script:

    #!/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
    
    0 讨论(0)
  • 2020-12-02 05:34

    I tried to reproduce MyDowell's method. Finally I could make it work. However I find that the answer though correct a bit too complicated for a novice ( in particular the directory structure is unnecessarily complicated ).

    I can reproduce this result with very simplistic means. To start with there is only one directory which contains three files:

    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
    

    first compile helloworld.scala:

    scalac helloworld.scala
    

    then create the jar:

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

    now you can run it with:

    java -jar helloworld.jar
    

    I found this simple solution because the original one did not work. Later I found out that not because it is wrong, but because of a trivial error: if I don't close the second line in MANIFEST.MF with a newline, then this line will be ignored. This took me an hour to find out and I tried all other things before, in the process finding this very simple solution.

    0 讨论(0)
  • 2020-12-02 05:35

    If you do not wish to use sbt facilities I recommend the use of a makefile.

    Here is an example where foo package is replaced by foo.bar.myApp for completeness.

    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
    
    0 讨论(0)
  • 2020-12-02 05:38

    I modified the bash script adding some intelligence including auto-manifest generation.

    This script assumes that the main object is named the same as the file it is in (case sensitive). Also, either the current directory name must equal to the main object name or the main object name should be provided as a command line parameter. Launch this script from the root directory of your project. Modify the variables at the top as required.

    Be aware that the script will generate the bin and dist folders and will ERASE any existing contents in 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 讨论(0)
  • 2020-12-02 05:40

    You can also use maven and the maven-scala-plugin. Once you set up maven, you can just do mvn package and it will create your jar for you.

    0 讨论(0)
  • 2020-12-02 05:48

    I ended up using sbt assembly, it is really simple to use. I added a file called assembly.sbt into the project/ directory at the root of the project with a one liner (Note your version might need to be changed).

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

    Then just run the assembly task in sbt:

    > assembly
    

    Or just 'sbt assembly' in project root directory

    $ sbt assembly
    

    It will first run your tests and then it will generate the new jar into the target/ directory (given that my build.sbt already lists all my dependencies).

    In my case, I just make that .jar file executable, rename to remove the extension and it is ready to ship!

    Also, if you are doing a command line tool, don't forget to add a man page (I hate scripts without proper manpages or with multi-page plain text documentation that is not even piped into a pager for you).

    0 讨论(0)
提交回复
热议问题