Skip to content

Commit de512cd

Browse files
cortinicokelset
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 d60da23 commit de512cd

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
@@ -676,12 +676,7 @@ react {
676676
// TODO: The library name is chosen for parity with Fabric components & iOS
677677
// This should be changed to a more generic name, e.g. `ReactCoreSpec`.
678678
libraryName = "rncore"
679-
root = file("..")
680679
jsRootDir = file("../Libraries")
681-
reactNativeDir = file("$projectDir/..")
682-
// We search for the codegen in either one of the `node_modules` folder or in the
683-
// root packages folder (that's for when we build from source without calling `yarn install`).
684-
codegenDir = file(findNodeModulePath(projectDir, "react-native-codegen") ?: "../packages/react-native-codegen/")
685680
}
686681

687682
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
@@ -10,6 +10,7 @@ package com.facebook.react
1010
import com.android.build.api.variant.AndroidComponentsExtension
1111
import com.android.build.gradle.AppExtension
1212
import com.android.build.gradle.internal.tasks.factory.dependsOn
13+
import com.facebook.react.internal.PrivateReactExtension
1314
import com.facebook.react.tasks.BuildCodegenCLITask
1415
import com.facebook.react.tasks.GenerateCodegenArtifactsTask
1516
import com.facebook.react.tasks.GenerateCodegenSchemaTask
@@ -35,8 +36,22 @@ class ReactPlugin : Plugin<Project> {
3536
checkJvmVersion(project)
3637
val extension = project.extensions.create("react", ReactExtension::class.java, project)
3738

39+
// We register a private extension on the rootProject so that project wide configs
40+
// like codegen config can be propagated from app project to libraries.
41+
val rootExtension =
42+
project.rootProject.extensions.findByType(PrivateReactExtension::class.java)
43+
?: project.rootProject.extensions.create(
44+
"privateReact", PrivateReactExtension::class.java, project)
45+
3846
// App Only Configuration
3947
project.pluginManager.withPlugin("com.android.application") {
48+
// We wire the root extension with the values coming from the app (either user populated or
49+
// defaults).
50+
rootExtension.root.set(extension.root)
51+
rootExtension.reactNativeDir.set(extension.reactNativeDir)
52+
rootExtension.codegenDir.set(extension.codegenDir)
53+
rootExtension.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
54+
4055
project.afterEvaluate {
4156
val reactNativeDir = extension.reactNativeDir.get().asFile
4257
val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties")
@@ -73,12 +88,12 @@ class ReactPlugin : Plugin<Project> {
7388
}
7489
}
7590
}
76-
configureCodegen(project, extension, isLibrary = false)
91+
configureCodegen(project, extension, rootExtension, isLibrary = false)
7792
}
7893

7994
// Library Only Configuration
8095
project.pluginManager.withPlugin("com.android.library") {
81-
configureCodegen(project, extension, isLibrary = true)
96+
configureCodegen(project, extension, rootExtension, isLibrary = true)
8297
}
8398
}
8499

@@ -101,34 +116,36 @@ class ReactPlugin : Plugin<Project> {
101116
}
102117
}
103118

