Using Gradle to build a jar with dependencies

2019-01-01 10:43发布

问题:

I have a multiproject build and I put a task to build a fat jar in one of the subprojects. I created the task similar to the one described in the cookbook.

jar {
  from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
  manifest { attributes \'Main-Class\': \'com.benmccann.gradle.test.WebServer\' }
}

Running it results in the following error:

Cause: You can\'t change a configuration which is not in unresolved state!

I\'m not sure what this error means. I also reported this on the Gradle JIRA in case it is a bug.

回答1:

I posted a solution in JIRA against Gradle:

// Include dependent libraries in archive.
mainClassName = \"com.company.application.Main\"

jar {
  manifest { 
    attributes \"Main-Class\": \"$mainClassName\"
  }  

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


回答2:

If you want the jar task to behave normally and a additinal fatJar task, use the following:

task fatJar(type: Jar) {
    classifier = \'all\'
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
}

The important part is with jar. Without it, the classes of this project are not included.



回答3:

The answer by @felix almost brought me there. I had two issues:

  1. With Gradle 1.5, the manifest tag was not recognised inside the fatJar task, so the Main-Class attribute could not directly be set
  2. the jar had conflicting external META-INF files.

The following setup resolves this

jar {
  manifest {
    attributes(
      \'Main-Class\': \'my.project.main\',
    )
  }
}

task fatJar(type: Jar) {
  manifest.from jar.manifest
  classifier = \'all\'
  from {
    configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
  } {
    exclude \"META-INF/*.SF\"
    exclude \"META-INF/*.DSA\"
    exclude \"META-INF/*.RSA\"
  }
  with jar
}

To add this to the standard assemble or build task, add:

artifacts {
    archives fatJar
}

Edit: thanks to @mjaggard: in recent versions of Gradle, change configurations.runtime to configurations.runtimeClasspath



回答4:

This works fine for me.

My Main class:

package com.curso.online.gradle;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

public class Main {

    public static void main(String[] args) {
        Logger logger = Logger.getLogger(Main.class);
        logger.debug(\"Starting demo\");

        String s = \"Some Value\";

        if (!StringUtils.isEmpty(s)) {
            System.out.println(\"Welcome \");
        }

        logger.debug(\"End of demo\");
    }

}

And it is the content of my file build.gradle:

apply plugin: \'java\'

apply plugin: \'eclipse\'

repositories {
    mavenCentral()
}

dependencies {
    compile group: \'commons-collections\', name: \'commons-collections\', version: \'3.2\'
    testCompile group: \'junit\', name: \'junit\', version: \'4.+\'
    compile  \'org.apache.commons:commons-lang3:3.0\'
    compile  \'log4j:log4j:1.2.16\'
}

task fatJar(type: Jar) {
    manifest {
        attributes \'Main-Class\': \'com.curso.online.gradle.Main\'
    }
    baseName = project.name + \'-all\'
    from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
    with jar
}

And I write the following in my console:

java -jar ProyectoEclipseTest-all.jar

And the output is great:

log4j:WARN No appenders could be found for logger (com.curso.online.gradle.Main)
.
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more in
fo.
Welcome


回答5:

To generate a fat JAR with a main executable class, avoiding problems with signed JARs, I suggest gradle-one-jar plugin. A simple plugin that uses the One-JAR project.

Easy to use:

apply plugin: \'gradle-one-jar\'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath \'com.github.rholder:gradle-one-jar:1.0.4\'
    }
}

task myjar(type: OneJar) {
    mainClass = \'com.benmccann.gradle.test.WebServer\'
}


回答6:

Simple sulution

jar {
    manifest {
        attributes \'Main-Class\': \'cova2.Main\'
    } 
    doFirst {
        from { configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) } }
    }
}


回答7:

The answer from @ben almost works for me except that my dependencies are too big and I got the following error

Execution failed for task \':jar\'.
> archive contains more than 65535 entries.

  To build this archive, please enable the zip64 extension.

To fix this problem, I have to use the following code

mainClassName = \"com.company.application.Main\"

jar {
  manifest { 
    attributes \"Main-Class\": \"$mainClassName\"
  }  
  zip64 = true
  from {
    configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
  }
}


回答8:

If you\'re used to ant then you could try the same with Gradle too:

task bundlemyjava{
    ant.jar(destfile: \"build/cookmyjar.jar\"){
        fileset(dir:\"path to your source\", includes:\'**/*.class,*.class\', excludes:\'if any\')
        } 
}