How to sync Java source and target between Gradle and Eclipse? - java

Gradle has source Compatability and the targetCompatability variables that can be set. Eclipse has JDK compliance, generated class files comapatability, and source compatibility.
Is there any way to automagically set one from the other? Ideally, the Gradle stuff would be set from the Eclipse stuff.
edit: these things appear to be stored in: org.eclipse.jdt.core.prefs
edit2: they look like:
D:\ray\dev\conradapps\printg>cat .settings\org.eclipse.jdt.core.prefs
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.8
i can make it work as follows, but it's a hack :)
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
plugins {
id 'java-library'
id 'application'
id 'distribution'
}
repositories {
jcenter()
}
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
dependencies {
testImplementation 'junit:junit:4.12'
}
String myMainClass='p.Main'
jar {
manifest {
attributes(
'Main-Class': myMainClass
)
}
}
application {
mainClassName = myMainClass
}
class Hack {
static String[] hack() throws IOException {
System.out.println("Working Directory = "+System.getProperty("user.dir"));
String dir="./.settings";
String name="org.eclipse.jdt.core.prefs";
File file=new File(dir,name);
String[] strings=new String[3];
for(int i=0;i<strings.length;i++)
strings[i]="";
if(file.exists()) System.out.println(file.toString()+" exists.");
else return strings;
List<String> lines=new ArrayList<>();
try {
if(usePath) {
Path path=FileSystems.getDefault().getPath(dir,name);
lines=java.nio.file.Files.readAllLines(path);
} else {
BufferedReader bufferedReader=new BufferedReader(new FileReader(file));
for(String line=bufferedReader.readLine();line!=null;line=bufferedReader.readLine())
lines.add(line);
bufferedReader.close();
}
int index;
for(String line:lines) {
if(line.startsWith("org.eclipse.jdt.core.compiler.compliance")) {
index=line.indexOf("=");
if(index>0) {
System.out.println("compliance: "+line.substring(index+1));
strings[0]=line.substring(index+1);
}
}
if(line.startsWith("org.eclipse.jdt.core.compiler.source=1.8")) {
index=line.indexOf("=");
if(index>0) {
System.out.println("source: "+line.substring(index+1));
strings[1]=line.substring(index+1);
}
}
if(line.startsWith("org.eclipse.jdt.core.compiler.codegen.targetPlatform")) {
index=line.indexOf("=");
if(index>0) {
System.out.println("target: "+line.substring(index+1));
strings[2]=line.substring(index+1);
}
}
}
} catch(Exception e) {
System.out.println("caught: "+e);
}
return strings;
}
public static void main(String[] args) throws IOException {
hack();
}
static boolean usePath;
}
println("java version is: ${JavaVersion.current()}")
String[] strings=Hack.hack();
if(strings[1]!="") {
println 'setting source'
sourceCompatibility = strings[1]
}
if(strings[2]!="") {
println 'setting target'
targetCompatibility = strings[2]
}

Yes. If you want Gradle to give your configuration to Eclipse, basically, as of Gradle 5.1.1, just add:
sourceCompatibility = '1.7'
targetCompatibility = '1.8'
to your build.gradle file. Note that until java 10 the enumeration was 1.8,1.9,1.10 but from Java 11 and future versions the enumeration is 11, 12, etc. Check the Gradle docs.
If you stumble upon this answer: For me, with Gradle 5.0, the java version works with or without quotes (either 1.8 or '1.8') and this is specified in the latest version of the javadocs. It also worked both when added inside and outside of compileJava{}. I tested this on a multiproject build.
I am not sure about the Eclipse to Gradle configuration transfer. Isn't it supposed to go the other way around though? Gradle is the central configuration tool that configures the build process and whatever IDE you are using (you, or your collaborator). Even if it is possible, the Gradle does manipulate the .classpath and other Eclipse files. So to be sure, if it was a crucial point, I would prefer to add the configuration to the Gradle and let that deal with Eclipse or any other IDE's files.

Related

IntelliJ: Unresolved reference, but gradle build still succeeds

