Android libraries are declared by family and short name in dependencies.android. Each expands to ordinary Maven
coordinates when the manifest is evaluated, so elide project info, elide project export and every other
consumer of the manifest see plain coordinates.
dependencies {
android {
androidx { ["activity-compose"] = "1.9.3"; ["core-ktx"] = "1.15.0" }
compose { bom = "2025.10.00"; modules { "ui"; "foundation"; "material3"; "ui-tooling" } }
material = "1.12.0"
playServices { ["location"] = "21.3.0" }
test { runner = "1.6.2"; modules { "ext-junit" }; espresso { "core" } }
robolectric = "4.16"
}
}
| Family | Expands to | Scope |
|---|---|---|
androidx | androidx. | app |
compose | androidx., with the BOM or one version | app |
material | com. | app |
playServices | com. | app |
test | androidx.test:runner and the named AndroidX Test and Espresso libraries | instrumented tests |
robolectric | org. and the JUnit vintage engine | host tests |
Anything a family does not name goes in dependencies.maven; the two merge:
dependencies {
android {
androidx { ["core-ktx"] = "1.15.0" }
}
maven {
packages { "com.squareup.okhttp3:okhttp:4.12.0" }
testPackages { "junit:junit:4.13.2" }
androidTestPackages { "androidx.test.uiautomator:uiautomator:2.3.0" }
}
}
| Bucket | Reaches |
|---|---|
packages | The app: compiled against and dexed into the APK. |
compileOnly | Compilation only; never dexed. |
testPackages | Host tests (src/test). |
androidTestPackages | Instrumented tests (src/androidTest); never the app or host tests. |
Short names
An AndroidX short name is the group’s segments after androidx. joined by -, then the rest of the artifact’s
name. The longest matching group wins:
| Short name | Coordinate |
|---|---|
core-ktx | androidx.core:core-ktx |
activity-compose | androidx. |
lifecycle-runtime-ktx | androidx. |
wear-compose-material3 | androidx. |
arch-core-runtime | androidx. |
The groups are those Google’s repository lists, less Compose, AndroidX Test and Gradle plugins. A name no family knows (a group it does not list, an unknown Espresso or Play services library) fails evaluation, naming the family and the name:
dependencies.android.playServices: no library of the `playServices` family is named "locations"
A misspelt artifact within a known AndroidX group passes evaluation and fails resolution.
Compose
compose takes bom or version, never both. Modules are named the same way, by the group’s segments under
androidx.compose.: ui, ui-tooling, ui-tooling-preview, foundation, material3,
material-icons-extended, runtime-livedata.
// Versions from the BOM
compose { bom = "2025.10.00"; modules { "ui"; "material3" } }
// Every module at one release
compose { version = "1.7.8"; modules { "ui"; "foundation" } }
With bom, the modules are declared without a version and take the one the BOM lists; a module the BOM does not
list fails resolution, naming it. With version, every module is declared at that release. Material 3 is released
on its own schedule, so take it from the BOM or declare it in maven at its own version.
The Compose compiler is a separate switch: set kotlin.features.compose = true in a module with Compose sources
(Kotlin compiler).
AndroidX Test
AndroidX Test’s libraries are released together but versioned apart: runner 1.6.2 ships with Espresso 3.6.1 and
ext:junit 1.2.1. test.runner’s major and minor version select the release, and every other library is taken at
its version in that release:
runner | core, rules | ext-junit | Espresso |
|---|---|---|---|
1.5.x | 1.5.0 | 1.1.5 | 3.5.1 |
1.6.x | 1.6.1 | 1.2.1 | 3.6.1 |
1.7.x | 1.7.0 | 1.3.0 | 3.7.0 |
| Key | Names |
|---|---|
modules | core, core-ktx, rules, ext-junit, ext-junit-ktx, ext-truth |
espresso | core, contrib, intents, accessibility, web, idling-resource, remote |
A runner from a release not listed may be declared alone; naming another library under it fails evaluation and says
to declare that library in dependencies.maven.androidTestPackages. test resolves into the instrumented-test
scope only (Android tests).
Robolectric
robolectric adds org.robolectric:robolectric to the host tests, and the JUnit vintage engine that runs its
JUnit 4 runners under elide test, at the JUnit Platform release Elide runs tests with
(Android tests).
Google Maven
A project declaring android or dependencies.android resolves against Google’s Maven repository,
https://dl.google.com/dl/android/maven2, after the default repositories, without declaring it. It appears in
dependencies.maven.repositories as google. A manifest that already declares Google’s repository keeps its own,
and enableDefaultRepositories = false adds nothing, since a mirror then serves it.
Published AARs
No coordinate needs an aar type. Packaging is read from the library’s Gradle module metadata when it publishes
one, else from its POM, for a declared coordinate and a transitive one alike. Gradle module metadata also selects
Android variants, follows available-at redirects, and applies published version constraints and platforms.
Each resolved .aar is unpacked once per project under .dev/android/libraries/. Its classes.jar goes on the
compile and runtime classpaths, its resources are linked, its manifest merged, its assets and native libraries
packaged, and its proguard.txt applied when the app is minified.
Dagger and Hilt
In an Android project, annotation processors declared in dependencies.maven.processors run through kapt before
the Kotlin sources compile, over Kotlin and Java sources together. kapt needs a managed Kotlin toolchain (or a Kotlin
distribution that ships it):
amends "elide:project.pkl"
import "elide:Android.pkl" as Android
import "elide:Kotlin.pkl" as Kotlin
name = "di-app"
kotlin {
toolchain = new Kotlin.ManagedKotlinToolchain { version = "2.2.21" }
}
android {
namespace = "com.example.app"
compileSdk = 36
hilt = true
}
dependencies {
maven {
packages { "com.google.dagger:hilt-android:2.57.2" }
processors { "com.google.dagger:hilt-compiler:2.57.2" }
}
}
artifacts {
["app"] = new Android.AndroidApk { release = false }
}
- Declare
dagger-compilerfor plain Dagger orhilt-compilerfor Hilt inprocessors, at the runtime’s version. android.hilt = truerewrites@HiltAndroidAppand@AndroidEntryPointclasses to extend their generatedHilt_*base, as Hilt’s Gradle plugin does. Plain Dagger needs no flag.@InstallInbindings in workspace libraries reach the application’s component; libraries holding bindings run the Hilt processor too.- Generated sources are under
.dev/jvm/generated-sources/<source set>/kapt.
KSP-only processors do not run in Android project builds.