feat(android): dex the compose runtime into compose-enabled apks - #14
Merged
Conversation
The host injects the compose BOM, runtime/ui/material3, and the compose compiler plugin jar, and the plugin already passed the compiler jar to kotlinc via -Xplugin. But the injected composeRuntimes classpath bucket (the unwrapped AAR classes.jar) was never consumed, so the apk shipped the module's own composables without the Compose types they call into. Feed that bucket to d8 alongside kotlin-stdlib whenever a variant compiles Kotlin, and add the compose end-to-end scenario to the android-build CI job: it compiles a @composable source against the unwrapped runtime and asserts the dex holds both the module's GreetingKt and a runtime implementation class that generated code never names, so it can only be present when the runtime classes were actually dexed. Documents the compose key and the composeRuntimes bucket.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Team Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
compose-runtime 1.12 replaced the ComposerImpl implementation class with the GapComposer/LinkComposer recomposition engine, so the dex assertion chased a class that no longer exists. Assert the GapComposer class instead: it is runtime-internal machinery the generated @composable code never names, so its presence still proves the unwrapped runtime classes.jar were dexed.
- extra_d8_jars doc no longer claims the host resolves composeRuntimes specifically for d8; the bucket is a side-effect of compose resolution and AAR unwrapping, and the plugin dexes those classes.jar files. - stdlib selection for d8 skips the kotlin-stdlib-jdk8 bridge jar so the core stdlib types are always dexed, not the thin jdk8 facade; pinned with a unit test. - the compose dex assertions extract strings to a file before grep -q; a head-of-pipe grep -q closes its read end on the first match and the producer then SIGPIPEs the pipeline to a false 141 under pipefail. - PROGRESS.md records the kmp plugin's identical composeRuntimes gap, the non-transitive compose bundle's reliance on d8's missing-class tolerance, and the pre-existing kotlin Main; assertion still carrying the pipefail flake.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
classpath.composeRuntimesbucket (the unwrappedclasses.jarofandroidx.compose.runtime/ui/material3): when a variant compiles Kotlin andcompose = true, those jars are fed to d8 alongsidekotlin-stdlib.jar, so the APK ships the Compose types the@Composablelowering emits — not just the module's own composables. Prior to this, the plugin passed the compiler jar to kotlinc via-Xpluginbut never dexed the runtime, so a compose apk was missing the runtime classes.android-buildCI job gains a compose end-to-end scenario: a module withcompose = trueand a@Composablesource compiles through the full packaging chain against real SDK + real injected androidx artifacts, and the dex assertion requires bothLcom/example/ulite/GreetingKt;andLandroidx/compose/runtime/ComposerImpl;— the latter is a runtime implementation class the generated code never names, so its presence proves the unwrapped runtime classes.jar were actually dexed.extra_d8_jars_bundles_compose_runtimes_when_the_variant_compiles_kotlin/extra_d8_jars_omits_compose_runtimes_when_compose_is_off_or_no_kotlin; docs/android-plugin.md documents thecomposekey andclasspath.composeRuntimes.Details
The compose BOM, runtime/UI/material3 (Android AARs), and the compose compiler plugin jar are host-injected (Uliab driver
compose_deps), with AAR unwrapping via the-androidvariant substitution (Uliab PR #45). The E2E validates that chain on real Google-Maven artifacts for the first time (compose-bom:2026.08.00, compiler version matched to the runner kotlinc).Checklist
cargo build --workspacecleancargo clippy --workspace --all-features -- -D warningscleancargo fmt --all --checkcleancargo test --workspace: 98 tests pass (55 android incl. 2 new, 19 jvm, 24 kmp)android-buildRequires Uliab
mainto carry the-androidvariant substitution (merged, 39d9ead).