While trying to figure out how to use Compose for Desktop with proguard (and conveyor) I got some interesting stats:
I used an empty template app from https://kmp.jetbrains.com
I'll be using an apple silicon mac as my benchmark for app sizes.
./gradlew packageDmg
64.9MB for .dmg
124.1MB for .app (what you get when you open .dmg)
This doesn't use conveyor, but we can use it as a baseline.
./gradlew packageReleaseDmg
51.9MB for .dmg
109.9MB for .app (what you get when you open .dmg)
This is all we need to run proguard apparently as any release
task will have proguard enabled. See: https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-native-distribution.html#minification-and-obfuscation
-
./gradlew packageReleaseDmg
but withobfuscate.set(true)
48MB for .dmg
105.8MB for .app (what you get when you open .dmg)
-
./gradlew desktopJar && conveyor make site
A regular unproguarded app with conveyor
71.5MB example-1.0.0-mac-aarch64.zip
120.9MB Example.app
At this point I have a minimal conveyor.conf
include "#!./gradlew -q printConveyorConfig"
app {
display-name = "SampleProject"
site.base-url = "localhost:3000"
}
conveyor.compatibility-level = 17
-
./gradlew desktopJar && conveyor make site
A regular unproguarded app with conveyor but enables "native library extraction" (I still don't really know what it is, but it seems like it's the recommended way to distribute apps with conveyor, even though it's off by default) See: https://github.com/hydraulic-software/compose-multiplatform-starter/blob/master/conveyor.conf#L5-L10
62.3MB example-1.0.0-mac-aarch64.zip
123.8MB Example.app
My conveyor.conf
include "#!./gradlew -q printConveyorConfig"
include required("https://raw.githubusercontent.com/hydraulic-software/conveyor/master/configs/jvm/extract-native-libraries.conf")
app {
display-name = "SampleProject"
site.base-url = "localhost:3000"
}
conveyor.compatibility-level = 17
Surprisingly small zip!
-
./gradlew proguardReleaseJars && conveyor make site
Proguarded app with conveyor
58.6MB example-1.0.0-mac-aarch64.zip
106.3MB Example.app
My conveyor.conf gets a little tricky. You can figure out the paths at the bottom by running ./gradlew printConveyorConfig
See: https://conveyor.hydraulic.dev/17.0/configs/jvm/#proguard-obfuscation
include "#!./gradlew -q printConveyorConfig"
gradle-cache = ${env.HOME}/.gradle # Note: UNIX specific config!
app {
display-name = "SampleProject"
site.base-url = "localhost:3000"
# Import all the obfuscated JARs, except the JAR that contains the platform native graphics code.
inputs = [{
from = composeApp/build/compose/tmp/main-release/proguard
remap = [
"**"
"-skiko-awt-runtime-*.jar"
]
}]
# Put the dropped JAR back with the right version for each platform.
linux.amd64.inputs = ${app.inputs} [ ${gradle-cache}/caches/modules-2/files-2.1/org.jetbrains.skiko/skiko-awt-runtime-linux-x64/0.9.3/e6fa1645020a9ed8ca2c1058f541dfeff8a9df6/skiko-awt-runtime-linux-x64-0.9.3.jar ]
mac.aarch64.inputs = ${app.inputs} [ ${gradle-cache}/caches/modules-2/files-2.1/org.jetbrains.skiko/skiko-awt-runtime-macos-arm64/0.9.3/9fb269f5829942f307e14d0f9bafdd30c886cabc/skiko-awt-runtime-macos-arm64-0.9.3.jar ]
mac.amd64.inputs = ${app.inputs} [ ${gradle-cache}/caches/modules-2/files-2.1/org.jetbrains.skiko/skiko-awt-runtime-macos-x64/0.9.3/33b53df6426efa34461268b1005cb8c9ceaffce1/skiko-awt-runtime-macos-x64-0.9.3.jar ]
windows.amd64.inputs = ${app.inputs} [ ${gradle-cache}/caches/modules-2/files-2.1/org.jetbrains.skiko/skiko-awt-runtime-windows-x64/0.9.3/89198469fcb0543ccd8e09a0cfbe0334d4a1f6dd/skiko-awt-runtime-windows-x64-0.9.3.jar ]
}
conveyor.compatibility-level = 17
-
./gradlew proguardReleaseJars && conveyor make site
Proguarded app with conveyor with "native library extraction"
49.4MB example-1.0.0-mac-aarch64.zip
109.2MB Example.app
Same conveyor.conf as above, but adds
include required("https://raw.githubusercontent.com/hydraulic-software/conveyor/master/configs/jvm/extract-native-libraries.conf")
.zip format seems to really like when you enable native library extraction
-
./gradlew proguardReleaseJars && conveyor make site
Proguarded app with conveyor withobfuscate.set(true)
54.6MB example-1.0.0-mac-aarch64.zip
102.3MB Example.app
Same conveyor.conf as above, but now we remove
include required("https://raw.githubusercontent.com/hydraulic-software/conveyor/master/configs/jvm/extract-native-libraries.conf")
-
./gradlew proguardReleaseJars && conveyor make site
Proguarded app with conveyor with "native library extraction" withobfuscate.set(true)
45.5MB example-1.0.0-mac-aarch64.zip
105.2MB Example.app
Same conveyor.conf as above, but adds
include required("https://raw.githubusercontent.com/hydraulic-software/conveyor/master/configs/jvm/extract-native-libraries.conf")
Short n' Sweet
-
./gradlew packageDmg
No conveyor.
64.9MB for .dmg
124.1MB for .app (what you get when you open .dmg)
-
./gradlew packageReleaseDmg
No conveyor, but runs proguard.
51.9MB for .dmg
109.9MB for .app (what you get when you open .dmg)
-
./gradlew packageReleaseDmg
No conveyor, but runs proguard withobfuscate.set(true)
48MB for .dmg
105.8MB for .app (what you get when you open .dmg)
-
./gradlew desktopJar && conveyor make site
A regular unproguarded app with conveyor
71.5MB example-1.0.0-mac-aarch64.zip
120.9MB Example.app
-
./gradlew desktopJar && conveyor make site
A regular unproguarded app with conveyor + conveyor's "native library extraction"
62.3MB example-1.0.0-mac-aarch64.zip
123.8MB Example.app
-
./gradlew proguardReleaseJars && conveyor make site
Proguarded app with conveyor
58.6MB example-1.0.0-mac-aarch64.zip
106.3MB Example.app
-
./gradlew proguardReleaseJars && conveyor make site
Proguarded app with conveyor with conveyor's "native library extraction"
49.4MB example-1.0.0-mac-aarch64.zip
109.2MB Example.app
-
./gradlew proguardReleaseJars && conveyor make site
Proguarded and obfuscated app with conveyor
54.6MB example-1.0.0-mac-aarch64.zip
102.3MB Example.app
-
./gradlew proguardReleaseJars && conveyor make site
Proguarded and obfuscated app with conveyor with conveyor's "native library extraction"
45.5MB example-1.0.0-mac-aarch64.zip
105.2MB Example.app
The winner!
I care about initial download size. An app built with conveyor + conveyor's "native library extraction" + proguard + obfuscation (arguably the combo that everyone should aim for) comes in at the smallest download size of 45.5MB. And that contains extra code in there for sparkle (the update framework) that you don't get with the typical gradlew package*
tasks (and its also supposed to help with small delta updates). Way cool!