3

I'm trying to build an executable JAR with a Groovy main class. I can get a Java main class to run exactly as expected, but the Groovy equivalent just isn't found and won't run.

In src/main/groovy/foo/Test.groovy:

package foo

public class Test { // (Yes, the public keywords here and below are redundant)
    public static void main(String[] args) {
        println "groovy world"
    }
}

In src/main/groovy/foo/Test2.java:

package foo;

public class Test2 {
    public static void main(String[] args) {
        System.out.println("java world");
    }
}

Gradle file:

plugins {
    id 'java'
    id 'groovy'
    id 'application'
}

sourceCompatibility = 1.8
targetCompatibility = 1.8
mainClassName = 'foo.Test'

repositories {
    mavenCentral()
}
dependencies {
    compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.7'
}
jar {
    manifest {
        attributes 'Main-Class': mainClassName
    }
}

I build a JAR:

$ ./gradlew build

And try and run it (overriding the manifest Main-Class):

$  java -cp build/libs/test-groovy-main.jar foo.Test2
java world

$ java -cp build/libs/test-groovy-main.jar foo.Test
Error: Could not find or load main class foo.Test

If I turn on verbose output whilst doing this, in the second case I see "Loaded foo.Test2", but no "Loaded foo.Test" in the first.

I had thought that Groovy source compiles to plain java classes, and indeed decompiling the Test.class file I can see a public static main(String...) method in a public Test class. What am I doing wrong?

I'm using Gradle 2.6, Java 1.8.0 and Groovy 2.4.7 on Ubuntu 16.04.

I have the test case in version control here:

https://github.com/wu-lee/test-groovy-main

Anton Hlinisty
  • 1,441
  • 1
  • 20
  • 35
wu-lee
  • 749
  • 4
  • 17
  • You still need the groovy runtime jar – tim_yates Mar 20 '18 at 16:25
  • 1
    You can bundle your jar and the dependencies (ie: groovy) using the shadow plugin http://imperceptiblethoughts.com/shadow/#getting_started – tim_yates Mar 20 '18 at 16:29
  • I'll come up with an example/answer in a few hours if no-one else jumps in – tim_yates Mar 20 '18 at 16:29
  • Aha! I've missed something: in my other projects there's some additional complexity I've removed, but which included a platform-dependent fat-jar task built dynamically, adapted from http://stackoverflow.com/questions/10986244/building-a-uberjar-with-gradle – wu-lee Mar 20 '18 at 17:05
  • The shadow plugin is awesome, don't take the Uber jar route in that question – tim_yates Mar 20 '18 at 17:21
  • @tim_yates, would you be able to add an answer which elaborates on why the shadow plugin is super-awesome? Just for the record... and I'd vote it up. – wu-lee Mar 22 '18 at 10:05

1 Answers1

2

Adding of from section worked for me:

jar {
    manifest {
        attributes 'Main-Class': mainClassName
    }

    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

it puts the org.codehaus.groovy:groovy-all:2.4.7 dependency to your jar.

UPD

Created a pull-request https://github.com/wu-lee/test-groovy-main/pull/1

Anton Hlinisty
  • 1,441
  • 1
  • 20
  • 35
  • 1
    I was going to answer my own question, but it's much nicer to give someone a tick for doing it for me :). – wu-lee Mar 20 '18 at 23:31
  • Works in the simple case, however note that the SO answer I found on uberjars says that you need to exclude .SF, .RSA and .DSA file extensions from the META_INF directory when trying this approach with signed jars, or you'll hit this issue: https://discuss.gradle.org/t/signing-a-custom-gradle-plugin-thats-downloaded-by-the-build-system-from-github/1365/2 – wu-lee Mar 22 '18 at 10:10