Metadata in kotlin is being changed regardles of -keep kotlin.Metadata
I am using kotlin RPC (v0.6.2) and I am getting a crash because the @Metadata tag information is wrong. If I set isEnabled.set(false), the code does not crash the meta data tag is this:
@Metadata(mv = {2, 1, 0}, k = 1, xi = 48, d1 = {"��&\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n��\n\u0002\u0018\u0002\n��\n\u0002\u0018\u0002\n\u0002\b\u0005\n\u0002\u0018\u0002\n\u0002\u0010 \n\u0002\u0018\u0002\n��\b\u0001\u0018��2\u00020\u0001B\u0019\u0012\b\b\u0001\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0005¢\u0006\u0004\b\u0006\u0010\u0007J\u0014\u0010\n\u001a\u000e\u0012\n\u0012\b\u0012\u0004\u0012\u00020\r0\f0\u000bH\u0016R\u0014\u0010\u0002\u001a\u00020\u0003X\u0096\u0004¢\u0006\b\n��\u001a\u0004\b\b\u0010\tR\u000e\u0010\u0004\u001a\u00020\u0005X\u0082\u0004¢\u0006\u0002\n��¨\u0006\u000e"}, d2 = {"Lcom/jtaps/coreservice/devices/DeviceStatusImpl;", "Lcom/jtaps/coreservice/api/devices/DeviceStatus;", "coroutineContext", "Lkotlin/coroutines/CoroutineContext;", "infoProvider", "Lmil/jtaps/coreservice/devices/InternalDeviceInformationProvider;", "
If I set isEnabled.set(true)
@Metadata(mv = {2, 1, 0}, k = 1, xi = 48, d1 = {"��&\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n��\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0018\u0002\n\u0002\u0010 \n\u0002\u0018\u0002\n\u0002\b\u0006\b\u0001\u0018��2\u00020\u0001B\u0019\u0012\b\b\u0001\u0010\u0003\u001a\u00020\u0002\u0012\u0006\u0010\u0005\u001a\u00020\u0004¢\u0006\u0004\b\u0006\u0010\u0007J\u001b\u0010\u000b\u001a\u000e\u0012\n\u0012\b\u0012\u0004\u0012\u00020\n0\t0\bH\u0016¢\u0006\u0004\b\u000b\u0010\fR\u001a\u0010\u0003\u001a\u00020\u00028\u0016X\u0096\u0004¢\u0006\f\n\u0004\b\u0003\u0010\r\u001a\u0004\b\u000e\u0010\u000fR\u0014\u0010\u0005\u001a\u00020\u00048\u0002X\u0082\u0004¢\u0006\u0006\n\u0004\b\u0005\u0010\u0010"}, d2 = {"Lcom/jtaps/coreservice/devices/DeviceStatusImpl;", "Lcom/jtaps/coreservice/api/devices/DeviceStatus;", "Lkotlin/coroutines/CoroutineContext;", "coroutineContext", "Lcom/jtaps/coreservice/devices/InternalDeviceInformationProvider;", "infoProvider", "
The above tag causes the code to crash. I have looked at the class implementations and they are exactly identical. I have tried everything I can find in the documentation to not alter those tags.
# ProGuard rules
# Based on requirements: Kotlin Metadata, Kotlin Serialization, Kotlin RPC (Reflection), Logback, keep com.jtaps.coreservice
-dontshrink
-dontoptimize
-dontobfuscate
-printmapping mapping.txt
#-------------------------------------------------------------------------------
# General Kotlin Rules
#-------------------------------------------------------------------------------
# Keep essential Kotlin attributes
#-keepattributes Signature,InnerClasses,RuntimeVisibleAnnotations,AnnotationDefault,EnclosingMethod,RuntimeInvisibleAnnotations,RuntimeVisibleParameterAnnotations,RuntimeInvisibleParameterAnnotations,RuntimeVisibleTypeAnnotations,RuntimeInvisibleTypeAnnotations,*Annotation*,RuntimeVisibleAnnotations,SourceFile,SourceDir,Record,Synthetic,MethodParameters,Signature
-keepattributes *
# Keep Kotlin stdlib classes potentially used via reflection
-keep class kotlin.reflect.** { *; }
-keep class kotlin.jvm.internal.** { *; }
-keep class kotlin.Metadata
# Keep Kotlin Metadata annotation, crucial for reflection and frameworks
-keep class kotlin.Metadata { *; }
# Keep classes annotated with @Keep, a common practice
-keep class kotlin.annotation.Keep
# Keep suspend functions and continuations for Coroutines
-keep class kotlin.jvm.internal.** { *; }
-keep class kotlin.coroutines.** { *; }
-keep class kotlinx.coroutines.** { *; }
-keepclasseswithmembers class * { @kotlin.coroutines.jvm.internal.DebugMetadata <fields>; }
-keepclassmembers class * implements kotlin.coroutines.Continuation {
<fields>;
<methods>;
}
# Keep companion objects if needed (e.g., for static-like access or serialization)
-keepclassmembers class * {
** Companion;
}
# Keep default constructors for data classes if needed by reflection/serialization frameworks
-keepclassmembers class * extends kotlin.jvm.internal.markers.KMappedMarker {
public <init>(...);
}
# Keep members annotated with JvmField/JvmStatic if reflection is used
-keepclassmembers,allowobfuscation class * {
@kotlin.jvm.JvmStatic <methods>;
}
-keepclassmembers class * {
@kotlin.jvm.JvmField <fields>;
}
#-------------------------------------------------------------------------------
# Kotlinx Serialization Rules
#-------------------------------------------------------------------------------
# Keep classes annotated with @Serializable and related annotations
-keep @kotlinx.serialization.Serializable class * { *; }
-keepclassmembers class * {
@kotlinx.serialization.SerialName <fields>;
@kotlinx.serialization.Required <fields>;
@kotlinx.serialization.Transient <fields>;
@kotlinx.serialization.Contextual <fields>;
@kotlinx.serialization.Polymorphic <fields>;
}
-keepclassmembers enum * {
@kotlinx.serialization.SerialName <fields>;
}
# Keep generated serializers
-keep,includedescriptorclasses class kotlinx.serialization.internal.*Serializer { *; }
-keepclassmembers class **.*$*Serializer {
<init>(...);
*** Companion;
*** INSTANCE;
}
-keep class **.*$*Serializer { *; }
# Ignore warnings about internal SerializationKt if they occur
-dontnote kotlinx.serialization.SerializationKt
#-------------------------------------------------------------------------------
# Logback / SLF4J Rules
#-------------------------------------------------------------------------------
# Keep Logback and SLF4J core classes
-keep class ch.qos.logback.** { *; }
-keep class org.slf4j.** { *; }
-keep class net.tactware.link16.** { *; }
# Keep specific members needed by Logback/SLF4J
-keepclassmembers class ch.qos.logback.** { *; }
-keepclassmembers class org.slf4j.** { *; }
# Keep native methods if any (safety measure)
-keepclasseswithmembernames class * {
native <methods>;
}
#-------------------------------------------------------------------------------
# Keep Specific Package: com.jtaps.coreservice
#-------------------------------------------------------------------------------
# Keep all classes, interfaces, enums, annotations and their members in the specified package
-keep class com.jtaps.coreservice.** { *; }
-keep interface com.jtaps.coreservice.** { *; }
-keep enum com.jtaps.coreservice.** { *; }
-keep @interface com.jtaps.coreservice.** { *; }
-keepclassmembers class com.jtaps.coreservice.** { *; }
-keep class com.jtaps.exampleapp.**
-keepclassmembers interface com.jtaps.coreservice.** { *; }
-keepclassmembers enum com.jtaps.coreservice.** { *; }
-keepclassmembers @interface com.jtaps.coreservice.** { *; }
-keep @kotlinx.rpc.annotations.Rpc interface com.jtaps.coreservice.** { *; }
#-------------------------------------------------------------------------------
# Koin Rules (Based on libs.toml)
#-------------------------------------------------------------------------------
-dontwarn kotlinx.coroutines.debug.*
-keep class app.cash.sqldelight.** { *; }
-keep class ch.qos.logback.** { *; }
-keep class com.google.code.gson.** { *; }
-keep class com.google.gson.** { *; }
-keep class org.json.** { *; }
-keep class org.lwjgl.** { *; }
-keep class org.objenesis.** { *; }
-keep class okio.** { *; }
-keep class uk.co.caprica.** { *; }
-keep class kotlinx.rpc.** { *; }
# Keep them completely (no shrinking, no obfuscation, no optimisation)
-keep class **$$RpcServiceStub { *; }
-keep class **$$RpcServiceImpl { *; }
-keep class **$$RpcMethod* { *; }
# If your generator uses the lowercase pattern add these too
-keep class **$$rpcServiceStub { *; }
-keep class **$$rpcServiceImpl { *; }
-keep class **$$rpcMethod* { *; }
-if @kotlinx.rpc.annotations.Rpc interface **
-keep class <1>$$RpcServiceStub { *; }
-keep class org.jetbrains.** { *; }
-keep class androidx.** { *; }
-keep class androidx.datastore.preferences.** { *; }
-keep class javax.sql.** { *; }
-keep class org.sqlite.** { *; }
-keep class java.sql.** { *; }
-keep public class org.slf4j.** { *; }
-keep public class ch.** { *; }
# Keep javax.mail classes
-keep class javax.mail.** { *; }
-keep class javax.naming.** { *; }
-keep class javax.servlet.** { *; }
# Keep classes that could use javax.mail
-keep class my.package.using.mail.** { *; }
-keep class org.apache.commons.logging.** { *; }
-keep class org.apache.logging.** { *; }
-keep class org.openxmlformats.schemas.** { *; }
-keep class org.apache.log4j.** { *; }
-keep class org.apache.log.** { *; }
-keep class org.apache.poi.** { *; }
-keep class org.apache.xmlbeans.** { *; }
-keep class io.netty.** { *; }
-keep class io.ktor.** { *; }
-keep class net.codecrete.** { *; }
-keep class org.koin.** { *; }
-keep class io.insertkoin.** { *; }
-keep class * implements org.koin.core.module.Module { *; }
-keep class * implements org.koin.core.component.KoinComponent { *; }
-keepclassmembers class * { @org.koin.core.annotation.Module <methods>; }
-keepclassmembers class * { @org.koin.core.annotation.Single <methods>; }
-keepclassmembers class * { @org.koin.core.annotation.Factory <methods>; }
-keepclassmembers class * { @org.koin.core.annotation.Scope <methods>; }
-keepclassmembers class * { @org.koin.core.annotation.Scoped <methods>; }
-keepclassmembers class * { @org.koin.android.annotation.ViewModel <methods>; }
-keepclassmembers class * { @org.koin.androidx.compose.inject.ViewModelComposeExtKt <methods>; }
-keepclassmembers class * { @org.koin.androidx.compose.getViewModel <methods>; }
#-------------------------------------------------------------------------------
# SQLDelight Rules (Based on libs.toml)
#-------------------------------------------------------------------------------
-keep class app.cash.sqldelight.** { *; }
-keep interface app.cash.sqldelight.** { *; }
-keep class * implements app.cash.sqldelight.db.SqlDriver { *; }
-keep class * implements app.cash.sqldelight.db.SqlCursor { *; }
-keep class * implements app.cash.sqldelight.db.SqlPreparedStatement { *; }
-keep class * implements app.cash.sqldelight.ColumnAdapter { *; }
# Keep classes used by adapters
-keepclassmembers class * {
public <init>(app.cash.sqldelight.db.SqlCursor);
}
# Keep generated query implementations
-keep class **.**QueriesImpl { *; }
-keep class **.**Impl { *; } # For generated database implementations
# Preserve specific logging classes and constructors
-keepclassmembers class org.apache.logging.log4j.* {
public <init>(...);
}
#-keep class org.apache.logging.log4j.LogManager
-keepclassmembers class org.apache.poi.** {
*;
public <init>(...);
}
#-------------------------------------------------------------------------------
# Suppress Warnings (Use with caution - ideally fix underlying issues)
#-------------------------------------------------------------------------------
# Suppress warnings about duplicate class definitions found in logs.
# This is often due to dependency conflicts and should ideally be resolved in Gradle.
-dontwarn org.koin.ksp.generated.**
-dontwarn com.jtaps.coreservice.network.StatusChangeNotifier
-dontwarn module-info
# Note: ProGuard doesn't have a specific -dontwarn for duplicate *resource* files (like MANIFEST.MF).
# These notes are informational and usually don't stop the build, but indicate packaging issues.
-dontwarn io.netty.internal.tcnative.AsyncSSLPrivateKeyMethod
-dontwarn io.netty.internal.tcnative.AsyncTask
-dontwarn io.netty.internal.tcnative.Buffer
-dontwarn io.netty.internal.tcnative.CertificateCallback
-dontwarn io.netty.internal.tcnative.CertificateCompressionAlgo
-dontwarn io.netty.internal.tcnative.CertificateVerifier
-dontwarn io.netty.internal.tcnative.Library
-dontwarn io.netty.internal.tcnative.SSL
-dontwarn io.netty.internal.tcnative.SSLContext
-dontwarn io.netty.internal.tcnative.SSLPrivateKeyMethod
-dontwarn io.netty.internal.tcnative.SSLSessionCache
-dontwarn io.netty.internal.tcnative.SessionTicketKey
-dontwarn io.netty.internal.tcnative.SniHostNameMatcher
-dontwarn java.lang.management.ManagementFactory
-dontwarn java.lang.management.RuntimeMXBean
-dontwarn org.apache.log4j.Level
-dontwarn org.apache.log4j.Logger
-dontwarn org.apache.log4j.Priority
-dontwarn org.apache.logging.log4j.Level
-dontwarn org.apache.logging.log4j.LogManager
-dontwarn org.apache.logging.log4j.Logger
-dontwarn org.apache.logging.log4j.message.MessageFactory
-dontwarn org.apache.logging.log4j.spi.ExtendedLogger
-dontwarn org.apache.logging.log4j.spi.ExtendedLoggerWrapper
-dontwarn org.bouncycastle.asn1.pkcs.PrivateKeyInfo
-dontwarn org.bouncycastle.openssl.PEMDecryptorProvider
-dontwarn org.bouncycastle.openssl.PEMEncryptedKeyPair
-dontwarn org.bouncycastle.openssl.PEMKeyPair
-dontwarn org.bouncycastle.openssl.PEMParser
-dontwarn org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter
-dontwarn org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder
-dontwarn org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder
-dontwarn org.bouncycastle.operator.InputDecryptorProvider
-dontwarn org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo
-dontwarn org.conscrypt.BufferAllocator
-dontwarn org.conscrypt.Conscrypt
-dontwarn org.conscrypt.HandshakeListener
-dontwarn org.eclipse.jetty.npn.NextProtoNego$ClientProvider
-dontwarn org.eclipse.jetty.npn.NextProtoNego$Provider
-dontwarn org.eclipse.jetty.npn.NextProtoNego$ServerProvider
-dontwarn org.eclipse.jetty.npn.NextProtoNego
-dontwarn io.netty.**
-dontwarn io.ktor.**
-dontwarn net.codecrete.**
-dontwarn androidx.compose.ui.scene.ComposeSceneMediator$DesktopSemanticsOwnerListener
-dontwarn kotlin.jvm.internal.**
-dontwarn ch.qos.logback.**
-dontwarn org.lwjgl.**
-dontwarn com.boss.transport.**
-dontnote com.jtaps.sdk.**
-dontnote *MANIFEST.MF*
-dontwarn net.tactware.link16.**
-dontwarn com.fazecast.jSerialComm.**
-dontwarn kotlin.concurrent.atomics.**
-dontwarn io.github.oshai.kotlinlogging.**
Hi, could you explain what is the crash you are seeing? It would also help if you could provide a minimally reproducing sample.
@piazzesiNiccolo-GS I have created a sample project for you that uses Compose Desktop as well as Android, so you can pick your deployment, but it is more set up for desktop (It doesn't have the keystore file for Android). If you run the gradle task packageUberJarForCurrentOS versus packageReleaseUberJarForCurrentOS (in the exampleapp module). You will be able to see the issue. It cannot find the service descriptor which is in the meta data.
https://github.com/kmbisset89/examplerpcproguard
Hi, thanks for the sample! It really helps investigating this. I managed to get the app working by additionally adding -dontprocesskotlinmetadata, which restores the app behaviour.
I was also able to remove -dontshrink and -dontobfuscate with the app still functioning.
I also tried to remove dontoptimize but encountered this processing error:
Can't find common super class of [io.netty.util.internal.logging.Log4J2Logger] (with 1 known super classes: io.netty.util.internal.logging.Log4J2Logger and 1 unknown classes: org.apache.logging.log4j.spi.ExtendedLoggerWrapper) and [io.netty.util.internal.logging.JdkLogger] (with 3 known super classes: java.lang.Object, io.netty.util.internal.logging.AbstractInternalLogger, io.netty.util.internal.logging.JdkLogger)^[[0m
Variables: [P0:Lio/netty/util/internal/logging/Log4J2LoggerFactory;=!][P1:Ljava/lang/String;=]
Stack: [3:0:Lio/netty/util/internal/logging/Log4J2Logger;=!]
I was able to fix this by adding a -libraryjars rule for the log4j api jar:
-libraryjars log4j-api-2.24.2.jar
I suggest you try these workarounds on the actual project, let us know if they help prevent the issue.
@piazzesiNiccolo-GS thank you so much I wasn't able to get -libraryjars log4j-api-2.24.2.jar to work yet, but you solved my major issue.
@piazzesiNiccolo-GS I am having trouble again incorporating this fix into the larger project we have. It seems to a problem with synthetics.
Caused by: java.lang.ExceptionInInitializerError: Exception kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Unresolved class: class mil.jtaps.coreservice.api.devices.DeviceStatus$$rpcServiceStub$getAttachedDevices$rpcMethod (kind = CLASS) [in thread "eventLoopGroupProxy-3-1"] at kotlin.reflect.jvm.internal.KClassImpl.createSyntheticClassOrFail(KClassImpl.kt:339)