Android Studio cannot index Skip modules without explicit includeBuild in project's settings.gradle.kts
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 3.2k
- Forks
- 106
- Avg merge
- 6d 13h
- Merged PRs (30d)
- 1
Description
Problem
When using Skip with Android Studio, the generated Kotlin project (skipstone) is included as a composite build via includeBuild. However, the path to skipstone is only known at Gradle configuration time — it's computed dynamically from the Skip plugin's output directory.
Android Studio requires a static, pre-configured settings.gradle.kts to properly index composite builds. When the skipstone path is computed dynamically, AS fails to index Skip modules, resulting in:
- All Skip-generated types showing as unresolved (red) in the Android app module
- No code completion for Skip module APIs
- No navigation to Skip module source
- The IDE being effectively unusable for the Android side of a Skip project
Root Cause
The skip-build-plugin generates the Gradle project configuration dynamically. When Skip's Gradle plugin is applied, the skipstone includeBuild path is added at runtime. This works for command-line Gradle builds but not for Android Studio's indexing, which requires the composite build structure to be statically declared in settings.gradle.kts before any plugin resolution occurs.
Workaround
We worked around this by maintaining a custom static settings.gradle.kts in the Android project that:
- Resolves the skipstone path from Xcode's DerivedData (always authoritative since the project is built from Xcode)
- Reads version configuration from skipstone's auto-generated
settings.gradle.kts - Calls
includeBuild(skipstonePath)statically so AS can index it
val skipstonePath = run {
val derivedDataDir = file("${System.getProperty("user.home")}/Library/Developer/Xcode/DerivedData/")
derivedDataDir.listFiles()
?.filter { it.name.startsWith("WhatWatt-") }
?.maxByOrNull { it.lastModified() }
?.resolve("Build/Intermediates.noindex/BuildToolPluginIntermediaries/whatwatthybrid.output/WhatWatt/skipstone")
?.takeIf { it.exists() }
?: error("Skipstone not found in DerivedData — build the project from Xcode first")
}.path
This workaround has some rough edges — though we mitigate them:
- It hardcodes the DerivedData path structure (which may change between Xcode versions)
- Version information is read directly from skipstone's own auto-generated
settings.gradle.ktsat sync time, so it stays in sync without manual duplication - Multiple WhatWatt DerivedData entries are handled by picking the most recently modified one, which is always the active build
Suggested Fix
The ideal fix would be for Skip to either:
-
Generate a static
includeBuildentry — write a stable, path-resolvedincludeBuild(...)call into the Android project'ssettings.gradle.ktsafter transpilation, so AS can see it without dynamic evaluation. -
Document the
includeBuildrequirement — clarify in the Skip docs that AS users need to manually add theincludeBuildpath and explain how to find it.
Environment
- Skip: 1.7.0
- Xcode: 26.2 (Build 17C52)
- Android Studio: 2025.2 (Meerkat)
- Gradle: 8.14.3
- Android Gradle Plugin: 8.13.0
- Kotlin: 2.2.0
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the skip-build-plugin behavior and the generated skipstone and Android project's settings.gradle.kts files described in the issue. Reproduce Android Studio indexing with the current dynamic includeBuild, then determine whether a generated static includeBuild or documentation is appropriate; done means Skip modules index with resolved types, completion, and navigation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, kotlin, swift
- Domain
- build-system, developer-experience, mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100