Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ShadowJar Executable #5081

Closed
makeevrserg opened this issue Jul 8, 2024 · 2 comments
Closed

ShadowJar Executable #5081

makeevrserg opened this issue Jul 8, 2024 · 2 comments
Labels
enhancement New feature or request

Comments

@makeevrserg
Copy link

makeevrserg commented Jul 8, 2024

Background

Currently, Compose Desktop provides limited set of binaries: [Dmg, Msi, Deb]. Furthermore, macos and linux binaries can only be built on macos/linux.

Bundled .jar can supply us with multiplatform .jar executable which can be built on any platform.

The documentation itself lacks information about .jar packaging. In my opinione, there should be section or another .MD document with tutorial about shadowJar and it's ProGuard obfuscation.

I don't have a knowledge and skill to create high-quality documentation, thus I'm creating this issue with required information about shadowJar packaging with some pitfalls and not PR.

Here's sample guide to enable shadowJar packaging from my project:

Setup

Firstly, to create shadow, we need to specify excactly kotlin.jvm plugin and not kotlin.multiplatform. This is required because shadowJar plugin doesn't work with kotlin.multiplatform. It requers jvm.

plugins {
    kotlin("jvm")
    id("org.jetbrains.compose")
    alias(libs.plugins.kotlin.compose.gradle)
    alias(libs.plugins.shadow)
}

Secondly, to enable binaries for all native targets - we need to specify compose.desktop.ANY_OS:

dependencies {
    // Compose
    implementation(compose.desktop.macos_arm64)
    implementation(compose.desktop.windows_x64)
    implementation(compose.desktop.linux_x64)
}

Then, we need to proceed default Compose Desktop Setu from official README.

Setup Shadow

Now, we need to setup shadowJar.

val shadowJar by tasks.named<ShadowJar>("shadowJar") {
    isReproducibleFileOrder = true
    mergeServiceFiles()
    // Define required configurations
    dependsOn(configurations)
    archiveClassifier = null as String?
    // Setup minimize - exclude some libraries
    minimize {
        exclude(dependency(libs.decompose.compose.get()))
        exclude(dependency(libs.kotlin.coroutines.swing.get()))
        exclude(dependency(dependencies.compose.desktop.currentOs))
        exclude(dependency("org.apache.poi:poi-ooxml:.*"))
        // Exlclude every dependency project from your gradle project
        rootProject.subprojects.map(::dependency).forEach(::exclude)
    }
    archiveVersion = "${projectInfo.versionString}-desktop"
    archiveBaseName = projectInfo.name
    rootProject.file("jars").also { destination ->
        if (!destination.exists()) destination.parentFile?.mkdirs()
        destinationDirectory = destination
    }
    // Don't forget manifest with Main file executable
    manifest {
        attributes("Main-Class" to "com.example.desktop.MainKt")
    }
}

Setup ProGuard 1

To setup proguard, we need to specify an input job - our shadowJar task. We also need to add configuration .pro files from original ComposeDesktop repo

Also, when build, we shouldn't use JetbrainsJDK because it lacks some JDK modules.

tasks.register<ProGuardTask>("obfuscate") {
    // Don't use Jetbrains JDK!!
    val projectInfo = requireProjectInfo
    dependsOn(shadowJar)
    // Specify library jars of current java
    // compose.desktop should be configured as it said above!!
    libraryjars("${compose.desktop.application.javaHome}/jmods")
    val obfuscated = rootProject.file("jars")
        .resolve("${projectInfo.name}-${projectInfo.versionString}-desktop-obf.jar")
    injars(shadowJar.outputs.files)
    outjars(obfuscated)
    printseeds("$buildDir/obfuscated/seeds.txt")
    printmapping("$buildDir/obfuscated/mapping.txt")
    verbose()
    dontoptimize()
    configuration(files("proguard-rules.pro", "default-compose-desktop-rules.pro"))
}

Setup Proguard 2

Now we need to run proguard to see what we're missing

./gradlew :composeDesktop:obfuscate --info --stacktrace

With info ProGuard task will tell you what classes you're missing. You will add it into proguard-rules.pro. To understand this, refer to official proguard documentation.

After all this steps, obfuscated .jar executable for all os's will be created and you're ready to go.

@makeevrserg makeevrserg added enhancement New feature or request submitted labels Jul 8, 2024
@eymar eymar removed the submitted label Jul 10, 2024
@okushnikov
Copy link
Collaborator

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

@makeevrserg
Copy link
Author

makeevrserg commented Aug 2, 2024

In case someone will be interested in more detailed tutorial - I've created one.

btw related to #518

https://github.com/makeevrserg/ComposeShadow

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants