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) } }
}
}
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) } }
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.
I am trying to run a jar from Android studio. After a long Workaround, the jar file run perfectly.
Now i need to export the jar file from the android studio. I got the Jar file from the build/libs folder.
But the problem is that the jar file shows a error.
no main manifest attribute, in "app.jar"
So i found this solution. Can't execute jar- file: "no main manifest attribute"
Then i read about MANIFEST.MF & added the mail class to that file.
jar {
manifest.attributes(
'Main-Class': "com.Remo.server.RemoServerApp"
)
}
After adding those in my gradle. My MANIFEST.MF contains the MainClass.
But im still getting the same error? How can i solve this ?
Note: The objective is that I want to Export a Runnable Jar file from the project.
UPDATE:
After Adding the MainClass in MANIFEST.MF. I got stuck with the below error.
Exception in thread "main" java.lang.NoClassDefFoundError: com/Remo/protocol/RemoConnection
at com.Remo.server.RemoServerApp.<init>(RemoServerApp.java:33)
at com.Remo.server.RemoServerApp.main(RemoServerApp.java:97)
Caused by: java.lang.ClassNotFoundException: com.Remo.protocol.RemoConnection
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 2 more
MANIFEST.MF
Manifest-Version: 1.0
Main-Class: com.Remo.server.RemoServerApp
UPDATE 2
From your solution what i understood is that we need to copy the remoprotocol jar file to the remoserver.
remoserver project gradle file
apply plugin: 'java'
sourceSets {
main {
resources.srcDirs = ['src/main/resources']
}
}
dependencies {
compile project(':remoprotocol')
compile files('libs/bluecove-2.1.1.jar')
}
jar {
manifest.attributes(
'Main-Class': "com.remo.server.remoServerApp"
)
manifest.attributes(
'Class-Path': configurations.runtime.files.collect { it.getName() }.join(' '))
}
task copyRTDependenciesToLib(type: Copy) {
into "$buildDir/output/lib"
from configurations.runtime
}
After running the gradle task also i am getting the same error.
First you want to copy all your runtime dependencies into the builddir/libs folder (or a different distribution folder if you so choose). Here is a custom task that would achieve this:
task copyRTDependenciesToLib(type: Copy) {
into "$buildDir/libs"
from configurations.runtime
}
Add your runtime dependency jars as a Class-Path attribute in your manifest file. The jars need to be in the same directory as your runnable jar - which the copy task above achieves. (alternatively, you can provide full relative path for your dependency jar location)
jar {
manifest {
attributes(
"Main-Class": "com.Remo.server.RemoServerApp",
"Class-Path": configurations.runtime.files.collect { it.getName() }.join(' '))
)
}
}
Some more things to consider:
The application plugin does the same thing; it adds a task installDist that produces a runnable set of jars along with any dependencies, any readme's, documentation you want to include.
If you want to produce a single runnable jar without having to bundle dependencies along with it, you should look into creating a "fatjar", for example:
task fatJar(type: Jar) {
manifest {
attributes 'Implementation-Title': 'Gradle Jar File Example',
'Implementation-Version': version,
'Main-Class': "com.Remo.server.RemoServerApp"
}
baseName = project.name
//collect all dependencies
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
I have no experience with Android Studio (or Gradle), but I have with Java. Aren't you trying to set main class instead?
Therefore I suggest changing Class-Path attribute to Main-Class as the manifest should contain Main-Class to be able to invoke something when "running" JAR.
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'
}
}