104-
/**
105-
* A plugin to enable react-native-codegen in Gradle environment. See the Gradle API docs for more
106-
* information: https://docs.gradle.org/current/javadoc/org/gradle/api/Project.html
107-
*/
119+
/** This function sets up `react-native-codegen` in our Gradle plugin. */
108120
@Suppress("UnstableApiUsage")
109-
private fun configureCodegen(project: Project, extension: ReactExtension, isLibrary: Boolean) {
121+
private fun configureCodegen(
122+
project: Project,
123+
localExtension: ReactExtension,
124+
rootExtension: PrivateReactExtension,
125+
isLibrary: Boolean
126+
) {
110127
// First, we set up the output dir for the codegen.
111128
val generatedSrcDir = File(project.buildDir, "generated/source/codegen")
112129

113130
// We specify the default value (convention) for jsRootDir.
114131
// It's the root folder for apps (so ../../ from the Gradle project)
115132
// and the package folder for library (so ../ from the Gradle project)
116133
if (isLibrary) {
117-
extension.jsRootDir.convention(project.layout.projectDirectory.dir("../"))
134+
localExtension.jsRootDir.convention(project.layout.projectDirectory.dir("../"))
118135
} else {
119-
extension.jsRootDir.convention(extension.root)
136+
localExtension.jsRootDir.convention(localExtension.root)
120137
}
121138

122139
val buildCodegenTask =
123140
project.tasks.register("buildCodegenCLI", BuildCodegenCLITask::class.java) {
124-
it.codegenDir.set(extension.codegenDir)
141+
it.codegenDir.set(rootExtension.codegenDir)
125142
val bashWindowsHome = project.findProperty("REACT_WINDOWS_BASH") as String?
126143
it.bashWindowsHome.set(bashWindowsHome)
127144

128145
// Please note that appNeedsCodegen is triggering a read of the package.json at
129146
// configuration time as we need to feed the onlyIf condition of this task.
130147
// Therefore, the appNeedsCodegen needs to be invoked inside this lambda.
131-
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(extension)
148+
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(rootExtension.root)
132149
it.onlyIf { isLibrary || needsCodegenFromPackageJson }
133150
}
134151

@@ -137,23 +154,24 @@ class ReactPlugin : Plugin<Project> {
137154
project.tasks.register(
138155
"generateCodegenSchemaFromJavaScript", GenerateCodegenSchemaTask::class.java) { it ->
139156
it.dependsOn(buildCodegenTask)
140-
it.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
141-
it.codegenDir.set(extension.codegenDir)
157+
it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
158+
it.codegenDir.set(rootExtension.codegenDir)
142159
it.generatedSrcDir.set(generatedSrcDir)
143160

144161
// We're reading the package.json at configuration time to properly feed
145162
// the `jsRootDir` @Input property of this task & the onlyIf. Therefore, the
146163
// parsePackageJson should be invoked inside this lambda.
147-
val packageJson = findPackageJsonFile(project, extension)
164+
val packageJson = findPackageJsonFile(project, rootExtension.root)
148165
val parsedPackageJson = packageJson?.let { JsonUtils.fromCodegenJson(it) }
149166

150167
val jsSrcsDirInPackageJson = parsedPackageJson?.codegenConfig?.jsSrcsDir
151168
if (jsSrcsDirInPackageJson != null) {
152169
it.jsRootDir.set(File(packageJson.parentFile, jsSrcsDirInPackageJson))
153170
} else {
154-
it.jsRootDir.set(extension.jsRootDir)
171+
it.jsRootDir.set(localExtension.jsRootDir)
155172
}
156-
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(extension)
173+
val needsCodegenFromPackageJson =
174+
project.needsCodegenFromPackageJson(rootExtension.root)
157175
it.onlyIf { isLibrary || needsCodegenFromPackageJson }
158176
}
159177

@@ -162,17 +180,18 @@ class ReactPlugin : Plugin<Project> {
162180
project.tasks.register(
163181
"generateCodegenArtifactsFromSchema", GenerateCodegenArtifactsTask::class.java) {
164182
it.dependsOn(generateCodegenSchemaTask)
165-
it.reactNativeDir.set(extension.reactNativeDir)
166-
it.nodeExecutableAndArgs.set(extension.nodeExecutableAndArgs)
183+
it.reactNativeDir.set(rootExtension.reactNativeDir)
184+
it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
167185
it.generatedSrcDir.set(generatedSrcDir)
168-
it.packageJsonFile.set(findPackageJsonFile(project, extension))
169-
it.codegenJavaPackageName.set(extension.codegenJavaPackageName)
170-
it.libraryName.set(extension.libraryName)
186+
it.packageJsonFile.set(findPackageJsonFile(project, rootExtension.root))
187+
it.codegenJavaPackageName.set(localExtension.codegenJavaPackageName)
188+
it.libraryName.set(localExtension.libraryName)
171189

172190
// Please note that appNeedsCodegen is triggering a read of the package.json at
173191
// configuration time as we need to feed the onlyIf condition of this task.
174192
// Therefore, the appNeedsCodegen needs to be invoked inside this lambda.
175-
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(extension)
193+
val needsCodegenFromPackageJson =
194+
project.needsCodegenFromPackageJson(rootExtension.root)
176195
it.onlyIf { isLibrary || needsCodegenFromPackageJson }
177196
}
178197

Lines changed: 35 additions & 0 deletions
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)