feat(iOS): serve third-party deps from the prebuilt ReactNativeDependencies pod via dependency-only facades#57440
Open
chrfalch wants to merge 34 commits into
Open
feat(iOS): serve third-party deps from the prebuilt ReactNativeDependencies pod via dependency-only facades#57440chrfalch wants to merge 34 commits into
chrfalch wants to merge 34 commits into
Conversation
The minimal machinery to build the packaged header structures: - headers-spec.js: the executable layout contract (rules R1-R8) — which namespaces are hoisted into the React framework, which carry module maps, and how collisions are rejected. - headers-inventory.js: scans the source tree and classifies every shipped header (language surface + modularizability bucket) — the input to the spec. computeInventory() feeds the build in-memory; the CLI writes a JSON manifest. - headers-compose.js: emits the layout — writes the <React/...> headers + umbrella + module map into each React.framework slice (detected by the framework's presence), and assembles the headers-only ReactNativeHeaders.xcframework (every other namespace + deps + Hermes). Called by xcframework.js during compose. This is the alternative header source that lets consumers resolve React Native headers without a clang VFS overlay. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Emit the headers-spec layout unconditionally and delete the VFS overlay across
JS, CI publish, and Ruby. Consumers resolve headers the way the SwiftPM branch
does: <React/...> from the vendored React.framework, every other namespace from
ReactNativeHeaders. No root Headers/ on the xcframework, no VFS.
JS:
- xcframework.js: always emit the React.framework spec layout and build
ReactNativeHeaders.xcframework (was gated behind RN_ZERO_I_LAYOUT=1). Remove
the legacy header path entirely — the podspec->root-Headers enumeration,
createModuleMapFile, and copyHeaderFilesToSlices — so the published
React.xcframework is a standard framework (Info.plist + per-slice
React.framework/{Headers,Modules}), no root Headers/ or Modules/. Ship
ReactNativeHeaders.xcframework inside the reactnative-core tarball (sibling of
React.xcframework) so the prebuilt pod can vend both; keep the standalone
ReactNativeHeaders.xcframework.tar.gz for the SPM path. Drop the
React-VFS-template.yaml emit and the ./vfs import.
- vfs.js: deleted (its only consumer was xcframework.js).
- types.js: drop the now-unused VFSEntry/VFSOverlay/HeaderMapping types.
- replace-rncore-version.js: drop the React-VFS.yaml preservation rationale.
Ruby/CocoaPods:
- React-Core-prebuilt.podspec: vend React.xcframework (its per-slice
React.framework + module map serves <React/...> and @import React via
FRAMEWORK_SEARCH_PATHS); flatten ReactNativeHeaders' headers into a top-level
Headers/ in prepare_command and expose them via the pod header search path.
Drop the VFS-era root header_mappings_dir/module_map.
- rncore.rb: remove the -ivfsoverlay injection and process_vfs_overlay;
add_rncore_dependency and configure_aggregate_xcconfig now add a
HEADER_SEARCH_PATHS to React-Core-prebuilt/Headers for podspec, aggregate, and
third-party targets.
- react_native_pods.rb: drop the process_vfs_overlay post-install call.
Docs: replace the "VFS Overlay System" section with the headers-spec layout;
drop the obsolete "Known Issues" (pre-headers-spec) section.
Verified end-to-end: prebuild compose produces a VFS-free, root-Headers-free
React.xcframework; rn-tester pod install + xcodebuild (prebuilt path) BUILD
SUCCEEDED with zero -ivfsoverlay.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
React.framework is a clang module; when an SPM consumer precompiles it, a
modular React/ header that #imports <react/...> hit
-Wnon-modular-include-in-framework-module because the lowercase react/
namespace (served from ReactNativeHeaders, per R1's Linux/Windows-safe layout)
was deliberately kept out of any module.
Give react/ a module where it already lives instead of relocating it (relocation
would require case-folding react.framework -> React.framework, which only works
on case-insensitive filesystems):
- headers-spec.js: drop the react/ namespace-module exemption so its
objc-modular-candidates get a module; emit that module as
ReactNativeHeaders_react (a module literally named 'react' would alias the
React framework module on a case-insensitive filesystem). Module names are
internal; <react/...> still resolves by header path and is now modular.
- headers-inventory.js: classify C++ default member initializers in aggregates
(e.g. struct { NSString *family = nil; } in RCTFontProperties.h) as ObjC++ so
these are not misclassified objc-modular-candidate and pulled into a plain
ObjC module they cannot compile in.
The unguarded ObjC/C react/ headers (e.g. JSRuntimeFactoryCAPI.h) now resolve
modularly; the C++ react/renderer/* includes are #ifdef __cplusplus-guarded and
skipped during the ObjC module emit, so they need no module.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
In prebuilt mode the React core pods' code + headers live entirely in React.xcframework / React-Core-prebuilt. Re-installing their SOURCE podspecs made them ship duplicate headers that shadow the prebuilt artifact and break the React framework's clang explicit-module precompile (-Wnon-modular-include-in-framework-module) under Xcode 26. Install those core pods as dependency-only FACADES instead: generated podspecs with no sources/headers, installed via :path (so nothing is fetched), each depending on React-Core-prebuilt. Version, subspecs, default_subspec and resources (e.g. the privacy manifest) are DERIVED from the real podspec so the facade stays graph- and resource-equivalent to the source pod. With the shadowing gone the React module precompiles cleanly with SWIFT_ENABLE_EXPLICIT_MODULES on, so the Xcode-26 workaround (#53457) is removed. The prebuilt header search path + ReactNativeHeaders module-map activation are consolidated into a single post-install injection site (configure_aggregate_xcconfig); add_rncore_dependency now only declares the React-Core-prebuilt dependency. rn-tester's NativeComponentExample uses the canonical <React/...> include for RCTFabricComponentsPlugins.h (resolved from the framework) so it builds against the facaded React-RCTFabric in prebuilt mode. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`yarn format-check` (prettier) was failing CI on PR #57285. Run prettier on the ios-prebuild headers scripts (headers-compose.js, headers-inventory.js), replace-rncore-version.js, and __docs__/README.md so format-check passes. No logic changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tarball The prebuilt React core now ships two xcframeworks — React.xcframework and the headers-only ReactNativeHeaders.xcframework. React-Core-prebuilt's prepare_command flattens the latter's Headers (including module.modulemap) into the pod. The compose step only tar'd React.xcframework, so consumers got no React-Core-prebuilt/Headers/module.modulemap and failed the build with "module map file ... not found". Tar both xcframeworks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…le (R9) The public-umbrella model (which replaced the VFS overlay) excludes `+Private` and objc-blocked headers from React.framework's module, so privileged framework consumers (e.g. Expo) that `#import <React/RCTBridge+Private.h>`, `<React/RCTMountingManager.h>`, etc. fail to compile under explicit modules even though the headers still ship in React.framework/Headers. Add R9: a curated allowlist appended to the React module map — `RCTBridge+Private.h` as a real `header` (objc-modular-candidate, reaches no C++) and the six Fabric headers as `textual header` (objc-blocked; a real member would re-trip -Wnon-modular-include, and their <react/...> C++ includes resolve at the consumer's use site). Backwards-compatible: existing `#import <React/...>` (and Swift `import React`) sites are unchanged. Fails closed if an allowlisted header is removed/renamed or drifts bucket. Note: RCTUIKit.h / RCTRootContentView.h are absent from source entirely and need restoration, not exposure — out of scope here.
The flattened ReactNativeHeaders layout ships the individual React_RCTAppDelegate/*.h headers but no per-namespace umbrella. Consumers like Expo probe `<React_RCTAppDelegate/React_RCTAppDelegate-umbrella.h>` via __has_include (RCTAppDelegateUmbrella.h); with the umbrella gone the probe fails and RCTReactNativeFactory / RCTRootViewFactory are never declared, breaking the Expo pod's clang module. Add R10: emit a per-namespace umbrella (content DERIVED from namespaceModules so it can't drift — e.g. RCTArchConfiguratorProtocol.h, gone from this branch, is correctly omitted) and add it to that namespace's module so the import stays modular under explicit modules. Targeted via UMBRELLA_NAMESPACES (currently just React_RCTAppDelegate, the only umbrella Expo imports); fails closed if a listed namespace loses its modular headers.
…ic facade Community Fabric modules quote-import "RCTFabricComponentsPlugins.h" (~47x: slider, maps, pager-view, keyboard-controller, ...). In source, React-RCTFabric vended it at header_dir "React", so it landed in dependents' CocoaPods header maps and the bare quoted name resolved. In prebuilt mode React-RCTFabric is a dependency-only facade that ships no headers — the only copy is baked angle-only into React.framework (it's objc-blocked, excluded from the framework module map), so quoted imports fail to resolve. Re-vend JUST that one header from the facade (FACADE_REEXPOSED_HEADERS), copied as a self-contained snapshot at header_dir "React", restoring dependents' header maps exactly as the source pod did. Header-only (no compiled sources, no duplicate symbols — the impl stays in React.framework). Re-exposing a single header does not put <react/...>/<yoga/...> on -I, so it does not reintroduce the non-modular-include shadowing the modular layout eliminates. Fails closed if the glob matches nothing.
…the headers compose buildReactNativeHeadersXcframework copied each declared deps namespace (folly/glog/boost/fmt/double-conversion/fast_float) from the staged ReactNativeDependencies headers, but only console.warn'd on a missing one and kept going — silently shipping a ReactNativeHeaders.xcframework without third-party header resolution (consumers then fail on <folly/...> etc.). The summary log also printed the INTENDED namespace list, masking the gap. Throw instead: a missing declared deps namespace means deps weren't staged (third-party/ReactNativeDependencies.xcframework/Headers — from a full prebuild or the cache slot), so refuse to emit an incomplete artifact.
…est imports Two CI fixes for the prebuild-ios-core workflow: - prebuild-ios-core.yml: the compose-xcframework job downloaded the build slices and React headers but never staged third-party/ReactNativeDependencies.xcframework. buildReactNativeHeadersXcframework folds the third-party deps namespaces (folly/glog/boost/...) into ReactNativeHeaders.xcframework, so it fail-closed with "deps namespace 'folly' missing ... refusing to ship an incomplete ReactNativeHeaders.xcframework". Add the Download + Extract ReactNativeDependencies steps (mirroring build-slices) so the deps headers are present before composing. - headers-spec-test.js: reorder requires so the `../headers-spec` import sorts before `fs`, fixing the @react-native/monorepo/sort-imports warning that failed `eslint --max-warnings 0` in test_js. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- copy facade resources into the facade pod dir: CocoaPods file accessors cannot glob past the pod root, so the ..-escaping globs shipped the privacy-manifest / i18n resource bundles empty - quote -fmodule-map-file so a PODS_ROOT containing spaces stays a single clang argument (matches the quoted HEADER_SEARCH_PATHS beside it) - fail closed in React-Core-prebuilt's prepare_command and in replace-rncore-version.js when the tarball lacks ReactNativeHeaders.xcframework, instead of silently leaving an empty or deleted Headers/ with the module-map flag dangling - thread rnRoot through planFromInventory/isUmbrellaSafe instead of the hardcoded hosting-package root (the one spot that didn't take the inventory's root) - drop the dead ios-prebuild templates/ files (their only consumers were removed by the headers-spec compose) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…1, verify, ratchet) Hardens the header layout against new/changed headers so consumer-facing regressions fail the PREBUILD instead of a downstream (rn-tester/Expo/ community) build: - headers-verify.js (new; runs in the prebuild compose CI job): include-health ratchet against a committed baseline (notShipped/unresolved/quoted- unresolvable includes in shipped headers — 27 baselined today); structural byte-compare of the composed module maps/umbrellas against the spec render; compile smokes — an ObjC TU precompiling the React module (every umbrella header) + all 14 R5 namespace modules + the R10 umbrella + __has_include asserts, an Expo-shaped ObjC++ TU (the R9 textual Fabric surface), and a Swift TU (import React + RCTBridge.moduleRegistry). - R11: one source, one content location. 116 sources ship under multiple spellings (React/X.h + CoreModules/RCTImage/RCTAnimation/... forms, bare React_RCTAppDelegate aliases). The flattened layout duplicated their declarations, so any -fmodules consumer touching two spellings (even transitively via a legacy spelling) hit redefinition errors — found by the gate's first run. The module-owned spelling keeps the content; every other spelling is emitted as a one-line redirect shim. - Single source of truth for third-party namespaces: the inventory's include classifier now derives from DEPS_NAMESPACES, and compose enforces set-equality with the deps artifact in BOTH directions (missing OR undeclared namespace fails). The undeclared direction immediately surfaced SocketRocket, which the deps artifact ships but was never relocated. - R5 exemption assert: an invalid-module-identifier namespace gaining a modular-candidate header now fails the plan instead of silently shipping a non-modular header. - Inventory records quoted includes that don't resolve to a shipped header (quotedNotShipped) instead of dropping them. Docs: __docs__/headers-rules.md documents rules R1-R11, the emission pipeline, and the resilience model.
…iveHeaders Declaring SocketRocket in DEPS_NAMESPACES (added to satisfy the deps set-equality guard) relocated textual copies of its headers into ReactNativeHeaders and thus into the flattened React-Core-prebuilt/Headers, which sits on every pod's HEADER_SEARCH_PATHS. The real SocketRocket pod still exists in consumer graphs and vends the same headers, so the copies collide: duplicate @interface definitions when the SocketRocket pod compiles (rn-tester, prebuilt + USE_FRAMEWORKS=dynamic), and a poisoned module graph in use_frameworks/explicit-modules apps that don't compile SocketRocket sources (Expo: ReactCodegen/RCTModulesConformingToProtocolsProvider.h "file not found"). Bisect confirmed removing the SocketRocket dir flips red->green and the R11 shims are not implicated. Add DEPS_NAMESPACES_NOT_RELOCATED: such namespaces count as declared for the set-equality guard (a genuinely new dep still fails closed), the include classifier still recognizes them, and the headers gate now asserts they stay ABSENT from the artifact. Note: no CI lane covers prebuilt + use_frameworks (the dynamic-frameworks lane builds from source), which is why CI stayed green.
8158f41 to
562985d
Compare
069c350 to
017ca51
Compare
cipolleschi
reviewed
Jul 8, 2026
cipolleschi
reviewed
Jul 8, 2026
cipolleschi
reviewed
Jul 8, 2026
cipolleschi
reviewed
Jul 8, 2026
cipolleschi
reviewed
Jul 8, 2026
562985d to
3a57ac3
Compare
017ca51 to
ef5ae80
Compare
Collaborator
Author
|
Thanks! Addressed in Major — RSpec/minitest for Minors / nits — all done:
|
… sort-imports) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3a57ac3 to
8b50e2c
Compare
ef5ae80 to
f4c5147
Compare
/bin/cp -c (APFS clonefile) is macOS-only; Linux CI exercises these paths through the jest integration tests, where GNU cp rejects it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
8b50e2c to
ca08afa
Compare
f4c5147 to
93187f3
Compare
cipolleschi
reviewed
Jul 10, 2026
…oolchain calls The xcrun/clang/libtool/lipo/xcodebuild invocations in buildReactNativeHeadersXcframework used execSync with template-interpolated double-quoted paths; a path containing a quote or dollar sign could break or shell-expand. Converted to execFileSync argument arrays (matching headers-verify.js and the existing cp calls); libtool's 2>/dev/null shell redirect becomes stdio: ['ignore', 'pipe', 'ignore']. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…prebuilt/SwiftPM
Source builds get React-Core's non-header resources from the podspec
resource_bundles, but in the prebuilt path the source pods aren't installed
(CocoaPods facades) / not present (SwiftPM), so they were lost. Reproduce them in
the artifact at compose time via scripts/ios-prebuild/framework-resources.js:
- Privacy manifest: merge the PrivacyInfo.xcprivacy of the pods baked into
React.framework into one manifest at the framework root, where Xcode's
privacy-report aggregation picks it up (React.framework is dynamic; no runtime).
- RCTI18nStrings: rebuild RCTI18nStrings.bundle from React/I18n/strings/*.lproj
inside React.framework, resolved at runtime by the framework-aware loader.
RCTLocalizedString.mm now resolves the strings bundle from the code's own bundle
first (React.framework when prebuilt/SwiftPM) with a main-bundle fallback (static
source builds), keeping the graceful nil -> default behaviour.
React-Core.podspec declares RCTI18nStrings and React-Core_privacy in one
resource_bundles map (a later resource_bundles= had silently overwritten the
first), so source builds ship both again. The RNCore facade no longer carries
React-Core_privacy: the prebuilt artifact owns these resources now.
Red/green unit + integration tests in __tests__/framework-resources-test.js.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The FACADE_REEXPOSED_HEADERS loop passed an undefined `podspec_dir` to
copy_reexposed_headers, crashing `pod install` at `use_react_native!`
("undefined local variable or method 'podspec_dir'"). Derive it from the
real podspec path.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Review feedback on embedding React.framework's non-header resources:
- Privacy-manifest merge no longer fabricates an empty NSPrivacyAccessedAPITypeReasons
key when a source omitted it (tracks per-category key presence), so a single
manifest truly passes through unchanged.
- NSPrivacyCollectedDataTypes dedup canonicalizes (recursively sorts) keys before
hashing, so two pods declaring the same dict in different key order dedup.
- RCTI18nStrings.bundle is now built ONCE into a temp stage and cloned into each
slice (mirrors the privacy manifest) instead of rebuilt inside the slice loop;
i18nLocales is computed once.
- i18n bundle Info.plist gains CFBundleShortVersionString / CFBundleVersion so
Apple validation tooling doesn't warn on a version-less bundle.
- Fixed stale "see" pointers ios-prebuild/{i18n,privacy}.js -> framework-resources.js
(React-Core.podspec + rncore_facades.rb).
- Tests: serialize->readPrivacyManifest round-trip, order-insensitive collected-type
dedup, omitted-reasons-key regression, CFBundleVersion presence, and an
emitReactFrameworkHeaders integration test asserting PrivacyInfo.xcprivacy +
RCTI18nStrings.bundle land in EVERY slice.
Note: resources are written after _CodeSignature is stripped (R7 — signed after
compose), so no signature invalidation; readdirSync({recursive})/cpSync are safe on
the repo's Node >=22 engine.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mixed is now a deprecated utility type (use unknown), and the NSPrivacyAccessedAPITypeReasons refinement was invalidated by the intervening Map.get() call — hoist it into a local first. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dle clone Same macOS-only clonefile issue as the other copy sites; this one is on the resource-landing path the jest integration test exercises on Linux. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ca08afa to
7b29638
Compare
93187f3 to
61ceff4
Compare
The resources shipped inside the prebuilt React.framework fail SILENTLY if the framework is ever consumed without being embedded: every localized string falls back to its untranslated default and the privacy manifest drops out of the app's aggregated privacy report, with no build error. Emit a dev-only, once-per-process RCTLogWarn when the bundle resolves nil so an embed regression is observable in every dev console instead of shipping quietly. No behavior change in release (RCT_DEV compiled out) or on the happy path. Not unit-testable (ObjC runtime diagnostics, no jest surface); both RCT_DEV and NDEBUG preprocessing paths compile-verified via clang -fsyntax-only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
61ceff4 to
1b0f40e
Compare
… manifests collectReactPrivacyManifestPaths merged ANY PrivacyInfo.xcprivacy found recursively under the React privacy roots — a latent trap: a future pod under those roots that ships as its OWN framework would silently have its manifest folded into React.framework's aggregate (over-declaration in the app's privacy report). The scan is now validated against an explicit REACT_PRIVACY_MANIFESTS allowlist and FAILS the prebuild on an unlisted manifest, with instructions for both resolutions. Listed-but-absent stays legal: partial fixture trees keep working, an upstream deletion under-declares exactly as a source build would, and a MOVED manifest cannot slip through since its new path is unlisted. Also: only create the i18n temp stage when there are .lproj dirs to bundle, and document that the dedup key's `?? ''` is Flow appeasement (JSON.stringify is typed `string | void`), not dead code. 56 ios-prebuild tests green (drift-gate cases red-first); Flow + prettier clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… handling - rndependencies.rb: the previous "||= [] << path" only added the deps header search path when HEADER_SEARCH_PATHS was unset — with an existing value the path was silently dropped. Normalize string values and always append. Also point the search path at the pod-local flattened Headers/ (single header home) instead of the artifact root. - ReactNativeDependencies.podspec: prepare_command exited 0 when XCFRAMEWORK_PATH/HEADERS_PATH resolved empty, producing a silent no-link pod; fail closed with exit 1. - reactNativeDependencies.js: an artifact without a version marker logged "we are going to use it anyway" but then fell through to rmSync + re-download, destroying locally staged deps builds; honor the message and use it as-is. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…uilt-deps mode In prebuilt-deps mode the real source pods (RCT-Folly, glog, boost, DoubleConversion, fmt, fast_float, SocketRocket) are not declared in the Podfile, so a community podspec's hardcoded 's.dependency "RCT-Folly"' would resolve from the CocoaPods trunk and compile from source next to the prebuilt binary — the dual-copy bug class behind the 2026-07-03 SocketRocket regression. RNDepsFacades generates dependency-only local facade podspecs (build/rndeps-facades/<Name>/): no sources, no headers, a single dependency on ReactNativeDependencies. Versions and subspecs are derived from the real podspecs in third-party-podspecs/ (RCT-Folly keeps /Default + /Fabric with default_subspecs = ["Default"]; SocketRocket is synthesized from socket_rocket_config, fail-closed if absent). Declared as :path pods so Podfile-local resolution beats trunk and nothing is fetched. ReactNativeDependencies remains the single header authority. Docs: scripts/cocoapods/__docs__/prebuilt-deps.md describes the full prebuilt-deps header/facade contract and the mode-by-mode supplier table. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ayout
- Embed an RN-authored, accurate-empty PrivacyInfo.xcprivacy for
SocketRocket in the deps prebuild (upstream ships no privacy manifest;
the deps artifact historically carried resource bundles only for
boost/folly/glog). SocketRocket uses no Required Reason APIs.
- Xcode 26's SwiftPM rejects a public-headers directory where the
umbrella header has sibling directories ("target 'SocketRocket' has
invalid header layout"): stage the flat public headers into include/
via prepareScript and point publicHeaderFiles there. Xcode 16 accepts
both layouts; the artifact's Headers/SocketRocket namespace is
unaffected.
- Rename the dependency 'socket-rocket' -> 'SocketRocket' so the bundle
and header-namespace naming line up
(ReactNativeDependencies_SocketRocket.bundle).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pure formatting — no content change. Fixes the format-check lint lane. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review feedback on the prebuilt-deps facades: - rndeps_facades.rb: soften the "graph-equivalent" comment — facades match the source pod's spec/subspec shape but route every derived subspec's dep to ReactNativeDependencies, so intra-pod subspec deps (RCT-Folly/Fabric -> RCT-Folly/Default) are not reproduced (harmless; the deps are declared explicitly in react_native_pods.rb). - rndeps_facades.rb: add `require_relative './helpers'` so Helpers::Constants is self-contained against require ordering (the defined? guard stays as backstop). - reactNativeDependencies.js: document that the no-version-marker branch returns before the version.txt write, so a locally-staged artifact never gains a marker and later runs re-hit the branch (intentional; downstream must not assume it). - rndependencies.rb: uniq the HEADER_SEARCH_PATHS so a second call can't duplicate. - prebuilt-deps.md: drop the stray space before SocketRocket. Note: the Ruby-tests request is not actioned — the repo's cocoapods Ruby test harness is not wired into CI, so the tests would not run. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ODS hint distinct Two review follow-ups on the deps facades: - The prebuilt branch of add_rn_third_party_dependencies appended the deps Headers search path unconditionally; a second call on the same spec would duplicate the entry. Now .uniq, mirroring the from-source branch. - load_real_spec's method-level rescue also caught the tailored not-found raise and re-wrapped it generically, burying the "Update FACADE_PODS" hint. The rescue is now scoped to Pod::Specification.from_file only, so the two failure messages stay distinct. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1b0f40e to
665f06a
Compare
|
@cipolleschi has imported this pull request. If you are a Meta employee, you can view this in D111449257. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Step 1 of making the prebuilt
ReactNativeDependenciespod the single header authority for the third-party C/C++ deps (RCT-Folly, glog, boost, DoubleConversion, fmt, fast_float, SocketRocket) in prebuilt-deps mode.Today the deps binary replaces the source pods' code, but the pod still
s.dependency's the real source pods and borrows their headers via$(PODS_ROOT)/<pod>search paths. That split header authority is the dual-copy bug class behind the 2026-07-03 SocketRocket regression (duplicate @interfaceunderuse_frameworks!— SocketRocket's ObjC headers have no include guards).Three commits:
fix(cocoapods): harden prebuilt-deps header search paths and artifact handling —
rndependencies.rb's||= [] << pathonly added the deps header search path whenHEADER_SEARCH_PATHSwas unset (silently dropped otherwise); normalize and always append, and point at the pod-local flattenedHeaders/.ReactNativeDependencies.podspecprepare_commandnow fails closed (exit 1) instead of silently producing a no-link pod.reactNativeDependencies.jsno longer deletes + re-downloads a locally staged artifact that lacks a version marker.feat(cocoapods): dependency-only facades for third-party pods in prebuilt-deps mode — in prebuilt-deps mode the real source pods are not declared, so a community podspec's hardcoded
s.dependency "RCT-Folly"would resolve from trunk and compile from source next to the prebuilt binary.RNDepsFacadesgenerates dependency-only local facade podspecs (no sources, no headers, a single dependency onReactNativeDependencies); versions/subspecs are derived from the real podspecs inthird-party-podspecs/(SocketRocket synthesized fail-closed fromsocket_rocket_config). Full contract documented inscripts/cocoapods/__docs__/prebuilt-deps.md.feat(ios-prebuild): SocketRocket privacy manifest + Xcode 26 header layout — embed an RN-authored, accurate-empty
PrivacyInfo.xcprivacyfor SocketRocket (upstream ships none), and stage flat public headers intoinclude/so Xcode 26's SwiftPM accepts the header layout.Stacked on #57305 (base:
chrfalch/prebuilt-resources); the SwiftPM preview (#57332) rebases on top of this. Follow-up (separate PR): headers-onlyReactNativeDependenciesHeaders.xcframeworksidecar so SPM auto-serves the deps namespaces andReactNativeHeadersgoes pure-RN.Changelog:
[IOS][CHANGED] - Prebuilt-deps mode: serve third-party headers from the ReactNativeDependencies pod itself and resolve community
s.dependencyon RCT-Folly/glog/boost/etc. via dependency-only facade podsTest Plan
E2E matrix (2026-07-06, locally built deps artifact via
prepare-ios-prebuilds.js):USE_FRAMEWORKS=dynamic— builds (the SocketRocket-regression config)Podfile.lockidentical to baseline🤖 Generated with Claude Code