Skip to content

feat(android): dex the compose runtime into compose-enabled apks - #14

Merged
ulite-Amr merged 3 commits into
mainfrom
feat/compose-e2e
Sep 5, 2026
Merged

feat(android): dex the compose runtime into compose-enabled apks#14
ulite-Amr merged 3 commits into
mainfrom
feat/compose-e2e

Conversation

@ulite-Amr

Copy link
Copy Markdown
Contributor

What

  • The android-plugin now consumes the host-injected classpath.composeRuntimes bucket (the unwrapped classes.jar of androidx.compose.runtime/ui/material3): when a variant compiles Kotlin and compose = true, those jars are fed to d8 alongside kotlin-stdlib.jar, so the APK ships the Compose types the @Composable lowering emits — not just the module's own composables. Prior to this, the plugin passed the compiler jar to kotlinc via -Xplugin but never dexed the runtime, so a compose apk was missing the runtime classes.
  • The android-build CI job gains a compose end-to-end scenario: a module with compose = true and a @Composable source compiles through the full packaging chain against real SDK + real injected androidx artifacts, and the dex assertion requires both Lcom/example/ulite/GreetingKt; and Landroidx/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.
  • New unit tests 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 the compose key and classpath.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 -android variant 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 --workspace clean
  • cargo clippy --workspace --all-features -- -D warnings clean
  • cargo fmt --all --check clean
  • cargo test --workspace: 98 tests pass (55 android incl. 2 new, 19 jvm, 24 kmp)
  • CI compose E2E scenario added to android-build
  • CI green (pending this run)

Requires Uliab main to carry the -android variant substitution (merged, 39d9ead).

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.
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Team

Run ID: 9782f41f-6305-4c01-b7dc-8db18ef1345f


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.
@ulite-Amr
ulite-Amr merged commit 87a2b66 into main Sep 5, 2026
9 checks passed
@ulite-Amr
ulite-Amr deleted the feat/compose-e2e branch September 5, 2026 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant