Reputation: 210
I would like to ask if you know a guide or if you know how to use Proguard with Kotlin and Gradle and would like to share your knowledge, I would really appreciate it. Already searched Stack Overflow, but couldn't find a single (answered) question about using Proguard + Kotlin JVM (not android!) + Gradle.
I only found guides on the Internet regarding this matter for android, but I'm not using Kotlin for android, I'm building a Java Plugin (in other words, JVM) with Kotlin and I would like to use Proguard to minify and obfuscate my code. Note that my project is using Gradle Shadow to shadow its dependencies into the final jar (these dependencies don't need to be obfuscated but can be minified, and definitively do need to exist in the obfuscated jar created by Proguard).
I would like to know all the steps, things like how to step up Gradle to automatically minify & obfuscate my code (with a custom task), how to remove Kotlin metadata from java compiled classes, common issues & solutions to that issues, and anything else that you think it can be useful to know, everything is helpful. Thank you very much.
Upvotes: 2
Views: 2931
Reputation: 567
https://github.com/Guardsquare/proguard/releases provide samples.
Kotlin application
import proguard.gradle.ProGuardTask
buildscript {
repositories {
mavenLocal()
mavenCentral()
google()
}
dependencies {
classpath 'com.guardsquare:proguard-gradle:7.3.1'
}
}
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.8.0'
id 'application'
}
group = 'org.example'
version = '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testImplementation 'org.jetbrains.kotlin:kotlin-test'
}
test {
useJUnitPlatform()
}
compileKotlin {
kotlinOptions.jvmTarget = '1.8'
}
compileTestKotlin {
kotlinOptions.jvmTarget = '1.8'
}
application {
mainClassName = 'AppKt'
}
ext.baseCoordinates = "${project.name}-${project.version}"
tasks.register('proguard', ProGuardTask) {
configuration file('proguard.pro')
injars(tasks.named('jar', Jar).flatMap { it.archiveFile })
// Automatically handle the Java version of this build.
if (System.getProperty('java.version').startsWith('1.')) {
// Before Java 9, the runtime classes were packaged in a single jar file.
libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
} else {
// As of Java 9, the runtime classes are packaged in modular jmod files.
libraryjars "${System.getProperty('java.home')}/jmods/java.base.jmod", jarfilter: '!**.jar', filter: '!module-info.class'
//libraryjars "${System.getProperty('java.home')}/jmods/....."
}
// This will include the Kotlin library jars
libraryjars sourceSets.main.compileClasspath
verbose
outjars(layout.buildDirectory.file("libs/${baseCoordinates}-minified.jar"))
}
Kotlin Kts
buildscript {
repositories {
mavenCentral()
google()
}
dependencies {
classpath("com.guardsquare:proguard-gradle:7.3.0")
}
}
plugins {
java
application
}
repositories {
mavenCentral()
}
dependencies {
testImplementation("junit:junit:4.12")
}
application {
mainClassName = "gradlekotlindsl.App"
}
tasks.withType<Jar> {
manifest {
attributes["Main-Class"] = application.mainClassName
}
}
tasks.register<proguard.gradle.ProGuardTask>("proguard") {
verbose()
// Alternatively put your config in a separate file
// configuration("config.pro")
// Use the jar task output as a input jar. This will automatically add the necessary task dependency.
injars(tasks.named("jar"))
outjars("build/proguard-obfuscated.jar")
val javaHome = System.getProperty("java.home")
// Automatically handle the Java version of this build.
if (System.getProperty("java.version").startsWith("1.")) {
// Before Java 9, the runtime classes were packaged in a single jar file.
libraryjars("$javaHome/lib/rt.jar")
} else {
// As of Java 9, the runtime classes are packaged in modular jmod files.
libraryjars(
// filters must be specified first, as a map
mapOf("jarfilter" to "!**.jar",
"filter" to "!module-info.class"),
"$javaHome/jmods/java.base.jmod"
)
}
allowaccessmodification()
repackageclasses("")
printmapping("build/proguard-mapping.txt")
keep("""class gradlekotlindsl.App {
public static void main(java.lang.String[]);
}
""")
}
Upvotes: 2
Reputation: 210
I got this working some time ago, refer to the Proguard manual to learn about the syntax of its configuration file. Also, for some reason, sometimes Proguard just says that the current obfuscated jar is "up-to-date" even when there's no jar, seems to be a bug that I could not find the steps to reproduce. If that happens to you, just change the Proguard version you're using and reload Gradle dependencies.
build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
// add this
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("com.guardsquare:proguard-gradle:7.1.1") {
exclude("com.android.tools.build")
}
}
}
plugins {
kotlin("jvm") version "1.5.30"
id("com.github.johnrengelman.shadow") version "7.0.0"
}
group = "com.yourgroup"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
maven("https://oss.sonatype.org/content/groups/public/")
}
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0")
// your dependencies here
}
tasks.test {
useJUnitPlatform()
}
// disables the normal jar task
tasks.jar { enabled = false }
// and enables shadowJar task
artifacts.archives(tasks.shadowJar)
tasks.shadowJar {
archiveFileName.set(rootProject.name + ".jar")
val dependencyPackage = "${rootProject.group}.dependencies.${rootProject.name.toLowerCase()}"
// your relocations here
exclude("ScopeJVMKt.class")
exclude("DebugProbesKt.bin")
exclude("META-INF/**")
}
tasks.register<proguard.gradle.ProGuardTask>("proguard") {
// here is where you configure your Proguard stuff, you can include libraries directly
// through here, or in the configuration file, I usually just use the configuration file
// to do everything (it can be any name and extension you want, just using .pro here cause
// that's what Android uses)
configuration("proguard-rules.pro")
}
// keep this if you want run proguard task automatically after building
tasks.build.get().finalizedBy(tasks.getByName("proguard"))
tasks.withType<JavaCompile> {
sourceCompatibility = "16"
targetCompatibility = "16"
options.encoding = "UTF-8"
}
tasks.withType<KotlinCompile> { kotlinOptions.jvmTarget = "16" }
proguard-rules.pro
: This is just an example file used to minify a Minecraft plugin, but the logic applies no matter what kind of jar you're dealing with.
# injars = your shadowed jar
-injars build/libs/YourShadowedJarHere.jar
# outjars = the name of the new obfuscated/minified jar
-outjars build/libs/YourShadowedJarHere-min.jar
# important! you need to add this "rt" file from your JDK as libraryjars!
-libraryjars "C:\Program Files\Java\jdk1.8.0_291\jre\lib\rt.jar"
# add here the jar to any compileOnly dependency you might have (or add "dontwarn" to make proguard ignore the missing classes)
-libraryjars "PathTo\YourProject\SomeLocalLibraries\SomeLocalLibrary.jar"
-dontwarn net.minecraft.**
-dontwarn org.bukkit.**
-dontwarn com.google.**
-dontwarn com.comphenix.**
-dontwarn android.**
-dontwarn org.hibernate.**
-dontwarn com.sk89q.worldedit**
-dontwarn com.sk89q.worldguard**
-dontwarn net.milkbowl.vault.economy**
-keep class yourpackage.dependencies.yourproject.hikari.metrics.**
-dontwarn com.codahale.metrics.**
-keep class com.codahale.metrics.**
-dontwarn **hikari.metrics**
-dontwarn javax.crypto.**
-dontwarn javassist.**
-dontwarn **slf4j**
-dontwarn io.micrometer.core.instrument.MeterRegistry
-dontwarn org.codehaus.mojo.**
-dontwarn **prometheus**
-dontwarn **configurate.**
-dontwarn **koin.core.time.**
-dontwarn net.Indyuce.**
-dontwarn **xseries.**
-keepnames class kotlin.coroutines.** { *; }
-dontwarn **kotlinx.coroutines.**
-dontwarn **org.apache.commons.codec**
#-dontshrink
#-dontobfuscate
#-dontoptimize
# Keep your main class
-keep,allowobfuscation,allowoptimization class * extends org.bukkit.plugin.java.JavaPlugin { *; }
# Keep event handlers
-keep,allowobfuscation,allowoptimization class * extends org.bukkit.event.Listener {
@org.bukkit.event.EventHandler <methods>;
}
# Keep main package name
// -keeppackagenames "your.package"
# Keep public enum names
-keepclassmembers public enum yourpackage.** {
<fields>;
public static **[] values();
public static ** valueOf(java.lang.String);
}
# Keep all ProtocolLib packet listeners (this was rough to get working, don't turn on optimization, it ALWAYS breaks the sensible ProtocolLib)
-keepclassmembers class yourpackage.yourproject.** {
void onPacketSending(com.comphenix.protocol.events.PacketEvent);
void onPacketReceiving(com.comphenix.protocol.events.PacketEvent);
}
# Keep static fields in custom Events
-keepclassmembers,allowoptimization class yourpackage.yourproject.** extends org.bukkit.event.Event {
@yourpackage.dependencies.kotlin.jvm.JvmStatic <fields>;
public static final <fields>;
@yourpackage.dependencies.kotlin.jvm.JvmStatic <methods>;
public static <methods>;
}
# Remove dependencies obsfuscation to remove bugs factor
#-keep,allowshrinking class yourpackage.dependencies.** { *; }
# If your goal is obfuscating and making things harder to read, repackage your classes with this rule
-repackageclasses yourpackage.yourproject
-allowaccessmodification
-mergeinterfacesaggressively
# You can parse any resource files that might contain a reference of your classes here (so they are updated according to the modifications made by Proguard)
-adaptresourcefilecontents **.yml,META-INF/MANIFEST.MF
# Some attributes that you'll need to keep (warning: removing *Annotation* might break some stuff)
-keepattributes Exceptions,Signature,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
#-keepattributes Exceptions,Signature,Deprecated,LineNumberTable,*Annotation*,EnclosingMethod
#-keepattributes LocalVariableTable,LocalVariableTypeTable,Exceptions,InnerClasses,Signature,Deprecated,LineNumberTable,*Annotation*,EnclosingMethod
Upvotes: 2