Skip to content

Commit 0cf8803

Browse files
feat(client): ensure compat with proguard
1 parent b2ad49e commit 0cf8803

6 files changed

Lines changed: 406 additions & 0 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,12 @@ both of which will raise an error if the signature is invalid.
362362
Note that the "body" parameter must be the raw JSON string sent from the server (do not parse it first).
363363
The `.unwrap()` method can parse this JSON for you.
364364

365+
## ProGuard and R8
366+
367+
Although the SDK uses reflection, it is still usable with [ProGuard](https://github.com/Guardsquare/proguard) and [R8](https://developer.android.com/topic/performance/app-optimization/enable-app-optimization) because `lithic-java-core` is published with a [configuration file](lithic-java-core/src/main/resources/META-INF/proguard/lithic-java-core.pro) containing [keep rules](https://www.guardsquare.com/manual/configuration/usage).
368+
369+
ProGuard and R8 should automatically detect and use the published rules, but you can also manually copy the keep rules if necessary.
370+
365371
## Jackson
366372

367373
The SDK depends on [Jackson](https://github.com/FasterXML/jackson) for JSON serialization/deserialization. It is compatible with version 2.13.4 or higher, but depends on version 2.18.2 by default.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Jackson uses reflection and depends heavily on runtime attributes.
2+
-keepattributes
3+
4+
# Jackson uses Kotlin reflection utilities, which themselves use reflection to access things.
5+
-keep class kotlin.reflect.** { *; }
6+
-keep class kotlin.Metadata { *; }
7+
8+
# Jackson uses reflection to access enum members (e.g. via `java.lang.Class.getEnumConstants()`).
9+
-keepclassmembers class com.fasterxml.jackson.** extends java.lang.Enum {
10+
<fields>;
11+
public static **[] values();
12+
public static ** valueOf(java.lang.String);
13+
}
14+
15+
# Jackson uses reflection to access annotation members.
16+
-keepclassmembers @interface com.fasterxml.jackson.annotation.** {
17+
*;
18+
}
19+
20+
# Jackson uses reflection to access the default constructors of serializers and deserializers.
21+
-keepclassmembers class * extends com.lithic.api.core.BaseSerializer {
22+
<init>();
23+
}
24+
-keepclassmembers class * extends com.lithic.api.core.BaseDeserializer {
25+
<init>();
26+
}
27+
28+
# Jackson uses reflection to serialize and deserialize our classes based on their constructors and annotated members.
29+
-keepclassmembers class com.lithic.api.** {
30+
<init>(...);
31+
@com.fasterxml.jackson.annotation.* *;
32+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
plugins {
2+
id("lithic.kotlin")
3+
id("com.gradleup.shadow") version "8.3.8"
4+
}
5+
6+
buildscript {
7+
dependencies {
8+
classpath("com.guardsquare:proguard-gradle:7.4.2")
9+
}
10+
}
11+
12+
dependencies {
13+
testImplementation(project(":lithic-java"))
14+
testImplementation(kotlin("test"))
15+
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3")
16+
testImplementation("org.assertj:assertj-core:3.25.3")
17+
testImplementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.4")
18+
testImplementation("org.junit.platform:junit-platform-console:1.10.1")
19+
}
20+
21+
tasks.shadowJar {
22+
from(sourceSets.test.get().output)
23+
configurations = listOf(project.configurations.testRuntimeClasspath.get())
24+
}
25+
26+
val proguardJarPath = "${layout.buildDirectory.get()}/libs/${project.name}-${project.version}-proguard.jar"
27+
val proguardJar by tasks.registering(proguard.gradle.ProGuardTask::class) {
28+
group = "verification"
29+
dependsOn(tasks.shadowJar)
30+
notCompatibleWithConfigurationCache("ProGuard")
31+
32+
injars(tasks.shadowJar)
33+
outjars(proguardJarPath)
34+
printmapping("${layout.buildDirectory.get()}/proguard-mapping.txt")
35+
36+
verbose()
37+
dontwarn()
38+
39+
val javaHome = System.getProperty("java.home")
40+
if (System.getProperty("java.version").startsWith("1.")) {
41+
// Before Java 9, the runtime classes were packaged in a single jar file.
42+
libraryjars("$javaHome/lib/rt.jar")
43+
} else {
44+
// As of Java 9, the runtime classes are packaged in modular jmod files.
45+
libraryjars(
46+
// Filters must be specified first, as a map.
47+
mapOf("jarfilter" to "!**.jar", "filter" to "!module-info.class"),
48+
"$javaHome/jmods/java.base.jmod"
49+
)
50+
}
51+
52+
configuration("./test.pro")
53+
configuration("../lithic-java-core/src/main/resources/META-INF/proguard/lithic-java-core.pro")
54+
}
55+
56+
val testProGuard by tasks.registering(JavaExec::class) {
57+
group = "verification"
58+
dependsOn(proguardJar)
59+
notCompatibleWithConfigurationCache("ProGuard")
60+
61+
mainClass.set("org.junit.platform.console.ConsoleLauncher")
62+
classpath = files(proguardJarPath)
63+
args = listOf(
64+
"--classpath", proguardJarPath,
65+
"--scan-classpath",
66+
"--details", "verbose",
67+
)
68+
}
69+
70+
tasks.test {
71+
dependsOn(testProGuard)
72+
// We defer to the tests run via the ProGuard JAR.
73+
enabled = false
74+
}

0 commit comments

Comments
 (0)