AlexG AlexG - 11 months ago 88
Java Question

Ant to Gradle: Executable JAR conversion

I've been stuck on this for several days. I'm currently tasked with converting quite a few ant scripts to gradle. I'm not very familiar with the complexities of either, so please forgive my ignorance.

I don't use dependencies or repositories in my code since this is for an offline pc, but all of the classes and jars I need are available locally. So far the conversion has been progressing, but I'm rather stuck on the conversion of the executable jar task.

This is (essentially) the ant script:

<target name="makeJar">
<jar destfile="someJar.jar">
<fileset dir="somePlace"/>
<fileset dir="someOtherPlace"/>
<attribute name="Main-Class" value="SOME.ARCHIVE.Main"/>
<zipgroupfileset dir="localFiles" includes="*.jar"/>

SOME.ARCHIVE is in one of the local .jar files. I think my major problem is that when I run my gradle script, it just loads all of the classes and jars together and just pretty much zips them up, so it's not executable, though it should be. (This causes the Main-Class of SOME.ARCHIVE to not be found.)
I compared the extracted .jars from either script and there's a very severe difference in the directory structure -- there's all kinds of poms from the extracted jars which the gradle script doesn't generate.

I assume the main problem is that I don't know how to replicate the effects of zipgroupfileset. The way I assume I can circumvent seems excessive (extract jars, copying to a directory with class files, jar-ing that directory). At that point, it seems like it would just make more sense to import the ant script and run it from gradle, but I'd prefer not to.

Here's my gradle script:

task makeJar (type: Jar) {
baseName = "someJar"
from files('somePlace')
from files('someOtherPlace')
from fileTree(dir: 'localFiles', include: '*.jar')

manifest {
attributes 'Main-Class': 'SOME.ARCHIVE.Main'

EDIT: I posted what worked for me. JBirdVegas's answer is likely a more proper way of doing it.

Answer Source

You can package all the classes from the classpath through the configurations component. Of course if needed you could provide more logic in the from to only include specific files or whatever your specific use case happens to be.

task fatJar(type: Jar) {
    manifest.attributes = jar.manifest.attributes
    includeEmptyDirs = jar.includeEmptyDirs
    baseName = + '-fat'
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }

jar {
    manifest {
        attributes 'Implementation-Title': 'My executable jar!',
                'Implementation-Version': version,
                'Main-Class': ''
    includeEmptyDirs false
    dependsOn 'fatJar'