I have a Java library https://github.com/skycavemc/skycavelib which I want to use in a Kotlin project. This is the build.gradle where I have the library as dependency:
import java.util.Properties
import java.io.FileInputStream
plugins {
kotlin("jvm") version "1.7.10"
}
group = "de.skycave"
version = "1.0.0"
val localProperties = Properties()
localProperties.load(FileInputStream(rootProject.file("local.properties")))
repositories {
mavenCentral()
maven { url = uri("https://repo.papermc.io/repository/maven-public/") }
maven { url = uri("https://jitpack.io") }
maven {
url = uri("https://maven.pkg.github.com/skycavemc/skycavelib")
credentials {
username = localProperties.getProperty("gpr.user")
password = localProperties.getProperty("gpr.key")
}
}
}
dependencies {
implementation(kotlin("stdlib"))
compileOnly("io.papermc.paper:paper-api:1.19.2-R0.1-SNAPSHOT")
implementation("de.skycave:skycavelib:1.0.2")
}
java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
}
I also made sure I have a file named local.properties in my project where I set gpr.user and gpr.key correctly. The authentication works and the library is downloaded and indexed. IntelliJ also shows the library under "External Libraries".
When I try to use a class from that library, IntelliJ's autocompletion suggests the correct import. But after importing it, IntelliJ says "Unresolved reference" in the line of the import and the line where I use that class.
However, the gradle build still succeeds. Also, I only experience this issue when I import something from that library in a Kotlin class. In a Java class, IntelliJ can resolve the reference. This problem does not only happen in one specific project, but in all projects where I try to import something from that library, which means it's probably not an issue with the project configuration. The other Java library I use (paper-api) works fine when importing in both Kotlin and Java files. Invalidating caches and reloading all gradle projects has not solved the issue.
I suppose there is something misconfigured in my library https://github.com/skycavemc/skycavelib. Does someone have an idea what could have went wrong there? This is my build.gradle for the library I am trying to import from:
plugins {
id 'java'
id 'org.jetbrains.kotlin.jvm' version '1.7.10'
id 'com.github.johnrengelman.shadow' version '7.1.2'
id 'maven-publish'
}
group = 'de.skycave'
version = '1.0.2'
def localProperties = new Properties()
localProperties.load(new FileInputStream(rootProject.file("local.properties")))
repositories {
mavenCentral()
maven {
name = 'papermc-repo'
url = 'https://repo.papermc.io/repository/maven-public/'
}
maven {
name = 'sonatype'
url = 'https://oss.sonatype.org/content/groups/public/'
}
maven {
name = 'jitpack'
url = 'https://jitpack.io'
}
}
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.7.10'
compileOnly 'io.papermc.paper:paper-api:1.19.2-R0.1-SNAPSHOT'
implementation 'org.mongodb:mongodb-driver-sync:4.7.1'
implementation 'com.google.code.gson:gson:2.9.1'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0'
implementation group: 'commons-io', name: 'commons-io', version: '2.11.0'
implementation 'com.github.heuerleon:mcguiapi:v1.3.5'
}
def targetJavaVersion = 17
java {
def javaVersion = JavaVersion.toVersion(targetJavaVersion)
sourceCompatibility = javaVersion
targetCompatibility = javaVersion
if (JavaVersion.current() < javaVersion) {
toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion)
}
manifest {
attributes 'Main-Class': "de.skycave.skycavelib.SkyCaveLib"
}
}
tasks.withType(JavaCompile).configureEach {
if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) {
//noinspection GroovyAssignabilityCheck, GroovyAccessibility
options.release = targetJavaVersion
}
}
processResources {
def props = [version: version]
inputs.properties props
filteringCharset 'UTF-8'
filesMatching('plugin.yml') {
expand props
}
}
build {
dependsOn(shadowJar)
}
shadowJar {
archiveFileName.set("${project.name}-${project.version}.jar")
}
publishing {
repositories {
maven {
name = "GitHubPackages"
url = "https://maven.pkg.github.com/skycavemc/skycavelib"
credentials {
username = localProperties.getProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR")
password = localProperties.getProperty("gpr.token") ?: System.getenv("ACCESS_TOKEN")
}
}
}
publications {
library(MavenPublication) {
from components.java
}
}
}
Solution
I did some more research and found a hint that shadowed jars might not work well. I removed this part
build {
dependsOn(shadowJar)
}
from the build.gradle of my library and published it to the package repository. Everything works fine since then, seems like that was the issue.

TaskExecutionException when i try to throw exception in main method

I'm trying run with code with gradle and without it.
public class Example {
public static void main(String[] args) throws RuntimeException {
throw new RuntimeException();
}
}
With gradle it's not working.
[Exception when i run code with gradle][1]
[1]: https://i.stack.imgur.com/kJtyL.png
Without gradle it works.
I tried to run this code on java 8 - java 18 versions and used different versions of gradle.
My gradle file
plugins {
id 'java'
}
group 'org.example'
version '0.0.1'
repositories {
mavenCentral()
}
sourceCompatibility = JavaVersion.VERSION_18
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
}
test {
useJUnitPlatform()
}
How can I fix it?
Thank you!

ClassNotFoundException When running jar file but runs fine in Intellij

I created a small mqtt application using eclipse paho mqtt library in kotlin with Gradle in Intellij IDE. it runs fine when running it through Intellij but when I build it and run the jar file that gets created I get a NoClassDefFoundError error.
From other questions I have seen about this it looks like it has something to do with the class path but I am not sure what needs to be done if that is indeed the issue because I am using gradle and not jar files for libraries.
I was following this tutorial
Here is my gradle file
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.4.31'
id 'application'
}
group = 'me.package'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
maven {
url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
}
}
dependencies {
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
test {
useJUnit()
}
compileKotlin {
kotlinOptions.jvmTarget = '1.8'
}
compileTestKotlin {
kotlinOptions.jvmTarget = '1.8'
}
application {
mainClassName = 'com.publisher.MainKt'
}
tasks.jar {
manifest {
attributes 'Main-Class': 'com.publisher.MainKt'
}
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
And my MainKt file
package com.publisher
import org.eclipse.paho.client.mqttv3.*
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence
import java.io.File
fun main(args: Array<String>) {
val client = MqttClient("tcp://192.168.0.55:1883","publisher", MemoryPersistence())
val connOpts = MqttConnectOptions()
connOpts.isCleanSession = false
connOpts.isAutomaticReconnect = true
client.setCallback(object: MqttCallback {
override fun connectionLost(cause: Throwable?) {
println("Connection lost")
println(cause!!.message)
}
override fun messageArrived(topic: String?, message: MqttMessage?) {
println("Message Received for topic: $topic")
println("Message: ${message!!.payload}")
}
override fun deliveryComplete(token: IMqttDeliveryToken?) {
println("Message delivered")
}
})
try{
client.connect(connOpts)
println("Connected")
client.subscribe("config/+", 1) { topic, message ->
println("Getting configuration for $message")
val path = System.getProperty("user.dir")
val file = File("$path/${message}.json")
if(file.exists()){
client.publish("/devices/ + $message + /config", MqttMessage(file.readBytes()))
}
}
}catch (e: MqttException){
println("Error: ${e.localizedMessage}")
e.printStackTrace()
}
}
The way you start your application does not include the dependencies, meaning your MQTT driver and the Kotlin dependencies are not included.
Do the following:
gradle distZip
# alternatively
gradle distTar
This will create a zip/tar file containing all the dependencies and a start script. Use that to start your application.
You could consider the Shadow plugin, as it is straightforward to use. Your build.gradle would look something like this:
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.4.31'
// Shadow plugin
id 'com.github.johnrengelman.shadow' version '6.1.0'
id 'java'
}
group = 'me.package'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
maven {
url "https://repo.eclipse.org/content/repositories/paho-snapshots/"
}
}
dependencies {
implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5'
testImplementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
test {
useJUnit()
}
compileKotlin {
kotlinOptions.jvmTarget = '1.8'
}
compileTestKotlin {
kotlinOptions.jvmTarget = '1.8'
}
application {
mainClassName = 'com.publisher.MainKt'
}
tasks.jar {
manifest {
attributes 'Main-Class': 'com.publisher.MainKt'
}
}
So your fat JAR is generated in the /build/libs directory with all the dependencies included.

api configuration of java-library plugin is not recognized

I am new to Gradle and i am using Gradle 6.1.
I am writing small application to understand the concepts of multi project Application and Java-Library plugin of Gradle.
My Question is :
How App.java is running perfectly fine without importing DefaultRandomGenerator class from SubProject-2
Why am i getting the error message "No candidates found for method call api" in build.grade file of Parent project (MultiProjectApp).
Below are my application code :
Parent Project (MultiProjectApp) files
settings.gradle
rootProject.name = 'MultiProjectApp'
include 'SubProject-1'
include 'SubProject-2'
build.gradle
allprojects {
apply plugin: 'java'
group 'org.example'
version '1.0-SNAPSHOT'
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
}
project(':SubProject-1') {
dependencies {
implementation project(':SubProject-2')
}
}
project(':SubProject-2') {
apply plugin: 'java-library'
dependencies {
api 'org.apache.commons:commons-math3:3.2'
implementation 'org.apache.logging.log4j:log4j-core:2.5'
testImplementation "junit:junit:4.12"
}
}
SubProject-2 files
build.gradle
Empty file
RandomGenerator.java
package org.examples;
public interface RandomGenerator {
String name();
int generate();
}
DefaultRandomGenerator.java
package org.examples;
import org.apache.commons.math3.random.RandomDataGenerator;
public class DefaultRandomGenerator implements RandomGenerator {
public String name() {
return "Main Random Number Generator";
}
public int generate() {
final RandomDataGenerator randomDataGenerator = new RandomDataGenerator();
return randomDataGenerator.nextInt(5, 10);
}
}
SubProject-1 files
build.gradle
Empty file
App.java
package org.examples;
import org.apache.commons.math3.random.RandomDataGenerator;
public class App {
public static void main(String[] args) {
RandomGenerator aRandomGenerator = new DefaultRandomGenerator();
System.out.println("The 1st random number is :" + aRandomGenerator.generate());
System.out.println("The 2nd random number is :" + generateMy());
}
public static int generateMy() {
final RandomDataGenerator aRandomDataGenerator = new RandomDataGenerator();
return aRandomDataGenerator.nextInt(5, 10);
}
}
How App.java is running perfectly fine without importing
DefaultRandomGenerator class from SubProject-2
It works because they are both in the same package (org.examples).
Note that this will not work if using the new module system introduced in Java 9. Because the two projects are considered "split", and you will need various hacks to make it work.
Why am I getting the error message "No candidates found for method
call api" in build.grade file of Parent project (MultiProjectApp).
This is an IDE problem, not a gradle problem. If you run, it should still work.
Example of runnig it in Vscode

Building a Kotlin + Java 9 project with Gradle

I'm fairly new to Gradle (and Java 9, to be honest), and I'm trying to use Gradle to build a simple library project that is a mix of Java 9 and Kotlin. More in detail, there is an interface in Java and an implementation in Kotlin; I'd do everything in Kotlin, but the modules-info.java is java anyway, so I decided to do things this way.
I'm building on IntelliJ Idea, with the 1.2.0 kotlin plugin and gradle 4.3.1 defined externally.
Filesystem schema is:
+ src
+ main
+ java
+ some.package
- Roundabout.java [an interface]
- module-info.java
+ kotlin
+ some.package.impl
- RoundaboutImpl.kt [implementing the interface]
module-info.java is:
module some.package {
requires kotlin.stdlib;
exports some.package;
}
and build.gradle is:
buildscript {
ext.kotlin_version = '1.2.0'
repositories {
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
group 'some.package'
version '1.0-PRE_ALPHA'
apply plugin: 'java-library'
apply plugin: 'kotlin'
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
sourceCompatibility = 9
compileJava {
dependsOn(':compileKotlin')
doFirst {
options.compilerArgs = [
'--module-path', classpath.asPath,
]
classpath = files()
}
}
repositories {
mavenCentral()
}
dependencies {
compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: "$kotlin_version"
testCompile group: 'junit', name: 'junit', version: '4.12'
}
compileKotlin {
kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
kotlinOptions.jvmTarget = "1.8"
}
Notice that I had to specify a module path on the java compile task, or the compilation fails with:
error: module not found: kotlin.stdlib
requires kotlin.stdlib;
Anyway, now this build fails with this error, and I can't figure out how to solve it:
error: package some.package.impl does not exist
import some.package.impl.RoundaboutImpl;
error: cannot find symbol
return new RoundaboutImpl<>(queueSize, parallelism, worker, threadPool);
I think that the Kotlin part of the compilation is going ok, then the java part fails because it doesn't "see" the kotlin side, so to speak.
I think I should tell it somehow to to load the already compiled kotlin classes in the classpath; but (first) how do I do this in gradle? and (second) is it even possible? I think you can't mix module path and class path in Java 9.
How can I solve this? I think it is a pretty common situation, as every java9-style module will be a mixed-language module (because of module-info.java), so I think I'm missing something really basic here.
Thanks in advance!
Solved! It was sufficient to set the kotlin compilation dir to the same dir as Java:
compileKotlin.destinationDir = compileJava.destinationDir
It works now, both with the sources in the same tree or in different trees; but with a quirk: the jar task produces a jar with all the entries duplicated. I'll work on fix this, next.
Thanks to everyone!
I am using the following gradle script where I put the module-info.java under src/module. It gets automatically included in the jar (without duplicates):
if (JavaVersion.current() >= JavaVersion.VERSION_1_9) {
subprojects {
def srcModule = "src/module"
def moduleInfo = file("${project.projectDir}/$srcModule/module-info.java")
if (moduleInfo.exists()) {
sourceSets {
module {
java {
srcDirs = [srcModule]
compileClasspath = main.compileClasspath
sourceCompatibility = '9'
targetCompatibility = '9'
}
}
main {
kotlin { srcDirs += [srcModule] }
}
}
compileModuleJava.configure {
dependsOn compileKotlin
destinationDir = compileKotlin.destinationDir
doFirst {
options.compilerArgs = ['--module-path', classpath.asPath,]
classpath = files()
}
}
jar.dependsOn compileModuleJava
}
}
}
I won't update it any longer, have a look at https://github.com/robstoll/atrium/blob/master/build.gradle
to see the current version in use.
The accepted answer did not work for me (atleast not the way it was presented), but this is what worked:
plugins {
id "org.jetbrains.kotlin.jvm" version "1.3.50"
}
compileKotlin {
doFirst {
destinationDir = compileJava.destinationDir
}
}
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
Doing it the way the accepted answer suggests led to me getting this error:
Directory '/path/to/project/build/classes/kotlin/main' specified for
property 'compileKotlinOutputClasses' does not exist.
Gradle version: 5.6
I ran into the same problem and the existing answers fixed only part of the issue for me, so I searched over all internet and ended up with a working solution. I don't know exactly why this works, but I decided to share my build.gradle.kts file here to help other people to find they way. This file is a combination of many pieces that I found on the internet.
I'm using Java 16, Kotlin 1.5.31 and Gradle 7.1.
The file tree is:
+ project
- build.gradle.kts
+ src
+ main
+ java
- module-info.java
+ my
+ package
- SomeClasses.java
+ kotlin
+ my
+ package
- MoreClasses.kt
module-info.java
module name.of.your.javamodule {
requires kotlin.stdlib;
requires kotlinx.coroutines.core.jvm;
requires org.jetbrains.annotations;
exports my.pacakge;
}
build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
application
kotlin("jvm") version "1.5.31"
id("org.jetbrains.kotlin.plugin.serialization") version "1.5.31"
}
val kotlinVersion = "1.5.31"
group = "your.group.id"
version = "0.0.1-SNAPSHOT"
application {
mainClass.set("full.name.of.your.MainClass")
mainModule.set("name.of.your.javamodule") // Same defined in module-info.java
executableDir = "run"
}
repositories {
mavenCentral()
}
dependencies {
implementation(kotlin("stdlib-jdk8", kotlinVersion))
implementation("com.michael-bull.kotlin-inline-logger:kotlin-inline-logger:1.0.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2-native-mt")
implementation("org.jetbrains:annotations:22.0.0")
testImplementation(kotlin("test", kotlinVersion))
}
java {
sourceCompatibility = JavaVersion.VERSION_16
targetCompatibility = JavaVersion.VERSION_16
}
tasks {
run.configure {
dependsOn(jar)
doFirst {
jvmArgs = listOf(
"--module-path", classpath.asPath
)
classpath = files()
}
}
compileJava {
dependsOn(compileKotlin)
doFirst {
options.compilerArgs = listOf(
"--module-path", classpath.asPath
)
}
}
compileKotlin {
destinationDirectory.set(compileJava.get().destinationDirectory)
}
jar {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = "16"
}
}
On gradle 7.4 with kotlin DSL, I need to:
move the module-info.java to src/main/java
create any java file inside each package to export under src/main/java, at least empty package-info.java
In build.gradle.kts:
val compileKotlin: KotlinCompile by tasks
val compileJava: JavaCompile by tasks
compileKotlin.destinationDirectory.set(compileJava.destinationDirectory)
Also discussed here:
https://github.com/gradle/gradle/issues/17271

Categories

Resources