Search code examples
javakotlingradlewindowcompose-multiplatform

Inspecting a Kotlin Desktop application with jar command for Java modules


I've got a Kotlin Compose Desktop application that I would like to create a custom JRE for to reduce its size. Currently after the compilation it's around 160MB (I guess because I use the JDK to compile it).

I've found that I can use the jre command line tool to inspect it for which modules I need to include in the custom JRE.

However, I cannot figure out which file exacly I need to inspect. I'v tried to do this with the *.exe, but it fails as not being a *.zip. Then I tried it with a MyApp.1.0-SNAPSHOT.jar, but this is apparently also the wrong file:

Unable to derive module descriptor for: [file-path] ComposableSingletons$MainKt.class found in top-level directory (unnamed package not allowed in module)

So, now I think I probably need to compile the application differently to get an inspectable *.jar file as a result.

Do you know how can I do this or which file I should inspect to get the list of modules my custom JRE needs to contain?

Here's its build.gradle.kt configuration:

import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptions
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    // https://github.com/JetBrains/compose-multiplatform/releases
    kotlin("jvm") version "2.0.20"
    kotlin("plugin.compose") version "2.0.20"
    // https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-compatibility-and-versioning.html#jetpack-compose-and-compose-multiplatform-release-cycles
    id("org.jetbrains.compose") version "1.6.11"
}


group = "com.me"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
    maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
    google()
}

dependencies {
    implementation(compose.desktop.currentOs)
    // other libraries I use...

}

compose.desktop {
    application {
        mainClass = "MainKt"
        // https://www.oracle.com/in/java/technologies/downloads/#java21
        javaHome = System.getenv("JDK_21")
        jvmArgs += listOf("-Xmx2G")
        nativeDistributions {
            targetFormats(TargetFormat.Exe)
            packageVersion = "1.2.3"
            packageName = "MyApp-v$packageVersion"
            includeAllModules = true            
        }

        buildTypes.release.proguard {
            isEnabled = false
        }
    }
}

tasks.wrapper {
    // https://gradle.org/releases/
    gradleVersion = "8.9"
    // You can either download the binary-only version of Gradle (BIN) or
    // the full version (with sources and documentation) of Gradle (ALL)
    distributionType = Wrapper.DistributionType.ALL
}

java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(21))
    }
}

kotlin {

    compilerOptions {
        jvmTarget.set(JvmTarget.JVM_21)
    }

    target {
        compilations.all {
            compileTaskProvider.configure {
                compilerOptions {
                    // https://www.oracle.com/ca-en/java/technologies/java-se-support-roadmap.html
                    if (this is KotlinJvmCompilerOptions) {
                        jvmTarget = JvmTarget.JVM_21
                    }
                }
            }
        }
    }
    sourceSets {
        val test by getting {
            dependencies {
                // https://mvnrepository.com/artifact/io.kotest/kotest-framework-engine-jvm
                implementation("io.kotest:kotest-framework-engine-jvm:5.9.1")

                // https://mvnrepository.com/artifact/io.kotest/kotest-runner-junit5-jvm
                implementation("io.kotest:kotest-runner-junit5-jvm:5.9.1")

                // https://mvnrepository.com/artifact/io.kotest/kotest-assertions-core
                implementation("io.kotest:kotest-assertions-core:5.9.1")
            }
        }
    }
}


Solution

  • based on this document, compose multiplatform uses jlink to make a custom distributable that comes in with a custom JRE which only includes modules needed in your app. You can create this installer via packageMsi, packageDmg or packageDeb gradle tasks available in compose desktop gradle plugin. You can also run your app with the custom JRE without creating an installer with runDistributable. The document itself states that if things go wrong with JLink, you can run the suggestModules task to further help you in finding the right jvm modules for your app.