Kotlin always tells me KotlinReflectionNotSupportedError - gradle

I've just started converting to Kotlin from Java. I have a question: when I'm reflecting something like:
XXClass::class.java
or anything else. They don't work in JAR, but work fine from IDE.
I'm using gradle :module:build or gradle :module:jar to generate the JAR file. After generating, it always tells me KotlinReflectionNotSupportedError: Kotlin reflection implementation is not found at runtime. Make sure you have kotlin-reflect.jar in the classpath. But I've already added them into dependencies, and they work fine in IDE.
These are parts of my gradle files (Please notice that this is a kotlin application module, not an Android module):
// (Module level)
apply plugin: 'kotlin'
sourceCompatibility = 1.8
dependencies {
// Others...
// I've already added reflect and stdlib
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
}
jar {
manifest {
// ...
}
// To include dependencies to build a fat jar
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
// Include reflect and stdlib
compileKotlin {
kotlinOptions {
jvmTarget = "1.8"
javaParameters = true
noReflect = false
noStdlib = false
}
}
// (Project level)
buildscript {
ext.kotlin_version = '1.3.11'
repositories {
// ...
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Could anyone give me a hand? Thanks a lot.

The issue is in this block of script
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
You've included the dependencies ( kotlin-reflect ) using implementation configuration, but collecting the dependencies from compile configuration. If you changed your config from implementation to compile, this block will work. Since compile is deprecated, you can try something like this to collect your dependencies to your jar
compileJava.classpath.collect {
it.isDirectory() ? it : zipTree(it)
}
see this answer

Related

Two versions of module javafx.base in gradle/JavaFX project

My team is working on a java project. (git repo # https://github.com/RaiderRobotix/Scouting-Client-FX). We are trying to package it using jlink. The following is displayed when running the jlink task (gradlew jlink). I'm using gradle 6.1.1, with jdk 11 on macOS. If you'd like to see our module-info.java, please check the repo. I didn't want to make this question too lengthy.
BTW, I have multiple JDK's on my machine (8,11). Building the project works, but running it with gradlew run does not (I think its an unrelated issue with lombok).
Full error: Error: Two versions of module javafx.base found in ./build/jlinkbase/jlinkjars (Infinite Recharge Client-v4.0.0-alpha.jar and javafx-base-11-mac.jar)
build.gradle
plugins {
id 'java'
id 'idea'
id "org.openjfx.javafxplugin" version "0.0.8"
id 'org.beryx.jlink' version '2.17.2'
id "com.github.johnrengelman.shadow" version "5.2.0"
id 'org.jetbrains.kotlin.jvm' version '1.3.61'
id 'com.github.gmazzo.buildconfig' version '1.6.2'
}
group = "com.github.RaiderRobotix"
version = "v4.0.0-alpha"
repositories {
maven { url 'https://jitpack.io' }
jcenter()
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
jar {
manifest {
attributes (
'Implementation-Title': 'Raider Robotix Scouting Client',
'Implementation-Version': project.version,
'Main-Class': 'org.usfirst.frc.team25.scouting.client.ui.Main'
)
}
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
}
dependencies {
implementation 'com.github.RaiderRobotix:Scouting-Models:29617b7dcc'
implementation 'com.github.RaiderRobotix:blue-alliance-api-java-library:3.0.0'
implementation 'org.projectlombok:lombok:1.18.10'
annotationProcessor 'org.projectlombok:lombok:1.18.10'
implementation 'com.google.code.gson:gson:2.+'
implementation 'commons-io:commons-io:2.+'
implementation 'org.apache.commons:commons-math3:3.+'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.61"
}
buildConfig {
packageName "com.raiderrobotix"
buildConfigField 'String', 'TBA_API_KEY', project['TBA_API_KEY']
// The escaped quotes here are NEEDED. The plugin copies the TEXT LITERAL given to it. The quotes are part of this.
buildConfigField 'String', 'VERSION', "\"$version\""
}
javafx {
version = "11"
modules = [
'javafx.base',
'javafx.graphics',
'javafx.controls',
'javafx.fxml',
]
}
jlink {
launcher {
name = 'Scouting Client'
}
}
application {
mainClassName = 'org.raiderrobotix.scouting.client/org.raiderrobotix.scouting.client.ui.Main'
}
wrapper {
gradleVersion = '6.1.1'
}
compileKotlin {
kotlinOptions {
jvmTarget = "11"
}
}
compileTestKotlin {
kotlinOptions {
jvmTarget = "11"
}
}
I had a similar issue, my specific error during jlink was:
Error: Two versions of module javafx.base found in C:\Users\tareh\code\cleopetra\build\jlinkbase\jlinkjars (javafx-base-11.0.2-win.jar and javafx-base-11.0.2-linux.jar)
Execution failed for task ':jlink'.
I got some inspiration from https://github.com/openjfx/javafx-gradle-plugin/issues/65 and changed one of my dependencies in build.gradle:
dependencies {
// Get rid of this
// compile group: 'org.controlsfx', name: 'controlsfx', version: '11.0.3'
// Use this instead
implementation('org.controlsfx:controlsfx:11.0.3') {
exclude group: 'org.openjfx'
}
}
After I did that, the jlink error went away, and also I noticed the linux jars which had been listed in my IntelliJ module dependencies had disappeared (I'm on Windows).
This doesn't address your specific problem, but hopefully this will be helpful to others.
In my case I had to comment jar block in build.gradle to perform jlink command. I guess that problem is related to how plugin 'org.beryx.jlink' resolves modules when jar block is appear.

Adding external source files to a kotlin project

I have Kotlin sources located at, say, repo/project_a/src/. I created a Kotlin Gradle project in IntelliJ IDEA, located at repo/project_b/.... And I can't for the life of me figure out how to add the sources. If I add them through project structure menu it works fine, but as soon as it wants to re-read the gradle file id deletes the structure (It warns as much in the UI).
This is my gradle file:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.2.70'
}
group 'cli'
version '1.0'
repositories {
mavenCentral()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
I've tried adding all variations of
sourceSets {
main {
kotlin {
srcDirs += "repo/project_a/"
}
}
}
But it does absolutely nothing.
Any ideas?
The path you are giving to Gradle will compile to the current project path plus "repo/project_a/". Try with:
sourceSets {
main {
kotlin {
srcDirs += "../project_a/"
}
}
}

How to build Google protocol buffers and Kotlin using Gradle?

I'm trying to build a project that uses both Google protocol buffers and Kotlin using Gradle. I want the proto files to compile into Java source, which is then called from my Kotlin code.
My source files are arranged like this:
src/main/proto/*.proto
src/main/kotlin/*.kt
src/test/kotlin/*.kt
Here's my build.gradle file:
version '1.0-SNAPSHOT'
apply plugin: 'kotlin'
apply plugin: 'java'
apply plugin: 'com.google.protobuf'
repositories {
mavenCentral()
maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
}
buildscript {
ext.kotlin_version = '1.1-M02'
repositories {
mavenCentral()
maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
}
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.0.0'
}
}
dependencies {
compile 'com.google.protobuf:protobuf-java:3.0.0'
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
testCompile 'junit:junit:4.12'
}
When I run ./gradlew assemble I get a bunch of "Unresolved reference" errors during :compileKotlin. Afterwards I can see that there are no Java source files generated, so it appears that the proto compiler is not being invoked at all.
If I remove the apply plugin: 'kotlin' line, then ./gradlew assemble successfully generates the Java source, but of course my Kotlin source is never compiled.
How do I fix my build.gradle so that I can call my protobuf code from Kotlin?
To get protobuf-gradle-plugin and kotlin-gradle-plugin to cooperate, you need to ensure that the Java code is (re)generated before invoking the Kotlin compiler.
For Gradle's default source sets, main and test, you can do that like this:
compileKotlin.dependsOn ':generateProto'
compileTestKotlin.dependsOn ':generateTestProto'
If you are using other source sets, you'll need to make adjustments.
Older versions of protobuf-gradle-plugin also required updating sourceSets, but newer versions do not seem to require this.
// Don't do this with protobuf-gradle-plugin 0.9.0 or higher
sourceSets.main.java.srcDirs += "${protobuf.generatedFilesBaseDir}/main/java"
sourceSets.test.java.srcDirs += "${protobuf.generatedFilesBaseDir}/test/java"
For Kotlin and Android:
android {
sourceSets {
debug.java.srcDirs += 'build/generated/source/proto/debug/java'
release.java.srcDirs += 'build/generated/source/proto/release/java'
}
}
An additional source directory has to be added for every build type. In this sample there are two build types: debug and release.
If you're using grpc, another line has to be added per build type:
android {
sourceSets {
debug.java.srcDirs += 'build/generated/source/proto/debug/java'
debug.java.srcDirs += 'build/generated/source/proto/debug/grpc'
release.java.srcDirs += 'build/generated/source/proto/release/java'
release.java.srcDirs += 'build/generated/source/proto/release/grpc'
}
}
At least with Kotlin 1.0.6, protobuf-gradle-plugin 0.8.0, protobuf 3.2.x and grpc 1.x it's not required to fiddle with the task order.
if you are working with multiple build types and flavors in android and with protobuf-lite use below with kotlin.
for example I have debug and release builds with demo and prod flavors it will create demoDebug, demoRelease and prodDebug and prodRelease variants.
then use
`
android{
sourceSets {
debug.java.srcDirs += 'build/generated/source/proto/demoDebug/javalite'
debug.java.srcDirs += 'build/generated/source/proto/prodDebug/javalite'
release.java.srcDirs += 'build/generated/source/proto/demoRelease/javalite'
release.java.srcDirs += 'build/generated/source/proto/prodRelease/javalite'
}
}
`
tie the different compileKotlin with generateProto
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
if (getName() == 'compileDemoDebugKotlin')
dependsOn(':app:generateDemoDebugProto')
if (getName() == 'compileDemoReleaseKotlin')
dependsOn(':app:generateDemoReleaseProto')
if (getName() == 'compileProdDebugKotlin')
dependsOn(':app:generateProdDebugProto')
if (getName() == 'compileProdReleaseKotlin')
dependsOn(':app:generateProdReleaseProto')
}
For the gradle setup :
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'com.google.protobuf' version "0.8.17"
}
Then at the bottom of the build.gradle
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.10.0"
}
// Generates the java Protobuf-lite code for the Protobufs in this project. See
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
// for more information.
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option 'lite'
}
}
}
}
}

Build jar for each main class using gradle

1.My project has two main class i want to build jar for each main class using gradle. my source has 2 files ValidationRule.java
SupportValidator.java both the file have one main class each i want to
build the jar for each main class
i can achieve the same from eclipse working fine
2.I want to load the source file for my project from 2 different folder,some part is there in one folder and remaining is
there in
another folder i.e like
project/src snd another folder outside the project(../../../SharedClass)
my script as follows
apply plugin: 'eclipse'
apply plugin: 'java'
sourceCompatibility = 1.6
archivesBaseName = 'Process_XY'
configurations {
configurations.compile.transitive = false
}
dependencies {
compile fileTree(dir:'/trunk/Solutions/project/Source/Binaries/CommonFunctions/build/libs', include: '*.jar')
compile fileTree(dir:'/trunk/Solutions/project/lib/GeoTools/geotools-2.7.4-bin/geotools-2.7.4', include: '*.jar')
compile "org.apache.hadoop:hadoop-core:1.0.3"
compile "commons-collections:commons-collections:3.2.1"
compile "commons-configuration:commons-configuration:1.6"
compile "commons-discovery:commons-discovery:0.2"
compile "commons-lang:commons-lang:2.4"
compile "commons-logging:commons-logging:1.1.1"
compile "commons-logging:commons-logging:1.0.4"
compile "log4j:log4j:1.2.16"
compile "com.vividsolutions:jts:1.8"
compile "commons-net:commons-net:1.4.1"
compile "org.apache.hadoop:hadoop-core:1.0.3"
compile "commons-httpclient:commons-httpclient:3.0.1"
compile "org.mortbay.jetty:servlet-api:2.5-20081211"
compile "org.apache.hbase:hbase:0.94.0"
compile "org.apache.zookeeper:zookeeper:3.4.3"
}
repositories {
mavenCentral()
maven { url "https://repository.cloudera.com/artifactory/cloudera-repos/" }
maven { url "http://repo.springsource.org/libs-release" }
maven { url "http://repo.springsource.org/libs-milestone" }
maven { url "http://repo.springsource.org/libs-snapshot" }
maven { url "http://www.datanucleus.org/downloads/maven2/" }
maven { url "http://oss.sonatype.org/content/repositories/snapshots" }
maven { url "http://people.apache.org/~rawson/repo" }
}
jar {
from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
manifest.attributes("Main-Class":"org.project.seismic.Process_XY")
}
sourceSets {
main {
java {
source = ['src/org', '../../../SharedClass/org']
}
}
}
above in sourceSets method i tried to load source from 2 folder but it
didnt worked
Thanks in advance..!!
How to achieve using gradle.
Ok, first of all, the source on a SourceDirectorySet takes another SourceDirectorySet. The srcDirs method however takes paths. Change that block to the following:
sourceSets {
main {
java {
srcDirs ['src/org', '../../../SharedClass/org']
}
}
}
And you can easily add a second jar task as follows:
task secondJar(type: Jar) {
name = other-main-jar
from ...
manifest.attributes(...)
}
assemble.dependsOn(secondJar)
This will register a new Jar task called secondJar and makes sure that when the project is assembled, this jar is also created.

Using Gradle to build a JAR with dependencies

I have a multiproject build and I put a task to build a fat JAR in one of the subprojects. I created the task similar to the one described in this cookbook.
jar {
from configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
manifest { attributes 'Main-Class': 'com.benmccann.gradle.test.WebServer' }
}
Running it results in the following error:
Cause: You can't change a
configuration which is not in
unresolved state!
I'm not sure what this error means. I also reported this on the Gradle JIRA in case it is a bug.
I posted a solution in JIRA against Gradle:
// Include dependent libraries in archive.
mainClassName = "com.company.application.Main"
jar {
manifest {
attributes "Main-Class": "$mainClassName"
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
Note that mainClassName must appear BEFORE jar {.
The answer by #felix almost brought me there. I had two issues:
With Gradle 1.5, the manifest tag was not recognised inside the fatJar task, so the Main-Class attribute could not directly be set
the jar had conflicting external META-INF files.
The following setup resolves this
jar {
manifest {
attributes(
'Main-Class': 'my.project.main',
)
}
}
task fatJar(type: Jar) {
manifest.from jar.manifest
classifier = 'all'
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
} {
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
with jar
}
To add this to the standard assemble or build task, add:
artifacts {
archives fatJar
}
Edit: thanks to #mjaggard: in recent versions of Gradle, change configurations.runtime to configurations.runtimeClasspath
If you want the jar task to behave normally and also have an additional fatJar task, use the following:
task fatJar(type: Jar) {
classifier = 'all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
The important part is with jar. Without it, the classes of this project are not included.
Since use of compile to list dependencies is now deprecated and all should switch to implementation the solution to build a Jar with all dependencies should use the example from this website.
https://docs.gradle.org/current/userguide/working_with_files.html#sec:creating_uber_jar_example
Specifically this command:
configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it)
Here is full gradle section:
[1]: https://docs.gradle.org/current/userguide/working_with_files.html#sec:creating_uber_jar_example
task uberJar(type: Jar) {
archiveClassifier = 'uber'
from sourceSets.main.output
dependsOn configurations.runtimeClasspath
from {
configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) }
}}
This works fine for me.
My Main class:
package com.curso.online.gradle;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
public class Main {
public static void main(String[] args) {
Logger logger = Logger.getLogger(Main.class);
logger.debug("Starting demo");
String s = "Some Value";
if (!StringUtils.isEmpty(s)) {
System.out.println("Welcome ");
}
logger.debug("End of demo");
}
}
And it is the content of my file build.gradle:
apply plugin: 'java'
apply plugin: 'eclipse'
repositories {
mavenCentral()
}
dependencies {
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
testCompile group: 'junit', name: 'junit', version: '4.+'
compile 'org.apache.commons:commons-lang3:3.0'
compile 'log4j:log4j:1.2.16'
}
task fatJar(type: Jar) {
manifest {
attributes 'Main-Class': 'com.curso.online.gradle.Main'
}
baseName = project.name + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
And I write the following in my console:
java -jar ProyectoEclipseTest-all.jar
And the output is great:
log4j:WARN No appenders could be found for logger (com.curso.online.gradle.Main)
.
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more in
fo.
Welcome
The answer from #ben almost works for me except that my dependencies are too big and I got the following error
Execution failed for task ':jar'.
> archive contains more than 65535 entries.
To build this archive, please enable the zip64 extension.
To fix this problem, I have to use the following code
mainClassName = "com.company.application.Main"
jar {
manifest {
attributes "Main-Class": "$mainClassName"
}
zip64 = true
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
To generate a fat JAR with a main executable class, avoiding problems with signed JARs, I suggest gradle-one-jar plugin. A simple plugin that uses the One-JAR project.
Easy to use:
apply plugin: 'gradle-one-jar'
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.github.rholder:gradle-one-jar:1.0.4'
}
}
task myjar(type: OneJar) {
mainClass = 'com.benmccann.gradle.test.WebServer'
}
Based on the proposed solution by #blootsvoets, I edited my jar target this way :
jar {
manifest {
attributes('Main-Class': 'eu.tib.sre.Main')
}
// Include the classpath from the dependencies
from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
// This help solve the issue with jar lunch
{
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
}
Simple sulution
jar {
manifest {
attributes 'Main-Class': 'cova2.Main'
}
doFirst {
from { configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) } }
}
}
I use next script for Gradle 7.3.3. It resolves errors and exceptions that I was faced with when I was trying to implement solutions from this question.
jar {
manifest {
attributes(
"Main-Class": "path.to.main.Application",
)
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
For those who need to build more than one jar from the project.
Create a function in gradle:
void jarFactory(Jar jarTask, jarName, mainClass) {
jarTask.doFirst {
println 'Build jar ' + jarTask.name + + ' started'
}
jarTask.manifest {
attributes(
'Main-Class': mainClass
)
}
jarTask.classifier = 'all'
jarTask.baseName = jarName
jarTask.from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
{
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
jarTask.with jar
jarTask.doFirst {
println 'Build jar ' + jarTask.name + ' ended'
}
}
then call:
task makeMyJar(type: Jar) {
jarFactory(it, 'MyJar', 'org.company.MainClass')
}
Works on gradle 5.
Jar will be placed at ./build/libs.
I use task shadowJar by plugin .
com.github.jengelman.gradle.plugins:shadow:5.2.0
Usage just run ./gradlew app::shadowJar
result file will be at MyProject/app/build/libs/shadow.jar
top level build.gradle file :
apply plugin: 'kotlin'
buildscript {
ext.kotlin_version = '1.3.61'
repositories {
mavenLocal()
mavenCentral()
jcenter()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
}
}
app module level build.gradle file
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
sourceCompatibility = 1.8
kapt {
generateStubs = true
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.seleniumhq.selenium:selenium-java:4.0.0-alpha-4"
shadow "org.seleniumhq.selenium:selenium-java:4.0.0-alpha-4"
implementation project(":module_remote")
shadow project(":module_remote")
}
jar {
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'
manifest {
attributes(
'Main-Class': 'com.github.kolyall.TheApplication',
'Class-Path': configurations.compile.files.collect { "lib/$it.name" }.join(' ')
)
}
}
shadowJar {
baseName = 'shadow'
classifier = ''
archiveVersion = ''
mainClassName = 'com.github.kolyall.TheApplication'
mergeServiceFiles()
}
Excluding unwanted Manifest entries fixed the MainClass file not found error in a Gradle build jar file.
jar{
exclude 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/*.MF'
from {
-----
}
}
There is gradle plugin shadow jar with seamless setup.
plugins {
id "com.github.johnrengelman.shadow" version "5.0.0"
}
shadowJar {
mergeServiceFiles()
}
Please check about version compatibilities with your gradle version here:
https://github.com/johnrengelman/shadow#latest-test-compatibility
Gradle 6.3, Java library. The code from "jar task" adds the dependencies to the "build/libs/xyz.jar" when running "gradle build" task.
plugins {
id 'java-library'
}
jar {
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
There's something to keep in mind about this type of solution:
task fatJar(type: Jar) {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
It works so long as you're using "compile" dependencies. It doesn't work if you're using "implementation" dependencies.
Try "runtimeClasspath" if "compile" and "implementation" not working.
jar {
manifest {
attributes "Main-Class": "com.example.app"
}
from {
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
This is for Kotlin DSL (build.gradle.kts).
Method 1 (no need for application or other plugins)
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
// OR another notation
// manifest {
// attributes["Main-Class"] = "com.example.MyMainClass"
// }
}
If you use any external libraries, use below code. Copy library JARs in libs sub-directory of where you put your result JAR. Make sure your library JAR files do not contain space in their file name.
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
manifest.attributes["Class-Path"] = configurations
.runtimeClasspath
.get()
.joinToString(separator = " ") { file ->
"libs/${file.name}"
}
}
Note that Java requires us to use relative URLs for the Class-Path attribute. So, we cannot use the absolute path of Gradle dependencies (which is also prone to being changed and not available on other systems). If you want to use absolute paths, maybe this workaround will work.
Create the JAR with the following command:
./gradlew jar
The result JAR will be created in build/libs/ directory by default.
Method 2: Embedding libraries (if any) in the result JAR (fat or uber JAR)
tasks.jar {
manifest.attributes["Main-Class"] = "com.example.MyMainClass"
val dependencies = configurations
.runtimeClasspath
.get()
.map(::zipTree) // OR .map { zipTree(it) }
from(dependencies)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
Creating the JAR is exactly the same as the previous method.
Method 3: Using the Shadow plugin (to create a fat or uber JAR)
plugins {
id("com.github.johnrengelman.shadow") version "6.0.0"
}
// Shadow task depends on Jar task, so these will be reflected for Shadow as well
tasks.jar {
manifest.attributes["Main-Class"] = "org.example.MainKt"
}
Create the JAR with this command:
./gradlew shadowJar
See Shadow documentations for more information about configuring the plugin.
Running the created JAR
java -jar my-artifact.jar
The above solutions were tested with:
Java 17
Gradle 7.1 (which uses Kotlin 1.4.31 for .kts build scripts)
See the official Gradle documentation for creating uber (fat) JARs.
For more information about manifests, see Oracle Java Documentation: Working with Manifest files.
Note that your resource files will be included in the JAR file automatically (assuming they were placed in /src/main/resources/ directory or any custom directory set as resources root in the build file). To access a resource file in your application, use this code (note the / at the start of names):
Kotlin
val vegetables = MyClass::class.java.getResource("/vegetables.txt").readText()
// Alternative ways:
// val vegetables = object{}.javaClass.getResource("/vegetables.txt").readText()
// val vegetables = MyClass::class.java.getResourceAsStream("/vegetables.txt").reader().readText()
// val vegetables = object{}.javaClass.getResourceAsStream("/vegetables.txt").reader().readText()
Java
var stream = MyClass.class.getResource("/vegetables.txt").openStream();
// OR var stream = MyClass.class.getResourceAsStream("/vegetables.txt");
var reader = new BufferedReader(new InputStreamReader(stream));
var vegetables = reader.lines().collect(Collectors.joining("\n"));
If you're used to ant then you could try the same with Gradle too:
task bundlemyjava{
ant.jar(destfile: "build/cookmyjar.jar"){
fileset(dir:"path to your source", includes:'**/*.class,*.class', excludes:'if any')
}
}

Resources