From 49cf01dd046a5c7de3014313fc96765342e13e77 Mon Sep 17 00:00:00 2001 From: Alex Hunt Date: Wed, 22 Apr 2026 05:46:50 -0700 Subject: [PATCH] Fix REACT_NATIVE_PATH resolution when Pods/ is a symlink (#56453) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/56453 **Problem** On Meta's virtual filesystem, we've started making `Pods/` a symlink for performance reasons. The `REACT_NATIVE_PATH` Xcode build setting was constructed as `${PODS_ROOT}/../`, assuming that `${PODS_ROOT}/..` equals the Podfile directory (`ios/`). When `Pods/` is a symlink to a different filesystem depth, Xcode resolves `${PODS_ROOT}` to the physical target, and `..` traverses the wrong tree — landing one directory too high and producing a nonexistent path. This broke four Xcode script phases at build time: `hermes-engine`, `ReactNativeDependencies`, `React-Core-prebuilt`, and `ReactCodegen`. **Root cause** The `$PODS_ROOT/..` idiom appears in four independent locations across Ruby, JavaScript, and shell layers. Each assumes the Pods directory is a direct child of the Podfile directory, which breaks when Pods/ is a symlink to a different filesystem depth. **Fix** All four sites are patched with the same approach: a new `PODFILE_DIR` Xcode build setting (set to the absolute Podfile directory path at pod install time) replaces the broken `$PODS_ROOT/..` derivation, with a fallback to the old behavior for backward compatibility. **Changed files** - **react_native_pods.rb** — Constructs the `REACT_NATIVE_PATH` build setting in `react_native_post_install`. Now resolves both the Pods directory and the react-native path to their real (physical) filesystem locations via `Pathname#realpath` before computing the relative path. Also adds the new `PODFILE_DIR` build setting. - **with-environment.sh** — Sourced by script phases to set up `NODE_BINARY`. Located `.xcode.env` via `$PODS_ROOT/../.xcode.env`. Now uses `$PODFILE_DIR` with fallback. - **script_phases.rb** — Ruby ERB template that generates shell scripts for codegen build phases. Used `pushd "$PODS_ROOT/../"` to derive the Podfile directory. Now uses `$PODFILE_DIR` with fallback. - **generateReactCodegenPodspec.js** — JavaScript template that generates the ReactCodegen.podspec "Generate Specs" script phase. Had its own hardcoded `pushd "$PODS_ROOT/../"`. Now uses `$PODFILE_DIR` with fallback. Reviewed By: cipolleschi Differential Revision: D100830088 --- .../generateReactCodegenPodspec.js | 12 +++++++++--- packages/react-native/scripts/react_native_pods.rb | 11 ++++++++++- .../scripts/react_native_pods_utils/script_phases.rb | 12 +++++++++--- .../react-native/scripts/xcode/with-environment.sh | 8 +++++++- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/packages/react-native/scripts/codegen/generate-artifacts-executor/generateReactCodegenPodspec.js b/packages/react-native/scripts/codegen/generate-artifacts-executor/generateReactCodegenPodspec.js index f12e7c4a4062..70221d3c988a 100644 --- a/packages/react-native/scripts/codegen/generate-artifacts-executor/generateReactCodegenPodspec.js +++ b/packages/react-native/scripts/codegen/generate-artifacts-executor/generateReactCodegenPodspec.js @@ -82,10 +82,16 @@ function codegenScripts(appPath /*: string */, baseOutputPath /*: string */) { baseOutputPath, REACT_NATIVE_PACKAGE_ROOT_FOLDER, ); + // Use PODFILE_DIR (set by react_native_post_install) to locate the Podfile + // directory. PODS_ROOT/.. does not work when Pods/ is a symlink. return `<<-SCRIPT -pushd "$PODS_ROOT/../" > /dev/null -RCT_SCRIPT_POD_INSTALLATION_ROOT=$(pwd) -popd >/dev/null +if [ -n "$PODFILE_DIR" ]; then + RCT_SCRIPT_POD_INSTALLATION_ROOT="$PODFILE_DIR" +else + pushd "$PODS_ROOT/../" > /dev/null + RCT_SCRIPT_POD_INSTALLATION_ROOT=$(pwd) + popd >/dev/null +fi export RCT_SCRIPT_RN_DIR="$RCT_SCRIPT_POD_INSTALLATION_ROOT/${relativeReactNativeRootFolder}" export RCT_SCRIPT_APP_PATH="$RCT_SCRIPT_POD_INSTALLATION_ROOT/${relativeAppPath.length === 0 ? '.' : relativeAppPath}" diff --git a/packages/react-native/scripts/react_native_pods.rb b/packages/react-native/scripts/react_native_pods.rb index d647270c2243..3296a20bbd55 100644 --- a/packages/react-native/scripts/react_native_pods.rb +++ b/packages/react-native/scripts/react_native_pods.rb @@ -531,7 +531,16 @@ def react_native_post_install( ReactNativePodsUtils.fix_library_search_paths(installer) ReactNativePodsUtils.update_search_paths(installer) ReactNativePodsUtils.set_build_setting(installer, build_setting: "USE_HERMES", value: use_hermes()) - ReactNativePodsUtils.set_build_setting(installer, build_setting: "REACT_NATIVE_PATH", value: File.join("${PODS_ROOT}", "..", react_native_path)) + # Compute REACT_NATIVE_PATH relative to PODS_ROOT using real (physical) + # paths, so the relative traversal is correct even when Pods/ is a symlink. + pods_dir_real = Pathname.new(Pod::Config.instance.sandbox_root.to_s).realpath + rn_absolute = File.expand_path(react_native_path, Pod::Config.instance.installation_root.to_s) + rn_real = Pathname.new(rn_absolute).realpath + rn_relative_to_pods = rn_real.relative_path_from(pods_dir_real) + ReactNativePodsUtils.set_build_setting(installer, build_setting: "REACT_NATIVE_PATH", value: File.join("${PODS_ROOT}", rn_relative_to_pods.to_s)) + # Store the Podfile directory as a build setting so that shell scripts can + # locate it without relying on PODS_ROOT/.. (breaks when Pods/ is a symlink). + ReactNativePodsUtils.set_build_setting(installer, build_setting: "PODFILE_DIR", value: Pod::Config.instance.installation_root.to_s) ReactNativePodsUtils.set_build_setting(installer, build_setting: "SWIFT_ACTIVE_COMPILATION_CONDITIONS", value: ['$(inherited)', 'DEBUG'], config_name: "Debug") if (ENV['RCT_REMOVE_LEGACY_ARCH'] == '1') diff --git a/packages/react-native/scripts/react_native_pods_utils/script_phases.rb b/packages/react-native/scripts/react_native_pods_utils/script_phases.rb index f5dd2ed70831..b3bd2f997f3f 100644 --- a/packages/react-native/scripts/react_native_pods_utils/script_phases.rb +++ b/packages/react-native/scripts/react_native_pods_utils/script_phases.rb @@ -35,9 +35,15 @@ def get_script_phases_no_codegen_discovery(options) def get_script_template(react_native_path, export_vars={}) template =<<~EOS - pushd "$PODS_ROOT/../" > /dev/null - RCT_SCRIPT_POD_INSTALLATION_ROOT=$(pwd) - popd >/dev/null + # Use PODFILE_DIR (set by react_native_post_install) to locate the + # Podfile directory. PODS_ROOT/.. does not work when Pods/ is a symlink. + if [ -n "$PODFILE_DIR" ]; then + RCT_SCRIPT_POD_INSTALLATION_ROOT="$PODFILE_DIR" + else + pushd "$PODS_ROOT/../" > /dev/null + RCT_SCRIPT_POD_INSTALLATION_ROOT=$(pwd) + popd >/dev/null + fi <% export_vars.each do |(varname, value)| %> export <%= varname -%>=<%= value -%> <% end %> diff --git a/packages/react-native/scripts/xcode/with-environment.sh b/packages/react-native/scripts/xcode/with-environment.sh index d2ddf3c02cab..e25f5af4edfc 100755 --- a/packages/react-native/scripts/xcode/with-environment.sh +++ b/packages/react-native/scripts/xcode/with-environment.sh @@ -17,7 +17,13 @@ NODE_BINARY=$(command -v node || echo "") export NODE_BINARY # Override the default with the global environment -ENV_PATH="$PODS_ROOT/../.xcode.env" +# Use PODFILE_DIR (set by react_native_post_install) to locate .xcode.env. +# PODS_ROOT/.. does not work when Pods/ is a symlink. +if [ -n "$PODFILE_DIR" ]; then + ENV_PATH="$PODFILE_DIR/.xcode.env" +else + ENV_PATH="$PODS_ROOT/../.xcode.env" +fi if [ -f "$ENV_PATH" ]; then source "$ENV_PATH" fi