realm-kotlin
realm-kotlin copied to clipboard
Document Proguard configuration for non-Android JVM builds
Application with Realm database (KMM project for Desktop and Android) causes crush at start if package is built with Release version on any desktop platform (I have only tested on Win 10 and Ubuntu). The same project which built as 'Debug' version (Gradle task <package*> without word 'release') starts normally without any logging in Console. Crush is accompanied with the following output in log:
Exception in thread "main" java.lang.ExceptionInInitializerError at ComposableSingletons$MainKt$lambda-1$1$1.invoke(main.kt:1016) at org.koin.core.context.GlobalContext.startKoin(GlobalContext.kt:64) at ComposableSingletons$MainKt$lambda-1$1.invoke(main.kt:3031) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.ui.window.Application_desktopKt$application$1$1.invoke(Application.desktop.kt:2115) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:116) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:34) at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1$1.invoke(Application.desktop.kt:1226) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:21107) at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:228) at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2$1.invoke(Application.desktop.kt:1221) at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:21107) at AppKt.invokeComposable(App.kt:26073) at androidx.compose.runtime.ComposerImpl$doCompose$2$5.invoke(Composer.kt:4248) at androidx.compose.runtime.SnapshotStateKt__DerivedStateKt.observeDerivedStateRecalculations(DerivedState.kt:341) at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:50175) at androidx.compose.runtime.ComposerImpl.composeContent$runtime(Composer.kt:3173) at androidx.compose.runtime.CompositionImpl.composeContent(Composition.kt:587) at androidx.compose.runtime.Recomposer.composeInitial$runtime$5576a047(Recomposer.kt:950) at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:519) at androidx.compose.ui.window.Application_desktopKt$awaitApplication$2$1$2.invokeSuspend(Application.desktop.kt:219) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) at java.desktop/java.awt.event.InvocationEvent.dispatch(Unknown Source) at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source) at java.desktop/java.awt.EventQueue$4.run(Unknown Source) at java.desktop/java.awt.EventQueue$4.run(Unknown Source) at java.base/java.security.AccessController.doPrivileged(Unknown Source) at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source) at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source) at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source) at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source) at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source) at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source) at java.desktop/java.awt.EventDispatchThread.run(Unknown Source) Caused by: java.lang.IllegalStateException: Incomplete hierarchy for class BaseRealmObject, unresolved classes [io.realm.kotlin.Deleteable] at kotlin.reflect.jvm.internal.impl.descriptors.runtime.components.RuntimeErrorReporter.reportIncompleteHierarchy(RuntimeErrorReporter.kt:26) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$DeserializedClassTypeConstructor.computeSupertypes(DeserializedClassDescriptor.kt:272) at kotlin.reflect.jvm.internal.impl.types.AbstractTypeConstructor$supertypes$1.invoke(AbstractTypeConstructor.kt:78) at kotlin.reflect.jvm.internal.impl.types.AbstractTypeConstructor$supertypes$1.invoke(AbstractTypeConstructor.kt:77) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValueWithPostCompute.invoke(LockBasedStorageManager.java:481) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValueWithPostCompute.invoke(LockBasedStorageManager.java:512) at kotlin.reflect.jvm.internal.impl.types.AbstractTypeConstructor.getSupertypes(AbstractTypeConstructor.kt:27) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$DeserializedClassMemberScope.getNonDeclaredVariableNames(DeserializedClassDescriptor.kt:382) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope$OptimizedImplementation$variableNames$2.invoke(DeserializedMemberScope.kt:262) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope$OptimizedImplementation$variableNames$2.invoke(DeserializedMemberScope.kt:261) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:527) at kotlin.reflect.jvm.internal.impl.storage.StorageKt.getValue(storage.kt:42) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope$OptimizedImplementation.getVariableNames(DeserializedMemberScope.kt:261) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope.getVariableNames(DeserializedMemberScope.kt:60) at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaClassMemberScope.computePropertyNames(LazyJavaClassMemberScope.kt:861) at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope$propertyNamesLazy$2.invoke(LazyJavaScope.kt:260) at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope$propertyNamesLazy$2.invoke(LazyJavaScope.kt:260) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:527) at kotlin.reflect.jvm.internal.impl.storage.StorageKt.getValue(storage.kt:42) at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope.getPropertyNamesLazy(LazyJavaScope.kt:260) at kotlin.reflect.jvm.internal.impl.load.java.lazy.descriptors.LazyJavaScope.getVariableNames(LazyJavaScope.kt:264) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$DeserializedClassMemberScope.getNonDeclaredVariableNames(DeserializedClassDescriptor.kt:383) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope$OptimizedImplementation$variableNames$2.invoke(DeserializedMemberScope.kt:262) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope$OptimizedImplementation$variableNames$2.invoke(DeserializedMemberScope.kt:261) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:527) at kotlin.reflect.jvm.internal.impl.storage.StorageKt.getValue(storage.kt:42) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope$OptimizedImplementation.getVariableNames(DeserializedMemberScope.kt:261) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope.getVariableNames(DeserializedMemberScope.kt:60) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$DeserializedClassMemberScope.getNonDeclaredVariableNames(DeserializedClassDescriptor.kt:383) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope$OptimizedImplementation$variableNames$2.invoke(DeserializedMemberScope.kt:262) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope$OptimizedImplementation$variableNames$2.invoke(DeserializedMemberScope.kt:261) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:527) at kotlin.reflect.jvm.internal.impl.storage.StorageKt.getValue(storage.kt:42) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope$OptimizedImplementation.getVariableNames(DeserializedMemberScope.kt:261) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope$OptimizedImplementation.addFunctionsAndPropertiesTo(DeserializedMemberScope.kt:349) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedMemberScope.computeDescriptors(DeserializedMemberScope.kt:115) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$DeserializedClassMemberScope$allDescriptors$1.invoke(DeserializedClassDescriptor.kt:301) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$DeserializedClassMemberScope$allDescriptors$1.invoke(DeserializedClassDescriptor.kt:300) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408) at kotlin.reflect.jvm.internal.impl.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:527) at kotlin.reflect.jvm.internal.impl.serialization.deserialization.descriptors.DeserializedClassDescriptor$DeserializedClassMemberScope.getContributedDescriptors(DeserializedClassDescriptor.kt:311) at kotlin.reflect.jvm.internal.impl.resolve.scopes.InnerClassesScopeWrapper.getContributedDescriptors(InnerClassesScopeWrapper.kt:35) at kotlin.reflect.jvm.internal.impl.resolve.scopes.InnerClassesScopeWrapper.getContributedDescriptors(InnerClassesScopeWrapper.kt:27) at kotlin.reflect.jvm.internal.impl.resolve.scopes.ResolutionScope$DefaultImpls.getContributedDescriptors$default(ResolutionScope.kt:50) at kotlin.reflect.jvm.internal.KClassImpl$Data$nestedClasses$2.invoke(KClassImpl.kt:100) at kotlin.reflect.jvm.internal.KClassImpl$Data$nestedClasses$2.invoke(KClassImpl.kt:99) at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:93) at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:32) at kotlin.reflect.jvm.internal.KClassImpl$Data.getNestedClasses(KClassImpl.kt:99) at kotlin.reflect.jvm.internal.KClassImpl.getNestedClasses(KClassImpl.kt:240) at kotlin.reflect.full.KClasses.getCompanionObject(KClasses.kt:47) at kotlin.reflect.full.KClasses.getCompanionObjectInstance(KClasses.kt:57) at AppKt.realmObjectCompanionOrNull(App.kt:0) at io.realm.kotlin.Configuration$SharedBuilder.
(Configuration.kt:406) at io.realm.kotlin.RealmConfiguration$Builder. (RealmConfiguration.kt:51) at di.RealmModuleKt$realmModule$1.invoke(realmModule.kt:1019) at AppKt.module$default$33a95d8f$7dff6feb(App.kt:0) at di.RealmModuleKt. (realmModule.kt:17) ... 37 more Failed to launch JVM
I believe that clue in this line in the bottom:
at AppKt.realmObjectCompanionOrNull(App.kt:0)
as I already found similar Issue on this tracker but for Android.
Kotlin 1.8.0 Realm 1.7.0 Gradle 7.5.1
Hi @alexmaryin. This looks a bit like if our plugin has not been applied or when of our internal properties have been stripped with Proguard on Android. Can you elaborate a bit on your configuration, especially:
- How you define your 'debug' and 'release' configurations,
- If you are using obfuscators/minimizers like proguard, and
- What version of Gradle, Realm and compose you are using
I guess there is also a
Incomplete hierarchy for class BaseRealmObject, unresolved classes [io.realm.kotlin.Deleteable]
and some methods related to inner classes. Can you verify that our library is in fact a dependency and comment whether you are using inner classes in your model? I fail to see how this should cause different behavior for debug and release runs, but I guess that depends heavily on how you debug/release configuration differs.
Hi, @rorbech , thank you for reply. Release version of desktop-compose build really differs from Debug at least by applying the Proguard. Here is the part from build.gradle.kts for desktop-module of the project:
compose.desktop {
application {
mainClass = "MainKt"
buildTypes.release.proguard {
configurationFiles.from(project.file("compose-desktop.pro"))
}
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
...
modules("java.instrument", "java.management", "java.naming", "java.sql", "jdk.unsupported")
....
}
}
}
And the file with Proguard-rules. I am not very confident with Proguard, so I just copied it from another issue at Desktop-Compose github. compose-desktop.pro.txt
@alexmaryin Then I guess you are most probably just missing some proguard rules. For Android there is a convention on how to supply library proguard files to consumers, but I am not aware of a similar convention for non-Android JVM builds. As an initial experiment you could try to add the rules that we supply on Android. You can grab them here:
- Base-library: https://github.com/realm/realm-kotlin/blob/main/packages/library-base/proguard-rules-consumer-common.pro
- Sync-library: https://github.com/realm/realm-kotlin/blob/main/packages/library-sync/proguard-rules-consumer-common.pro Then we can try to investigate if there are mechanism for supplying it as part of our distributables.
@rorbech I copied Base-library rules (I don't use Sync there), but result is still the same, and log-output is the same. But when I disabled Proguard for 'release' build type in Gradle, it started to run successfully.
@alexmaryin Thanks for the confirmation. I guess it is then a matter of adding the appropriate rules. Please start by adding rules for the immediate unresolvable classes (reported like the unresolved classes [io.realm.kotlin.Deleteable] in the stack trace) along with the proguard-rules-consumer-common.pro rules mentioned above. Then feel free to reach out if you cannot derive the missing ones and/or if you get different stack traces.
I inserted the next rule:
-keep class io.realm.kotlin.Deleteable {
*;
}
Now here are two errors which cause fail on start:
Caused by: java.lang.RuntimeException: Couldn't load Realm native libraries
Caused by: java.lang.NoSuchMethodException: io.realm.kotlin.jvm.SoLoader.load()
Yeah, I guess the SoLoader is also loaded through reflection. Have you tried adding the io.realm.kotlin.jvm.SoLoader too?
I do run it successfully at last. But this happened only as I set in proguard rules -dontshrink, -dontoptimize, -dontobfuscate as well. So I don't see a point in this. A difference between release version with Proguard and one without is only 300 Kb when whole package takes 60 Mb.
What are the other issues that you experience that requires you to add -dontshrink, etc.? Maybe I can direct you on some less agressive rules if I you post the stack traces or at least what you should check for in the proguard outputs/seeds to know which rule that is causing troubles. It could be that your manual additions of our consumer-rules is somehow overruled by your other configuration
@rorbech I have made separate issue on it here https://github.com/JetBrains/compose-multiplatform/issues/2969 and it concerns only Compose multiplatform managing of resources for app.
@alexmaryin Thanks. So after adding
io.realm.kotlin.Deleteable
io.realm.kotlin.jvm.SoLoader
to the keep rules along with our proguard-consumer rules, you don't see issues related to realm?
@rorbech No, I don't. These two rules were enough for Realm.
@alexmaryin I have updated the title to reflect that this is in fact just proguard configuration. I don't think we can distribute non-Android proguard-consumer-files, so I guess the work around for now is to clone the Android proguard consumer file and add rules for
io.realm.kotlin.Deleteable
io.realm.kotlin.jvm.SoLoader
Hi, I tried the above solution but still encountered an "ExceptionInInitializerError" crash when I called the realm function. packageMsi or packageReleaseMsi
OS: Windows 11 Compose multiplatform: 1.6.10-beta03 Realm: 1.15.0 Kotlin: 1.9.23 Jdk: 18
But it works well during debugging desktopRun
Can you help me?