Building an uberjar with Gradle - java

I want to build an uberjar (AKA fatjar) that includes all the transitive dependencies of the project. What lines do I need to add to build.gradle?
This is what I currently have:
task uberjar(type: Jar) {
from files(sourceSets.main.output.classesDir)
manifest {
attributes 'Implementation-Title': 'Foobar',
'Implementation-Version': version,
'Built-By': System.getProperty('user.name'),
'Built-Date': new Date(),
'Built-JDK': System.getProperty('java.version'),
'Main-Class': mainClassName
}
}

I replaced the task uberjar(.. with the following:
jar {
from(configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) {
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
manifest {
attributes 'Implementation-Title': 'Foobar',
'Implementation-Version': version,
'Built-By': System.getProperty('user.name'),
'Built-Date': new Date(),
'Built-JDK': System.getProperty('java.version'),
'Main-Class': mainClassName
}
}
The exclusions are needed because in their absence you will hit this issue.

Have you tried the fatjar example in the gradle cookbook?
What you're looking for is the shadow plugin for gradle

Simply add this to your java module's build.gradle.
mainClassName = "my.main.Class"
jar {
manifest {
attributes "Main-Class": "$mainClassName"
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
This will result in [module_name]/build/libs/[module_name].jar file.

I found this project very useful. Using it as a reference, my Gradle uberjar task would be
task uberjar(type: Jar, dependsOn: [':compileJava', ':processResources']) {
from files(sourceSets.main.output.classesDir)
from configurations.runtime.asFileTree.files.collect { zipTree(it) }
manifest {
attributes 'Main-Class': 'SomeClass'
}
}

Related

Remove unwanted files and jars when creating jar with Gradle

I am pretty new to using Gradle. I am using Gradle-7.5.1. I just want a jar file created using Gradle that contains just the manifest file and the source files I wrote but why does the created jar contain a million other things in it?
My gradle.build file is as follows:
apply plugin: 'java'
apply plugin: 'application'
version = '1.0'
sourceSets {
main {
java {
srcDirs = ['src']
}
}
}
task fatJar(type: Jar) {
manifest {
attributes 'Manifest-Version': version,
'Class-Path': configurations.runtimeClasspath.files.collect { it.getName() }.join(' ')
}
baseName = 'MyProj'
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
repositories {
mavenCentral()
}
dependencies {
implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.25' < etc >
}
What I want in the MyProj-1.0 is:
META-INF (holding the Manifest.MF)
com (holding my src files)
What I get in the jar is:
assets
style
corejava
javax
..and a lot more such things
Thanks in advance for any pointers. As I have banged my head to the point of snapping it!

How do I create a jar with all dependencies using Gradle 4.4?

This question is related to this one -- however, due to the deprecation of compile in favor of implementation, it doesn't work. It does pick up dependencies that are declared with compile. However, with it being deprecated, using it isn't an option (and we'd be right back here when it's removed anyway)
I got this Gradle task:
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'rpi-sense-hat-lib',
'Implementation-Version': version,
'Main-Class': 'io.github.lunarwatcher.pi.sensehat.Tests'
}
baseName = project.name
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
with jar
}
And there's just one dependency, looking aside test dependencies:
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
testImplementation group: 'junit', name: 'junit', version: '4.12'
}
Running from the IDE works fine. However, when I deploy to my Raspberry Pi (or use the jar gradlew fatJar results in locally), I get this exception:
$ java -jar java-sense-hat-1.0a.jar
Exception in thread "main" java.lang.NoClassDefFoundError: kotlin/jvm/internal/Intrinsics
at io.github.lunarwatcher.pi.sensehat.UtilsKt.getSenseHat(Utils.kt:18)
at io.github.lunarwatcher.pi.sensehat.SenseHat.<init>(SenseHat.java:12)
at io.github.lunarwatcher.pi.sensehat.Tests.main(Tests.java:9)
Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.Intrinsics
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
... 3 more
Which is triggered by this line:
return Optional.empty()
in a Kotlin method returning an Optional<File>. Answer on related question:
Either kotlin-runtime has to be in classpath and verify with $ echo $CLASSPATH.
Or you have to add kotlin-runtime to maven and then assemble inside the jar itself with mvn compile assembly:single,
Which means the kotlin-runtime isn't included in the classpath. Before you go ahead and answer "add kotlin-runtime to your dependencies", it's a part of the stdlib:
Kotlin Runtime (deprecated, use kotlin-stdlib artifact instead)
I use kotlin-stdlib-jdk8 and it works in the IDE. Just for testing purposes, using kotlin-stdlib instead does not change anything.
In addition, replacing implementation with compile fixes it.
In the post I linked at the top of the question, there's a suggestion to use runtime in the fatJar task. So:
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'rpi-sense-hat-lib',
'Implementation-Version': version,
'Main-Class': 'io.github.lunarwatcher.pi.sensehat.Tests'
}
baseName = project.name
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
configurations.runtime.collect {
it.isDirectory() ? it : zipTree(it)
}
}
with jar
}
The dependency is still not included, and the program crashes.
So why not add implementation as a configuration to copy from?
I tried. I ended up with this:
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'rpi-sense-hat-lib',
'Implementation-Version': version,
'Main-Class': 'io.github.lunarwatcher.pi.sensehat.Tests'
}
baseName = project.name
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
configurations.runtime.collect {
it.isDirectory() ? it : zipTree(it)
}
configurations.implementation.collect {
it.isDirectory() ? it : zipTree(it)
}
}
with jar
}
And an exception:
Resolving configuration 'implementation' directly is not allowed
So, considering:
compile instead of implementation works
The runtime exception is from Kotlin not being in the classpath
It works in the IDE
Adding an implementation clause to the fatJar task crashes the compiling with a separate exception
How do I generate a jar with all dependencies in Gradle 4.4 when using the implementation keyword?
Have you tried the Shadow Plugin like:
shadowJar {
manifest {
attributes 'Implementation-Title': 'rpi-sense-hat-lib',
'Implementation-Version': version,
'Main-Class': 'io.github.lunarwatcher.pi.sensehat.Tests'
}
configurations = [project.configurations.compile, project.configurations.runtime]
}
Edit:
You can also do this (as described in an answer for this question):
configurations {
fatJar
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
testImplementation group: 'junit', name: 'junit', version: '4.12'
fatJar "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'rpi-sense-hat-lib',
'Implementation-Version': version,
'Main-Class': 'io.github.lunarwatcher.pi.sensehat.Tests'
}
baseName = project.name
from {
configurations.fatJar.collect {
it.isDirectory() ? it : zipTree(it)
}
}
with jar
}
but then you have to repeat all implementation dependencies as fatJar dependencies. For your current project it's fine since you have only one dependency, but for anything bigger it will become a mess...
Edit 2:
As #Zoe pointed out in the comments this is also an acceptable solution:
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'rpi-sense-hat-lib',
'Implementation-Version': version,
'Main-Class': 'io.github.lunarwatcher.pi.sensehat.Tests'
}
baseName = project.name
from {
configurations.runtimeClasspath.collect {
it.isDirectory() ? it : zipTree(it)
}
}
with jar
}
however make notice as per source code that runtimeClasspath is a combination of runtimeOnly, runtime and implementation, which may or may not be desirable depending on the situation - for instance you may not want to include runtime dependencies because they are provided by the container.

