Skip to content

Commit fc25f28

Browse files
idefacebook-github-bot
authored andcommitted
Use Node's module resolution algo to find JSC & Hermes (#26773)
Summary: The Gradle build file looks up jsc-android and hermes-engine using hard-coded paths. Rather than assuming the location of these packages, which are distributed and installed as npm packages, this commit makes the Gradle file use Node's standard module resolution algorithm. It looks up the file hierarchy until it finds a matching npm package or reaches the root directory. ## Changelog: [Android] [Changed] - ReactAndroid's Gradle file uses Node's module resolution algorithm to find JSC & Hermes Pull Request resolved: #26773 Test Plan: Ensure that CI tests pass, and that `./gradlew :ReactAndroid:installArchives` works. Printed out the paths that the Gradle script found for jsc-android and hermes-engine (both were `<my stuff>/react-native/node_modules/jsc-android|hermes-engine`). Differential Revision: D17903179 Pulled By: cpojer fbshipit-source-id: 9ac3ba509974f39f87b511d5bc3398451c12393f
1 parent 6c7d34e commit fc25f28

File tree

1 file changed

+56
-16
lines changed

1 file changed

+56
-16
lines changed

ReactAndroid/build.gradle

+56-16
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ plugins {
99
id("de.undercouch.download")
1010
}
1111

12+
import java.nio.file.Paths
13+
1214
import de.undercouch.gradle.tasks.download.Download
1315
import org.apache.tools.ant.taskdefs.condition.Os
1416
import org.apache.tools.ant.filters.ReplaceTokens
@@ -92,17 +94,16 @@ task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy
9294
}
9395

9496
task prepareHermes() {
95-
def hermesAAR = file("$projectDir/../node_modules/hermes-engine/android/hermes-debug.aar")
96-
if (!hermesAAR.exists()) {
97-
// For an app to build from RN source, hermes-engine is located at /path/to/app/node_modules
98-
// and $projectDir is located at /path/to/app/node_modules/react-native/ReactAndroid
99-
hermesAAR = file("$projectDir/../../hermes-engine/android/hermes-debug.aar")
97+
def hermesPackagePath = findNodeModulePath(projectDir, "hermes-engine")
98+
if (!hermesPackagePath) {
99+
throw new GradleScriptException("Could not find the hermes-engine npm package")
100+
}
100101

101-
if (!hermesAAR.exists()) {
102-
// At Facebook, this file is in a different folder
103-
hermesAAR = file("$projectDir/../../node_modules/hermes-engine/android/hermes-debug.aar")
104-
}
102+
def hermesAAR = file("$hermesPackagePath/android/hermes-debug.aar")
103+
if (!hermesAAR.exists()) {
104+
throw new GradleScriptException("The hermes-engine npm package is missing \"android/hermes-debug.aar\"")
105105
}
106+
106107
def soFiles = zipTree(hermesAAR).matching({ it.include "**/*.so" })
107108

108109
copy {
@@ -160,16 +161,20 @@ task prepareGlog(dependsOn: dependenciesPath ? [] : [downloadGlog], type: Copy)
160161
// Create Android.mk library module based on jsc from npm
161162
task prepareJSC {
162163
doLast {
163-
def jscPackageRoot = file("$projectDir/../node_modules/jsc-android/dist")
164-
if (!jscPackageRoot.exists()) {
165-
// For an app to build from RN source, the jsc-android is located at /path/to/app/node_modules
166-
// and $projectDir may located at /path/to/app/node_modules/react-native/ReactAndroid
167-
jscPackageRoot = file("$projectDir/../../jsc-android/dist")
164+
def jscPackagePath = findNodeModulePath(projectDir, "jsc-android")
165+
if (!jscPackagePath) {
166+
throw new GradleScriptException("Could not find the jsc-android npm package")
167+
}
168+
169+
def jscDist = file("$jscPackagePath/dist")
170+
if (!jscDist.exists()) {
171+
throw new GradleScriptException("The jsc-android npm package is missing its \"dist\" directory")
168172
}
169-
def jscAAR = fileTree(jscPackageRoot).matching({ it.include "**/android-jsc/**/*.aar" }).singleFile
173+
174+
def jscAAR = fileTree(jscDist).matching({ it.include "**/android-jsc/**/*.aar" }).singleFile
170175
def soFiles = zipTree(jscAAR).matching({ it.include "**/*.so" })
171176

172-
def headerFiles = fileTree(jscPackageRoot).matching({ it.include "**/include/*.h" })
177+
def headerFiles = fileTree(jscDist).matching({ it.include "**/include/*.h" })
173178

174179
copy {
175180
from(soFiles)
@@ -192,6 +197,41 @@ task downloadNdkBuildDependencies {
192197
dependsOn(downloadGlog)
193198
}
194199

200+
/**
201+
* Finds the path of the installed npm package with the given name using Node's
202+
* module resolution algorithm, which searches "node_modules" directories up to
203+
* the file system root. This handles various cases, including:
204+
*
205+
* - Working in the open-source RN repo:
206+
* Gradle: /path/to/react-native/ReactAndroid
207+
* Node module: /path/to/react-native/node_modules/[package]
208+
*
209+
* - Installing RN as a dependency of an app and searching for hoisted
210+
* dependencies:
211+
* Gradle: /path/to/app/node_modules/react-native/ReactAndroid
212+
* Node module: /path/to/app/node_modules/[package]
213+
*
214+
* - Working in a larger repo (e.g., Facebook) that contains RN:
215+
* Gradle: /path/to/repo/path/to/react-native/ReactAndroid
216+
* Node module: /path/to/repo/node_modules/[package]
217+
*
218+
* The search begins at the given base directory (a File object). The returned
219+
* path is a string.
220+
*/
221+
def findNodeModulePath(baseDir, packageName) {
222+
def basePath = baseDir.toPath().normalize()
223+
// Node's module resolution algorithm searches up to the root directory,
224+
// after which the base path will be null
225+
while (basePath) {
226+
def candidatePath = Paths.get(basePath.toString(), "node_modules", packageName)
227+
if (candidatePath.toFile().exists()) {
228+
return candidatePath.toString()
229+
}
230+
basePath = basePath.getParent()
231+
}
232+
return null
233+
}
234+
195235
def getNdkBuildName() {
196236
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
197237
return "ndk-build.cmd"

0 commit comments

Comments
 (0)