From 13ae11152a51444c0d8494faa1fcd221d0d89f2f Mon Sep 17 00:00:00 2001 From: Gabriel Donadel Date: Wed, 25 Oct 2023 12:11:21 -0700 Subject: [PATCH] Ensure namespace is specified for all the 3rd party libraries (#41085) Summary: As stated here https://github.com/react-native-community/discussions-and-proposals/issues/671 React Native 0.73 will depend on Android Gradle Plugin (AGP) 8.x which requires all libraries to specify a namespace in their build.gradle file, even though this issue was raised many months ago, lots of libraries have not been updated and don't specify a `namespace` inside their build.gradle files ## Changelog: [ANDROID] [CHANGED] - Ensure namespace is specified for all the 3rd party libraries Pull Request resolved: https://github.com/facebook/react-native/pull/41085 Test Plan: Run RNGP tests and test building rn-tester after doing the following procedure 1. Remove `namespace "com.facebook.react"` from react-native/packages/react-native/ReactAndroid/build.gradle 2. Add `package="com.facebook.react"` to react-native/packages/react-native/ReactAndroid/src/main/AndroidManifest.xml 3. Build rn-tester Also tested this using [BareExpo](https://github.com/expo/expo/tree/main/apps/bare-expo) with AGP 8.1.1 and all libraries that were missing the `namespace` compiled correctly Reviewed By: cipolleschi Differential Revision: D50556667 Pulled By: cortinico fbshipit-source-id: 3d75ec0a8b82427ff0ede89aa7bc58b28b288945 --- .../kotlin/com/facebook/react/ReactPlugin.kt | 2 + .../react/utils/AgpConfiguratorUtils.kt | 42 ++++++++++++ .../react/utils/AgpConfiguratorUtilsTest.kt | 65 +++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100644 packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/AgpConfiguratorUtilsTest.kt diff --git a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt index f64aa685382766..5709501f3b594e 100644 --- a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt +++ b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt @@ -15,6 +15,7 @@ import com.facebook.react.tasks.GenerateCodegenSchemaTask import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForApp import com.facebook.react.utils.AgpConfiguratorUtils.configureBuildConfigFieldsForLibraries import com.facebook.react.utils.AgpConfiguratorUtils.configureDevPorts +import com.facebook.react.utils.AgpConfiguratorUtils.configureNamespaceForLibraries import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap import com.facebook.react.utils.DependencyUtils.configureDependencies import com.facebook.react.utils.DependencyUtils.configureRepositories @@ -82,6 +83,7 @@ class ReactPlugin : Plugin { // Library Only Configuration configureBuildConfigFieldsForLibraries(project) + configureNamespaceForLibraries(project) project.pluginManager.withPlugin("com.android.library") { configureCodegen(project, extension, rootExtension, isLibrary = true) } diff --git a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt index 4f19af3ff1d1a7..90593bdbf7419c 100644 --- a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt +++ b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/AgpConfiguratorUtils.kt @@ -8,12 +8,17 @@ package com.facebook.react.utils import com.android.build.api.variant.AndroidComponentsExtension +import com.android.build.gradle.LibraryExtension import com.facebook.react.ReactExtension import com.facebook.react.utils.ProjectUtils.isHermesEnabled import com.facebook.react.utils.ProjectUtils.isNewArchEnabled +import java.io.File +import javax.xml.parsers.DocumentBuilder +import javax.xml.parsers.DocumentBuilderFactory import org.gradle.api.Action import org.gradle.api.Project import org.gradle.api.plugins.AppliedPlugin +import org.w3c.dom.Element @Suppress("UnstableApiUsage") internal object AgpConfiguratorUtils { @@ -63,6 +68,43 @@ internal object AgpConfiguratorUtils { project.pluginManager.withPlugin("com.android.application", action) project.pluginManager.withPlugin("com.android.library", action) } + + fun configureNamespaceForLibraries(appProject: Project) { + appProject.rootProject.allprojects { subproject -> + subproject.pluginManager.withPlugin("com.android.library") { + subproject.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl { ext -> + if (ext.namespace == null) { + val android = subproject.extensions.getByType(LibraryExtension::class.java) + val manifestFile = android.sourceSets.getByName("main").manifest.srcFile + + manifestFile + .takeIf { it.exists() } + ?.let { file -> + getPackageNameFromManifest(file)?.let { packageName -> + ext.namespace = packageName + } + } + } + } + } + } + } } const val DEFAULT_DEV_SERVER_PORT = "8081" + +fun getPackageNameFromManifest(manifest: File): String? { + val factory: DocumentBuilderFactory = DocumentBuilderFactory.newInstance() + val builder: DocumentBuilder = factory.newDocumentBuilder() + + try { + val xmlDocument = builder.parse(manifest) + + val manifestElement = xmlDocument.getElementsByTagName("manifest").item(0) as? Element + val packageName = manifestElement?.getAttribute("package") + + return if (packageName.isNullOrEmpty()) null else packageName + } catch (e: Exception) { + return null + } +} diff --git a/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/AgpConfiguratorUtilsTest.kt b/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/AgpConfiguratorUtilsTest.kt new file mode 100644 index 00000000000000..1c9c07eb0b6fb7 --- /dev/null +++ b/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/AgpConfiguratorUtilsTest.kt @@ -0,0 +1,65 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +package com.facebook.react.utils + +import java.io.File +import org.junit.Assert.* +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder + +class AgpConfiguratorUtilsTest { + + @get:Rule val tempFolder = TemporaryFolder() + + @Test + fun getPackageNameFromManifest_withEmptyFile_returnsNull() { + val mainFolder = tempFolder.newFolder("awesome-module/src/main/") + val manifest = File(mainFolder, "AndroidManifest.xml").apply { writeText("") } + + val actual = getPackageNameFromManifest(manifest) + assertNull(actual) + } + + @Test + fun getPackageNameFromManifest_withMissingPackage_returnsNull() { + val mainFolder = tempFolder.newFolder("awesome-module/src/main/") + val manifest = + File(mainFolder, "AndroidManifest.xml").apply { + writeText( + // language=xml + """ + + + """ + .trimIndent()) + } + + val actual = getPackageNameFromManifest(manifest) + assertNull(actual) + } + + @Test + fun getPackageNameFromManifest_withPackage_returnsPackage() { + val mainFolder = tempFolder.newFolder("awesome-module/src/main/") + val manifest = + File(mainFolder, "AndroidManifest.xml").apply { + writeText( + // language=xml + """ + + + """ + .trimIndent()) + } + + val actual = getPackageNameFromManifest(manifest) + assertNotNull(actual) + assertEquals("com.facebook.react", actual) + } +}