Get variable from child project in subproject configuration in multi module gradle project

I have a multi modul java project, where i want to create jar (bin,source,javadoc) and i have trouble getting variable, from subproejcts in subproject configuration.
this is my build.gradle file in the root project:
subprojects {
/*...*/
jar {
manifest {
attributes(
"Implementation-Title": project.name,
"Implementation-Version": project.version,
"Implementation-Vendor": "XXX",
"Main-Class": project.mainClassName
)
}
include '**/**'
}
/*...*/
}
And this the build.gradle file one of the subproejcts:
version = '1.2.0-SNAPSHOT'
ext {
mainClassName = "..."
}
/*...*/
when i want to run the build script i got the following exception:
Could not get unknown property 'mainClassName' for object of type
org.gradle.api.java.archives.internal.DefaultManifest.
In the sub process configuration i have other task where i can easily access the proejct.version from each subproejct also project.name, and works well, and when i change a the "Main-Class": project.mainClassName to "Main-Class": "i.hate.mylife.SoMuch" the script runs perfectly.
In the subproejct i also try the followings
def mainClassName = "..."
project.ext{
mainClassName = "..."
}
Is there any way in gradle to get variable of subroject from subproejcts in subproejct configuration?
In the subproject configuration i also try the followings:
project.ext.mainClassName
project.ext.get("mainClassName")
And non of them work, when i try to run it, it's says:
Cannot get property 'mainClassName' on extra properties extension as it does
not exist
To override extra properties in subproject's build.gradle you must put your desired task (in this case the jar task) inside the afterEvaluate block.
root project build.gradle:
subprojects {
apply plugin: 'java'
apply plugin: 'application'
ext {
// the property that should be overridden in suproject's build.gradle
mainClassName = ''
}
afterEvaluate {
jar {
manifest {
attributes(
"Implementation-Title": project.name,
"Implementation-Version": project.version,
"Implementation-Vendor": "XXX",
"Main-Class": mainClassName
)
}
include '**/**'
}
}
}
sub project build.gradle:
mainClassName = 'project.specific.Main'
ext.set("mainClassName", mainClassName)

