fix(android): skip explicit kotlin-android plugin when AGP 9 built-in Kotlin is enabled - #1602
Conversation
…in Kotlin is enabled
Since AGP 9.0 Kotlin support is built into the Android Gradle Plugin and is
enabled by default. When a library still applies `kotlin-android` on top of
it, the build fails while configuring this module:
Failed to apply plugin 'org.jetbrains.kotlin.android'.
> Cannot add extension with name 'kotlin', as there is an extension
already registered with that name.
Apply `kotlin-android` only when AGP's built-in Kotlin is not in play, i.e.
when the AGP major version is below 9, or when the consumer explicitly opted
out via `android.builtInKotlin=false`.
On AGP 8 and older the condition is always false, so the plugin is applied
exactly as before. `com.android.Version` is already used a few lines below
to detect AGP 7+, so it is safe to read here too.
| // AGP 9 ships built-in Kotlin support, which is on by default but can be disabled | ||
| // via the `android.builtInKotlin` Gradle property. | ||
| def hasBuiltInKotlinSupport() { | ||
| def agpMajorVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0].toInteger() | ||
| def builtInKotlin = project.findProperty('android.builtInKotlin') | ||
|
|
||
| return agpMajorVersion >= 9 && (builtInKotlin == null || builtInKotlin.toString().toBoolean()) | ||
| } |
There was a problem hiding this comment.
Can we move it to the react-native-helpers? https://github.com/kirillzyusko/react-native-keyboard-controller/blob/main/android/react-native-helpers.gradle 🙏
There was a problem hiding this comment.
Moved it to react-native-helpers.gradle as project.ext.hasBuiltInKotlinSupport() 👍
One knock-on change: apply from: "$projectDir/react-native-helpers.gradle" used to sit after the plugin block, so the helper was not defined yet at the point where the decision is made. It now runs right after com.android.library. The helper script only defines project.ext closures and resolves the RN directory, so evaluating it a couple of lines earlier has no other effect.
There was a problem hiding this comment.
Follow-up: my first push of this failed CI with
Script '.../android/react-native-helpers.gradle' line: 51
> Could not get unknown property 'com' for root project 'android'
com.android.Version is on the buildscript classpath of build.gradle, and a script applied with apply from: does not inherit that, so the reference silently degrades to a property lookup on the project. Fixed by passing the version string into the helper instead:
if (!project.ext.hasBuiltInKotlinSupport(com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION)) {
apply plugin: 'kotlin-android'
}Verified the classpath behaviour and the truth table (AGP 9 → true, AGP 8 and 4.2.2 → false, android.builtInKotlin=false → false) on a minimal reproduction before pushing. Just pushed, so CI is re-running — happy to switch to reflection via project.buildscript.classLoader if you would rather keep the call site argument-free.
📊 Package size report
|
555cb70 to
81846de
Compare
…lpers.gradle` Per review: keep the built-in Kotlin logic next to the other shared gradle helpers instead of inlining it in `build.gradle`. Two knock-on details: * `apply from: react-native-helpers.gradle` now runs before the plugin block, so `project.ext.hasBuiltInKotlinSupport()` is defined when the decision is made. The helper script only defines `project.ext` closures, so evaluating it earlier has no other effect. * The AGP version is passed in rather than read inside the helper. `com.android.Version` sits on the buildscript classpath of `build.gradle`, which scripts applied via `apply from:` do not inherit, so reading it there fails with "Could not get unknown property 'com'".
81846de to
0972d17
Compare
|
📜 Description
android/build.gradleapplieskotlin-androidunconditionally. Since AGP 9.0 the Android Gradle Plugin provides Kotlin support itself, and applying the standalone Kotlin plugin on top of it makes configuration of this module fail.This PR keeps
apply plugin: 'kotlin-android'for every AGP version that needs it, and skips it only when AGP's built-in Kotlin is actually in play:💡 Motivation and Context
(a) The symptom. On a project built with AGP 9, configuring this module fails with one of:
There is no consumer-side workaround other than pinning AGP 8 or globally disabling built-in Kotlin, so the fix has to live in the library.
(b) Why the condition has that shape. Built-in Kotlin is not simply "AGP >= 9" — it is a flag:
android.builtInKotlin=falseingradle.properties(documented as a temporary escape hatch that goes away in AGP 10).So both halves are required. Dropping the property check would break every project that opted out — they need the explicit plugin. Dropping the version check would skip the plugin on AGP 8, where nothing else provides Kotlin. The
builtInKotlin == nullbranch covers "property not set", which is the default-on case. References: Migrate to built-in Kotlin, AGP 9.0 release notes.(c) Nothing changes on older AGP. For any AGP major below 9,
hasBuiltInKotlinSupport()short-circuits tofalseandkotlin-androidis applied exactly as before — same plugin, same position in the script, samekotlin_versionresolution andkotlin-stdlibdependency.com.android.Versionis safe to read here: this file already usescom.android.Version.ANDROID_GRADLE_PLUGIN_VERSIONa few lines below to gatenamespace/buildFeatures.buildConfigbehind AGP 7+, and the call happens afterapply plugin: 'com.android.library', so the AGP classes are on the classpath.📢 Changelog
Android
kotlin-androidplugin when AGP 9's built-in Kotlin support is enabled, which unblocks building this library with AGP 9.🤔 How Has This Been Tested?
Verified the modified
android/build.gradlestill parses as Groovy.Verified the guard's evaluation directly, by running the same Groovy in a Gradle build script with the AGP version string supplied as a parameter, across
7.2.2/8.13.0/9.0.0/9.0.0-alpha01/10.1.2and withandroid.builtInKotlinunset /true/false. Results match the truth table below; qualifier versions such as9.0.0-alpha01parse correctly because only the first.-separated token is read.The
hasBuiltInKotlinSupport()truth table:android.builtInKotlinkotlin-androidappliedtrue/falsetruefalseI did not run the full
android/Gradle build against AGP 9 in CI here — the repository's Android jobs currently build against the pinnedREACT_NATIVE_VERSION/ AGP of the example app, so this path is not exercised by CI. Happy to add an AGP 9 build matrix entry if you'd like it in the same PR.📸 Screenshots (if appropriate):
Not applicable — build script only.
📝 Checklist