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.kts at 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 includeBuild entry — write a stable, path-resolved includeBuild(...) call into the Android project's settings.gradle.kts after transpilation, so AS can see it without dynamic evaluation.
-
Document the includeBuild requirement — clarify in the Skip docs that AS users need to manually add the includeBuild path 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
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.ktsto properly index composite builds. When the skipstone path is computed dynamically, AS fails to index Skip modules, resulting in:Root Cause
The
skip-build-plugingenerates the Gradle project configuration dynamically. When Skip's Gradle plugin is applied, the skipstoneincludeBuildpath 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 insettings.gradle.ktsbefore any plugin resolution occurs.Workaround
We worked around this by maintaining a custom static
settings.gradle.ktsin the Android project that:settings.gradle.ktsincludeBuild(skipstonePath)statically so AS can index itThis workaround has some rough edges — though we mitigate them:
settings.gradle.ktsat sync time, so it stays in sync without manual duplicationSuggested 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