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.
Related
I am creating a fat jar using gradle, build.gradle is as below:
...
dependencies {
compile files('local_path1')
compile files('local_path2')
runtime files('local_path3')
}
task customFatJar(type: Jar) {
manifest {
attributes 'Main-Class': 'MyMainClass'
}
archiveName = 'my-jar'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
Now, once I run the "customFatJar" task, the jar generated contains dependent jars in paths "local_path1" and "local_path2". But the jar generated does not contain dependent jar in path "local_path3".
Please let me know correct dependency configuration to achieve this.
As you defined the local_path3 as runtime, you would need to add them into the script, too. Currently it just collecting all the runtime libs. It may looks like:
from { configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) } }
You should have a look at the documentation around the configurations created by the Java plugin and how they relate to each other.
In order to not miss a single runtime dependency in your fat jar, you should rely on the runtimeClasspath configuration:
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
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.
When I run this FatJar/UberJar task, I end up with a .jar file whose META-INF\ folder contains two MANIFEST.MF files. One is the correct one with the attributes I inserted into the task definition, and the other simply contains "Manifest-Version: 1.0". This causes a "Could not find or load main class" error when attempting to run the application (I assume it is solely reading the first one and can't identify a main-class)
task fatJar(type: Jar) {
baseName = project.name
manifest {
attributes 'Implementation-Title': 'Synchronizer',
'Main-Class': 'net.xxx.yyy.Main'
}
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
MANIFEST.MF #1
Manifest-Version: 1.0
MANIFEST.MF #2:
Manifest-Version: 1.0
Implementation-Title: Synchronizer
Main-Class: net.xxx.yyy.Main
Whats the reason for the duplicate Manifest?
I've tried excluding the MANIFEST.MF using
it.isDirectory() ? it : zipTree(it).matching{exclude{it.path.contains == 'META-INF'}}
it.isDirectory() ? it : zipTree(it).matching{exclude{it.name.contains == 'MANIFEST'}}
*** These do exclude the child JAR META-INF folders and Manifest files, so I'm not sure where MANIFEST.MF #1 is coming from. It appears to be a default?
On my version of gradle, I'm not getting 2 manifests with your code - I only end up with a single manifest containing Manifest-Version: 1.0 as you described.
The problem appears to be that your manifest closure isn't actually associated with the jar closure. The Jar task type assignement and "with jar" keyword don't do this for you. Therefore, it isn't recognized as the manifest file for the jar task. You should be able to fix this through one of these options:
task fatJar(type: Jar) {
baseName = project.name
jar.manifest {
attributes 'Implementation-Title': 'Synchronizer',
'Main-Class': 'net.xxx.yyy.Main'
}
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } with jar
}
Or, a cleaner way to write the same this would be to include the manifest and from closures in a jar closure:
task fatJar(type: Jar) {
baseName = project.name
jar{
manifest {
attributes 'Implementation-Title': 'Synchronizer',
'Main-Class': 'net.xxx.yyy.Main'
}
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
}
}
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'
}
}
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'
}
}