Skip to content

Commit 90a1471

Browse files
kraenhansenCopilot
andauthored
Check Hermes override pre-condition from Host package when building for Android (#234)
* Ensure REACT_NATIVE_OVERRIDE_HERMES_DIR is set * Add test for linkNodeApiModules task * Use relative path to CLI instead of "npx" * Add test calling directly into bin * Include stderr in assert message * Add optional commandLinePrefix on windows to run CLI in a shell * Skip Gradle tests by default and enable it on CI * Add changesets * Update packages/host/src/node/gradle.test.ts Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent b53f84a commit 90a1471

File tree

7 files changed

+158
-3
lines changed

7 files changed

+158
-3
lines changed

.changeset/many-candies-retire.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-native-node-api": patch
3+
---
4+
5+
Fix auto-linking from Gradle builds on Windows

.changeset/silver-suits-double.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"react-native-node-api": minor
3+
---
4+
5+
Assert that REACT_NATIVE_OVERRIDE_HERMES_DIR is set when Android / Gradle projects depend on the host package

.github/workflows/check.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ name: Check
33
env:
44
# Version here should match the one in React Native template and packages/cmake-rn/src/cli.ts
55
NDK_VERSION: 27.1.12297006
6+
# Enabling the Gradle test on CI (disabled by default because it downloads a lot)
7+
ENABLE_GRADLE_TESTS: true
68

79
on:
810
push:

packages/host/android/build.gradle

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import java.nio.file.Paths
22
import groovy.json.JsonSlurper
3+
import org.gradle.internal.os.OperatingSystem
34

45
buildscript {
56
ext.getExtOrDefault = {name ->
@@ -134,12 +135,30 @@ dependencies {
134135
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
135136
}
136137

138+
task checkHermesOverride {
139+
doFirst {
140+
if (!System.getenv("REACT_NATIVE_OVERRIDE_HERMES_DIR")) {
141+
throw new GradleException([
142+
"React Native Node-API needs a custom version of Hermes with Node-API enabled.",
143+
"Run the following in your terminal, to clone Hermes and instruct React Native to use it:",
144+
"",
145+
"export REACT_NATIVE_OVERRIDE_HERMES_DIR=`npx react-native-node-api vendor-hermes --silent --force`",
146+
"",
147+
"And follow this guide to build React Native from source:",
148+
"https://reactnative.dev/contributing/how-to-build-from-source#update-your-project-to-build-from-source"
149+
].join('\n'))
150+
}
151+
}
152+
}
153+
154+
def commandLinePrefix = OperatingSystem.current().isWindows() ? ["cmd", "/c", "node"] : []
155+
def cliPath = file("../bin/react-native-node-api.mjs")
156+
137157
// Custom task to fetch jniLibs paths via CLI
138158
task linkNodeApiModules {
139159
doLast {
140160
exec {
141-
// TODO: Support --strip-path-suffix
142-
commandLine 'npx', 'react-native-node-api', 'link', '--android', rootProject.rootDir.absolutePath
161+
commandLine commandLinePrefix + [cliPath, 'link', '--android', rootProject.rootDir.absolutePath]
143162
standardOutput = System.out
144163
errorOutput = System.err
145164
// Enable color output
@@ -150,5 +169,5 @@ task linkNodeApiModules {
150169
}
151170
}
152171

153-
preBuild.dependsOn linkNodeApiModules
172+
preBuild.dependsOn checkHermesOverride, linkNodeApiModules
154173

packages/host/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"build-weak-node-api": "cmake-rn --no-auto-link --no-weak-node-api-linkage --xcframework-extension --source ./weak-node-api --out ./weak-node-api",
4848
"build-weak-node-api:all-triplets": "cmake-rn --android --apple --no-auto-link --no-weak-node-api-linkage --xcframework-extension --source ./weak-node-api --out ./weak-node-api",
4949
"test": "tsx --test --test-reporter=@reporters/github --test-reporter-destination=stdout --test-reporter=spec --test-reporter-destination=stdout src/node/**/*.test.ts src/node/*.test.ts",
50+
"test:gradle": "ENABLE_GRADLE_TESTS=true node --run test",
5051
"bootstrap": "node --run copy-node-api-headers && node --run generate-weak-node-api-injector && node --run generate-weak-node-api && node --run build-weak-node-api",
5152
"prerelease": "node --run copy-node-api-headers && node --run generate-weak-node-api-injector && node --run generate-weak-node-api && node --run build-weak-node-api:all-triplets"
5253
},
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import assert from "node:assert/strict";
2+
import { describe, it } from "node:test";
3+
import cp from "node:child_process";
4+
import path from "node:path";
5+
6+
const PACKAGE_ROOT = path.join(__dirname, "../../..");
7+
const BIN_PATH = path.join(PACKAGE_ROOT, "bin/react-native-node-api.mjs");
8+
9+
describe("bin", () => {
10+
describe("help command", () => {
11+
it("should succeed with a mention of usage", () => {
12+
const { status, stdout, stderr } = cp.spawnSync(
13+
process.execPath,
14+
[BIN_PATH, "help"],
15+
{
16+
cwd: PACKAGE_ROOT,
17+
encoding: "utf8",
18+
},
19+
);
20+
21+
assert.equal(
22+
status,
23+
0,
24+
`Expected success (got ${status}): ${stdout} ${stderr}`,
25+
);
26+
assert.match(
27+
stdout,
28+
/Usage: react-native-node-api/,
29+
`Failed to find expected output (stdout: ${stdout} stderr: ${stderr})`,
30+
);
31+
});
32+
});
33+
34+
describe("link command", () => {
35+
it("should succeed with a mention of Node-API modules", () => {
36+
const { status, stdout, stderr } = cp.spawnSync(
37+
process.execPath,
38+
[BIN_PATH, "link", "--android", "--apple"],
39+
{
40+
cwd: PACKAGE_ROOT,
41+
encoding: "utf8",
42+
},
43+
);
44+
45+
assert.equal(
46+
status,
47+
0,
48+
`Expected success (got ${status}): ${stdout} ${stderr}`,
49+
);
50+
assert.match(
51+
stdout + stderr,
52+
/Auto-linking Node-API modules/,
53+
`Failed to find expected output (stdout: ${stdout} stderr: ${stderr})`,
54+
);
55+
});
56+
});
57+
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import assert from "node:assert/strict";
2+
import { describe, it } from "node:test";
3+
import cp from "node:child_process";
4+
import path from "node:path";
5+
6+
const PACKAGE_ROOT = path.join(__dirname, "../..");
7+
const MONOREPO_ROOT = path.join(PACKAGE_ROOT, "../..");
8+
const TEST_APP_ANDROID_PATH = path.join(MONOREPO_ROOT, "apps/test-app/android");
9+
10+
describe(
11+
"Gradle tasks",
12+
// Skipping these tests by default, as they download a lot and takes a long time
13+
{ skip: process.env.ENABLE_GRADLE_TESTS !== "true" },
14+
() => {
15+
describe("checkHermesOverride task", () => {
16+
it("should fail if REACT_NATIVE_OVERRIDE_HERMES_DIR is not set", () => {
17+
const { status, stdout, stderr } = cp.spawnSync(
18+
"sh",
19+
["gradlew", "react-native-node-api:checkHermesOverride"],
20+
{
21+
cwd: TEST_APP_ANDROID_PATH,
22+
env: {
23+
...process.env,
24+
REACT_NATIVE_OVERRIDE_HERMES_DIR: undefined,
25+
},
26+
encoding: "utf-8",
27+
},
28+
);
29+
30+
assert.notEqual(status, 0, `Expected failure: ${stdout} ${stderr}`);
31+
assert.match(
32+
stderr,
33+
/React Native Node-API needs a custom version of Hermes with Node-API enabled/,
34+
);
35+
assert.match(
36+
stderr,
37+
/Run the following in your terminal, to clone Hermes and instruct React Native to use it/,
38+
);
39+
assert.match(
40+
stderr,
41+
/export REACT_NATIVE_OVERRIDE_HERMES_DIR=`npx react-native-node-api vendor-hermes --silent --force`/,
42+
);
43+
assert.match(
44+
stderr,
45+
/And follow this guide to build React Native from source/,
46+
);
47+
});
48+
});
49+
50+
describe("linkNodeApiModules task", () => {
51+
it("should call the CLI to autolink", () => {
52+
const { status, stdout, stderr } = cp.spawnSync(
53+
"sh",
54+
["gradlew", "react-native-node-api:linkNodeApiModules"],
55+
{
56+
cwd: TEST_APP_ANDROID_PATH,
57+
encoding: "utf-8",
58+
},
59+
);
60+
61+
assert.equal(status, 0, `Expected success: ${stdout} ${stderr}`);
62+
assert.match(stdout, /Auto-linking Node-API modules/);
63+
});
64+
});
65+
},
66+
);

0 commit comments

Comments
 (0)