Skip to content

Commit 263a3bc

Browse files
kraenhansenclaude
andauthored
Configure the host Hermes compiler for one architecture (#442)
* Configure the host Hermes compiler for one architecture Passing CMAKE_OSX_ARCHITECTURES=arm64;x86_64 to build a universal hermesc makes llvh's feature try-compiles fail — standard headers report as missing and the configure dies on CheckAtomic. Configure it the way Hermes and React Native do, for the host architecture only. hermesc is then native to the Mac that built the archive, so the archive name carries the host architecture: a Mac of the other architecture finds no published archive and builds its own, instead of downloading a hermesc it cannot execute. Also assign each command substitution before echoing it into GITHUB_OUTPUT. Inside `echo "x=$(cmd)"` the step's exit status is echo's, so the failing build above passed its step with an empty path and only surfaced one step later, as `gh release upload ""`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkNbgdyuKgHaFwT27RahGH * Trigger CI with the labels attached The workflow's pull_request trigger doesn't fire on `labeled`, so the label-gated jobs need a synchronize event to be evaluated against. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UkNbgdyuKgHaFwT27RahGH --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 166b3bf commit 263a3bc

4 files changed

Lines changed: 38 additions & 10 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
"react-native-node-api": patch
3+
---
4+
5+
Fix `prebuilt-hermes` failing to configure the host Hermes compiler. It passed
6+
`CMAKE_OSX_ARCHITECTURES=arm64;x86_64` to build a universal `hermesc`, but a
7+
multi-arch host configure makes llvh's feature try-compiles fail — standard
8+
headers report as missing and the configure dies with "Host compiler appears to
9+
require libatomic, but cannot find it". The host compiler is now configured the
10+
way Hermes and React Native configure it, for the host architecture only.
11+
12+
`hermesc` is consequently native to the Mac that built the archive, so the
13+
archive name now carries the host architecture. An Intel Mac finds no published
14+
archive for its architecture and builds its own, rather than downloading one
15+
whose `hermesc` it cannot execute.

.github/workflows/hermes-prebuilt.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,17 @@ jobs:
4343
- run: pnpm run build
4444
# Resolved from the test app so the archive is built against the React
4545
# Native version this repository actually pins.
46+
# Each command substitution is assigned before it is echoed: inside
47+
# `echo "x=$(cmd)"` the step's exit status is echo's, so a failing cmd
48+
# passes the step with an empty value.
4649
- name: Resolve prebuilt Hermes name
4750
id: hermes
4851
working-directory: apps/test-app
4952
run: |
50-
echo "archive=$(pnpm exec react-native-node-api prebuilt-hermes --print name)" >> "$GITHUB_OUTPUT"
51-
echo "tag=$(pnpm exec react-native-node-api prebuilt-hermes --print tag)" >> "$GITHUB_OUTPUT"
53+
archive=$(pnpm exec react-native-node-api prebuilt-hermes --print name)
54+
tag=$(pnpm exec react-native-node-api prebuilt-hermes --print tag)
55+
echo "archive=$archive" >> "$GITHUB_OUTPUT"
56+
echo "tag=$tag" >> "$GITHUB_OUTPUT"
5257
- name: Cache prebuilt Hermes
5358
uses: actions/cache@v6
5459
with:
@@ -59,7 +64,9 @@ jobs:
5964
- name: Build prebuilt Hermes
6065
id: build
6166
working-directory: apps/test-app
62-
run: echo "path=$(pnpm exec react-native-node-api prebuilt-hermes --no-download)" >> "$GITHUB_OUTPUT"
67+
run: |
68+
path=$(pnpm exec react-native-node-api prebuilt-hermes --no-download)
69+
echo "path=$path" >> "$GITHUB_OUTPUT"
6370
# --latest=false keeps these out of the "latest release" slot, which
6471
# belongs to the package releases changesets publishes.
6572
- name: Publish as a release asset

docs/CLI.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ The archive is looked for in this order, and cached under `~/Library/Caches/reac
2121
2. The [release asset](https://github.com/callstackincubator/react-native-node-api/releases) published for the pinned commit by the `Hermes prebuilt` workflow, unless `--no-download` is passed.
2222
3. A local build from the vendored source, unless `--no-build` is passed. This requires macOS and Xcode, and takes a while — but only once per pinned commit.
2323

24-
Its name covers everything that changes its contents: the pinned Hermes commit, the React Native version whose `ReactCommon/jsi` it is compiled against, the build type and the platforms. That makes it usable as a CI cache key.
24+
Its name covers everything that changes its contents: the pinned Hermes commit, the React Native version whose `ReactCommon/jsi` it is compiled against, the build type, the platforms and the host architecture. That makes it usable as a CI cache key.
25+
26+
The host architecture is part of it because `destroot/bin/hermesc` is a native binary for whichever Mac built the archive. Archives are published from Apple Silicon runners, so an Intel Mac finds none to download and builds its own instead of getting a `hermesc` it cannot execute.
2527

2628
- `[from]` — Path to a file inside the app package. Defaults to the current working directory.
2729
- `--react-native-package <package-name>` — The React Native package to resolve Hermes for. Defaults to `react-native`.

packages/host/src/node/cli/hermes-prebuilt.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ export function getPrebuiltDirectory() {
7474
* Identifies an archive by everything that changes its contents. The React
7575
* Native version is part of it because Hermes is compiled against that
7676
* package's ReactCommon/jsi: a JSI mismatch between the framework and the app
77-
* linking it is an ABI break.
77+
* linking it is an ABI break. The host architecture is part of it because the
78+
* hermesc in destroot/bin is a native binary for whichever Mac built it, so a
79+
* host of the other architecture has to build its own rather than download one
80+
* it cannot execute.
7881
*/
7982
export function getArchiveName({
8083
reactNativeVersion,
@@ -89,7 +92,7 @@ export function getArchiveName({
8992
// GitHub rewrites every character outside [A-Za-z0-9._-] in a release asset
9093
// name, so the name has to stay within that set to survive a round-trip.
9194
const platformSuffix = [...platforms].sort().join("-");
92-
return `hermes-${shortSha}-rn${reactNativeVersion}-${buildType}-${platformSuffix}.tar.gz`;
95+
return `hermes-${shortSha}-rn${reactNativeVersion}-${buildType}-${platformSuffix}-${process.arch}.tar.gz`;
9396
}
9497

9598
export function getReleaseTag() {
@@ -174,9 +177,11 @@ async function buildArchive({
174177
},
175178
});
176179

177-
// Configured here instead of letting build-apple-framework.sh's
178-
// build_host_hermesc do it: that one takes no architectures, and the hermesc
179-
// we ship has to run on both Apple Silicon and Intel Macs.
180+
// Configured here rather than by build-apple-framework.sh's
181+
// build_host_hermesc only so the build type is explicit — the pinned Hermes
182+
// hard-errors without one. Do not add CMAKE_OSX_ARCHITECTURES: a multi-arch
183+
// host configure makes llvh's try-compiles fail, down to "Host compiler
184+
// appears to require libatomic, but cannot find it".
180185
if (!fs.existsSync(importHostCompilersPath)) {
181186
await run("cmake", [
182187
"-S",
@@ -185,7 +190,6 @@ async function buildArchive({
185190
hermescPath,
186191
`-DJSI_DIR=${jsiPath}`,
187192
"-DCMAKE_BUILD_TYPE=Release",
188-
"-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64",
189193
]);
190194
await run("cmake", [
191195
"--build",

0 commit comments

Comments
 (0)