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.

  1. ./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.

  1. ./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

  1. ./gradlew packageReleaseDmg but with obfuscate.set(true)

48MB for .dmg
105.8MB for .app (what you get when you open .dmg)

  1. ./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
  1. ./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!

  1. ./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
  1. ./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

  1. ./gradlew proguardReleaseJars && conveyor make site Proguarded app with conveyor with obfuscate.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")
  1. ./gradlew proguardReleaseJars && conveyor make site Proguarded app with conveyor with "native library extraction" with obfuscate.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

  1. ./gradlew packageDmg No conveyor.

64.9MB for .dmg
124.1MB for .app (what you get when you open .dmg)

  1. ./gradlew packageReleaseDmg No conveyor, but runs proguard.

51.9MB for .dmg
109.9MB for .app (what you get when you open .dmg)

  1. ./gradlew packageReleaseDmg No conveyor, but runs proguard with obfuscate.set(true)

48MB for .dmg
105.8MB for .app (what you get when you open .dmg)

  1. ./gradlew desktopJar && conveyor make site A regular unproguarded app with conveyor

71.5MB example-1.0.0-mac-aarch64.zip
120.9MB Example.app

  1. ./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

  1. ./gradlew proguardReleaseJars && conveyor make site Proguarded app with conveyor

58.6MB example-1.0.0-mac-aarch64.zip
106.3MB Example.app

  1. ./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

  1. ./gradlew proguardReleaseJars && conveyor make site Proguarded and obfuscated app with conveyor

54.6MB example-1.0.0-mac-aarch64.zip
102.3MB Example.app

  1. ./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!