How to include dependencies in jar?

Hello,

I have a project which requires the JNA libraries on runtime. I included compile group: ‘net.java.dev.jna’, name: ‘jna-platform’, version: ‘4.2.2’ in my gradle.build file and everything compiles fine.

However, when I open the jar I get a NoClassDeffounderror error because the JNA library is nowhere to be found in the jar. How can I make sure the libraries are also available in the jar itself? Here is my build.gradle:

plugins {
    id "groovy"
    id "java"
    id "idea"
}

//Grade attributes
group 'com.nikecow'
version '0.1'

task wrapper(type: Wrapper) {
    gradleVersion = "3.0";
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.7'
    compile group: 'net.java.dev.jna', name: 'jna-platform', version: '4.2.2'
    testCompile group: 'junit', name: 'junit', version: '+'
}
3 Likes

You might be interested in using the Shadow plugin. It generates a “fat-jar”, which is a Jar with the necessary dependencies. Note that the plugin creates a new task shadowJar to generate the “fat-jar”.

Hope this helps :slight_smile:

2 Likes

Hey thank you that worked.

Howevever, I am very surprised gradle offers no native functionality for this.
I thought managing dependencies was the entire purpose of gradle. Very confusing.

Anyone knows why this is?

First of all, the default jar that Gradle produces will only contain the classes compiled from the source code in the project. It would be counterproductive to always include all of the dependencies in that jar because depending on the deployment environment, those dependencies would already be provided. If you were using the “war” plugin, those dependencies would be included in the WEB-INF/lib.

Second, concerning why the Shadow plugin isn’t provided “natively” in Gradle, the Gradle core is intentionally minimal because different people have different ideas about what they need for their build, and there are widely differing needs. The rich plugin ecosystem that Gradle provides and supports makes this possible.

1 Like

Hi David,

Thanks for your answer. It’s just that for a rookie like me it is very confusing that the program works fine in the IDE, but once it is compiled into a jar it is missing dependencies.

Fortunately I have solved my issue without the usage of external plugins. Here is the fixed code which took me ages to find:

plugins {
    id "groovy"
    id "java"
    id "idea"
}

//Grade attributes
group 'com.nikecow'
version '0.1'

task wrapper(type: Wrapper) {
    gradleVersion = "3.0";
}

repositories {
    mavenCentral()
}

configurations {
    // configuration that holds jars to include in the jar
    extraLibs
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.4.7'
    extraLibs group: 'net.java.dev.jna', name: 'jna-platform', version: '4.2.2'
    testCompile group: 'junit', name: 'junit', version: '+'
    configurations.compile.extendsFrom(configurations.extraLibs)
}

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

Now what you put behind “extraLibs” will be both compiled and included into your jar as a dependency. Hopefully it helps out some other people.

8 Likes

This is very interesting, but in my case I can only compile the code if I add the lib to both compile and extraLibs, any idea?

@David_Karr, your information makes sense. However, when everything runs fine during compilation (and run), but gradle cannot find dependencies during jar packaging (none of them found), then the process of building a jar file so as to make a library project available as a traditional jar library, becomes a timethief.

Timethieves should be avoided, if nothing else, then at the very least by teaspoon explanations in the Gradle tutorials. Here is an example on a tutorial that I would expect to find in a Gradle tutorial on how to migrate to Gradle: https://www.mkyong.com/gradle/gradle-create-a-jar-file-with-dependencies/

Honestly speaking, it should be commodity and “a click of a button” at the most, to create such a jar file. This is what people has done for ages in the Java world, it is automagically happening in any java Netbeans project.

Gradle for Android Studio is a very good system, but as it seems, the pure java part of Gradle is not yet ready for proffesional usage. Another commodity example is that the compilation of native files that works fine in the Gradle Android plugin, is unavailable in the Java plugin. (This is a major missing)

The reason we need a gradle project converted into a jar library, is that some of our projects are based on traditional netbeans build systems. That will change (to Gradle), but not with any priority.

I am using a Gradle plugin for Netbeans precisely for that purpose of migrating everything to Gradle, and it is working just fine in other respects.

Please comment.

