Closed EchoEllet closed 2 months ago
This comment provide a better workaround.
I managed to get it working with a workaround that's far from perfect and shouldn't be used in real production app 1. First of all, try to reproduce the bug mentioned above 2. Exclude Material module (Material 2) as mentioned in this https://github.com/JetBrains/compose-multiplatform/issues/497#issuecomment-1472218306 in [#497](https://github.com/JetBrains/compose-multiplatform/issues/497) from all compose libraries: ```kotlin implementation(compose.foundation) { exclude("org.jetbrains.compose.material") } implementation(compose.material3) { exclude("org.jetbrains.compose.material") } implementation(compose.ui) { exclude("org.jetbrains.compose.material") } // ... ``` Or: ``` implementation(compose.desktop.currentOs) { exclude("org.jetbrains.compose.material") } ``` 3. Create a new Proguard rules file and use it in the compose desktop module/source set ```kotlin compose.desktop { application { // Your configurations buildTypes.release.proguard { configurationFiles.from(files("compose-desktop.pro")) } } } ``` The name of the file is up to you, in my case, I created this project using (kmp.jetbrains.com), so I have to include this file in `composeApp` module (not inside `desktopMain` source set), the content will be something like ``` # TODO: Workaround to solve https://github.com/JetBrains/compose-multiplatform/issues/4883 -dontwarn androidx.compose.material.** -keep class androidx.compose.material3.** { *; } -ignorewarnings ``` This will keep all classes from Material 2 and ignore all the warnings from Material 2 and other warnings 4. Try to launch the release version of the app to use Proguard, a task like `runReleaseDistributable`, make sure to run it using your IDE as using `./gradlew runReleaseDistributable` might use Java version 21 or above if installed and configured in the system path, the app will compile but you will get a runtime error if you use anything from Material 3 that depends on Material 2 which might be another issue, the code snippet: ```kotlin import androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable @Composable fun App() { Button(onClick = {}) { Text(text = "Hi") } } ``` 5. You should get the following error when launching the release version of the app: ![image](https://github.com/JetBrains/compose-multiplatform/assets/73608287/ed136f63-d9b2-432a-bf44-562ec8af627e) This can be easily fixed by not excluding Material 2 from Material 3: ```kotlin implementation(compose.material3) ``` or exclude Material 2 completely if used from Material 3 and only include the needed modules: ```kotlin implementation(compose.material3) { // Explicitly exclude Material 2 completely from Material 3 in case Material 3 or any of its modules depends on the Material 2 module or any of its modules // At the moment, it seems to be only depending on material-ripple and material-icons-core modules exclude("org.jetbrains.compose.material") } // TODO: A workaround to include the needed modules after excluding the Material 2 module from Material 3 (see https://github.com/JetBrains/compose-multiplatform/issues/4883#issuecomment-2142445581) for more details implementation("org.jetbrains.compose.material:material-ripple:${libs.versions.compose.get()}") implementation("org.jetbrains.compose.material:material-icons-core:${libs.versions.compose.get()}") ``` The app works now ![image](https://github.com/JetBrains/compose-multiplatform/assets/73608287/9ab1aa95-c2eb-4a0b-8871-73d3aeccfc6b) Noticed that the default Kotlin icon included in Compose desktop is not included and you will get this instead ![image](https://github.com/JetBrains/compose-multiplatform/assets/73608287/32006b2d-c9f9-4252-8232-dbdc670bc202) I didn't investigate further about this Another unrelated issue is that Material 3 depends on some Material 2 modules like the ripple effect and icons core https://github.com/JetBrains/compose-multiplatform-core/blob/1694668f0d423babe94805bba603e087bd5ca943/compose/material3/material3/build.gradle#L104 This issue in https://github.com/JetBrains/compose-multiplatform-core which is not in this repository **Additional details**: The binary bundle size that's created with `packageReleaseDmg` (requires Java installed) after the workaround to use Material 3 with Proguard in the release version of the app ![image](https://github.com/JetBrains/compose-multiplatform/assets/73608287/a08207dc-2547-4c28-8340-e07082acc0c1) Before the workaround (Use Material 2) ![image](https://github.com/JetBrains/compose-multiplatform/assets/73608287/ee5c1c4d-12db-4210-9eb6-a3e05edd3698) The bundle size is around 6MB larger when using Material 3, I only used a `Button` and `Text` from Material 2 and Material 3, the size might not get much bigger when using more components from Material 3, this is another thing to look at but I'm not sure as I haven't looked into the source code in: https://github.com/JetBrains/compose-multiplatform-core
See the comment bellow for a better workaround
A better workaround, to use Material 3 (excluding Material 2), Kotlin 2.0.0, Proguard 7.5.0, and Compose 1.6.10:
kotlinx-serialization-core
:implementation(compose.material3)
implementation(compose.desktop.currentOs) {
exclude("org.jetbrains.compose.material")
}
// Explicitly include this is required to fix Proguard warnings coming from Kotlinx.DateTime
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.7.0")
compose.desktop {
application {
buildTypes.release.proguard {
version.set("7.5.0")
configurationFiles.from("proguard.pro")
}
}
}
proguard.pro
if doesn't exist, use the following rules:-keep class androidx.compose.runtime.** { *; }
-keep class androidx.collection.** { *; }
-keep class androidx.lifecycle.** { *; }
-keep class androidx.compose.ui.text.platform.ReflectionUtil { *; }
# We're excluding Material 2 from the project as we're using Material 3
-dontwarn androidx.compose.material.**
# Kotlinx coroutines rules seems to be outdated with the latest version of Kotlin and Proguard
-keep class kotlinx.coroutines.** { *; }
This doesn't have the icon issue as the previous workaround does.
The Uber JAR on macOS Apple silicon:
The native application binary created with packageReleaseDmg
Note that there are Proguard notes that need to be solved.
I'm facing this issue too, is it going to be addressed in the next release? I think it should be mentioned in release notes too
I'm facing this issue too, is it going to be addressed in the next release? I think it should be mentioned in release notes too
A workaround solution for now is to disable Proguard in the release or fix the issue with a workaround as mentioned above.
The workaround solution doesn't increase the size much. The difference is due to using Material 3
I'm using material 3(1.7.0-alpha02), ktor, androidx.navigation, androidx.datastore, coroutine, coil on compose desktop, My proguard is the default 7.2.2. I got no problems with release build. @EchoEllet Full proguard.pro below.
-obfuscationdictionary "dic.txt" -renamesourcefileattribute "dic.txt" -classobfuscationdictionary "dic.txt" -packageobfuscationdictionary "dic.txt"
-mergeinterfacesaggressively -overloadaggressively -repackageclasses
-keep class androidx.datastore.preferences. { *; } -keep class io.ktor.* { ; } -keep class coil3. { *; } -keep class ui.navigation.* { ; }
-keepclasseswithmembernames class androidx.compose.foundation.text.* { ; }
-assumenosideeffects class kotlin.jvm.internal.Intrinsics { public static void check(...); public static void throw(...); } -assumenosideeffects public class kotlin.coroutines.jvm.internal.DebugMetadataKt { private static getDebugMetadataAnnotation(...); } -assumenosideeffects class java.util.Objects { public static requireNonNull(...); }
####################################################################################################
-assumenosideeffects interface org.slf4j.Logger { public void trace(...); public void debug(...); public void info(...); public void warn(...); public void error(...);
public boolean isTraceEnabled(...);
public boolean isDebugEnabled(...);
public boolean isWarnEnabled(...);
}
-assumenosideeffects class org.slf4j.LoggerFactory { public static ** getLogger(...); }
-dontwarn org.slf4j.**
####################################################################################################
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} -keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
-keepclassmembers class kotlinx.coroutines.** {
volatile
-keepclassmembers class kotlin.coroutines.SafeContinuation {
volatile
-dontwarn java.lang.instrument.ClassFileTransformer -dontwarn sun.misc.SignalHandler -dontwarn java.lang.instrument.Instrumentation -dontwarn sun.misc.Signal
kotlinx.coroutines.internal.ExceptionsConstructor
.try
-catch
, as well as a check for Android.-dontwarn java.lang.ClassValue
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-keep class kotlinx.coroutines.internal.MainDispatcherFactory { ; } -keep class kotlinx.coroutines.swing.SwingDispatcherFactory { ; }
####################################################################################################
Companion
object fields of serializable classes.getDeclaredClasses
as done for named companion objects.-if @kotlinx.serialization.Serializable class ** -keepclassmembers class <1> { static <1>$Companion Companion; }
serializer()
on companion objects (both default and named) of serializable classes.-if @kotlinx.serialization.Serializable class { static $ ; } -keepclassmembers class <2>$<3> { kotlinx.serialization.KSerializer serializer(...); }
INSTANCE.serializer()
of serializable objects.-if @kotlinx.serialization.Serializable class { public static INSTANCE; } -keepclassmembers class <1> { public static <1> INSTANCE; kotlinx.serialization.KSerializer serializer(...); }
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
-dontnote kotlinx.serialization.**
java.lang.ClassValue
for caching inside these specified classes.java.lang.ClassValue
(for example, in Android), then R8/ProGuard will print a warning.-dontwarn kotlinx.serialization.internal.ClassValueReferences
Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.
Thanks to the workaround , Ive been able to compile but i still have this crash error on,1.7.0-alpha03 from MaterialTheme comosable on MaterialTheme.shapes
: Exception in thread "main" java.lang.ClassFormatError: Invalid index 2 in LocalVariableTable in class file androidx/compose/material3/ShapesKt at java.base/java.lang.ClassLoader.defineClass1(Native Method) at java.base/java.lang.ClassLoader.defineClass(Unknown Source) at java.base/java.security.SecureClassLoader.defineClass(Unknown Source) at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(Unknown Source) at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(Unknown Source) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(Unknown Source) at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source) at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source) at java.base/java.lang.ClassLoader.loadClass(Unknown Source) at androidx.compose.material3.MaterialTheme.getShapes(MaterialTheme.kt:100)
Describe the bug Starting from 1.2, Compose Desktop Plugin supports Proguard without any additional rules to get it working, but if you use Material 3 by using
implementation(compose.material3)
then it will cause failure with all the tasks that use Proguard likecreateReleaseDistributable
Affected platforms
Versions
To Reproduce Steps to reproduce the behavior:
2.0.0
and Compose1.6.10
./gradlew createReleaseDistributable
and see if there is an error, I didn't get any in my casebuild.gradle.kts
of the desktop source set in thecomposeApp
Gradle module (if you used the kmp.jetbrains.com) and replaceimplementation(compose.material)
withimplementation(compose.material3)
, remove any usages to Material module (Material 2) and use anything from Material 3 moduleimport androidx.compose.material3.Button import androidx.compose.material3.Text import androidx.compose.runtime.Composable
@Composable fun App() { Button(onClick = {}) { Text(text = "Hi") } }
The Proguard version that's used in the Compose desktop Gradle plugin is
7.2.2
and the one that supports Kotlin 2.0.0 and versions of Java 21 or higher is7.5.0
More details: https://github.com/Guardsquare/proguard/issues/376 and https://github.com/Guardsquare/proguard/issues/387I was able to run the example application using
7.2.2
with no issuesWith Material 2 (the module name is called Material)
The file
KotlinProject/composeApp/build/compose/logs/proguardReleaseJars/java-2024-05-28-05-06-55-err.txt
is empty, theKotlinProject/composeApp/build/compose/logs/proguardReleaseJars/java-2024-05-28-05-06-55-out.txt
was mostly warnings (about 940 lines), the end of the file:https://github.com/JetBrains/compose-multiplatform/blob/7131b5b9a665e8b1d72aabc9476947650145c11e/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/application/dsl/ProguardSettings.kt#L15
https://github.com/JetBrains/compose-multiplatform/blob/7131b5b9a665e8b1d72aabc9476947650145c11e/gradle-plugins/compose/src/main/kotlin/org/jetbrains/compose/desktop/tasks/AbstractUnpackDefaultComposeApplicationResourcesTask.kt#L19
This file might need to be updated: https://github.com/JetBrains/compose-multiplatform/blob/master/gradle-plugins/compose/src/main/resources/default-compose-desktop-rules.pro
Disabling Proguard optimization will give you the exact same issue:
Related Issues
3818
4391
497