Search code examples
scalagradleejml

Failed to build Scala program with gradle and java library dependencies


I have somewhat simple proplem (too hard for me though):

I am trying to build a simple Scala test program, which has Java library dependencies, using gradle. My test program is following

src/main/scala/test.scala

import org.ejml.simple.SimpleMatrix

object test {
  def main(args: Array[String]): Unit = {
    val M = new SimpleMatrix(2,2)
    println("Created matrix succesfully")
  }  
}

My build.gradle looks like this:

apply plugin: 'scala'

repositories {
    mavenCentral()  
}

dependencies {
    compile group: 'org.ejml', name: 'all', version: '0.30'
    compile 'org.scala-lang:scala-library:2.12.0'
}

When I build my test program with

./gradlew clean assemble

I get the proper folder structure (build/classes/main/test.class).

If I now try to run test.class with

scala test

I get the following error:

java.lang.ClassNotFoundException: org.ejml.simple.SimpleMatrix
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at test$.main(test.scala:7)
    at test.main(test.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at scala.reflect.internal.util.ScalaClassLoader.$anonfun$run$2(ScalaClassLoader.scala:98)
    at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:32)
    at scala.reflect.internal.util.ScalaClassLoader.run(ScalaClassLoader.scala:98)
    at scala.reflect.internal.util.ScalaClassLoader.run$(ScalaClassLoader.scala:90)
    at scala.reflect.internal.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:129)
    at scala.tools.nsc.CommonRunner.run(ObjectRunner.scala:22)
    at scala.tools.nsc.CommonRunner.run$(ObjectRunner.scala:21)
    at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:39)
    at scala.tools.nsc.CommonRunner.runAndCatch(ObjectRunner.scala:29)
    at scala.tools.nsc.CommonRunner.runAndCatch$(ObjectRunner.scala:28)
    at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:39)
    at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:65)
    at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:92)
    at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:103)
    at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:108)
    at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

So it seems that the EJML is not linked properly. Looks like I am overlooking something but I can't think of what. I confirmed that EJML library is downloaded

$ ls ~/.gradle/caches/modules-2/files-2.1/org.ejml/
 all        core        dense64     denseC64    equation    simple

This program also builds fine if I use Eclipse and add the .jars manually to the project

I've built java project with gradle successfully in the past using this same process (its been a few years though). Any help is appreciated.

PS. just to clarify, I am trying to build using command line. Eclipse (and other IDEs) work fine if I add the JARs manually

PPS. the program runs until line "val M = ..." (tested)


Solution

  • In this case I like to use the Gradle Application Plugin to

    • run your Scala Code
    • build a Distribution Tar (Linux) or Zip (Windows).

    I see you already use the Gradle Wrapper.

    Extend your build.gradle with:

    apply plugin: 'scala'
    apply plugin: 'application' // added
    
    repositories {
        mavenCentral()  
    }
    
    dependencies {
        compile group: 'org.ejml', name: 'all', version: '0.30'
        compile 'org.scala-lang:scala-library:2.12.0'
    }
    
    mainClassName = 'test' // added
    

    Extend your test program src/main/scala/test.scala with more Output:

    import org.ejml.simple.SimpleMatrix
    
    object test {
        def main(args: Array[String]): Unit = {
            val m = new SimpleMatrix(2,2)
            println("Created matrix succesfully: " + m)
        }  
    }
    

    Then let gradle build and run your Scala test program with - instead use of scala test

    ./gradlew clean run
    

    Now you get your Output without Error:

    $ ./gradlew clean run
    :clean
    :compileJava NO-SOURCE
    :compileScala
    :processResources NO-SOURCE
    :classes
    :run
    Created matrix succesfully: Type = dense real , numRows = 2 , numCols = 2
     0.000   0.000
     0.000   0.000
    

    Additionally let gradle build a Distribution Tar (Linux) or Zip (Windows)

    $ ./gradlew clean assemble
    :compileJava NO-SOURCE
    :compileScala UP-TO-DATE
    :processResources NO-SOURCE
    :classes UP-TO-DATE
    :jar
    :startScripts
    :distTar
    :distZip
    :assemble
    

    Have a look at build\distributions

    • a Tar File for Linux
    • a Zip File for Windows

    In this Distributions Tar / Zip you will find this Structure:

    +---bin
    |       YourProjectDirName (start up script for UN*X)
    |       YourProjectDirName.bat (startup script for Windows)
    |
    \---lib (alle needed Libs)
            all-0.30.jar
            core-0.30.jar
            dense64-0.30.jar
            denseC64-0.30.jar
            equation-0.30.jar
            scala-library-2.12.0.jar
            simple-0.30.jar
            SimpleMatrixScala.jar
    

    Alternativ use

    $ ./gradlew clean installDist
    :compileJava NO-SOURCE
    :compileScala UP-TO-DATE
    :processResources NO-SOURCE
    :classes UP-TO-DATE
    :jar UP-TO-DATE
    :startScripts UP-TO-DATE
    :installDist
    

    and have a look at build\install

    • you will find the distributions Structure Files