Make NON-FAT jar in Gradle on-demand

I'm using Gradle. Usually I'm build the FAT jar. There was no problem, but at this time, I also need to make NON-FAT jar.
I mean I want to exclude all depended library and run jar with -cp option like the following:
java -cp "/sample1/lib/myjar.jar:/sample2/lib/depended.jar" com.example.main.Runner
(In FAT jar, java -jar myjar.jar is the same thing and its contains depended.jar)
Here is my build.gradle.
apply plugin: 'java'
apply plugin: 'application'
jar.baseName = 'myjar'
version = ''
def mainClass = 'com.example.main.Runner'
mainClassName = mainClass
def defaultEncoding = 'UTF-8'
repositories {
flatDir dirs: "${projectDir}/libs"
}
dependencies {
compile ':commons-chain:1.2'
compile ':commons-io:2.4'
testCompile ':junit:4.12'
testCompile ':hamcrest-core:1.3'
}
jar {
manifest {
attributes 'Implementation-Version': version,
'Main-Class': mainClass
}
from {configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }}
}
I still want to keep this for usual FAT jar creation.
So, I tried to append a task like the following:
TRY #1
task makeSlimJar {
jar.baseName = 'myjar.slim'
jar {
manifest {
// remove main class
attributes 'Implementation-Version': version
}
from {configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }}
}
TRY #2
task makeSlimJar {
jar.baseName = 'myjar.slim'
jar {
manifest {
// remove main class
attributes 'Implementation-Version': version
}
from {configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }}
tasks.Jar.execute()
}
TRY #3
task makeSlimJar {
jar.baseName = 'myjar.slim'
jar {
manifest {
// remove main class
attributes 'Implementation-Version': version
}
from {configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }}
dependsOn Jar
}
After appended it, I ran the gradle makeSlimJar.
All of the above my tries were failed(programmatically, it were succeeded) and just created FAT jar with name myjar.slim.jar.
Is there way to live together FAT and NON-FAT jar on the same build.gradle file?
... Or am I something wrong?
I consider that removing apply plugin: 'application' for NON-FAT jar is my last resort.
Please help me, if you could.
Thanks,
the code which is actually adding the dependencies to your jar is this line:
from {configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }}
your build script will normally build a 'slim jar' by default, except that you have added code to override this behaviour:
jar {
manifest {
attributes 'Implementation-Version': version,
'Main-Class': mainClass
}
from {configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }}
}
I would recommend removing these lines. When you run gradle clean build this will then build a normal jar (with no dependencies).
You could then add an additional task to create your 'fatjar' like this:
task fatjar << {
jar.doFirst {
println "creating fatjar"
jar {
from {configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }}
}
}
}
You can use this task to build the fatjar by running gradle clean fatjar build.
Alternatively you could try looking at a framework such as springboot, which would do all this for you.

How to include CSS files of javafx project in gradle assemble?

I have a javafx project including css files. I want them directly in the main java src:
src/main/java/Foo.java
src/main/java/Foo.css
When I now use my gradle script to build a fat jar, it does not include the css files and running the jar ends up in a npe.
My gradle script looks like this:
apply plugin: 'java'
apply plugin: 'eclipse'
jar {
manifest {
attributes "Main-Class": "foo.Main"
}
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
configurations.runtime.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
repositories {
mavenCentral()
}
dependencies {
compile 'com.github.sarxos:webcam-capture:0.3.10'
}
What can I do to get a runnable jar file from gradle?
I tried already to explicitly include the css files - it did not work / I did it wrong ...
If you can't change your resource files to a resource directory, then you can use:
jar {
manifest {
attributes "Main-Class": "foo.Main"
}
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
configurations.runtime.collect {
it.isDirectory() ? it : zipTree(it)
}
}
from('src/main/java') {
include '**/*.css'
}
}

Categories