Skip to content

SwiftPM: stop baking an absolute HERMES_CLI_PATH into the app pbxproj - #58292

Open
chrfalch wants to merge 1 commit into
mainfrom
chrfalch/spm-drop-absolute-hermes-cli-path
Open

SwiftPM: stop baking an absolute HERMES_CLI_PATH into the app pbxproj#58292
chrfalch wants to merge 1 commit into
mainfrom
chrfalch/spm-drop-absolute-hermes-cli-path

Conversation

@chrfalch

@chrfalch chrfalch commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Summary:

npx react-native spm add writes a HERMES_CLI_PATH build setting into the app's project.pbxproj. The value is the absolute path of hermesc inside the hermes-compiler npm package, resolved on the machine that ran the command, for example /Users/<me>/repos/<app>/node_modules/hermes-compiler/hermesc/osx-bin/hermesc. The pbxproj is committed, so every SwiftPM-converted app commits one developer's disk layout to its repository.

The setting is redundant. react-native-xcode.sh already resolves hermesc at build time through react-native's own dependency graph when the current HERMES_CLI_PATH is not a file, using the same hermes-compiler version the artifact download pins (#57928). A relative build setting is not an option: $(REACT_NATIVE_PATH)/../hermes-compiler breaks when react-native is a symlink, and $(SRCROOT)/../node_modules/... breaks for hoisted monorepos.

This change:

  • Removes the HERMES_CLI_PATH write from generate-spm-xcodeproj.js. Existing injected projects clean themselves up on the next spm add or spm update, because removeRecordedBuildSettings already strips every scalar recorded in .spm-injected.json before re-injecting.
  • Fixes the shell fallback's gate. It was keyed on PODS_ROOT being absent, so a SwiftPM app that keeps side-by-side non-RN pods never reached it and had no hermesc. It is now keyed on the hermes-engine pod directory being absent, with Hermes enabled, so every app that has the pod keeps its previous behavior exactly, including a partial prebuild still failing the build.

Changelog:

[IOS] [FIXED] - SwiftPM: stop baking an absolute, machine-specific HERMES_CLI_PATH into the app's pbxproj; resolve hermesc at build time instead

Test Plan:

Red first. New test at the entry point that resolved the path, with a hermes-compiler fixture seeded so the old code provably wrote the setting:

yarn jest packages/react-native/scripts/spm/__tests__/remove-spm-injection-test.js --no-cache -i
# with the source changes stashed:
● injectSpmIntoExistingXcodeproj — HERMES_CLI_PATH › writes no HERMES_CLI_PATH, in any configuration or the marker
    Expected substring: not "HERMES_CLI_PATH"
    HERMES_CLI_PATH = "/private/var/.../node_modules/hermes-compiler/hermesc/osx-bin/hermesc";
    HERMES_CLI_PATH = "/private/var/.../node_modules/hermes-compiler/hermesc/osx-bin/hermesc";
Tests: 1 failed, 76 skipped, 77 total

Green after the change:

yarn jest packages/react-native/scripts/spm --no-cache -i
Test Suites: 18 passed, 18 total
Tests:       834 passed, 834 total

Also: yarn eslint --max-warnings 0 and yarn prettier --check on the changed files, yarn flow-check (0 errors), bash -n packages/react-native/scripts/react-native-xcode.sh.

End to end: a React Native 0.87.1 SwiftPM app (@shopify/react-native-skia example, no Pods) with the HERMES_CLI_PATH lines deleted from its pbxproj builds in Release. The bundling phase logs the fallback resolving hermesc from node_modules/hermes-compiler, and main.jsbundle is produced. There is no shell test harness for react-native-xcode.sh, so that path is covered by this build only.

🤖 Generated with Claude Code

`spm add` wrote the machine-specific absolute path of hermesc from the
hermes-compiler npm package into every build configuration, and apps
commit that pbxproj. react-native-xcode.sh already resolves hermesc at
build time through react-native's dependency graph, so drop the write.

Widen the shell fallback correctly: it was gated on PODS_ROOT being
absent, so a SwiftPM app keeping side-by-side non-RN pods never reached
it. Key it on the hermes-engine pod directory instead, so any app that
has the pod keeps its previous behavior exactly.

Existing injected projects clean up on the next `spm add`/`update`,
which strips every recorded scalar before re-injecting.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 2, 2026
@facebook-github-tools facebook-github-tools Bot added p: Expo Partner: Expo Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. labels Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. p: Expo Partner: Expo Partner Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant