I was trying to create runtime for my project, but suddenly I got an error and I couldn't find any info about it. The project is setted up as kotlin+gradle and nothing more.
I use badass-jlink-plugin and log4j2. The error:
Execution failed for task ':prepareModulesDir'.
> Error while evaluating property 'moduleName' of task ':prepareModulesDir'
> Failed to query the value of extension 'jlink' property 'moduleName'.
> Cannot find module-info.java in [ProjectFolder\src\main\java]
My build.gradle (not full):
buildscript {
...
}
plugins {
id 'application'
id 'org.jetbrains.kotlin.jvm' version '1.8.0'
id 'org.beryx.jlink' version '2.26.0'
// id 'org.jetbrains.kotlin.multiplatform' version '1.8.0'
id 'org.jetbrains.kotlin.plugin.serialization' version '1.8.0'
}
apply plugin: 'kotlin'
apply plugin: 'kotlinx-serialization'
group = 'com.adisalagic.codenames'
version = '1.0-SNAPSHOT'
mainClassName = 'com.adisalagic.codenames.Main'
repositories {
mavenCentral()
}
dependencies {
// implementation "org.jetbrains.kotlin:kotlin-serialization:1.8.0"
testImplementation 'org.jetbrains.kotlin:kotlin-test'
// implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0-RC")
implementation 'org.apache.logging.log4j:log4j-api:2.19.0'
implementation 'org.apache.logging.log4j:log4j-core:2.19.0'
implementation 'com.google.code.gson:gson:2.10.1'
}
test {
useJUnitPlatform()
}
jlink {
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
forceMerge 'log4j-api'
}
jar {
manifest {
attributes "Main-Class": "$mainClassName"
}
from {
configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) }
}
}
tasks.withType(Jar).configureEach {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
manifest {
attributes["Main-Class"] = "$mainClassName"
}
}
kotlin {
// jvmToolchain(8)
}
Should I create module-info myself?
I've discovered that it only happened when I use forceMerge. But everywhere it is suggested to be used.
I have a ToolWindowFactory class like this
class MyToolWindowFactory : ToolWindowFactory {
private val sharedFlow = MutableSharedFlow<String>(replay = 1)
override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
CoroutineScope(Dispatchers.IO).launch {
sharedFlow.emit("VALUE")
}
CoroutineScope(Dispatchers.IO).launch {
sharedFlow.collectLatest {
println("$it")
}
}
}
}
I use SharedFlow for variables, but I get this error after run.
Exception in thread "DefaultDispatcher-worker-3" java.lang.NoClassDefFoundError: Could not initialize class kotlinx.coroutines.CoroutineExceptionHandlerImplKt
at kotlinx.coroutines.CoroutineExceptionHandlerKt.handleCoroutineException(CoroutineExceptionHandler.kt:33)
at kotlinx.coroutines.DispatchedTask.handleFatalException$kotlinx_coroutines_core(DispatchedTask.kt:95)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:64)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:738)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
Does ShareStateFlow work in Intelji Plugin?
build.gradle
plugins {
id 'org.jetbrains.intellij' version '1.5.2'
id 'org.jetbrains.kotlin.jvm' version '1.6.10'
id 'java'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.10"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.3.9"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
// See https://github.com/JetBrains/gradle-intellij-plugin/
intellij {
version = '2021.3.3'
}
patchPluginXml {
changeNotes = """
Add change notes here.<br>
<em>most HTML tags may be used</em>"""
}
test {
useJUnitPlatform()
}
my team got a kotlin project with JavaFX as frontEnd. The problem we have is using jlink to produce a package.
Here is my build.gradle for the project.
Could anyone help me take a look at how to change this?
ps: I have already tried with
command-line javac
jlink from the intellij
create a new Main method and use the shadow plugin for jlink.
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4'
}
}
plugins {
id "java"
id "application"
id "org.jetbrains.kotlin.jvm" version "1.6.10"
id "org.openjfx.javafxplugin" version "0.0.12"
id "org.beryx.jlink" version "2.25.0"
// Json
id "org.jetbrains.kotlin.plugin.serialization" version "1.6.10"
}
group "com.yyil"
version "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
ext {
junitVersion = "5.8.1"
}
sourceCompatibility = "16"
targetCompatibility = "16"
tasks.withType(JavaCompile) {
options.encoding = "UTF-8"
}
application {
mainClass = 'com.yyil.noteapp.NewMain'
}
[compileKotlin, compileTestKotlin].forEach {
it.kotlinOptions {
jvmTarget = "16"
}
}
javafx {
version = "17.0.2"
modules = ["javafx.controls", "javafx.fxml", "javafx.web", "javafx.swing", "javafx.graphics", "javafx.base"]
}
dependencies {
implementation("org.controlsfx:controlsfx:11.1.0")
implementation("net.synedra:validatorfx:0.1.13") {
exclude(group: "org.openjfx")
}
implementation("org.kordamp.ikonli:ikonli-javafx:12.2.0")
implementation("eu.hansolo:tilesfx:11.48") {
exclude(group: "org.openjfx")
}
// JSON
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
// testing JavaFX
testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
testImplementation("org.testfx:testfx-core:4.0.16-alpha")
testImplementation("org.testfx:testfx-junit5:4.0.16-alpha")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
// database (sqlite)
implementation "org.xerial:sqlite-jdbc:3.30.1"
// svg support
implementation("org.apache.xmlgraphics:batik-transcoder:1.14")
}
test {
useJUnitPlatform()
}
When I try to build it, I got errors like:
I have a sample kotlin project that i am trying to get send to github as a package. Below is the contents of my buid.gradle.kts file. I am following this guide: https://docs.github.com/en/packages/guides/configuring-gradle-for-use-with-github-packages#example-using-kotlin-dsl-for-a-single-package-in-the-same-repository
But i can find any help as to why this error happens with the "from" function"
buid.gradle.kts content:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
repositories {
mavenCentral()
}
plugins {
kotlin("jvm") version "1.4.31"
`maven-publish`
}
publishing {
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/OWNER/REPOSITORY")
credentials {
username = project.findProperty("gpr.user") as String? ?: System.getenv("USERNAME")
password = project.findProperty("gpr.key") as String? ?: System.getenv("TOKEN")
}
}
}
publications {
register("gpr") {
from(components["java"]) //This part fails with a Unresolved reference. None of the following
//candidates is applicable because of receiver type mismatch:
}
}
}
group = "me.jeppe"
version = "1.0-SNAPSHOT"
dependencies {
testImplementation(kotlin("test-junit"))
}
tasks.test {
useJUnit()
}
tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "1.8"
}
After searching wide and far it appears that i need to write this:
register("gpr", MavenPublication::class)
instead of:
register("gpr")
I tried to use ShadowJar gradle pluging to pack my ktor app into fat jar. But as result of shadowJar task i get every time almost empty jar. It contains only manifest (main class is properly set).
Gradle configuration (groovy):
import org.gradle.jvm.tasks.Jar
buildscript {
ext.kotlin_version = '1.3.72'
ext.ktor_version = '1.3.2'
ext.serialization_version = '0.20.0'
ext.sl4j_version = '1.6.1'
repositories { jcenter() }
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:5.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.3.61'
}
apply plugin: 'kotlinx-serialization'
apply plugin: 'application'
apply plugin: 'java'
mainClassName = 'com.example.JvmMainKt'
apply plugin: 'com.github.johnrengelman.shadow'
repositories {
mavenCentral()
jcenter()
}
group 'com.example'
version '0.0.1'
apply plugin: 'maven-publish'
kotlin {
jvm {
}
js {
browser {
}
nodejs {
}
}
// For ARM, should be changed to iosArm32 or iosArm64
// For Linux, should be changed to e.g. linuxX64
// For MacOS, should be changed to e.g. macosX64
// For Windows, should be changed to e.g. mingwX64
mingwX64("mingw") {
binaries {
executable {
// Change to specify fully qualified name of your application's entry point:
entryPoint = 'main'
// Specify command-line arguments, if necessary:
runTask?.args('')
}
}
}
sourceSets {
commonMain {
dependencies {
implementation kotlin('stdlib-common')
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serialization_version"
implementation "io.ktor:ktor-client-core:$ktor_version"
}
}
commonTest {
dependencies {
implementation kotlin('test-common')
implementation kotlin('test-annotations-common')
}
}
jvmMain {
dependencies {
implementation kotlin('stdlib-jdk8')
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serialization_version"
implementation "io.ktor:ktor-serialization:$ktor_version"
implementation "io.ktor:ktor-server-netty:$ktor_version"
implementation "org.slf4j:slf4j-simple:$sl4j_version"
}
}
jvmTest {
dependencies {
implementation kotlin('test')
implementation kotlin('test-junit')
}
}
jsMain {
dependencies {
implementation kotlin('stdlib-js')
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-js:$serialization_version"
}
}
jsTest {
dependencies {
implementation kotlin('test-js')
}
}
mingwMain {
dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serialization_version"
}
}
mingwTest {
}
}
}
shadowJar {
mainClassName = 'com.example.JvmMainKt'
mergeServiceFiles()
}
marcu's answer is correct but he doesn't explain why that snipped solved his issue, and he's code snippet cannot be directly converted to build.gradle.kts because a cast is needed to see runtimeDependencyFiles from Kotlin. In groovy, his snippet works because groovy supports duck typing while Kotlin doesnt.
I needed this solution in Gradle Kotlin, so I'm sharing it this way.
The com.github.johnrengelman.shadow gradle plugin is designed to work with regular the java gradle plugin, which builds a single jar by default, so it automatically generates a single fat-jar based on that jar classpath.
The Kotlin-Multiplatform gradle plugin works differently, it creates jar files for each jvm target based on a lot of custom settings that is set up using a methodology that is unique to Kotlin-Multiplatform, that's why the default shadowJar task does not work out of the box on Kotlin-Multiplatform.
To solve this problem, we have to create a new ShadowJar task for each jvm target that we need a fat-jar manually, we can do it after we declare the targets (as marcu's example is showing), or during the creation of them. I'm going to show both ways, but I recommend doing during the creation to avoid having to cast objects.
Another thing is that I created function to apply the needed configuration because I have a 8 JVM targets, so this way I don't need to copy-paste the snipped 8 times, I only call the function instead.
During the creation of the target
The explanations are commented on the code:
// Add this imports on top of your build.gradle.kts file
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
// Since you need a main class, I added this default constant
// which you can change as you need:
val defaultMainClassName: String? = null
// Here I declare the function that will register the new ShadowJar task for the target
// If you need another mainClassName, you can pass it as parameter
fun KotlinJvmTarget.registerShadowJar(mainClassName: String? = defaultMainClassName) {
// We get the name from the target here to avoid conflicting
// with the name of the compilation unit
val targetName = name
// Access the main compilation
// We only want to create ShadowJar
// for the main compilation of the target, not the test
compilations.named("main") {
// Access the tasks
tasks {
// Here we register our custom ShadowJar task,
// it's being prefixed by the target name
val shadowJar = register<ShadowJar>("${targetName}ShadowJar") {
// Allows our task to be grouped alongside with other build task
// this is only for organization
group = "build"
// This is important, it adds all output of the build to the fat-jar
from(output)
// This tells ShadowJar to merge all jars in runtime environment
// into the fat-jar for this target
configurations = listOf(runtimeDependencyFiles)
// Here we configure the name of the resulting fat-jar file
// appendix makes sure we append the target name before the version
archiveAppendix.set(targetName)
// classifier is appended after the version,
// it's a common practice to set this classifier to fat-jars
archiveClassifier.set("all")
// Apply the main class name attribute
if (mainClassName != null) {
manifest {
attributes("Main-Class" to mainClassName)
}
}
// This instruction tells the ShadowJar plugin to combine
// ServiceLoader files together, this is needed because
// a lot of Kotlin libs uses service files and
// they would break without this instruction
mergeServiceFiles()
}
// Finally, we get the normal jar task for this target
// and tells kotlin to execute our recently created ShadowJar task
// after the normal jar task completes
getByName("${targetName}Jar") {
finalizedBy(shadowJar)
}
}
}
}
kotlin {
// Here we create a JVM target
jvm("jvm8") {
// We can configure anything we want
compilations.all {
kotlinOptions.jvmTarget = "1.8"
}
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
// If we need a ShadowJar for it,
// all we have to do now is call
// our custom function
// Here's an example of what I'm saying:
// https://stackoverflow.com/a/57951962/804976
registerShadowJar()
}
// Another one just to illustrate the example
jvm("jvm16") {
compilations.all {
kotlinOptions.jvmTarget = "16"
}
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
registerShadowJar()
}
}
With comments removed
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
val defaultMainClassName: String? = null
fun KotlinJvmTarget.registerShadowJar(mainClassName: String? = defaultMainClassName) {
val targetName = name
compilations.named("main") {
tasks {
val shadowJar = register<ShadowJar>("${targetName}ShadowJar") {
group = "build"
from(output)
configurations = listOf(runtimeDependencyFiles)
archiveAppendix.set(targetName)
archiveClassifier.set("all")
if (mainClassName != null) {
manifest {
attributes("Main-Class" to mainClassName)
}
}
mergeServiceFiles()
}
getByName("${targetName}Jar") {
finalizedBy(shadowJar)
}
}
}
}
kotlin {
jvm("jvm8") {
compilations.all {
kotlinOptions.jvmTarget = "1.8"
}
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
registerShadowJar()
}
jvm("jvm16") {
compilations.all {
kotlinOptions.jvmTarget = "16"
}
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
registerShadowJar()
}
}
After the creation of the target
Now, I'm going to comment only the changes:
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
kotlin {
jvm("jvm8") {
compilations.all {
kotlinOptions.jvmTarget = "1.8"
}
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
// Not registering on creation this time,
// we are going to register the task later
}
jvm("jvm16") {
compilations.all {
kotlinOptions.jvmTarget = "16"
}
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
}
}
val defaultMainClassName: String? = null
// Instead of having KotlinJvmTarget as receiver,
// we are going to cast the target to it.
// We are also getting the target name from
// the function parameter
fun registerShadowJar(targetName: String, mainClassName: String? = defaultMainClassName) {
// Get the target casting to KotlinJvmTarget in the process
kotlin.targets.named<KotlinJvmTarget>(targetName) {
// Access the main compilation
compilations.named("main") {
tasks {
val shadowJar = register<ShadowJar>("${targetName}ShadowJar") {
group = "build"
from(output)
configurations = listOf(runtimeDependencyFiles)
archiveAppendix.set(targetName)
archiveClassifier.set("all")
if (mainClassName != null) {
manifest {
attributes("Main-Class" to mainClassName)
}
}
mergeServiceFiles()
}
getByName("${targetName}Jar") {
finalizedBy(shadowJar)
}
}
}
}
}
// Now we call the method for each JVM target that we need to create a ShadowJar
registerShadowJar("jvm8")
registerShadowJar("jvm16")
With comments removed
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
kotlin {
jvm("jvm8") {
compilations.all {
kotlinOptions.jvmTarget = "1.8"
}
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
}
jvm("jvm16") {
compilations.all {
kotlinOptions.jvmTarget = "16"
}
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
}
}
val defaultMainClassName: String? = null
fun registerShadowJar(targetName: String, mainClassName: String? = defaultMainClassName) {
kotlin.targets.named<KotlinJvmTarget>(targetName) {
compilations.named("main") {
tasks {
val shadowJar = register<ShadowJar>("${targetName}ShadowJar") {
group = "build"
from(output)
configurations = listOf(runtimeDependencyFiles)
archiveAppendix.set(targetName)
archiveClassifier.set("all")
if (mainClassName != null) {
manifest {
attributes("Main-Class" to mainClassName)
}
}
mergeServiceFiles()
}
getByName("${targetName}Jar") {
finalizedBy(shadowJar)
}
}
}
}
}
registerShadowJar("jvm8")
registerShadowJar("jvm16")
I've faced the same issue in the past, the syntax that was working (for groovy gradle) for me was:
shadowJar {
mergeServiceFiles()
manifest {
attributes 'Main-Class': 'com.example.JvmMainKt'
}
}
I finally found solution to my problem.
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
//...
task shadowJar2(type: ShadowJar) {
def target = kotlin.targets.jvm
from target.compilations.main.output
def runtimeClasspath = target.compilations.main.runtimeDependencyFiles
manifest {
attributes 'Main-Class': mainClassName
}
configurations = [runtimeClasspath]
}