Categories
executable-jar fatjar gradle uberjar

Using Gradle to build a JAR with dependencies

161

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 this 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.

2

249

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) }
  }
}

Note that mainClassName must appear BEFORE jar {.

12

  • 5

    I had to modify this to configurations.runtime.collect for my project as I have runtime dependencies as well.

    Jun 29, 2016 at 17:32

  • 8

    I had to add def mainClassName to make the code work… I was receiving Could not set unknown property ‘mainClassName’ for root project

    – hanskoff

    May 12, 2017 at 12:45

  • 3

    How do you handle file name collisions? Files on the same path in different JARs will be overwritten.

    – wst

    Aug 20, 2017 at 10:47

  • 10

    Unfortunately this does not work any more. I use gradle 4.10 and the new implementation configuration instead of the now deprecated compile. The above code builds me a small jar without the dependencies. When I change it ( from { configurations.implementation.collect {...} }), an error occurs saying that resolving configuration ‘implementation’ directly is not allowed

    Mar 8, 2019 at 10:11


  • 6

    @BastianVoigt configurations.compileClasspath will fix all the implementations, but will leave out the api dependencies afik. Found here in another answer the solution runtimeClasspath. That includes the api dependencies too.

    – rekire

    Jan 14, 2020 at 7:48


79

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

  • 3

    This also fixed a problem I had where one of my dependency jars was signed. The signature files were put into my jar’s META-INF, but the signature no longer matched the content.

    – Flavin

    Mar 7, 2016 at 17:08

  • 2

    Special thanks for artifacts: exactly what I was looking for.

    – AlexR

    Mar 28, 2017 at 11:26

  • When you run gradle fatJar the runtime dependencies don’t seem to be compiled, so they cannot be copied.

    – mjaggard

    Jan 4, 2018 at 10:44

  • The best answer!

    – Giri

    Apr 7 at 1:31

65

If you want the jar task to behave normally and also have an additional 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.

8

  • 1

    Also see the following issue if you are using signed jars to included and run into a problem with signatures: stackoverflow.com/questions/999489/…

    Apr 4, 2014 at 23:56

  • 6

    This does not work. The Manifest file is empty with this solution.

    – Jonas

    Oct 28, 2015 at 20:30

  • 4

    My 2 cents: It is better to set a classifier than to change the name. Put classifier = ‘all’ instead of baseName = project.name + ‘-all’. That way you keep the artifact name in compliance with Maven/Nexus policies.

    – taciosd

    Apr 30, 2016 at 21:46


  • 1

    Add group "build" and this task will be in build group (with other tasks, i.e. jar task.

    – MAGx2

    Nov 20, 2016 at 17:28

  • 4

    I can’t find any kind of documentation on the with jar keyword, what exactly does it do?

    Jan 30, 2019 at 9:10