Skip to content

Commit 0487108

Browse files
cortinicofacebook-github-bot
authored andcommitted
RNGP - Monorepo: Make sure libraries are honoring codegenDir provided by app (#36128)
Summary: Pull Request resolved: #36128 This commit fixes a problem which is making harder to use the New Architecture in monorepos. Specifically if a user specifies a `codegenDir` in their app, libraries should honor it. This is not the case today. The fix is to register an extension on the root project which will "pass" values from app to libraries. I've also cleaned up some of the logic in `readPackageJsonFile` function restricting the access to those functions only to `.root` which is the only field they're accessing. Fixes #35495 Changelog: [Android] [Fixed] - Better Monorepo support for New Architecture Reviewed By: cipolleschi Differential Revision: D43186767 fbshipit-source-id: 5c5ca39397306120b6b6622cb728633bd331e021
1 parent b67a4ae commit 0487108

File tree

7 files changed

+98
-45
lines changed

7 files changed

+98
-45
lines changed

ReactAndroid/build.gradle

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -667,12 +667,7 @@ react {
667667
// TODO: The library name is chosen for parity with Fabric components & iOS
668668
// This should be changed to a more generic name, e.g. `ReactCoreSpec`.
669669
libraryName = "rncore"
670-
root = file("..")
671670
jsRootDir = file("../Libraries")
672-
reactNativeDir = file("$projectDir/..")
673-
// We search for the codegen in either one of the `node_modules` folder or in the
674-
// root packages folder (that's for when we build from source without calling `yarn install`).
675-
codegenDir = file(findNodeModulePath(projectDir, "@react-native/codegen") ?: "../packages/react-native-codegen/")
676671
}
677672

678673
apply plugin: "org.jetbrains.kotlin.android"

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

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

1010
import com.android.build.api.variant.AndroidComponentsExtension
1111
import com.android.build.gradle.internal.tasks.factory.dependsOn
12+
import com.facebook.react.internal.PrivateReactExtension
1213
import com.facebook.react.tasks.BuildCodegenCLITask
1314
import com.facebook.react.tasks.GenerateCodegenArtifactsTask
1415
import com.facebook.react.tasks.GenerateCodegenSchemaTask
@@ -34,8 +35,22 @@ class ReactPlugin : Plugin<Project> {
3435
checkJvmVersion(project)
3536
val extension = project.extensions.create("react", ReactExtension::class.java, project)
3637

38+
// We register a private extension on the rootProject so that project wide configs
39+
// like codegen config can be propagated from app project to libraries.
40+
val rootExtension =
41+
project.rootProject.extensions.findByType(PrivateReactExtension::class.java)
42+
?: project.rootProject.extensions.create(
43+
"privateReact", PrivateReactExtension::class.java, project)
44+
3745
// App Only Configuration
3846
project.pluginManager.withPlugin("com.android.application") {
47+
// We wire the root extension with the values coming from the app (either user populated or
48+
// defaults).
49+
rootExtension.root.set(extension.root)
50+
rootExtension.reactNativeDir.set(extension.reactNativeDir)
51+
rootExtension.codegenDir.set(extension.codegenDir)
52+
rootExtension.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
53+
3954
project.afterEvaluate {
4055
val reactNativeDir = extension.reactNativeDir.get().asFile
4156
val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties")
@@ -54,12 +69,12 @@ class ReactPlugin : Plugin<Project> {
5469
project.configureReactTasks(variant = variant, config = extension)
5570
}
5671
}
57-
configureCodegen(project, extension, isLibrary = false)
72+
configureCodegen(project, extension, rootExtension, isLibrary = false)
5873
}
5974

6075
// Library Only Configuration
6176
project.pluginManager.withPlugin("com.android.library") {
62-
configureCodegen(project, extension, isLibrary = true)
77+
configureCodegen(project, extension, rootExtension, isLibrary = true)
6378
}
6479
}
6580

@@ -82,34 +97,36 @@ class ReactPlugin : Plugin<Project> {
8297
}
8398
}
8499

