Skip to content
Browse files
Use Node's module resolution algo to find JSC & Hermes (#26773)
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
  • Loading branch information
ide authored and facebook-github-bot committed Oct 14, 2019
1 parent 6c7d34e commit fc25f288fe553cb7e8f04b8ce4b56297b7fa40d5
Showing 1 changed file with 56 additions and 16 deletions.
@@ -9,6 +9,8 @@ plugins {

import java.nio.file.Paths

@@ -92,17 +94,16 @@ task prepareFolly(dependsOn: dependenciesPath ? [] : [downloadFolly], type: Copy

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

if (!hermesAAR.exists()) {
// At Facebook, this file is in a different folder
hermesAAR = file("$projectDir/../../node_modules/hermes-engine/android/hermes-debug.aar")
def hermesAAR = file("$hermesPackagePath/android/hermes-debug.aar")
if (!hermesAAR.exists()) {
throw new GradleScriptException("The hermes-engine npm package is missing \"android/hermes-debug.aar\"")

def soFiles = zipTree(hermesAAR).matching({ it.include "**/*.so" })

copy {
@@ -160,16 +161,20 @@ task prepareGlog(dependsOn: dependenciesPath ? [] : [downloadGlog], type: Copy)
// Create library module based on jsc from npm
task prepareJSC {
doLast {
def jscPackageRoot = file("$projectDir/../node_modules/jsc-android/dist")
if (!jscPackageRoot.exists()) {
// For an app to build from RN source, the jsc-android is located at /path/to/app/node_modules
// and $projectDir may located at /path/to/app/node_modules/react-native/ReactAndroid
jscPackageRoot = file("$projectDir/../../jsc-android/dist")
def jscPackagePath = findNodeModulePath(projectDir, "jsc-android")
if (!jscPackagePath) {
throw new GradleScriptException("Could not find the jsc-android npm package")

def jscDist = file("$jscPackagePath/dist")
if (!jscDist.exists()) {
throw new GradleScriptException("The jsc-android npm package is missing its \"dist\" directory")
def jscAAR = fileTree(jscPackageRoot).matching({ it.include "**/android-jsc/**/*.aar" }).singleFile

def jscAAR = fileTree(jscDist).matching({ it.include "**/android-jsc/**/*.aar" }).singleFile
def soFiles = zipTree(jscAAR).matching({ it.include "**/*.so" })

def headerFiles = fileTree(jscPackageRoot).matching({ it.include "**/include/*.h" })
def headerFiles = fileTree(jscDist).matching({ it.include "**/include/*.h" })

copy {
@@ -192,6 +197,41 @@ task downloadNdkBuildDependencies {

* Finds the path of the installed npm package with the given name using Node's
* module resolution algorithm, which searches "node_modules" directories up to
* the file system root. This handles various cases, including:
* - Working in the open-source RN repo:
* Gradle: /path/to/react-native/ReactAndroid
* Node module: /path/to/react-native/node_modules/[package]
* - Installing RN as a dependency of an app and searching for hoisted
* dependencies:
* Gradle: /path/to/app/node_modules/react-native/ReactAndroid
* Node module: /path/to/app/node_modules/[package]
* - Working in a larger repo (e.g., Facebook) that contains RN:
* Gradle: /path/to/repo/path/to/react-native/ReactAndroid
* Node module: /path/to/repo/node_modules/[package]
* The search begins at the given base directory (a File object). The returned
* path is a string.
def findNodeModulePath(baseDir, packageName) {
def basePath = baseDir.toPath().normalize()
// Node's module resolution algorithm searches up to the root directory,
// after which the base path will be null
while (basePath) {
def candidatePath = Paths.get(basePath.toString(), "node_modules", packageName)
if (candidatePath.toFile().exists()) {
return candidatePath.toString()
basePath = basePath.getParent()
return null

def getNdkBuildName() {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
return "ndk-build.cmd"

0 comments on commit fc25f28

Please sign in to comment.