Ahmed Riyadh
05/18/2024, 2:03 PMAhmed Riyadh
05/18/2024, 2:04 PMbuildscript {
repositories { mavenCentral() }
dependencies {
classpath("com.guardsquare:proguard-gradle:7.4.2") {
exclude("com.android.tools.build")
}
}
}
tasks.register<proguard.gradle.ProGuardTask>("proguard") {
configuration(file("<http://proguard-rules.pro|proguard-rules.pro>"))
injars(tasks.shadowJar.flatMap { it.archiveFile })
outjars(
layout.buildDirectory.file("libs/${tasks.shadowJar.get().archiveFile.get().asFile.nameWithoutExtension}-minified.jar"),
)
verbose()
}
Using shadow jar to build the jar since if you use any import from Kotlin or from dependencies you will get error ClassNotFoundException
And using Proguard to make minimal jar size, if <http://proguard-rules.pro|proguard-rules.pro>
is empty then it will say it need the steps for what to keep, but I didn't really understand how it work, is Proguard automated tool and shouldn't be the steps of defining what to keep is optional?
Even after include some rules, it says please solve all the warrnings but it didn't print any, so I have to use ./gradlew proguard --info
but that show too much information (searching online, most people suggest to use -ignorewarnings
) and even after fixing them, I still get another error:
* What went wrong:
Execution failed for task ':proguard'.
>
It appears you are missing some classes resulting in an incomplete class hierarchy,
please refer to the troubleshooting page in the manual:
<https://www.guardsquare.com/en/products/proguard/manual/troubleshooting#superclass>
Using the link doesn't provide any info about that kind of error.
I definitely missed something, because on Compose Desktop and Android you usually don't need to touch the file and most of the time you don't even know it's exist, it's already configured and even when adding new libraries it will work without changing anything, let's say I fixed all of this, do I need to change the rules when adding libraries? I don't use reflections and all of my code are in type-safe way
There are other tools like [shakyboi](https://github.com/badlogic/shakyboi) which work perfectly like expected (from 1.6MB to about kilobytes for a project only use println
) but it's no longer maintained and doesn't work when you have more complex project, most tutorials online cover how to setup the plugin not the rules of proguard file, I would like to know more about proguard and use it like it should
Because even on blank Hello World Kotlin/JVM project with zero imports, if you shadow the jar with minimize()
option it usually around (1.6MB)Ahmed Riyadh
05/18/2024, 2:07 PMAhmed Riyadh
05/18/2024, 2:32 PMapplication-kotlin
example
but it didn't work directly, I had to use
-ignorewarnings
Chrimaeon
05/18/2024, 6:55 PMAhmed Riyadh
05/18/2024, 7:01 PM1.4
or 1.3
Compose desktop Gradle Plugin but I want to use it in Kotlin/JVM project that doesn't use Android or Compose Desktop or any framework that already has Proguard setup, only Kotlin/JVM code (with a few libraries)
But maybe checking the Progurad rules of Compose desktop in the source code might help
I managed to get it working in a minimal setup by using the application-kotlin
example from Proguard source code, but I had to use -ignorewarnings
and still another issue happen when using libraries like OkHttp, Kotlinx Coroutines and SerializationChrimaeon
05/18/2024, 7:03 PMChrimaeon
05/18/2024, 7:04 PMAhmed Riyadh
05/18/2024, 7:56 PMAhmed Riyadh
05/18/2024, 8:16 PMIt appears you are missing some classes resulting in an incomplete class hierarchy
once include OkHttp library), but I couldn't find it, the link is to the Okio repository
I found this: https://github.com/square/okio/pull/508/files/1d484861d789409bf62e2f5d84fa53b0e7b143fd#diff-f3c2293d6d6d9905b925f8b3[…]2a44372e5278e2f01440ff48dca12
But it's quite outdated and it looks like it's removed from the repository and README.md
Ahmed Riyadh
05/24/2024, 4:33 AMval javaHome = System.getProperty("java.home")
if (System.getProperty("java.version").startsWith("1.")) {
// Before Java 9, runtime classes are packaged in a single JAR file.
libraryjars("$javaHome/lib/rt.jar")
} else {
// Starting from Java 9, runtime classes are packaged in modular JMOD files.
libraryjars(
mapOf("jarfilter" to "!**.jar", "filter" to "!module-info.class"),
"$javaHome/jmods/java.base.jmod",
)
}
printmapping(destinationDirectory.file("$originalJarNameWithoutExtension.map"))
// TODO: Workaround to fix `Please correct the above warnings first`
ignorewarnings()
dontobfuscate()
dontoptimize()
dontwarn()
configuration(file("<http://proguard.pro|proguard.pro>"))
The rules depends on your application and mainly libraries, for my use-case:
# Proguard Kotlin Example <https://github.com/Guardsquare/proguard/blob/master/examples/application-kotlin/proguard.pro>
-keepattributes *Annotation*
-keep class kotlin.Metadata { *; }
# Entry point to the app.
-keep class MainKt { *; }
# Kotlinx Serialization <https://github.com/Kotlin/kotlinx.serialization/blob/master/rules/common.pro>
# Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
static <1>$Companion Companion;
}
# Keep `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(...);
}
# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
public static ** INSTANCE;
}
-keepclassmembers class <1> {
public static <1> INSTANCE;
kotlinx.serialization.KSerializer serializer(...);
}
# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
# Don't print notes about potential mistakes or omissions in the configuration for kotlinx-serialization classes
# See also <https://github.com/Kotlin/kotlinx.serialization/issues/1900>
-dontnote kotlinx.serialization.**
# Serialization core uses `java.lang.ClassValue` for caching inside these specified classes.
# If there is no `java.lang.ClassValue` (for example, in Android), then R8/ProGuard will print a warning.
# However, since in this case they will not be used, we can disable these warnings
-dontwarn kotlinx.serialization.internal.ClassValueReferences
# Kotlinx Coroutines <https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-core/jvm/resources/META-INF/proguard/coroutines.pro>
# ServiceLoader support
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
# Most of volatile fields are updated with AFU and should not be mangled
-keepclassmembers class kotlinx.coroutines.** {
volatile <fields>;
}
# Same story for the standard library's SafeContinuation that also uses AtomicReferenceFieldUpdater
-keepclassmembers class kotlin.coroutines.SafeContinuation {
volatile <fields>;
}
# These classes are only required by kotlinx.coroutines.debug.AgentPremain, which is only loaded when
# kotlinx-coroutines-core is used as a Java agent, so these are not needed in contexts where ProGuard is used.
-dontwarn java.lang.instrument.ClassFileTransformer
-dontwarn sun.misc.SignalHandler
-dontwarn java.lang.instrument.Instrumentation
-dontwarn sun.misc.Signal
# Only used in `kotlinx.coroutines.internal.ExceptionsConstructor`.
# The case when it is not available is hidden in a `try`-`catch`, as well as a check for Android.
-dontwarn java.lang.ClassValue
# An annotation used for build tooling, won't be directly accessed.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
# OkHttp <https://square.github.io/okhttp/features/r8_proguard/>
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**
# A resource is loaded with a relative path so the package of this class must be preserved.
-keeppackagenames okhttp3.internal.publicsuffix.*
-adaptresourcefilenames okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*
# OkHttp platform used only on JVM and when Conscrypt and other security providers are available.
-dontwarn okhttp3.internal.platform.**
-dontwarn org.conscrypt.**
-dontwarn org.bouncycastle.**
-dontwarn org.openjsse.**
# Okio (missing)
# FlatLaf
# Exclude the entire FlatLaf dependency from minimization to fix `no ComponentUI class for: javax.swing.<component>`
# Due to reflections, more details: <https://github.com/JFormDesigner/FlatLaf/issues/648#issuecomment-1441547550>
-keep class com.formdev.flatlaf.** { *; }