From 6d0049c60dcc99ffe8c433c45178c1ce2d7e7156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Ma=C5=82ecki?= Date: Wed, 24 Jun 2026 03:07:30 -0700 Subject: [PATCH] Add cxxstableapi package Summary: Adds `cxxstableapi` package containing umbrella public, private and for frameworks guards to be used in the C++ stable API. Differential Revision: D108422344 --- .../ReactAndroid/build.gradle.kts | 1 + .../ReactAndroid/src/main/jni/CMakeLists.txt | 1 + .../react/cxxstableapi/CMakeLists.txt | 18 +++++++ .../react/cxxstableapi/FrameworksGuard.h | 36 +++++++++++++ .../react/cxxstableapi/PrivateGuard.h | 34 ++++++++++++ .../cxxstableapi/React-cxxstableapi.podspec | 34 ++++++++++++ .../react/cxxstableapi/UmbrellaGuard.h | 53 +++++++++++++++++++ 7 files changed, 177 insertions(+) create mode 100644 packages/react-native/ReactCommon/react/cxxstableapi/CMakeLists.txt create mode 100644 packages/react-native/ReactCommon/react/cxxstableapi/FrameworksGuard.h create mode 100644 packages/react-native/ReactCommon/react/cxxstableapi/PrivateGuard.h create mode 100644 packages/react-native/ReactCommon/react/cxxstableapi/React-cxxstableapi.podspec create mode 100644 packages/react-native/ReactCommon/react/cxxstableapi/UmbrellaGuard.h diff --git a/packages/react-native/ReactAndroid/build.gradle.kts b/packages/react-native/ReactAndroid/build.gradle.kts index af16973dbc40..a23677f3f547 100644 --- a/packages/react-native/ReactAndroid/build.gradle.kts +++ b/packages/react-native/ReactAndroid/build.gradle.kts @@ -242,6 +242,7 @@ val preparePrefab by Pair("../ReactCommon/react/renderer/telemetry/", "react/renderer/telemetry/"), Pair("../ReactCommon/react/renderer/uimanager/", "react/renderer/uimanager/"), Pair("../ReactCommon/react/debug/", "react/debug/"), + Pair("../ReactCommon/react/cxxstableapi/", "react/cxxstableapi/"), Pair("../ReactCommon/react/utils/", "react/utils/"), Pair("src/main/jni/react/jni", "react/jni/"), // react_cxxreactpackage diff --git a/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt b/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt index 13300ea8f8b7..0d54f6f02b1b 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt +++ b/packages/react-native/ReactAndroid/src/main/jni/CMakeLists.txt @@ -68,6 +68,7 @@ add_react_common_subdir(jsitooling) add_react_common_subdir(cxxreact) add_react_common_subdir(jsi) add_react_common_subdir(callinvoker) +add_react_common_subdir(react/cxxstableapi) add_react_common_subdir(oscompat) add_react_common_subdir(jsinspector-modern) add_react_common_subdir(jsinspector-modern/cdp) diff --git a/packages/react-native/ReactCommon/react/cxxstableapi/CMakeLists.txt b/packages/react-native/ReactCommon/react/cxxstableapi/CMakeLists.txt new file mode 100644 index 000000000000..d75f1a06485e --- /dev/null +++ b/packages/react-native/ReactCommon/react/cxxstableapi/CMakeLists.txt @@ -0,0 +1,18 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +cmake_minimum_required(VERSION 3.13) +set(CMAKE_VERBOSE_MAKEFILE on) + +include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake) + +# Header-only: the cxxstableapi guards are pure preprocessor headers, so this is +# an INTERFACE library. Headers resolve as via the +# shared ${REACT_COMMON_DIR} include root. +add_library(react_cxxstableapi INTERFACE) + +target_include_directories(react_cxxstableapi INTERFACE ${REACT_COMMON_DIR}) +target_compile_reactnative_options(react_cxxstableapi INTERFACE) +target_compile_options(react_cxxstableapi INTERFACE -Wpedantic) diff --git a/packages/react-native/ReactCommon/react/cxxstableapi/FrameworksGuard.h b/packages/react-native/ReactCommon/react/cxxstableapi/FrameworksGuard.h new file mode 100644 index 000000000000..fe809446659f --- /dev/null +++ b/packages/react-native/ReactCommon/react/cxxstableapi/FrameworksGuard.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// ============================================================================= +// Shared "for frameworks" guard for React Native's C++ stable API. +// +// Include this header at the top of every *for-frameworks* module header (right +// after `#pragma once`): +// +// #include +// +// "For frameworks" headers are APIs intended for framework authors integrating +// React Native, not for ordinary app code. Including one directly emits a +// SUPPRESSIBLE warning under enforcement. A framework author acknowledges the +// usage by defining `RN_ALLOW_FRAMEWORKS`, which silences the warning. +// +// See for the full `RN_*` macro contract. +// In short: +// RN_STRICT_API Consumer opt-in master switch; the guard is inert +// without it. +// RN_ALLOW_FRAMEWORKS Consumer opt-out acknowledging framework-tier usage. +// RN_UMBRELLA_CONTEXT Internal marker set by an umbrella around its includes. +// RN_BUILDING Set by React Native's own build. +// +// This header is intentionally NOT `#pragma once`-guarded: it must be +// re-evaluated on every inclusion so each direct include is checked. +// ============================================================================= + +#if defined(RN_STRICT_API) && !defined(RN_ALLOW_FRAMEWORKS) && !defined(RN_UMBRELLA_CONTEXT) && !defined(RN_BUILDING) +#warning \ + "This is a 'for frameworks' React Native API, intended for framework authors rather than app code. Include it via the module umbrella .h>, or define RN_ALLOW_FRAMEWORKS to acknowledge framework-tier usage and silence this warning." +#endif diff --git a/packages/react-native/ReactCommon/react/cxxstableapi/PrivateGuard.h b/packages/react-native/ReactCommon/react/cxxstableapi/PrivateGuard.h new file mode 100644 index 000000000000..723eca8bc7de --- /dev/null +++ b/packages/react-native/ReactCommon/react/cxxstableapi/PrivateGuard.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// ============================================================================= +// Shared "private" guard for React Native's C++ stable API. +// +// Include this header at the top of every *private* module header (right after +// `#pragma once`): +// +// #include +// +// Private headers are implementation details with no stability guarantee. +// Including one from outside React Native's own build is a hard error under +// enforcement; there is no consumer-facing escape hatch (unlike the +// for-frameworks guard) — only React Native's own sources, which define +// `RN_BUILDING`, may include these headers. +// +// See for the full `RN_*` macro contract. +// In short: +// RN_STRICT_API Consumer opt-in master switch; the guard is inert without it. +// RN_BUILDING Set by React Native's own build. +// +// This header is intentionally NOT `#pragma once`-guarded: it must be +// re-evaluated on every inclusion so each direct include is checked. +// ============================================================================= + +#if defined(RN_STRICT_API) && !defined(RN_BUILDING) +#error \ + "This is a private React Native header and is not part of the public API. Do not include it directly; it has no stability guarantee." +#endif diff --git a/packages/react-native/ReactCommon/react/cxxstableapi/React-cxxstableapi.podspec b/packages/react-native/ReactCommon/react/cxxstableapi/React-cxxstableapi.podspec new file mode 100644 index 000000000000..5824fb5da97a --- /dev/null +++ b/packages/react-native/ReactCommon/react/cxxstableapi/React-cxxstableapi.podspec @@ -0,0 +1,34 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "..", "..", "..", "package.json"))) +version = package['version'] + +source = { :git => 'https://github.com/facebook/react-native.git' } +if version == '1000.0.0' + # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") +else + source[:tag] = "v#{version}" +end + +Pod::Spec.new do |s| + s.name = "React-cxxstableapi" + s.version = version + s.summary = "Header-only guards for React Native's C++ stable API." + s.homepage = "https://reactnative.dev/" + s.license = package["license"] + s.author = "Meta Platforms, Inc. and its affiliates" + s.platforms = min_supported_versions + s.source = source + s.source_files = podspec_sources("*.h", "*.h") + s.header_dir = "react/cxxstableapi" + s.pod_target_xcconfig = { "CLANG_CXX_LANGUAGE_STANDARD" => rct_cxx_language_standard(), + "DEFINES_MODULE" => "YES" } + + resolve_use_frameworks(s, header_mappings_dir: "../..", module_name: "React_cxxstableapi") +end diff --git a/packages/react-native/ReactCommon/react/cxxstableapi/UmbrellaGuard.h b/packages/react-native/ReactCommon/react/cxxstableapi/UmbrellaGuard.h new file mode 100644 index 000000000000..fa925805f1e2 --- /dev/null +++ b/packages/react-native/ReactCommon/react/cxxstableapi/UmbrellaGuard.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// ============================================================================= +// Shared "public API" guard for React Native's C++ stable API. +// +// Include this header at the top of every *public* module header (right after +// `#pragma once`): +// +// #include +// +// The guard turns a *direct* include of a public module header into a hard +// error, steering consumers to the module's umbrella header instead: +// +// #include .h> // do this +// +// ----------------------------------------------------------------------------- +// Macro contract (all `RN_*` macros are shared across modules) +// +// RN_STRICT_API Consumer opt-in master switch. Every guard in this +// directory is INERT unless the consuming build defines +// this macro. Shipping the guards therefore changes +// nothing for existing consumers — they only activate +// when a consumer opts into the strict public API by +// defining RN_STRICT_API. +// +// RN_UMBRELLA_CONTEXT Internal marker (implementation detail; consumers never +// set it). A module umbrella defines it around its own +// `#include`s to signal the blessed inclusion path: +// #define RN_UMBRELLA_CONTEXT +// #include +// #include +// #undef RN_UMBRELLA_CONTEXT +// The `#undef` matters: it keeps the marker scoped to the +// umbrella's includes so later *direct* includes in the +// same translation unit are still caught. +// +// RN_BUILDING Defined by React Native's own build targets so internal +// sources may keep including the fine-grained headers +// directly. +// +// This header is intentionally NOT `#pragma once`-guarded: it must be +// re-evaluated on every inclusion so each direct include is checked. +// ============================================================================= + +#if defined(RN_STRICT_API) && !defined(RN_UMBRELLA_CONTEXT) && !defined(RN_BUILDING) +#error \ + "Do not include this React Native header directly. Include the module's umbrella header .h> instead." +#endif