Mixing directory/file content and jar files inside a fat jar


(hmartin) #1

Hi. We are packaging several Java/Groovy projects, along with the jars they depend on into a “fat jar”. So the finished jar will look something like this:

...
lib/
*lib/my-subproject-0.0.1-SNAPSHOT.jar*
lib/oval-1.84.jar
lib/commons-lang3-3.0.jar
lib/slf4j-api-1.7.7.jar
lib/log4j-api-2.0-rc1.jar
lib/log4j-core-2.0-rc1.jar
lib/log4j-slf4j-impl-2.0-rc1.jar
lib/groovy-all-2.3.2.jar
...

I’m having issues with a utility class that doesn’t use the Class.getResource() mechanism to locate a resource (it seems to be using the File class directly) and hence it’s not able to locate the particular resource which lives inside the my-subproject-0.0.1-SNAPSHOT.jar jar file. To “fix” this problem, I would like to include the classes/resources from the subproject directly inside the “fat jar” (instead of the actual subproject jar file). But it’s not clear to me how I’d do that. I’m guessing that the Jar task will be called for subprojects by default, and I assume that I would have to rewrite the assembly task in the parent project to instead of invoking the Jar task on the subproject, just fetch the classes/resources? I’m still kind of a newbie when it comes to Gradle, and any help accomplishing this will be much appreciated. Thanks.


(Luke Daley) #2

I recommend looking at the shadow plugin.

http://plugins.gradle.org/plugin/com.github.johnrengelman.shadow


(hmartin) #3

Thanks for the response Luke! What I need to do seems pretty simple, and if I could get away with just filtering out the jar file I don’t want (from the subproject), and then directly include the resource/class trees from the compiled subproject, I’d be ok (until I have more time to delve into the shadow plugin…).

I’ve tried all sorts of things like:

jar {
  copy {
    exclude "**/my-subproject*"
  }
}

but the jar file gets included anyway. Is there a simple way to do this? Thanks.


(hmartin) #4

I’m going completely nuts on this. I’ve tried everything I can think of. Maybe if I explain a little better what I’m trying to do, someone could help me out… So I have a multi-project build like this:

top

|__ Foo

|

|__ Bar

where Foo depends on Bar. I’m trying to build a “fat jar” for Foo that contains all the external dependencies as well as the compiled code from Bar. The problem is that this fat jar contains Bar in the form of a jar file. I would like to simply include the compiled classes and resources from the Bar project instead. I’ve tried doing this by manipulating the Jar task for Foo, but that didn’t seem to work. If I invoke “gradle jar” for the Foo project, only classes/resources from the Foo project get incorporated into the fat jar. If I invoke “gradle assemble”, then all the dependencies will get included (including the Bar jar file, which I don’t want). I’m confused as to which task I’m supposed to try to manipulate. I have to believe there’s a really simple way of doing this, but I just can’t figure it out. Can I do it in the assemble task? If so, how would I go about it? I’ve tried to figure it out by printing out stuff like this:

assemble {
  println "assembling jar"
    dependsOn.each {
    println "assemble depends on: ${it}"
  }
}

and I get the following printed: assemble depends on: org.gradle.api.internal.artifacts.DefaultPublishArtifactSet$ArtifactsTaskDependency@4b381f5e assemble depends on: task ‘:admin:bootRepackage’ assemble depends on: file collection

Like I said, this is driving me nuts, and I’m now on day 2 of banging my head against the wall. Any help is much appreciated (preferably with an example snippet). Thanks!


(hmartin) #5

So I managed to get one step further by searching the archives here, and defining the following:

jar {
      from { project(":Bar").compileJava }
      from { project(":Bar").processResources }
  }

in the build.gradle file for project Foo. Now I’m getting the classes/resources included in the fat jar. However, I’m still getting the assembled Bar.jar file included. Any way I could exclude that from the fat jar?