bazelbuild / bazelbuild/rules_android
Support rules_kotlin's `kt_android_library` in aspect traversal
- Dominant language
- Java
- Stars
- 203
- Forks
- 95
- PR merge metrics
- No merged PRs in 30d
Description
## Problem
Several aspects in rules_android have hardcoded attribute traversal lists that predate `rules_kotlin`'s `kt_android_library` rule. They do not traverse `associates`, a `rules_kotlin`-specific attribute that serves as the sole dependency edge for Kotlin `internal` visibility across targets.
This causes two failures:
1. **`dex_desugar_aspect`**: Jars from associate targets are never dexed, producing "Dependencies on .jar artifacts are not allowed in Android binaries" errors.
2. **mobile-install default adapter**: Associate targets' dex shards are absent from the deployed APK, causing `NoClassDefFoundError` at runtime.
Both work fine with `bazel build` — only aspect-driven processing is affected.
## Background
For Kotlin Android targets, the conventional approach has been the "sandwich" pattern: a `kt_jvm_library` for Kotlin compilation wrapped by an `android_library` for Android resource merging. In this pattern, all targets are connected through `deps`, so aspects can discover them.
In https://github.com/bazel-contrib/rules_kotlin/pull/1333 `kt_android_library` collapses the sandwich into a single rule. As projects migrate to it, the dependency graph topology changes. Kotlin `internal` visibility across targets uses the `associates` attribute (implementing Kotlin's `-Xfriend-paths`), and `associates` is often the **only** edge connecting an implementation target to its wiring/DI target:
```starlark
# impl/BUILD.bazel
kt_android_library(
name = "impl",
srcs = ["FooImpl.kt"], # contains: internal class FooImpl
)
# wiring/BUILD.bazel
kt_android_library(
name = "wiring",
associates = ["//path/to/impl"], # sole edge to impl
deps = [...],
)
```
While associate targets' `JavaInfo` is included in the `deps` field of the wiring target's `JavaInfo` provider (so their jars appear in `transitive_runtime_jars` for the final binary), the aspects need to **visit** each target individually to dex its jar. Since `associates` is not in the aspect traversal lists, the associate target is never visited.
Additionally, `kt_android_library` doesn't have a dedicated mobile-install adapter (unlike `android_library` and `java_library`). It falls through to the default adapter, which works but means any future `kt_android_library`-specific attributes would also be missed.
## Affected aspects
| Aspect | File | Missing attribute |
|--------|------|-------------------|
| `dex_desugar_aspect` | `rules/dex_desugar_aspect.bzl` (`_ATTR_ASPECTS`) | `associates` |
| mobile-install | `mobile_install/adapters/default.bzl` (`_aspect_attrs`) | `associates` |
Adding an attribute to an aspect's traversal list that doesn't exist on a given rule is safe — Bazel silently skips it.
## Reproduction
1. Create `kt_android_library` target A with an `internal class`
2. Create `kt_android_library` target B with `associates = [A]` (A not in `deps`)
3. B instantiates A's internal class (e.g. via Dagger `@Provides`)
4. `bazel build //app` succeeds
5. `bazel mobile-install //app` crashes with `NoClassDefFoundError`
## Environment
- rules_android 0.7.1
- rules_kotlin 2.1.8
- Bazel 8.6
Contributor guide
Assessment
This issue has not been assessed yet.