Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/prebuilt-hermes-visionos-env.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"react-native-node-api": patch
---

Fix `prebuilt-hermes` failing to configure the host Hermes compiler with "Host
compiler appears to require libatomic, but cannot find it". It exported all
three deployment targets Hermes' `build-apple-framework.sh` can ask for to every
command it ran, including the host compiler build. `XROS_DEPLOYMENT_TARGET` is
also a clang driver variable, so clang targeted visionOS against the macOS
sysroot, and every API marked unavailable there — `pthread_mutexattr_init`, the
`fd_set` helpers reached through `unistd.h` — failed to compile. Each platform
build now gets only the deployment target it needs, and the host compiler build
gets none.
31 changes: 31 additions & 0 deletions .github/workflows/hermes-prebuilt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,37 @@ jobs:
run: |
path=$(pnpm exec react-native-node-api prebuilt-hermes --no-download)
echo "path=$path" >> "$GITHUB_OUTPUT"
# CMake reports a failed feature check as a bare "not found", with the
# compiler's actual complaint only in its configure log. Surface that log
# here so a failure is diagnosable without another round trip.
- name: Dump CMake configure log
if: failure()
run: |
log=$(find "$PWD" -name CMakeConfigureLog.yaml -path '*build_host_hermesc*' | head -1)
if [ -z "$log" ]; then
echo "No CMakeConfigureLog.yaml found"
find "$PWD" -name 'CMakeError.log' -path '*build_host_hermesc*' -exec cat {} \;
exit 0
fi
echo "::group::Environment seen by CMake"
env | sort
xcode-select --print-path
xcrun --show-sdk-path || true
echo "::endgroup::"
echo "::group::unistd.h check"
grep -n -B 5 -A 45 'unistd\.h' "$log" | head -150
echo "::endgroup::"
echo "::group::atomics check"
grep -n -B 5 -A 60 'HAVE_CXX_ATOMICS_WITHOUT_LIB' "$log" | head -180
echo "::endgroup::"
cp "$log" "$RUNNER_TEMP/CMakeConfigureLog.yaml"
- name: Upload CMake configure log
if: failure()
uses: actions/upload-artifact@v7
with:
name: hermesc-cmake-configure-log
path: ${{ runner.temp }}/CMakeConfigureLog.yaml
if-no-files-found: ignore
# --latest=false keeps these out of the "latest release" slot, which
# belongs to the package releases changesets publishes.
- name: Publish as a release asset
Expand Down
40 changes: 30 additions & 10 deletions packages/host/src/node/cli/hermes-prebuilt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,25 @@ const RELEASES_URL =

export const DEFAULT_PLATFORMS = ["iphoneos", "iphonesimulator"];

// Passed to Hermes' build-apple-framework.sh, which errors out rather than
// assume one. These match React Native's own podspec declarations.
const DEPLOYMENT_TARGETS = {
IOS_DEPLOYMENT_TARGET: "15.1",
MAC_DEPLOYMENT_TARGET: "10.15",
XROS_DEPLOYMENT_TARGET: "1.0",
};
/**
* The deployment target build-apple-framework.sh reads for a platform — it
* errors out rather than assume one. These match React Native's own podspec
* declarations.
*
* Only the variable that platform needs is passed. XROS_DEPLOYMENT_TARGET is
* also a clang driver variable, so leaking it into a build that isn't for
* visionOS makes clang target visionOS against whatever sysroot it was given —
* and every API marked unavailable there then fails to compile.
*/
function getDeploymentTarget(platform: string): Record<string, string> {
if (platform === "macosx") {
return { MAC_DEPLOYMENT_TARGET: "10.15" };
} else if (platform === "xros" || platform === "xrsimulator") {
return { XROS_DEPLOYMENT_TARGET: "1.0" };
} else {
return { IOS_DEPLOYMENT_TARGET: "15.1" };
}
}

export const BUILD_TYPES = ["debug", "release"] as const;
export type BuildType = (typeof BUILD_TYPES)[number];
Expand Down Expand Up @@ -162,15 +174,19 @@ async function buildArchive({
// vendored copy: the framework and the app linking it share jsi::Runtime.
const jsiPath = path.join(reactNativePath, "ReactCommon", "jsi");

const run = (command: string, args: string[]) =>
const run = (
command: string,
args: string[],
extraEnv: Record<string, string> = {},
) =>
spawn(command, args, {
cwd: hermesPath,
outputMode: "inherit",
// Keeps the build log off stdout, which callers parse for the final path.
stdout: process.stderr,
env: {
...DEPLOYMENT_TARGETS,
...process.env,
...extraEnv,
JSI_PATH: jsiPath,
BUILD_TYPE: buildType === "debug" ? "Debug" : "Release",
HERMES_OVERRIDE_HERMESC_PATH: importHostCompilersPath,
Expand Down Expand Up @@ -202,7 +218,11 @@ async function buildArchive({
}

for (const platform of platforms) {
await run("./utils/build-apple-framework.sh", [platform]);
await run(
"./utils/build-apple-framework.sh",
[platform],
getDeploymentTarget(platform),
);
}

const frameworksPath = path.join(
Expand Down
Loading