This guide covers setting up a development environment, building from source, running tests, and contributing to WLDroid.
| Tool | Version | Purpose |
|---|---|---|
| Android Studio | Latest stable | IDE with Android SDK management |
| Android SDK | compileSdk 35 | Android API level |
| Android NDK | r28 | Native code cross-compilation |
| Java | 17 | Gradle and Kotlin compilation |
| Meson | ≥ 1.0 | Native build system (compositor, shims) |
| Ninja | Latest | Build backend for Meson |
| Python 3 | ≥ 3.8 | Meson dependency |
| Git | ≥ 2.20 | Submodule support |
| Docker | Optional | Reproducible shim builds |
aarch64-linux-gnu-gcc |
Optional | Local shim cross-compilation |
brew install meson ninja python3
# NDK is managed by Android Studio or sdkmanagersudo apt install meson ninja-build python3 python3-pip
# For local shim cross-compilation:
sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnuVia Android Studio: Settings → Languages & Frameworks → Android SDK → SDK Tools → NDK (Side by side) → install version matching gradle.properties.
Or via command line:
sdkmanager "ndk;28.0.12674087"git clone --recursive https://github.com/Shelnutt2/wldroid.git
cd wldroidIf you forgot --recursive:
git submodule update --init --recursive# Full build (includes native compilation — may take 10+ minutes first time)
./gradlew assembleDebug
# Quick Kotlin-only build (skip native)
./gradlew assembleDebug -PskipCompositor -PskipProot -PskipVirgl -PskipShims- Open the
wldroiddirectory in Android Studio - Wait for Gradle sync to complete
- Select the
:testapprun configuration - Connect a device or start an emulator
- Run
wldroid/
├── build.gradle.kts # Root build: plugin declarations (no apply)
├── settings.gradle.kts # Module includes
├── gradle.properties # Version pins, NDK version, skip flags
├── gradle/
│ └── libs.versions.toml # Dependency version catalog
├── compositor/ # Core compositor module
│ ├── build.gradle.kts
│ ├── native/ # C source (Meson build)
│ │ ├── meson.build
│ │ ├── src/ # Compositor C source
│ │ ├── tests/ # Native unit tests
│ │ └── scripts/build.sh
│ └── src/main/java/ # Kotlin API
├── proot/ # Proot environment module
│ ├── build.gradle.kts
│ └── src/main/java/ # Kotlin API
├── virgl/ # VirGL + GPU detection module
│ ├── build.gradle.kts
│ └── src/main/java/ # Kotlin API
├── shims/ # Shim libraries module
│ ├── build.gradle.kts
│ ├── native/ # Shim C source (5 sub-libraries)
│ │ ├── drm-shim/
│ │ ├── gbm-shim/
│ │ ├── egl-override/
│ │ ├── netstub/
│ │ └── drm-wrapper/
│ └── src/main/java/ # Kotlin API
├── ui/ # Compose UI components
│ ├── build.gradle.kts
│ └── src/main/java/
├── testapp/ # Demo/test application
│ ├── build.gradle.kts
│ └── src/
├── external/ # Git submodules (17 dependencies)
├── scripts/ # Build and CI scripts
├── docs/ # Documentation
├── AGENTS.md # AI assistant context
├── README.md # Project overview
└── LICENSE # AGPL-3.0
./gradlew :compositor:assembleDebug # Compositor + native build
./gradlew :proot:assembleDebug # Proot module
./gradlew :virgl:assembleDebug # VirGL module
./gradlew :shims:assembleDebug # Shims + native build
./gradlew :ui:assembleDebug # UI components
./gradlew :testapp:assembleDebug # Test application APK# Compositor (Meson)
cd compositor/native
meson setup builddir --cross-file android-arm64.ini
ninja -C builddir
# Host native tests (no cross-compilation)
cd compositor/native
meson setup builddir-host
meson test -C builddir-host
# Shims (drm-shim)
cd shims/native/drm-shim
meson setup builddir --cross-file aarch64-linux-gnu.ini
ninja -C builddirFor faster iteration when not changing native code:
./gradlew assembleDebug \
-PskipCompositor \
-PskipProot \
-PskipVirgl \
-PskipShims# Build the Docker image (first time)
docker build -t wldroid-builder docker/
# Build all shims in Docker
docker run -v $(pwd):/workspace wldroid-builder
# Build specific shims with Docker via Gradle
./gradlew :shims:buildNative -PdrmShimDocker -PgbmShimDocker# All modules
./gradlew test
# Specific module
./gradlew :compositor:test
./gradlew :proot:test
./gradlew :virgl:test
./gradlew :shims:test
# With verbose output
./gradlew test --info# Compositor native tests (5 test files)
cd compositor/native
meson setup builddir-host
meson test -C builddir-host -v
# GBM shim tests (64 tests)
cd shims/native/gbm-shim
meson setup builddir-host
meson test -C builddir-host -v
# DRM shim tests
cd shims/native/drm-shim
meson setup builddir-host
meson test -C builddir-host -v# All instrumented tests
./gradlew connectedAndroidTest
# Specific module
./gradlew :compositor:connectedAndroidTestThe :testapp module provides interactive testing screens:
- Compositor Test — Renders test pattern, verifies rendering pipeline
- Environment Manager — Create/delete proot environments
- GPU Diagnostics — View GPU capabilities, test mode switching
- Shim Validator — Extract and verify shim loading
- Native Tests — Run native test suite from UI
- Settings — Configure GPU mode, XWayland, etc.
./gradlew :testapp:installDebug
adb shell am start -n nu.shel.wldroid.testapp/.TestAppActivity- Add enum value to
virgl/src/main/java/.../GpuMode.kt - Update
GpuCapabilityDetector.detectBestGpuMode()if auto-selectable - Update
ShimConfig.forGpuMode()with shim requirements - Add VirGL server flags in
VirglServerManager.start()if needed - Update
docs/gpu-rendering.mdwith pipeline diagram - Add tests for the new mode
- Add enum value to
DistroTemplateinProotConfig.kt - Set
downloadUrl,sha256,version, anddisplayName - If different libdrm soversion, update shim build configuration
- Test rootfs download, extraction, and proot execution
- Update documentation
cd external/pixman
git fetch origin
git checkout pixman-0.47.0
cd ../..
git add external/pixman
git commit -m "external: update pixman to 0.47.0"
# Verify build still works
./gradlew :compositor:assembleDebug-
In the fork repo, create new branch from upstream release:
cd external/wlroots git fetch upstream git checkout -b android-0.20.0 upstream/0.20.0 git cherry-pick <android-patch-commits> # Resolve conflicts, test, push to fork
-
Update submodule:
cd external/wlroots git checkout android-0.20.0 cd ../.. git add external/wlroots git commit -m "external: update wlroots fork to android-0.20.0"
-
Full rebuild and test:
./gradlew clean assembleDebug cd compositor/native && meson test -C builddir-host
- Create
shims/native/my-shim/withmeson.buildandsrc/ - Add build task in
shims/build.gradle.kts - Add asset output to
shims/src/main/assets/my-shim/ - Update
ShimExtractor.SHIM_ASSETSandShimSet - Update
ShimConfigwith enable flag - Write tests
- Update
docs/shims.md
- Follow Kotlin Coding Conventions
- Use trailing commas in multi-line declarations
- Prefer
StateFlowoverLiveDatafor observable state - Use
data classfor value types,enum classfor fixed sets - Use
suspend funfor async operations,Flowfor streams
- GNU C11 standard (
-std=gnu11) - wlroots naming conventions for compositor code
- Prefix all public symbols with
wldroid_or module-specific prefix - Use
snake_casefor functions and variables - Header guards:
#ifndef WLDROID_<MODULE>_<FILE>_H
- All public API classes and methods should have KDoc comments
- Complex algorithms should have inline comments
- Architecture decisions should be documented in
docs/ - README examples should be tested and compilable
Install Meson: pip3 install meson or brew install meson.
git submodule update --init --recursiveSet ANDROID_NDK_HOME or install via Android Studio SDK Manager. Verify the version matches gradle.properties.
Ensure submodules are initialized. The compositor depends on headers from external/wlroots, external/wayland, etc.
Install aarch64-linux-gnu-gcc or use Docker builds:
./gradlew :shims:buildNative -PdrmShimDockerCheck logcat for native crashes:
adb logcat -s wldroid:* AndroidRuntime:*Common causes:
- Missing native library (build the compositor first)
- Incompatible GPU mode for device
- Missing permissions (INTERNET for rootfs download)