Skip to content

Commit abb071b

Browse files
hurali97meta-codesync[bot]
authored andcommitted
feat: adopt AGP v9 (#57038)
Summary: This PR is part of Android Gradle Plugin v9 adoption. With this PR, we adopt the AGP9 repository wide and address the deprecated APIs with the newer ones. This is in combination with #57038 <details> <summary> <b>Changes to `configureBuildConfigFieldsForLibraries` and `configureNamespaceForLibraries`</b> </summary> <br/> These helpers are called from `ReactPlugin.apply`, but they currently iterate over `rootProject.allprojects`. Since `com.facebook.react` is applied by multiple Android library projects, each application of the plugin repeats the same traversal and attempts to register `finalizeDsl` callbacks for every Android library project. The first traversal can register the callbacks successfully. However, a later traversal can reach a project whose Android DSL finalization phase has already completed. Starting with AGP 9, AGP explicitly errors when `finalizeDsl` is called after that phase has passed, which causes the build failure. [see](https://issuetracker.google.com/issues/436595826) To understand this, consider following modules: ``` :react-native-safe-area-context :react-native-mmkv :react-native-skia ``` The current code on main, would result in executing the following block 3 times: ```kt fun configureBuildConfigFieldsForLibraries(appProject: Project) { appProject.rootProject.allprojects { subproject -> println("=== subproject ${subproject.name}") subproject.pluginManager.withPlugin("com.android.library") { subproject.extensions .getByType(LibraryAndroidComponentsExtension::class.java) .finalizeDsl { ext -> ext.buildFeatures.buildConfig = true } } } } ``` The output would be: ``` > Configure project: react-native-safe-area-context === subproject RNProject === subproject app === subproject react-native-safe-area-context === subproject react-native-mmkv === subproject react-native-skia > Configure project: react-native-mmkv === subproject RNProject === subproject app === subproject react-native-safe-area-context === subproject react-native-mmkv === subproject react-native-skia > Configure project: react-native-skia === subproject RNProject === subproject app === subproject react-native-safe-area-context === subproject react-native-mmkv === subproject react-native-skia ``` Now with AGP9, the same code will throw this error: ``` > Configure project: react-native-safe-area-context === subproject RNProject === subproject app === subproject react-native-safe-area-context === subproject react-native-mmkv === subproject react-native-skia > Configure project: react-native-mmkv === subproject RNProject === subproject app === subproject react-native-safe-area-context === subproject react-native-mmkv * What went wrong: A problem occurred evaluating project': react-native-mmkv'. > Failed to apply plugin 'com. facebook. react' > It is too late to call finalizeDst as the DSL finalization blocks have already been executed. \n In particular, you cannot call 'finalizeDsl' within the "beforeVariants' or "onVariants' blocks. Instead, you must call finalizeDsl' directly within the "androidComponents' block ``` **Solution:** Since these helpers are only intended to configure Android library projects that apply the React plugin, we can configure the current project inside `pluginManager.withPlugin("com.android.library")` instead of repeatedly configuring all projects from every plugin application. ```kt fun configureBuildConfigFieldsForLibraries(project: Project) { project.extensions .getByType(LibraryAndroidComponentsExtension::class.java) .finalizeDsl { ext -> ext.buildFeatures.buildConfig = true } } ``` ```kt fun configureNamespaceForLibraries(project: Project) { project.extensions .getByType(LibraryAndroidComponentsExtension::class.java) .finalizeDsl { ext -> if (ext.namespace == null) { val manifestFile = project.layout.projectDirectory.file("src/main/AndroidManifest.xml").asFile manifestFile .takeIf { it.exists() } ?.let { file -> getPackageNameFromManifest(file)?.let { packageName -> ext.namespace = packageName } } } } } ``` One important note for `configureNamespaceForLibraries` is that the old `com.android.build.gradle.LibraryExtension` is deprecated in favor of `com.android.build.api.dsl.LibraryExtension` - which does not expose `manifestFile` from the `sourceSets`. So we have to define a path for it. For most libraries, this should be OK but for the ones which define custom path, this will fail. Hence, I believe, if it's important to have this fallback, we take this tradeoff. Otherwise, since there has been 2 years passed for AGP8 adoption and the time when this fallback was added, we may safely remove this function as almost all libraries now define a `namespace` in `android { }` DSL. With the above in place, we make one final change and that is to move these two functions inside the `withPlugin("com.android.library")` as these are only intended for libraries and that way, we also ensure that these will only be applied once the `com.android.library` plugin has been applied. ```kt project.pluginManager.withPlugin("com.android.library") { configureBuildConfigFieldsForLibraries(project) configureNamespaceForLibraries(project) configureCodegen(project, extension, rootExtension, isLibrary = true) } ``` </details> ## Changelog: <!-- Help reviewers and the release process by writing your own changelog entry. Pick one each for the category and type tags: [ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message For more details, see: https://reactnative.dev/contributing/changelogs-in-pull-requests --> [ANDROID] [BREAKING] - Adopt AGP v9 Pull Request resolved: #57038 Test Plan: - CI passes - Verified on RN Tester - Verified on Local RN App https://github.com/user-attachments/assets/b424c211-6876-42b0-a5d1-f9ecade39a3a Reviewed By: cipolleschi Differential Revision: D107656378 Pulled By: cortinico fbshipit-source-id: 38fd7191e089ce29ccb84fa25bff6d9e920cf322
1 parent 69b58f3 commit abb071b

11 files changed

Lines changed: 70 additions & 66 deletions

File tree

gradle.properties

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,6 @@ org.gradle.caching=true
44

55
android.useAndroidX=true
66

7-
# Those 2 properties are needed to make our project compatible with
8-
# AGP 9.0.0 for the time being. Ideally we should not opt-out of
9-
# builtInKotlin and newDsl once AGP 9.0.0 hits stable.
10-
# More on this: https://developer.android.com/build/releases/agp-preview#android-gradle-plugin-built-in-kotlin
11-
android.builtInKotlin=false
12-
android.newDsl=false
137

148
# Use this property to specify which architecture you want to build.
159
# You can also override it from the CLI using

packages/gradle-plugin/gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[versions]
2-
agp = "8.12.0"
2+
agp = "9.2.1"
33
gson = "2.8.9"
44
guava = "31.0.1-jre"
55
javapoet = "1.13.0"

packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,9 @@ class ReactPlugin : Plugin<Project> {
116116
}
117117

118118
// Library Only Configuration
119-
configureBuildConfigFieldsForLibraries(project)
120-
configureNamespaceForLibraries(project)
121119
project.pluginManager.withPlugin("com.android.library") {
120+
configureBuildConfigFieldsForLibraries(project)
121+
configureNamespaceForLibraries(project)
122122
configureCodegen(project, extension, rootExtension, isLibrary = true)
123123
}
124124
}
@@ -222,12 +222,20 @@ class ReactPlugin : Plugin<Project> {
222222
if (isLibrary) {
223223
project.extensions.getByType(LibraryAndroidComponentsExtension::class.java).finalizeDsl { ext
224224
->
225-
ext.sourceSets.getByName("main").java.srcDir(generatedSrcDir.get().dir("java").asFile)
225+
ext.sourceSets
226+
.getByName("main")
227+
.java
228+
.directories
229+
.add(generatedSrcDir.get().dir("java").asFile.path)
226230
}
227231
} else {
228232
project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java).finalizeDsl {
229233
ext ->
230-
ext.sourceSets.getByName("main").java.srcDir(generatedSrcDir.get().dir("java").asFile)
234+
ext.sourceSets
235+
.getByName("main")
236+
.java
237+
.directories
238+
.add(generatedSrcDir.get().dir("java").asFile.path)
231239
}
232240
}
233241

packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ package com.facebook.react.utils
99

1010
import com.android.build.api.variant.ApplicationAndroidComponentsExtension
1111
import com.android.build.api.variant.LibraryAndroidComponentsExtension
12-
import com.android.build.gradle.LibraryExtension
1312
import com.facebook.react.ReactExtension
1413
import com.facebook.react.utils.ProjectUtils.isEdgeToEdgeEnabled
1514
import com.facebook.react.utils.ProjectUtils.isHermesEnabled
@@ -18,7 +17,6 @@ import java.net.Inet4Address
1817
import java.net.NetworkInterface
1918
import javax.xml.parsers.DocumentBuilder
2019
import javax.xml.parsers.DocumentBuilderFactory
21-
import kotlin.plus
2220
import org.gradle.api.Action
2321
import org.gradle.api.Project
2422
import org.gradle.api.plugins.AppliedPlugin
@@ -77,13 +75,9 @@ internal object AgpConfiguratorUtils {
7775
project.pluginManager.withPlugin("com.android.library", action)
7876
}
7977

80-
fun configureBuildConfigFieldsForLibraries(appProject: Project) {
81-
appProject.rootProject.allprojects { subproject ->
82-
subproject.pluginManager.withPlugin("com.android.library") {
83-
subproject.extensions
84-
.getByType(LibraryAndroidComponentsExtension::class.java)
85-
.finalizeDsl { ext -> ext.buildFeatures.buildConfig = true }
86-
}
78+
fun configureBuildConfigFieldsForLibraries(project: Project) {
79+
project.extensions.getByType(LibraryAndroidComponentsExtension::class.java).finalizeDsl { ext ->
80+
ext.buildFeatures.buildConfig = true
8781
}
8882
}
8983

@@ -111,23 +105,16 @@ internal object AgpConfiguratorUtils {
111105
project.pluginManager.withPlugin("com.android.library", action)
112106
}
113107

114-
fun configureNamespaceForLibraries(appProject: Project) {
115-
appProject.rootProject.allprojects { subproject ->
116-
subproject.pluginManager.withPlugin("com.android.library") {
117-
subproject.extensions
118-
.getByType(LibraryAndroidComponentsExtension::class.java)
119-
.finalizeDsl { ext ->
120-
if (ext.namespace == null) {
121-
val android = subproject.extensions.getByType(LibraryExtension::class.java)
122-
val manifestFile = android.sourceSets.getByName("main").manifest.srcFile
123-
124-
manifestFile
125-
.takeIf { it.exists() }
126-
?.let { file ->
127-
getPackageNameFromManifest(file)?.let { packageName ->
128-
ext.namespace = packageName
129-
}
130-
}
108+
fun configureNamespaceForLibraries(project: Project) {
109+
project.extensions.getByType(LibraryAndroidComponentsExtension::class.java).finalizeDsl { ext ->
110+
if (ext.namespace == null) {
111+
val manifestFile =
112+
project.layout.projectDirectory.file("src/main/AndroidManifest.xml").asFile
113+
manifestFile
114+
.takeIf { it.exists() }
115+
?.let { file ->
116+
getPackageNameFromManifest(file)?.let { packageName ->
117+
ext.namespace = packageName
131118
}
132119
}
133120
}

packages/react-native-popup-menu-android/android/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
plugins {
99
id("com.facebook.react")
1010
id("com.android.library")
11-
id("org.jetbrains.kotlin.android")
1211
}
1312

1413
android {

packages/react-native/ReactAndroid/build.gradle.kts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ plugins {
1818
id("com.facebook.react")
1919
alias(libs.plugins.android.library)
2020
alias(libs.plugins.download)
21-
alias(libs.plugins.kotlin.android)
2221
alias(libs.plugins.ktfmt)
2322
}
2423

@@ -612,19 +611,19 @@ android {
612611
":packages:react-native:ReactAndroid:hermes-engine:preBuild"
613612
)
614613

615-
sourceSets.getByName("main") {
616-
res.setSrcDirs(
617-
listOf(
618-
"src/main/res/devsupport",
619-
"src/main/res/shell",
620-
"src/main/res/views/alert",
621-
"src/main/res/views/modal",
622-
"src/main/res/views/uimanager",
623-
"src/main/res/views/view",
624-
)
625-
)
626-
java.exclude("com/facebook/react/processing")
627-
java.exclude("com/facebook/react/module/processing")
614+
sourceSets {
615+
named("main") {
616+
res.directories.addAll(
617+
listOf(
618+
"src/main/res/devsupport",
619+
"src/main/res/shell",
620+
"src/main/res/views/alert",
621+
"src/main/res/views/modal",
622+
"src/main/res/views/uimanager",
623+
"src/main/res/views/view",
624+
)
625+
)
626+
}
628627
}
629628

630629
lint {
@@ -715,6 +714,8 @@ dependencies {
715714
// Therefore hermes-engine is a compileOnly dependencies.
716715
compileOnly(project(":packages:react-native:ReactAndroid:hermes-engine"))
717716

717+
implementation(libs.androidx.collection)
718+
718719
testImplementation(libs.junit)
719720
testImplementation(libs.assertj)
720721
testImplementation(libs.mockito)

packages/react-native/ReactAndroid/hermes-engine/build.gradle.kts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,9 +408,16 @@ android {
408408
}
409409
}
410410

411-
sourceSets.getByName("main") {
412-
manifest.srcFile("$hermesDir/android/hermes/src/main/AndroidManifest.xml")
413-
java.srcDirs("$hermesDir/lib/Platform/Intl/java", "$hermesDir/lib/Platform/Unicode/java")
411+
sourceSets {
412+
named("main") {
413+
manifest.srcFile("$hermesDir/android/hermes/src/main/AndroidManifest.xml")
414+
java.directories.addAll(
415+
listOf(
416+
"$hermesDir/lib/Platform/Intl/java",
417+
"$hermesDir/lib/Platform/Unicode/java",
418+
)
419+
)
420+
}
414421
}
415422

416423
buildFeatures {

packages/react-native/gradle/libs.versions.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ compileSdk = "36"
66
buildTools = "36.0.0"
77
ndkVersion = "27.1.12297006"
88
# Dependencies versions
9-
agp = "8.12.0"
9+
agp = "9.2.1"
1010
androidx-annotation = "1.6.0"
1111
androidx-appcompat = "1.7.0"
1212
androidx-autofill = "1.3.0"
13+
androidx-collection = "1.4.0"
1314
androidx-benchmark-macro-junit4 = "1.3.3"
1415
androidx-profileinstaller = "1.4.1"
1516
androidx-swiperefreshlayout = "1.1.0"
@@ -56,6 +57,7 @@ androidx-annotation = { module = "androidx.annotation:annotation", version.ref =
5657
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
5758
androidx-appcompat-resources = { module = "androidx.appcompat:appcompat-resources", version.ref = "androidx-appcompat" }
5859
androidx-autofill = { module = "androidx.autofill:autofill", version.ref = "androidx-autofill" }
60+
androidx-collection = { module = "androidx.collection:collection", version.ref = "androidx-collection" }
5961
androidx-benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "androidx-benchmark-macro-junit4" }
6062
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
6163
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" }

packages/rn-tester/android/app/benchmark/build.gradle.kts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
plugins {
99
alias(libs.plugins.android.test)
10-
alias(libs.plugins.kotlin.android)
1110
}
1211

1312
android {

packages/rn-tester/android/app/build.gradle.kts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
1010
plugins {
1111
id("com.facebook.react")
1212
alias(libs.plugins.android.application)
13-
alias(libs.plugins.kotlin.android)
1413
}
1514

1615
val reactNativeDirPath = "$rootDir/packages/react-native"
@@ -135,15 +134,18 @@ android {
135134
}
136135
sourceSets.named("main") {
137136
// SampleTurboModule.
138-
java.srcDirs(
139-
"$reactNativeDirPath/ReactCommon/react/nativemodule/samples/platform/android",
140-
)
141-
res.setSrcDirs(
142-
listOf(
143-
"src/main/res",
144-
"src/main/public_res",
145-
)
137+
kotlin.directories.add(
138+
"$reactNativeDirPath/ReactCommon/react/nativemodule/samples/platform/android"
146139
)
140+
res.directories.apply {
141+
clear() // Mimics setSrcDirs by wiping out the defaults
142+
addAll(
143+
listOf(
144+
"src/main/res",
145+
"src/main/public_res",
146+
)
147+
)
148+
}
147149
}
148150
}
149151

0 commit comments

Comments
 (0)