1 Like

Thank you so much for actually revisiting the question. Helped me much.

1 Like

Perhaps because you use another Gradle version ? I am not sure though.

Btw, this thread has accumulated over 25.000 views, that’s amazing!

You are my hero - you saved my day!

Hi Nikecow,

Your solution worked for me.
Thanks for posting the solution.

Thanks so much! This was a big pain for me as I’m also a Gradle newbie. You solution made huge progress for me!

Thanks for your settings. I’m so happy.
For your reference, my gradle version is Gradle 5.2.1.
and I added build.gradle below.
configurations {
// configuration that holds jars to include in the jar
extraLibs
}
dependencies {
extraLibs group: ‘commons-net’, name: ‘commons-net’, version: ‘3.6’
configurations.compile.extendsFrom(configurations.extraLibs)
}
jar {
from {
configurations.extraLibs.collect { it.isDirectory() ? it : zipTree(it) }
}
}

@David_karr, please note that this thread has +25000 hits. It means many people have the problem herein outlined.

Please understand that when you define a main class in the jar {}, it AUTOMATICALLY means you are creating an executable jar file, and in consequence, obviously, it should cause this plugin to include all mandatory dependencies!

PLEASE HELP US GET THIS PLUGIN REVISED. An executable JAR should be commodity and a 1min job, not take days.

3 Likes

What if I want > 1 dependency in the dependencies {} block, how do I extend this to the other dependencies I want to include?

Hello everyone,

I know it’s a bit late to update this post, however, I just had very similar case and managed to fix it without any external plugins as well :).

My case was to include external jars into some directory inside a jar being generated - let’s call it “lib” (similar to ear way). The following snippet does the trick:

configurations {
	jarLibs
}

...

dependencies {
...
	jarLibs "my:awesome-dependecy:1.0"
...
}

jar {
	def libBuildDir = mkdir "${buildDir}/resources/main/lib"
	copy {
		from { configurations.jarLibs }
		into { libBuildDir }
	}
}

The trick is about creating directory dedicated to external deps directly into build dir, under main resources path, and copy there all jars from specified configuration. Gradle is kind enough to copy this whole directory into the jar. End of magic.

Hope it will help someone struggling with similar issue.
Have fun! :slight_smile:

1 Like

God, how can you find that. Amazing…

I did something a little different - and its the only solution that currently does the job for me. The problem I was facing was the runnable jar couldn’t find the Derby database driver.

plugins {
id ‘java’
}

repositories {
jcenter()
}

dependencies {
implementation “org.apache.derby:derby:10.15.2.0” // This provides among other things org.apache.derby.iapi.jdbc.JDBCBoot
implementation “org.apache.derby:derbytools:10.15.2.0” // This provides the EmbeddedDriver
}

jar {
manifest {
attributes(
‘Main-Class’ : ‘FatDerbyJar.Main’
)
}
from {
configurations.compileClasspath.filter{ it.exists() }.collect { it.isDirectory() ? it : zipTree(it) }
}
}

1 Like

The solution proposed by @slott worked for me but it didn’t add transitive dependencies (dependencies of the included dependencies).

The solution, found in this thread, was to use the runtimeClasspath instead of the compileClasspath. So the solution for my case was:

jar {

    from {
        configurations.runtimeClasspath.filter{ it.exists() }.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

Thank you for the solution. My scenario is slightly different. Consider two gradle projects. Core and App.

Core has all spring dependencies. App has only Core dependency.

What I want is when I build App, I should get the Core project also built, but NOT the transitive dependencies.

E.g. If you do a gradle build on Core, you will get a 200 KB jar. If you do a gradle shadowJar on Core, you will get a 35 MB Jar. If you do a gradle build on App, you will get a 300 KB Jar.

My requirement is to add Core to App, but WITHOUT the dependencies. So, if I do a gradle build on App (with the Core added as extraLibs), I should get 500 KB and not 35.3 MB.

Any way I can achieve this ? Thanks !

P.S. @Petrakeas said @slott’s solution worked and did not add transitive dependencies. But in my case, the jar is still 35 MB (which means transitive dependencies are added).