Closed StefanOltmann closed 1 month ago
This missing upgrade is the only blocker to use Java 21 in Compose desktop projects as far as I can tell. Debug releases (without ProGuard) seem to work out of the box.
The proguard version can be configured like this:
compose.desktop {
buildTypes.release.proguard {
version.set("7.4.0")
}
}
Ahh, now I know why it did not work when I tried it according to this advice on Slack: https://kotlinlang.slack.com/archives/C01D6HTPATV/p1699268691268389?thread_ts=1699231842.202439&cid=C01D6HTPATV I figured out that the argument has to be a String but I tried "7.4" instead of "7.4.0".
And packaging a release with Java 21 works too now. Thanks a lot.
Doesn't work for me: and I was able to reproduce the error using the out-of-the-box kotlin multiplatform project wizard (including desktop only).
Reproduction project here: https://github.com/mikedawson/ComposeProguardVersionTest/
Tested using: Kotlin 1.9.21 / Compose Multiplatform 1.5.11 (the latest production release until 3 days ago as per compatibility and versioning docs). Built on Ubuntu 23.10 using OpenJDK 17.
All you have to do is take the out-of-the-box project wizard, set the Proguard version to 7.4.0 (or 7.4.2), and you'll get:
./gradlew clean composeApp:runReleaseDistributable
...
pure virtual method called
terminate called without an active exception
Gtk-Message: 15:05:16.435: Failed to load module "canberra-gtk-module"
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
kotlinx/coroutines/scheduling/CoroutineScheduler$WorkerState.values$6b08e0a8()[I @3: invokevirtual
Reason:
Type '[I' (current frame, stack[0]) is not assignable to '[Ljava/lang/Object;'
Current Frame:
bci: @3
flags: { }
locals: { }
stack: { '[I' }
Bytecode:
0000000: b200 0ab6 0010 c000 05b0
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.<init>(CoroutineScheduler.kt:627)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.<init>(CoroutineScheduler.kt:606)
at kotlinx.coroutines.scheduling.CoroutineScheduler.createNewWorker(CoroutineScheduler.kt:496)
at kotlinx.coroutines.scheduling.CoroutineScheduler.tryCreateWorker(CoroutineScheduler.kt:453)
at kotlinx.coroutines.scheduling.CoroutineScheduler.dispatch(CoroutineScheduler.kt:4434)
at kotlinx.coroutines.scheduling.SchedulerCoroutineDispatcher.dispatchWithContext$kotlinx_coroutines_core(Dispatcher.kt:118)
at kotlinx.coroutines.scheduling.UnlimitedIoScheduler.dispatch(Dispatcher.kt:47)
at kotlinx.coroutines.internal.LimitedDispatcher.dispatch(LimitedDispatcher.kt:49)
at kotlinx.coroutines.scheduling.DefaultIoScheduler.dispatch(Dispatcher.kt:80)
at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:322)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default$6a2188af(Cancellable.kt:25)
at kotlinx.coroutines.AbstractCoroutine.start$62a77ca7(AbstractCoroutine.kt:2110)
at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source)
at org.jetbrains.skiko.FrameWatcher.start(FrameWatcher.kt:22)
at org.jetbrains.skiko.Setup.init(Setup.kt:35)
at org.jetbrains.skiko.Setup.init$default(Setup.kt:6)
at org.jetbrains.skiko.Library.load(Library.kt:62)
at org.jetbrains.skia.impl.Library$Companion.staticLoad(Library.jvm.kt:12)
at androidx.compose.ui.ConfigureSwingGlobalsForCompose_desktopKt.configureSwingGlobalsForCompose$default$626a2300(ConfigureSwingGlobalsForCompose.desktop.kt:1049)
at androidx.compose.ui.window.Application_desktopKt.application(Application.desktop.kt:110)
at MainKt.main(main.kt:1105)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@1b1426f4, Dispatchers.IO]
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
kotlinx/coroutines/scheduling/CoroutineScheduler$WorkerState.values$6b08e0a8()[I @3: invokevirtual
Reason:
Type '[I' (current frame, stack[0]) is not assignable to '[Ljava/lang/Object;'
Current Frame:
bci: @3
flags: { }
locals: { }
stack: { '[I' }
Bytecode:
0000000: b200 0ab6 0010 c000 05b0
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.<init>(CoroutineScheduler.kt:627)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.<init>(CoroutineScheduler.kt:606)
at kotlinx.coroutines.scheduling.CoroutineScheduler.createNewWorker(CoroutineScheduler.kt:496)
at kotlinx.coroutines.scheduling.CoroutineScheduler.tryCreateWorker(CoroutineScheduler.kt:453)
at kotlinx.coroutines.scheduling.CoroutineScheduler.dispatch(CoroutineScheduler.kt:4434)
at kotlinx.coroutines.scheduling.SchedulerCoroutineDispatcher.dispatchWithContext$kotlinx_coroutines_core(Dispatcher.kt:118)
at kotlinx.coroutines.scheduling.UnlimitedIoScheduler.dispatch(Dispatcher.kt:47)
at kotlinx.coroutines.internal.LimitedDispatcher.dispatch(LimitedDispatcher.kt:49)
at kotlinx.coroutines.scheduling.DefaultIoScheduler.dispatch(Dispatcher.kt:80)
at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:322)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30)
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default$6a2188af(Cancellable.kt:25)
at kotlinx.coroutines.AbstractCoroutine.start$62a77ca7(AbstractCoroutine.kt:2110)
at kotlinx.coroutines.BuildersKt.launch(Unknown Source)
at kotlinx.coroutines.BuildersKt.launch$default(Unknown Source)
at org.jetbrains.skiko.FrameWatcher.start(FrameWatcher.kt:22)
at org.jetbrains.skiko.Setup.init(Setup.kt:35)
at org.jetbrains.skiko.Setup.init$default(Setup.kt:6)
at org.jetbrains.skiko.Library.load(Library.kt:62)
at org.jetbrains.skia.impl.Library$Companion.staticLoad(Library.jvm.kt:12)
at androidx.compose.ui.ConfigureSwingGlobalsForCompose_desktopKt.configureSwingGlobalsForCompose$default$626a2300(ConfigureSwingGlobalsForCompose.desktop.kt:1049)
at androidx.compose.ui.window.Application_desktopKt.application(Application.desktop.kt:110)
at MainKt.main(main.kt:1105)
Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelled}@1b1426f4, Dispatchers.IO]
I really like Compose/Desktop: this is allowing us to get our Android app out on the desktop in a way that otherwise would not be possible (and we need things that don't work in web browsers: offline, peer-to-peer networking, etc). That said, the support for shrinking (using Proguard) seems in need of polishing. As far as I can see (and have documented/filed issues for), it's not working on JDK21 (which is supposed to be a supported JDK as per the docs), and KTOR serialization fails.
I would suggest that at all example desktop projects should have proguard enabled with obfuscation enabled (not just the proguard usage demo). That might help such issues get spotted.
The configuration works for me.
I kept the issue open as a reminder to set the default version ProGuard to a more current version.
@StefanOltmann interesting. What system were you building on? I just updated my post to include the versions being used (of course the Kotlin version, compose version, etc are in the Gradle files)
Kotlin 1.9.21, Compose Multiplatform 1.5.11, ProGuard 7.4.1, Gradle 8.5, latest macOS, Eclipse Temurin 18.0.2+9
I did not try your reproduction project. I meant ProGuard 7.4.1 works in general.
@mikedawson Just to be sure. Are we talking about a build failure or a run-time failure here?
Run-time failure. Compilation will succeed. I edited my post above to include the gradle tasks I was running just to be sure.
I can confirm that your example fails for me too on my Mac with the same error. The reason is that you did not switch off ProGuard optimization. With this
buildTypes.release.proguard {
obfuscate.set(false)
optimize.set(false)
version.set("7.4.0")
}
setup your example works perfectly. I have noticed previously that optimization causes nothing but problems on desktop, so I always switch it off.
Thanks @mipastgt for confirming the reproducer project reproduces the issue for you.
I think "working perfectly" would here should mean works as per the documentation. That means optimization (which itself is enabled by default) and obfuscation should work. As per the docs, compilation is supposed to support JDK17+ AND proguard.
Compose/Desktop is defined as stable, however, a key feature (code shrinking) does not work with the current LTS JDK release (21).
Yes, that should work out of the box according to the docs. From my previous reports I had the impression that optimization was switched off by default as a fix for these issues but at least as of today this assumption seems to be wrong. I still had the manual switch off in all my projects. That's the reason why I did not notice the problem at first.
In Ashampoo Photos I have optimization and obfuscation turned on. That works so far. So there is a way making ProGuard work. From that perspective the ProGuard default version could be updated.
But I also have rules like keep !com.ashampoo.photos.*
because I had issues.
Mike, from our discussion on Slack I remember that you have a rather fine-grained and complex configuration while mine is short, but does not optimize that much. I guess this has to do with the problems you experience.
I see my request to update the default ProGuard version unrelated to the fact that right now a proper ProGuard configuration is difficult to achieve.
I initially requested ProGuard support in Compose Multiplatform, because I have a closed source app that I need to obfuscate. I turned obfuscation on for all my own code, but for none of the third party libraries. So while it could be better the current ProGuard state satisfies the obfuscation needs of commercial apps.
On a side note: Some libs like Apache commons-compress just don’t work with ProGuard and Kotlin - at least for me. I needed to strip them using excludes in Gradle dependencies.
Using Java 21, with Kotlin 2.0.0 and Compose 1.6.10, will cause this error when using any of the tasks that use Proguard:
Caused by: java.io.IOException: Can't process class [AppKt.class] (Unsupported version number [65.0] (maximum 62.65535, Java 18))
Which can be solved by:
buildTypes.release.proguard {
version.set("7.4.2") // Or 7.5.0 when it's published for compatibility with Kotlin 2.0.0
}
Another task failure:
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
androidx/compose/runtime/SnapshotStateKt.snapshotFlow(Lkotlin/jvm/functions/Function0;)Lkotlinx/coroutines/flow/Flow; @20: invokestatic
Reason:
Type 'kotlin/jvm/functions/Function2' (current frame, stack[0]) is not assignable to 'androidx/compose/runtime/SnapshotStateKt__SnapshotFlowKt$snapshotFlow$1'
Current Frame:
bci: @20
flags: { }
locals: { 'kotlin/jvm/functions/Function0' }
stack: { 'kotlin/jvm/functions/Function2' }
Bytecode:
0000000: 2a59 4b12 0ab8 0031 bb00 1659 2a01 b700
0000010: 29c0 001e b800 32b0
at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1.invokeSuspend(Application.desktop.kt:205)
at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1.invoke(Application.desktop.kt:1000)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn$42ea79c7(Undispatched.kt:61)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:163)
at kotlinx.coroutines.BuildersKt.withContext(Unknown Source)
at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2.invokeSuspend(Application.desktop.kt:201)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:318)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:773)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:742)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
> Task :composeApp:runRelease FAILED
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.7/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
11 actionable tasks: 6 executed, 5 up-to-date
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':composeApp:runRelease'.
> Process 'command '/Library/Java/JavaVirtualMachines/amazon-corretto-21.jdk/Contents/Home/bin/java'' finished with non-zero exit value 1
This issue will happen when using Proguard 7.4.2 with Java 17 too
In short, Proguard might need to be updated to 7.5.0 to not only support Java 21, but also Kotlin 2.0.0
Issue should be fixed once updated to 7.5.0 as it's published now.
Overriding the Proguard version:
buildTypes.release.proguard {
version.set("7.5.0")
}
Doesn't solve the issue, it looks like the rules might needs to be updated or some additional rules
Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.
Currently, Compose Multiplatform relies on ProGuard version 7.2.2, which provides Java support only up to Java 18.
However, the latest ProGuard release, version 7.4, offers compatibility with Kotlin 1.9 and Java 21. I recommend updating this dependency for improved functionality and compatibility.
For more details, please refer to: https://github.com/Guardsquare/proguard/releases/tag/v7.4