diff --git a/ReactAndroid/build.gradle b/ReactAndroid/build.gradle index 81f0e0e1cbb229..9ef5b784dc9c5c 100644 --- a/ReactAndroid/build.gradle +++ b/ReactAndroid/build.gradle @@ -667,12 +667,7 @@ react { // TODO: The library name is chosen for parity with Fabric components & iOS // This should be changed to a more generic name, e.g. `ReactCoreSpec`. libraryName = "rncore" - root = file("..") jsRootDir = file("../Libraries") - reactNativeDir = file("$projectDir/..") - // We search for the codegen in either one of the `node_modules` folder or in the - // root packages folder (that's for when we build from source without calling `yarn install`). - codegenDir = file(findNodeModulePath(projectDir, "@react-native/codegen") ?: "../packages/react-native-codegen/") } apply plugin: "org.jetbrains.kotlin.android" 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 8aa1c5bbdb0d9a..4c8a6ff9996020 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 @@ -9,6 +9,7 @@ package com.facebook.react import com.android.build.api.variant.AndroidComponentsExtension import com.android.build.gradle.internal.tasks.factory.dependsOn +import com.facebook.react.internal.PrivateReactExtension import com.facebook.react.tasks.BuildCodegenCLITask import com.facebook.react.tasks.GenerateCodegenArtifactsTask import com.facebook.react.tasks.GenerateCodegenSchemaTask @@ -34,8 +35,22 @@ class ReactPlugin : Plugin { checkJvmVersion(project) val extension = project.extensions.create("react", ReactExtension::class.java, project) + // We register a private extension on the rootProject so that project wide configs + // like codegen config can be propagated from app project to libraries. + val rootExtension = + project.rootProject.extensions.findByType(PrivateReactExtension::class.java) + ?: project.rootProject.extensions.create( + "privateReact", PrivateReactExtension::class.java, project) + // App Only Configuration project.pluginManager.withPlugin("com.android.application") { + // We wire the root extension with the values coming from the app (either user populated or + // defaults). + rootExtension.root.set(extension.root) + rootExtension.reactNativeDir.set(extension.reactNativeDir) + rootExtension.codegenDir.set(extension.codegenDir) + rootExtension.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs) + project.afterEvaluate { val reactNativeDir = extension.reactNativeDir.get().asFile val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties") @@ -54,12 +69,12 @@ class ReactPlugin : Plugin { project.configureReactTasks(variant = variant, config = extension) } } - configureCodegen(project, extension, isLibrary = false) + configureCodegen(project, extension, rootExtension, isLibrary = false) } // Library Only Configuration project.pluginManager.withPlugin("com.android.library") { - configureCodegen(project, extension, isLibrary = true) + configureCodegen(project, extension, rootExtension, isLibrary = true) } } @@ -82,12 +97,14 @@ class ReactPlugin : Plugin { } } - /** - * A plugin to enable react-native-codegen in Gradle environment. See the Gradle API docs for more - * information: https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html - */ + /** This function sets up `react-native-codegen` in our Gradle plugin. */ @Suppress("UnstableApiUsage") - private fun configureCodegen(project: Project, extension: ReactExtension, isLibrary: Boolean) { + private fun configureCodegen( + project: Project, + localExtension: ReactExtension, + rootExtension: PrivateReactExtension, + isLibrary: Boolean + ) { // First, we set up the output dir for the codegen. val generatedSrcDir = File(project.buildDir, "generated/source/codegen") @@ -95,21 +112,21 @@ class ReactPlugin : Plugin { // It's the root folder for apps (so ../../ from the Gradle project) // and the package folder for library (so ../ from the Gradle project) if (isLibrary) { - extension.jsRootDir.convention(project.layout.projectDirectory.dir("../")) + localExtension.jsRootDir.convention(project.layout.projectDirectory.dir("../")) } else { - extension.jsRootDir.convention(extension.root) + localExtension.jsRootDir.convention(localExtension.root) } val buildCodegenTask = project.tasks.register("buildCodegenCLI", BuildCodegenCLITask::class.java) { - it.codegenDir.set(extension.codegenDir) + it.codegenDir.set(rootExtension.codegenDir) val bashWindowsHome = project.findProperty("REACT_WINDOWS_BASH") as String? it.bashWindowsHome.set(bashWindowsHome) // Please note that appNeedsCodegen is triggering a read of the package.json at // configuration time as we need to feed the onlyIf condition of this task. // Therefore, the appNeedsCodegen needs to be invoked inside this lambda. - val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(extension) + val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(rootExtension.root) it.onlyIf { isLibrary || needsCodegenFromPackageJson } } @@ -118,23 +135,24 @@ class ReactPlugin : Plugin { project.tasks.register( "generateCodegenSchemaFromJavaScript", GenerateCodegenSchemaTask::class.java) { it -> it.dependsOn(buildCodegenTask) - it.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs) - it.codegenDir.set(extension.codegenDir) + it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs) + it.codegenDir.set(rootExtension.codegenDir) it.generatedSrcDir.set(generatedSrcDir) // We're reading the package.json at configuration time to properly feed // the `jsRootDir` @Input property of this task & the onlyIf. Therefore, the // parsePackageJson should be invoked inside this lambda. - val packageJson = findPackageJsonFile(project, extension) + val packageJson = findPackageJsonFile(project, rootExtension.root) val parsedPackageJson = packageJson?.let { JsonUtils.fromCodegenJson(it) } val jsSrcsDirInPackageJson = parsedPackageJson?.codegenConfig?.jsSrcsDir if (jsSrcsDirInPackageJson != null) { it.jsRootDir.set(File(packageJson.parentFile, jsSrcsDirInPackageJson)) } else { - it.jsRootDir.set(extension.jsRootDir) + it.jsRootDir.set(localExtension.jsRootDir) } - val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(extension) + val needsCodegenFromPackageJson = + project.needsCodegenFromPackageJson(rootExtension.root) it.onlyIf { isLibrary || needsCodegenFromPackageJson } } @@ -143,17 +161,18 @@ class ReactPlugin : Plugin { project.tasks.register( "generateCodegenArtifactsFromSchema", GenerateCodegenArtifactsTask::class.java) { it.dependsOn(generateCodegenSchemaTask) - it.reactNativeDir.set(extension.reactNativeDir) - it.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs) + it.reactNativeDir.set(rootExtension.reactNativeDir) + it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs) it.generatedSrcDir.set(generatedSrcDir) - it.packageJsonFile.set(findPackageJsonFile(project, extension)) - it.codegenJavaPackageName.set(extension.codegenJavaPackageName) - it.libraryName.set(extension.libraryName) + it.packageJsonFile.set(findPackageJsonFile(project, rootExtension.root)) + it.codegenJavaPackageName.set(localExtension.codegenJavaPackageName) + it.libraryName.set(localExtension.libraryName) // Please note that appNeedsCodegen is triggering a read of the package.json at // configuration time as we need to feed the onlyIf condition of this task. // Therefore, the appNeedsCodegen needs to be invoked inside this lambda. - val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(extension) + val needsCodegenFromPackageJson = + project.needsCodegenFromPackageJson(rootExtension.root) it.onlyIf { isLibrary || needsCodegenFromPackageJson } } diff --git a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/internal/PrivateReactExtension.kt b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/internal/PrivateReactExtension.kt new file mode 100644 index 00000000000000..e0de3b1ed26281 --- /dev/null +++ b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/internal/PrivateReactExtension.kt @@ -0,0 +1,35 @@ +/* + * 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.internal + +import javax.inject.Inject +import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.ListProperty + +/** + * A private extension we set on the rootProject to make easier to share values at execution time + * between app project and library project. + * + * Specifically, the [codegenDir], [reactNativeDir] and other properties should be provided by apps + * (for setups like a monorepo which are app specific) and libraries should honor those values. + * + * Users are not supposed to access directly this extension from their build.gradle file. + */ +abstract class PrivateReactExtension @Inject constructor(project: Project) { + + private val objects = project.objects + + val root: DirectoryProperty = objects.directoryProperty() + + val reactNativeDir: DirectoryProperty = objects.directoryProperty() + + val nodeExecutableAndArgs: ListProperty = objects.listProperty(String::class.java) + + val codegenDir: DirectoryProperty = objects.directoryProperty() +} diff --git a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PathUtils.kt b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PathUtils.kt index 0710f04b994e07..2d94bf72a04867 100644 --- a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PathUtils.kt +++ b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PathUtils.kt @@ -14,6 +14,7 @@ import com.facebook.react.model.ModelPackageJson import com.facebook.react.utils.Os.cliPath import java.io.File import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty /** * Computes the entry file for React Native. The Algo follows this order: @@ -189,13 +190,13 @@ internal fun projectPathToLibraryName(projectPath: String): String = * Gradle module (generally the case for library projects) or we fallback to looking into the `root` * folder of a React Native project (generally the case for app projects). */ -internal fun findPackageJsonFile(project: Project, extension: ReactExtension): File? { +internal fun findPackageJsonFile(project: Project, rootProperty: DirectoryProperty): File? { val inParent = project.file("../package.json") if (inParent.exists()) { return inParent } - val fromExtension = extension.root.file("package.json").orNull?.asFile + val fromExtension = rootProperty.file("package.json").orNull?.asFile if (fromExtension?.exists() == true) { return fromExtension } @@ -207,12 +208,15 @@ internal fun findPackageJsonFile(project: Project, extension: ReactExtension): F * Function to look for the `package.json` and parse it. It returns a [ModelPackageJson] if found or * null others. * - * Please note that this function access the [ReactExtension] field properties and calls .get() on - * them, so calling this during apply() of the ReactPlugin is not recommended. It should be invoked - * inside lazy lambdas or at execution time. + * Please note that this function access the [DirectoryProperty] parameter and calls .get() on them, + * so calling this during apply() of the ReactPlugin is not recommended. It should be invoked inside + * lazy lambdas or at execution time. */ -internal fun readPackageJsonFile(project: Project, extension: ReactExtension): ModelPackageJson? { - val packageJson = findPackageJsonFile(project, extension) +internal fun readPackageJsonFile( + project: Project, + rootProperty: DirectoryProperty +): ModelPackageJson? { + val packageJson = findPackageJsonFile(project, rootProperty) return packageJson?.let { JsonUtils.fromCodegenJson(it) } } diff --git a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/ProjectUtils.kt b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/ProjectUtils.kt index ba490af271951c..e941da78b564f8 100644 --- a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/ProjectUtils.kt +++ b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/ProjectUtils.kt @@ -7,9 +7,9 @@ package com.facebook.react.utils -import com.facebook.react.ReactExtension import com.facebook.react.model.ModelPackageJson import org.gradle.api.Project +import org.gradle.api.file.DirectoryProperty internal object ProjectUtils { internal val Project.isNewArchEnabled: Boolean @@ -35,8 +35,8 @@ internal object ProjectUtils { HERMES_FALLBACK } - internal fun Project.needsCodegenFromPackageJson(extension: ReactExtension): Boolean { - val parsedPackageJson = readPackageJsonFile(this, extension) + internal fun Project.needsCodegenFromPackageJson(rootProperty: DirectoryProperty): Boolean { + val parsedPackageJson = readPackageJsonFile(this, rootProperty) return needsCodegenFromPackageJson(parsedPackageJson) } diff --git a/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/PathUtilsTest.kt b/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/PathUtilsTest.kt index 7871122498c9ae..1239f372f4d2bd 100644 --- a/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/PathUtilsTest.kt +++ b/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/PathUtilsTest.kt @@ -231,7 +231,7 @@ class PathUtilsTest { project.plugins.apply("com.facebook.react") val extension = project.extensions.getByType(ReactExtension::class.java) - assertEquals(project.file("../package.json"), findPackageJsonFile(project, extension)) + assertEquals(project.file("../package.json"), findPackageJsonFile(project, extension.root)) } @Test @@ -245,7 +245,7 @@ class PathUtilsTest { val extension = project.extensions.getByType(ReactExtension::class.java).apply { root.set(moduleFolder) } - assertEquals(localFile, findPackageJsonFile(project, extension)) + assertEquals(localFile, findPackageJsonFile(project, extension.root)) } @Test @@ -257,7 +257,7 @@ class PathUtilsTest { val extension = project.extensions.getByType(ReactExtension::class.java).apply { root.set(moduleFolder) } - val actual = readPackageJsonFile(project, extension) + val actual = readPackageJsonFile(project, extension.root) assertNull(actual) } @@ -272,7 +272,7 @@ class PathUtilsTest { val extension = project.extensions.getByType(ReactExtension::class.java).apply { root.set(moduleFolder) } - val actual = readPackageJsonFile(project, extension) + val actual = readPackageJsonFile(project, extension.root) assertNotNull(actual) assertNull(actual!!.codegenConfig) @@ -298,7 +298,7 @@ class PathUtilsTest { val extension = project.extensions.getByType(ReactExtension::class.java).apply { root.set(moduleFolder) } - val actual = readPackageJsonFile(project, extension) + val actual = readPackageJsonFile(project, extension.root) assertNotNull(actual) assertNotNull(actual!!.codegenConfig) diff --git a/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/ProjectUtilsTest.kt b/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/ProjectUtilsTest.kt index 164088ef132e4e..70ea15401cc664 100644 --- a/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/ProjectUtilsTest.kt +++ b/packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/ProjectUtilsTest.kt @@ -125,7 +125,7 @@ class ProjectUtilsTest { .trimIndent()) } extension.root.set(tempFolder.root) - assertTrue(project.needsCodegenFromPackageJson(extension)) + assertTrue(project.needsCodegenFromPackageJson(extension.root)) } @Test @@ -143,7 +143,7 @@ class ProjectUtilsTest { .trimIndent()) } extension.root.set(tempFolder.root) - assertFalse(project.needsCodegenFromPackageJson(extension)) + assertFalse(project.needsCodegenFromPackageJson(extension.root)) } @Test @@ -167,7 +167,7 @@ class ProjectUtilsTest { val project = createProject() val extension = TestReactExtension(project) - assertFalse(project.needsCodegenFromPackageJson(extension)) + assertFalse(project.needsCodegenFromPackageJson(extension.root)) } @Test