Skip to content

fix(iOS): exclude C++-guarded foreign-namespace headers from React umbrella#57575

Open
chrfalch wants to merge 1 commit into
mainfrom
chrfalch/exclude-cxx-guarded-umbrella-headers
Open

fix(iOS): exclude C++-guarded foreign-namespace headers from React umbrella#57575
chrfalch wants to merge 1 commit into
mainfrom
chrfalch/exclude-cxx-guarded-umbrella-headers

Conversation

@chrfalch

Copy link
Copy Markdown
Collaborator

Summary:

The first nightly containing the prebuilt/SwiftPM stack (0.88.0-nightly-20260716) broke the react-native-unistyles job in react-native-community/nightly-tests with:

Pods/Headers/Public/React-Core-prebuilt/react/timing/primitives.h:28:7: error: redefinition of 'HighResDuration'
note: additional include site in header from module 'ReactNativeHeaders_react'
note: additional include site in header from module 'React.RCTFrameTimingsObserver'
→ could not build module 'React'

Root cause: RCTFrameTimingsObserver.h includes <jsinspector-modern/tracing/FrameTimingSequence.h> under #ifdef __cplusplus, transitively reaching react/timing/primitives.h, which is owned by the ReactNativeHeaders_react module. The headers inventory's reachability fixpoint only follows unguarded include edges (modeling a pure-ObjC consumer), so it couldn't see this edge and classified the header as umbrella-safe. Swift pods with C++ interop (unistyles, Nitro-based libs) build the React clang module as ObjC++, opening the guard — one header owned by two modules in a single compilation → C++ redefinition errors. Pure-ObjC consumers never open the guard, which is why only C++-interop libraries failed.

Fix: a curated UMBRELLA_CXX_GUARDED_EXCLUSIONS set checked in isUmbrellaSafe, mirroring the existing PRIVATE_REACT_HEADERS pattern, with fail-closed validation (validateUmbrellaExclusions, wired into planFromInventory) so a renamed/deleted entry surfaces immediately.

The header still ships in React.framework/Headers and stays importable via #import <React/RCTFrameTimingsObserver.h> (its only consumer today is RCTHost.mm); it is only removed from the modular umbrella surface. If a modular consumer ever needs it, it can be promoted to a PRIVATE_REACT_HEADERS.textual-style entry instead.

Changelog:

[IOS] [FIXED] - Fix "redefinition of 'HighResDuration'" / "could not build module 'React'" when building Swift pods with C++ interop against the prebuilt React-Core artifact

Test Plan:

  • New unit tests in scripts/ios-prebuild/__tests__/headers-spec-test.js: the excluded header is kept out of the umbrella (fails against the previous code with Received array: ["React/RCTFrameTimingsObserver.h"]), and exclusion drift fails closed.
  • Full ios-prebuild suite: 5 suites, 58/58 tests passing; prettier/eslint clean.
  • E2E: to be confirmed via a nightly/prebuilt cut building react-native-unistyles in prebuilt mode (RCT_USE_RN_DEP=1 RCT_USE_PREBUILT_RNCORE=1).

🤖 Generated with Claude Code

…brella

The prebuilt React-Core artifact's generated module.modulemap placed
RCTFrameTimingsObserver.h in the React umbrella. That header includes
<jsinspector-modern/tracing/FrameTimingSequence.h> under #ifdef
__cplusplus, transitively reaching react/timing/primitives.h — a header
owned by the ReactNativeHeaders_react module. The headers inventory's
reachability fixpoint only follows unguarded include edges (modeling a
pure-ObjC consumer), so it could not see this edge and classified the
header as umbrella-safe.

For Swift pods with C++ interop (e.g. react-native-unistyles), building
the React clang module opens the __cplusplus guard, and primitives.h
ends up owned by two modules in one compilation: C++ redefinition
errors (HighResDuration, HighResTimeStamp, TraceEvent,
FrameTimingSequence) and "could not build module 'React'". This broke
the react-native-unistyles job in nightly-tests on
0.88.0-nightly-20260716.

Fix: add a curated UMBRELLA_CXX_GUARDED_EXCLUSIONS set, checked in
isUmbrellaSafe, mirroring the existing PRIVATE_REACT_HEADERS pattern,
with fail-closed validation (validateUmbrellaExclusions) so a renamed
or deleted entry surfaces immediately. The header still ships in
React.framework/Headers and remains importable via
#import <React/RCTFrameTimingsObserver.h>; it is only removed from the
modular umbrella surface.

Co-Authored-By: Claude Fable 5 <[email protected]>
@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 Jul 16, 2026
@chrfalch chrfalch requested a review from cipolleschi July 16, 2026 11:31

@cipolleschi cipolleschi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for fixing it

@meta-codesync

meta-codesync Bot commented Jul 16, 2026

Copy link
Copy Markdown

@cipolleschi has imported this pull request. If you are a Meta employee, you can view this in D112320697.

@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jul 16, 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.

2 participants