85-
/**
86-
* A plugin to enable react-native-codegen in Gradle environment. See the Gradle API docs for more
87-
* information: https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html
88-
*/
100+
/** This function sets up `react-native-codegen` in our Gradle plugin. */
89101
@Suppress("UnstableApiUsage")
90-
private fun configureCodegen(project: Project, extension: ReactExtension, isLibrary: Boolean) {
102+
private fun configureCodegen(
103+
project: Project,
104+
localExtension: ReactExtension,
105+
rootExtension: PrivateReactExtension,
106+
isLibrary: Boolean
107+
) {
91108
// First, we set up the output dir for the codegen.
92109
val generatedSrcDir = File(project.buildDir, "generated/source/codegen")
93110

94111
// We specify the default value (convention) for jsRootDir.
95112
// It's the root folder for apps (so ../../ from the Gradle project)
96113
// and the package folder for library (so ../ from the Gradle project)
97114
if (isLibrary) {
98-
extension.jsRootDir.convention(project.layout.projectDirectory.dir("../"))
115+
localExtension.jsRootDir.convention(project.layout.projectDirectory.dir("../"))
99116
} else {
100-
extension.jsRootDir.convention(extension.root)
117+
localExtension.jsRootDir.convention(localExtension.root)
101118
}
102119

103120
val buildCodegenTask =
104121
project.tasks.register("buildCodegenCLI", BuildCodegenCLITask::class.java) {
105-
it.codegenDir.set(extension.codegenDir)
122+
it.codegenDir.set(rootExtension.codegenDir)
106123
val bashWindowsHome = project.findProperty("REACT_WINDOWS_BASH") as String?
107124
it.bashWindowsHome.set(bashWindowsHome)
108125

109126
// Please note that appNeedsCodegen is triggering a read of the package.json at
110127
// configuration time as we need to feed the onlyIf condition of this task.
111128
// Therefore, the appNeedsCodegen needs to be invoked inside this lambda.
112-
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(extension)
129+
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(rootExtension.root)
113130
it.onlyIf { isLibrary || needsCodegenFromPackageJson }
114131
}
115132

@@ -118,23 +135,24 @@ class ReactPlugin : Plugin<Project> {
118135
project.tasks.register(
119136
"generateCodegenSchemaFromJavaScript", GenerateCodegenSchemaTask::class.java) { it ->
120137
it.dependsOn(buildCodegenTask)
121-
it.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
122-
it.codegenDir.set(extension.codegenDir)
138+
it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
139+
it.codegenDir.set(rootExtension.codegenDir)
123140
it.generatedSrcDir.set(generatedSrcDir)
124141

125142
// We're reading the package.json at configuration time to properly feed
126143
// the `jsRootDir` @Input property of this task & the onlyIf. Therefore, the
127144
// parsePackageJson should be invoked inside this lambda.
128-
val packageJson = findPackageJsonFile(project, extension)
145+
val packageJson = findPackageJsonFile(project, rootExtension.root)
129146
val parsedPackageJson = packageJson?.let { JsonUtils.fromCodegenJson(it) }
130147

131148
val jsSrcsDirInPackageJson = parsedPackageJson?.codegenConfig?.jsSrcsDir
132149
if (jsSrcsDirInPackageJson != null) {
133150
it.jsRootDir.set(File(packageJson.parentFile, jsSrcsDirInPackageJson))
134151
} else {
135-
it.jsRootDir.set(extension.jsRootDir)
152+
it.jsRootDir.set(localExtension.jsRootDir)
136153
}
137-
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(extension)
154+
val needsCodegenFromPackageJson =
155+
project.needsCodegenFromPackageJson(rootExtension.root)
138156
it.onlyIf { isLibrary || needsCodegenFromPackageJson }
139157
}
140158

@@ -143,17 +161,18 @@ class ReactPlugin : Plugin<Project> {
143161
project.tasks.register(
144162
"generateCodegenArtifactsFromSchema", GenerateCodegenArtifactsTask::class.java) {
145163
it.dependsOn(generateCodegenSchemaTask)
146-
it.reactNativeDir.set(extension.reactNativeDir)
147-
it.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
164+
it.reactNativeDir.set(rootExtension.reactNativeDir)
165+
it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
148166
it.generatedSrcDir.set(generatedSrcDir)
149-
it.packageJsonFile.set(findPackageJsonFile(project, extension))
150-
it.codegenJavaPackageName.set(extension.codegenJavaPackageName)
151-
it.libraryName.set(extension.libraryName)
167+
it.packageJsonFile.set(findPackageJsonFile(project, rootExtension.root))
168+
it.codegenJavaPackageName.set(localExtension.codegenJavaPackageName)
169+
it.libraryName.set(localExtension.libraryName)
152170

153171
// Please note that appNeedsCodegen is triggering a read of the package.json at
154172
// configuration time as we need to feed the onlyIf condition of this task.
155173
// Therefore, the appNeedsCodegen needs to be invoked inside this lambda.
156-
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(extension)
174+
val needsCodegenFromPackageJson =
175+
project.needsCodegenFromPackageJson(rootExtension.root)
157176
it.onlyIf { isLibrary || needsCodegenFromPackageJson }
158177
}
159178

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.internal
9+
10+
import javax.inject.Inject
11+
import org.gradle.api.Project
12+
import org.gradle.api.file.DirectoryProperty
13+
import org.gradle.api.provider.ListProperty
14+
15+
/**
16+
* A private extension we set on the rootProject to make easier to share values at execution time
17+
* between app project and library project.
18+
*
19+
* Specifically, the [codegenDir], [reactNativeDir] and other properties should be provided by apps
20+
* (for setups like a monorepo which are app specific) and libraries should honor those values.
21+
*
22+
* Users are not supposed to access directly this extension from their build.gradle file.
23+
*/
24+
abstract class PrivateReactExtension @Inject constructor(project: Project) {
25+
26+
private val objects = project.objects
27+
28+
val root: DirectoryProperty = objects.directoryProperty()
29+
30+
val reactNativeDir: DirectoryProperty = objects.directoryProperty()
31+
32+
val nodeExecutableAndArgs: ListProperty<String> = objects.listProperty(String::class.java)
33+
34+
val codegenDir: DirectoryProperty = objects.directoryProperty()
35+
}

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.facebook.react.model.ModelPackageJson
1414
import com.facebook.react.utils.Os.cliPath
1515
import java.io.File
1616
import org.gradle.api.Project
17+
import org.gradle.api.file.DirectoryProperty
1718

1819
/**
1920
* Computes the entry file for React Native. The Algo follows this order:
@@ -189,13 +190,13 @@ internal fun projectPathToLibraryName(projectPath: String): String =
189190
* Gradle module (generally the case for library projects) or we fallback to looking into the `root`
190191
* folder of a React Native project (generally the case for app projects).
191192
*/
192-
internal fun findPackageJsonFile(project: Project, extension: ReactExtension): File? {
193+
internal fun findPackageJsonFile(project: Project, rootProperty: DirectoryProperty): File? {
193194
val inParent = project.file("../package.json")
194195
if (inParent.exists()) {
195196
return inParent
196197
}
197198

198-
val fromExtension = extension.root.file("package.json").orNull?.asFile
199+
val fromExtension = rootProperty.file("package.json").orNull?.asFile
199200
if (fromExtension?.exists() == true) {
200201
return fromExtension
201202
}
@@ -207,12 +208,15 @@ internal fun findPackageJsonFile(project: Project, extension: ReactExtension): F
207208
* Function to look for the `package.json` and parse it. It returns a [ModelPackageJson] if found or
208209
* null others.
209210
*
210-
* Please note that this function access the [ReactExtension] field properties and calls .get() on
211-
* them, so calling this during apply() of the ReactPlugin is not recommended. It should be invoked
212-
* inside lazy lambdas or at execution time.
211+
* Please note that this function access the [DirectoryProperty] parameter and calls .get() on them,
212+
* so calling this during apply() of the ReactPlugin is not recommended. It should be invoked inside
213+
* lazy lambdas or at execution time.
213214
*/
214-
internal fun readPackageJsonFile(project: Project, extension: ReactExtension): ModelPackageJson? {
215-
val packageJson = findPackageJsonFile(project, extension)
215+
internal fun readPackageJsonFile(
216+
project: Project,
217+
rootProperty: DirectoryProperty
218+
): ModelPackageJson? {
219+
val packageJson = findPackageJsonFile(project, rootProperty)
216220
return packageJson?.let { JsonUtils.fromCodegenJson(it) }
217221
}
218222

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
package com.facebook.react.utils
99

10-
import com.facebook.react.ReactExtension
1110
import com.facebook.react.model.ModelPackageJson
1211
import org.gradle.api.Project
12+
import org.gradle.api.file.DirectoryProperty
1313

1414
internal object ProjectUtils {
1515
internal val Project.isNewArchEnabled: Boolean
@@ -35,8 +35,8 @@ internal object ProjectUtils {
3535
HERMES_FALLBACK
3636
}
3737

38-
internal fun Project.needsCodegenFromPackageJson(extension: ReactExtension): Boolean {
39-
val parsedPackageJson = readPackageJsonFile(this, extension)
38+
internal fun Project.needsCodegenFromPackageJson(rootProperty: DirectoryProperty): Boolean {
39+
val parsedPackageJson = readPackageJsonFile(this, rootProperty)
4040
return needsCodegenFromPackageJson(parsedPackageJson)
4141
}
4242

packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/PathUtilsTest.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ class PathUtilsTest {
231231
project.plugins.apply("com.facebook.react")
232232
val extension = project.extensions.getByType(ReactExtension::class.java)
233233

234-
assertEquals(project.file("../package.json"), findPackageJsonFile(project, extension))
234+
assertEquals(project.file("../package.json"), findPackageJsonFile(project, extension.root))
235235
}
236236

237237
@Test
@@ -245,7 +245,7 @@ class PathUtilsTest {
245245
val extension =
246246
project.extensions.getByType(ReactExtension::class.java).apply { root.set(moduleFolder) }
247247

248-
assertEquals(localFile, findPackageJsonFile(project, extension))
248+
assertEquals(localFile, findPackageJsonFile(project, extension.root))
249249
}
250250

251251
@Test
@@ -257,7 +257,7 @@ class PathUtilsTest {
257257
val extension =
258258
project.extensions.getByType(ReactExtension::class.java).apply { root.set(moduleFolder) }
259259

260-
val actual = readPackageJsonFile(project, extension)
260+
val actual = readPackageJsonFile(project, extension.root)
261261

262262
assertNull(actual)
263263
}
@@ -272,7 +272,7 @@ class PathUtilsTest {
272272
val extension =
273273
project.extensions.getByType(ReactExtension::class.java).apply { root.set(moduleFolder) }
274274

275-
val actual = readPackageJsonFile(project, extension)
275+
val actual = readPackageJsonFile(project, extension.root)
276276

277277
assertNotNull(actual)
278278
assertNull(actual!!.codegenConfig)
@@ -298,7 +298,7 @@ class PathUtilsTest {
298298
val extension =
299299
project.extensions.getByType(ReactExtension::class.java).apply { root.set(moduleFolder) }
300300

301-
val actual = readPackageJsonFile(project, extension)
301+
val actual = readPackageJsonFile(project, extension.root)
302302

303303
assertNotNull(actual)
304304
assertNotNull(actual!!.codegenConfig)

packages/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/ProjectUtilsTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class ProjectUtilsTest {
125125
.trimIndent())
126126
}
127127
extension.root.set(tempFolder.root)
128-
assertTrue(project.needsCodegenFromPackageJson(extension))
128+
assertTrue(project.needsCodegenFromPackageJson(extension.root))
129129
}
130130

131131
@Test
@@ -143,7 +143,7 @@ class ProjectUtilsTest {
143143
.trimIndent())
144144
}
145145
extension.root.set(tempFolder.root)
146-
assertFalse(project.needsCodegenFromPackageJson(extension))
146+
assertFalse(project.needsCodegenFromPackageJson(extension.root))
147147
}
148148

149149
@Test
@@ -167,7 +167,7 @@ class ProjectUtilsTest {
167167
val project = createProject()
168168
val extension = TestReactExtension(project)
169169

170-
assertFalse(project.needsCodegenFromPackageJson(extension))
170+
assertFalse(project.needsCodegenFromPackageJson(extension.root))
171171
}
172172

173173
@Test

0 commit comments

Comments
 (0)