From 62f03d4bc657aab4bcf9ee4b61413f48c2fd4e05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Thu, 13 Aug 2026 16:08:37 +0000 Subject: [PATCH 1/2] Dump the CMake configure log when the Hermes build fails CMake reports a failed feature check as a bare "not found", with the compiler's actual complaint only in its configure log. The host hermesc configure is failing on the macOS runner with checks that succeed everywhere else, so surface that log rather than guess at it. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UkNbgdyuKgHaFwT27RahGH --- .github/workflows/hermes-prebuilt.yml | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/.github/workflows/hermes-prebuilt.yml b/.github/workflows/hermes-prebuilt.yml index 9c9a1a79..f5fd7c27 100644 --- a/.github/workflows/hermes-prebuilt.yml +++ b/.github/workflows/hermes-prebuilt.yml @@ -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 From 1ad2e8fa15db46c8507d101c499e9b63c9aaaccd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=A6n=20Hansen?= Date: Thu, 13 Aug 2026 16:14:17 +0000 Subject: [PATCH 2/2] Stop the host Hermes compiler build from targeting visionOS Every command got all three deployment targets build-apple-framework.sh can ask for, including the host compiler build. XROS_DEPLOYMENT_TARGET is also a clang driver variable, so clang targeted visionOS against the macOS sysroot: clang: warning: using sysroot for 'MacOSX' but targeting 'XR' error: 'pthread_mutexattr_init' is unavailable: not available on visionOS Every API marked unavailable on visionOS then failed to compile, which is why unistd.h (which reaches _fd_def.h through sys/select.h) came back "not found" while sys/stat.h did not, and why the configure died on CheckAtomic. Each platform build now gets only the deployment target it needs, and the host compiler build gets none. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UkNbgdyuKgHaFwT27RahGH --- .changeset/prebuilt-hermes-visionos-env.md | 13 ++++++ packages/host/src/node/cli/hermes-prebuilt.ts | 40 ++++++++++++++----- 2 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 .changeset/prebuilt-hermes-visionos-env.md diff --git a/.changeset/prebuilt-hermes-visionos-env.md b/.changeset/prebuilt-hermes-visionos-env.md new file mode 100644 index 00000000..0b6876e4 --- /dev/null +++ b/.changeset/prebuilt-hermes-visionos-env.md @@ -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. diff --git a/packages/host/src/node/cli/hermes-prebuilt.ts b/packages/host/src/node/cli/hermes-prebuilt.ts index 3470cd09..574e24cf 100644 --- a/packages/host/src/node/cli/hermes-prebuilt.ts +++ b/packages/host/src/node/cli/hermes-prebuilt.ts @@ -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 { + 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]; @@ -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 = {}, + ) => 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, @@ -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(