fix(android): skip explicit Kotlin plugin when AGP registers the kotlin extension - #390
gabrieldonadel wants to merge 2 commits into
Conversation
…Kotlin AGP 9 enables built-in Kotlin by default and applies the Kotlin plugin itself. Applying it again fails configuration with "Cannot add extension with name 'kotlin'". Guard the explicit apply so it only runs when AGP is not providing Kotlin: AGP 8 and older, or AGP 9 with android.builtInKotlin=false. AGP 10 removes that opt-out, so built-in Kotlin is always active there and the explicit apply must never run.
2746841 to
99f2a1e
Compare
|
|
|
Pushed a revision that changes how the guard decides, not what it does. Before, it derived the answer from the AGP version and the if (project.extensions.findByName('kotlin') == null) {
apply plugin: 'kotlin-android'
}Three reasons this is the better shape:
This shape was suggested by the RevenueCat maintainers on |
Replace the AGP version / android.builtInKotlin check with a direct test for the registered kotlin extension. The version check reads com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION, which can resolve to a different classpath entry than the AGP actually in use, and the global android.builtInKotlin property can be overridden per module by the com.android.built-in-kotlin plugin -- so both inputs can disagree with reality. Asking whether the kotlin extension exists tests the condition that actually fails, needs no AGP version table, and covers AGP 10 where the opt-out is removed.
1c610e8 to
227133d
Compare
Problem
Android Gradle Plugin 9 ships built-in Kotlin support and enables it by default, so
AGP registers the
kotlinextension itself. When a library also applieskotlin-androidexplicitly, the two collide and configuration fails before anything compiles. AGP
words it two ways, both the same problem:
The apply is unconditional in this file, so on an AGP 9 project it cannot be built
at all. There is no consumer-side workaround short of patching the file — setting
android.builtInKotlin=falseproject-wide just to build one dependency is not areasonable ask, and that escape hatch is removed in AGP 10.
Change
Apply the plugin only when nothing has registered the
kotlinextension yet:Files changed:
android/build.gradleWhy this shape
Earlier revisions of this PR derived the answer from the AGP version and the
android.builtInKotlinproperty. Asking for the extension directly is better onthree counts:
registered
kotlin", so that is what the guard checks. No AGP version table to keepin sync.
android.builtInKotlinis a global switch, but built-inKotlin can also be enabled per module with the
com.android.built-in-kotlinplugin,so the global value can disagree with the module. Reading
com.android.Version.ANDROID_GRADLE_PLUGIN_VERSIONhas its own trap: it resolvesagainst the buildscript classpath, which is not always the AGP that ends up running.
android.builtInKotlinopt-out is removed there,and this guard needs no special case for it.
android.builtInKotlinkotlinextensionfalsetruefalseThe guard sits after
apply plugin: 'com.android.library'in every file it touches,so AGP has already registered its extensions by the time it runs. I checked that
ordering per file rather than assuming it.
What I verified, and what I did not
9.2.1 and Gradle 9.4.1:
:app:assembleDebugsucceeds both with-Pandroid.newDsl=true -Pandroid.builtInKotlin=trueand with both flags off. Thatrun used the earlier version-based guard; the extension guard in this revision is
the shape now used across the rest of this sweep and already merged in several of
those repos.
Phases.CONVERSION.and I could not do that from outside.
Found while sweeping 157 popular React Native libraries for AGP 9 new-DSL compatibility.
34 failed with the new DSL enabled, and 29 of those failed on exactly this — it is the
most common blocker by a wide margin.
This guard shape was suggested by the RevenueCat maintainers on
RevenueCat/react-native-purchases#1934,
who had already hit the same issue in their Capacitor and Flutter SDKs. I have
since standardised on it across this sweep.