Skip to content

Commit

Permalink
fix(android): Append .exe to hermesc binary path for Windows users (#…
Browse files Browse the repository at this point in the history
…34151)

Summary:
Resolves #34116.

In a nutshell, the problem was a missing `.exe` extension on the `hermesc` binary path when running on Windows OS. The missing extension causes the method `.exists()` of the File instance to always return false, so none of the conditions ever met and an error was thrown whenever a release build with Hermes enabled was run on Windows. More details can be found in the comments on the above issues.

## Changelog

<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->

[Android] [Fixed] - Fix error of release builds with Hermes enabled for Windows users

Pull Request resolved: #34151

Test Plan:
### Reproduce

Changes on Gradle scrips are better tested on an actual application. To reproduce the issue you can:
1. Create or reuse a React Native application with version `v0.69.1` on a Windows machine
2. Enable Hermes on Android following the steps on the [documentation](https://reactnative.dev/docs/hermes#enabling-hermes)
3. Clean the build folder: `cd android && ./gradlew clean`
4. Bundle the JS and assets for a release version: `./gradlew bundleReleaseJsAndAssets`
5. The build fails with the following error:
```shell
Execution failed for task ':app:bundleReleaseJsAndAssets'.
> java.lang.Exception: Couldn't determine Hermesc location. Please set `project.ext.react.hermesCommand` to the path of the hermesc binary file. node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc
```

### Test the changes

Follow the same steps above using the fix on this PR and the error should disappear 🙂

Reviewed By: NickGerleman

Differential Revision: D37755468

Pulled By: cortinico

fbshipit-source-id: 2ad0ced583555b907259df116f64a45da6d153f3
  • Loading branch information
JoseLion authored and facebook-github-bot committed Aug 4, 2022
1 parent 54a4fcb commit 7fcdb9d
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ internal fun detectOSAwareHermesCommand(projectRoot: File, hermesCommand: String

// 3. If the react-native contains a pre-built hermesc, use it.
val prebuiltHermesPath =
HERMESC_IN_REACT_NATIVE_PATH.replace("%OS-BIN%", getHermesOSBin())
HERMESC_IN_REACT_NATIVE_DIR.plus(getHermesCBin())
.replace("%OS-BIN%", getHermesOSBin())
// Execution on Windows fails with / as separator
.replace('/', File.separatorChar)

Expand All @@ -163,19 +164,21 @@ internal fun detectOSAwareHermesCommand(projectRoot: File, hermesCommand: String

/**
* Gets the location where Hermesc should be. If nothing is specified, built hermesc is assumed to
* be inside [HERMESC_BUILT_FROM_SOURCE_PATH]. Otherwise user can specify an override with
* be inside [HERMESC_BUILT_FROM_SOURCE_DIR]. Otherwise user can specify an override with
* [pathOverride], which is assumed to be an absolute path where Hermes source code is
* provided/built.
*
* @param projectRoot The root of the Project.
*/
internal fun getBuiltHermescFile(projectRoot: File, pathOverride: String?) =
if (!pathOverride.isNullOrBlank()) {
File(pathOverride, "build/bin/hermesc")
File(pathOverride, "build/bin/${getHermesCBin()}")
} else {
File(projectRoot, HERMESC_BUILT_FROM_SOURCE_PATH)
File(projectRoot, HERMESC_BUILT_FROM_SOURCE_DIR.plus(getHermesCBin()))
}

internal fun getHermesCBin() = if (Os.isWindows()) "hermesc.exe" else "hermesc"

internal fun getHermesOSBin(): String {
if (Os.isWindows()) return "win64-bin"
if (Os.isMac()) return "osx-bin"
Expand Down Expand Up @@ -203,7 +206,6 @@ internal fun findPackageJsonFile(project: Project, extension: ReactExtension): F
extension.root.file("package.json").orNull?.asFile
}

private const val HERMESC_IN_REACT_NATIVE_PATH =
"node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc"
private const val HERMESC_BUILT_FROM_SOURCE_PATH =
"node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/hermesc"
private const val HERMESC_IN_REACT_NATIVE_DIR = "node_modules/react-native/sdks/hermesc/%OS-BIN%/"
private const val HERMESC_BUILT_FROM_SOURCE_DIR =
"node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/"
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,41 @@ class PathUtilsTest {
getBuiltHermescFile(tempFolder.root, ""))
}

@Test
@WithOs(OS.WIN)
fun getBuiltHermescFile_onWindows_withoutOverride() {
assertEquals(
File(
tempFolder.root,
"node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/hermesc.exe"),
getBuiltHermescFile(tempFolder.root, ""))
}

@Test
fun getBuiltHermescFile_withOverride() {
assertEquals(
File("/home/circleci/hermes/build/bin/hermesc"),
getBuiltHermescFile(tempFolder.root, "/home/circleci/hermes"))
}

@Test
@WithOs(OS.WIN)
fun getHermesCBin_onWindows_returnsHermescExe() {
assertEquals("hermesc.exe", getHermesCBin())
}

@Test
@WithOs(OS.UNIX)
fun getHermesCBin_onUnix_returnsHermesc() {
assertEquals("hermesc", getHermesCBin())
}

@Test
@WithOs(OS.MAC)
fun getHermesCBin_onMax_returnsHermesc() {
assertEquals("hermesc", getHermesCBin())
}

@Test
fun findPackageJsonFile_withFileInParentFolder_picksItUp() {
tempFolder.newFile("package.json")
Expand Down
8 changes: 5 additions & 3 deletions react.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -102,20 +102,22 @@ def getHermesCommand = {
}
}

def hermescBin = Os.isFamily(Os.FAMILY_WINDOWS) ? 'hermesc.exe' : 'hermesc'

// 2. If the project is building hermes-engine from source, use hermesc from there
// Also note that user can override the hermes source location with
// the `REACT_NATIVE_OVERRIDE_HERMES_DIR` env variable.
def hermesOverrideDir = System.getenv("REACT_NATIVE_OVERRIDE_HERMES_DIR")
def builtHermesc = hermesOverrideDir ?
new File(hermesOverrideDir, "build/bin/hermesc") :
new File(reactRoot, "node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/hermesc")
new File(hermesOverrideDir, "build/bin/$hermescBin") :
new File(reactRoot, "node_modules/react-native/ReactAndroid/hermes-engine/build/hermes/bin/$hermescBin")

if (builtHermesc.exists()) {
return builtHermesc.getAbsolutePath()
}

// 3. If the react-native contains a pre-built hermesc, use it.
def prebuiltHermesPath = "node_modules/react-native/sdks/hermesc/%OS-BIN%/hermesc"
def prebuiltHermesPath = "node_modules/react-native/sdks/hermesc/%OS-BIN%/$hermescBin"
.replaceAll("%OS-BIN%", getHermesOSBin())
.replace('/' as char, File.separatorChar);
def prebuiltHermes = new File(reactRoot, prebuiltHermesPath)
Expand Down

0 comments on commit 7fcdb9d

Please sign in to comment.