From 441aaac38912add66f402e2db923e2e386848990 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 9 Oct 2026 00:39:54 -0700 Subject: [PATCH 1/2] Use umbrella includes in Android app-facing C++ (#58843) Summary: Pull Request resolved: https://github.com/react/react-native/pull/58843 App-facing C++ on Android included guarded ReactCommon headers directly, which is a hard error in app code under `RN_STRICT_API`. The default app `OnLoad.cpp` (and RN-Tester's), the generated `autolinking.h`, and the prefab-exported JNI headers in the `newarchdefaults`, `turbomodule` and `jni` packages now include React Native through module umbrellas (``, ``, ``, ``). `FabricMountingManager.h` also drops an unused include of `react/renderer/uimanager/primitives.h`. The `fabric`, `mapbuffer` and `DefaultComponentsRegistry.h` headers are handled in the parent diff. No behaviour change without `RN_STRICT_API`. Changelog: [Android][Changed] - Include React Native through module umbrella headers in app-facing JNI headers and the generated autolinking header Differential Revision: D123376722 --- .../tasks/GenerateAutolinkingNewArchitecturesFileTask.kt | 7 +++---- .../ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp | 2 +- .../src/main/jni/react/fabric/FabricMountingManager.h | 1 - .../ReactAndroid/src/main/jni/react/jni/JRuntimeExecutor.h | 2 +- .../newarchdefaults/DefaultTurboModuleManagerDelegate.h | 3 +-- .../turbomodule/ReactCommon/BindingsInstallerHolder.h | 4 ++-- .../jni/react/turbomodule/ReactCommon/CallInvokerHolder.h | 2 +- .../ReactCommon/NativeMethodCallInvokerHolder.h | 2 +- .../jni/react/turbomodule/ReactCommon/TurboModuleManager.h | 5 ++--- .../turbomodule/ReactCommon/TurboModuleManagerDelegate.h | 4 ++-- packages/rn-tester/android/app/src/main/jni/OnLoad.cpp | 2 +- 11 files changed, 15 insertions(+), 19 deletions(-) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateAutolinkingNewArchitecturesFileTask.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateAutolinkingNewArchitecturesFileTask.kt index 3301946072f7..068737880a96 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateAutolinkingNewArchitecturesFileTask.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateAutolinkingNewArchitecturesFileTask.kt @@ -266,11 +266,10 @@ abstract class GenerateAutolinkingNewArchitecturesFileTask : DefaultTask() { #pragma once - #include - #include - #include + #include + #include + #include #include - #include namespace facebook { namespace react { diff --git a/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp b/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp index 3e0915c8304b..f79eb1a45512 100644 --- a/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp +++ b/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp @@ -30,9 +30,9 @@ #include #include #include +#include #include #include -#include #ifdef REACT_NATIVE_APP_CODEGEN_HEADER #include REACT_NATIVE_APP_CODEGEN_HEADER diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h index 826449cee4fe..5a7fbc5ff001 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.h @@ -13,7 +13,6 @@ #include #include #include -#include namespace facebook::react { diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JRuntimeExecutor.h b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JRuntimeExecutor.h index 9caffec9c434..9c0fe9dbda66 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/JRuntimeExecutor.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/JRuntimeExecutor.h @@ -7,7 +7,7 @@ #pragma once -#include +#include #include namespace facebook::react { diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/newarchdefaults/DefaultTurboModuleManagerDelegate.h b/packages/react-native/ReactAndroid/src/main/jni/react/newarchdefaults/DefaultTurboModuleManagerDelegate.h index c8f87d39ce91..dc5dd8989506 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/newarchdefaults/DefaultTurboModuleManagerDelegate.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/newarchdefaults/DefaultTurboModuleManagerDelegate.h @@ -9,9 +9,8 @@ #include #include +#include #include -#include -#include #include #include diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/BindingsInstallerHolder.h b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/BindingsInstallerHolder.h index 1ca58bba4399..6501f4fc7094 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/BindingsInstallerHolder.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/BindingsInstallerHolder.h @@ -7,8 +7,8 @@ #pragma once -#include -#include +#include +#include #include namespace facebook::react { diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/CallInvokerHolder.h b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/CallInvokerHolder.h index ca1fd0ff3e39..f0a6eda8d8f8 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/CallInvokerHolder.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/CallInvokerHolder.h @@ -7,7 +7,7 @@ #pragma once -#include +#include #include #include diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/NativeMethodCallInvokerHolder.h b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/NativeMethodCallInvokerHolder.h index b6f9b1aed4c4..d54b493e45fa 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/NativeMethodCallInvokerHolder.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/NativeMethodCallInvokerHolder.h @@ -7,7 +7,7 @@ #pragma once -#include +#include #include #include diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.h b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.h index 0cc6a096436e..375bbfd4b91f 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManager.h @@ -12,11 +12,10 @@ #include #include +#include +#include #include -#include #include -#include -#include #include #include diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManagerDelegate.h b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManagerDelegate.h index ce7ac58fa364..6dfd3c996037 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManagerDelegate.h +++ b/packages/react-native/ReactAndroid/src/main/jni/react/turbomodule/ReactCommon/TurboModuleManagerDelegate.h @@ -7,8 +7,8 @@ #pragma once -#include -#include +#include +#include #include #include #include diff --git a/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp b/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp index 72d62fb09590..c54faffa30ee 100644 --- a/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp +++ b/packages/rn-tester/android/app/src/main/jni/OnLoad.cpp @@ -9,9 +9,9 @@ #include #include #include +#include #include #include -#include #ifdef REACT_NATIVE_APP_CODEGEN_HEADER #include REACT_NATIVE_APP_CODEGEN_HEADER From 53a39f33d31ed11d7e92037842c965b6dbe3e239 Mon Sep 17 00:00:00 2001 From: Jakub Piasecki Date: Fri, 9 Oct 2026 00:53:03 -0700 Subject: [PATCH 2/2] Add Android opt-in for RN_STRICT_API (#58849) Summary: Pull Request resolved: https://github.com/react/react-native/pull/58849 Adds a `strictCxxApiEnabled` (or `react.strictCxxApiEnabled`) Gradle property that defines `RN_STRICT_API` for an app's own native code: the app library, autolinked libraries and app codegen, through `common_flags` in `ReactNative-application.cmake`. React Native's own code is unaffected, since it defines `RN_BUILDING`. The shared precompiled header now includes React Native through module umbrellas, as its direct includes would otherwise fail under the flag. Codegen targets reuse that precompiled header, so a direct include of any header it already contains is skipped by `#pragma once` and the guards are effectively not enforced in those targets. Changelog: [Android][Added] - Add `strictCxxApiEnabled` Gradle property to enforce React Native's C++ stable API in app native code Reviewed By: cipolleschi Differential Revision: D123399797 --- .../react/utils/NdkConfiguratorUtils.kt | 4 +++ .../com/facebook/react/utils/ProjectUtils.kt | 9 +++++ .../com/facebook/react/utils/PropertyUtils.kt | 7 ++++ .../facebook/react/utils/ProjectUtilsTest.kt | 34 +++++++++++++++++++ .../cmake-utils/ReactNative-application.cmake | 5 +++ .../cmake-utils/precompiled-header/pch.h | 15 +++----- 6 files changed, 64 insertions(+), 10 deletions(-) diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt index dbbb5497fea4..8771ed97cd10 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt @@ -12,6 +12,7 @@ import com.android.build.api.variant.Variant import com.facebook.react.ReactExtension import com.facebook.react.internal.StubPchBuildService import com.facebook.react.utils.ProjectUtils.getReactNativeArchitectures +import com.facebook.react.utils.ProjectUtils.isStrictCxxApiEnabled import java.io.File import org.gradle.api.Project @@ -55,6 +56,9 @@ internal object NdkConfiguratorUtils { if (cmakeArgs.none { it.startsWith("-DANDROID_STL") }) { cmakeArgs.add("-DANDROID_STL=c++_shared") } + if (project.isStrictCxxApiEnabled && cmakeArgs.none { it.startsWith("-DRN_STRICT_API") }) { + cmakeArgs.add("-DRN_STRICT_API=ON") + } val architectures = project.getReactNativeArchitectures() // abiFilters are split ABI are not compatible each other, so we set the abiFilters diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/ProjectUtils.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/ProjectUtils.kt index 2a4d6cf7115d..855e34df90ab 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/ProjectUtils.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/ProjectUtils.kt @@ -15,7 +15,9 @@ import com.facebook.react.utils.PropertyUtils.REACT_NATIVE_ARCHITECTURES import com.facebook.react.utils.PropertyUtils.SCOPED_EDGE_TO_EDGE_ENABLED import com.facebook.react.utils.PropertyUtils.SCOPED_HERMES_ENABLED import com.facebook.react.utils.PropertyUtils.SCOPED_REACT_NATIVE_ARCHITECTURES +import com.facebook.react.utils.PropertyUtils.SCOPED_STRICT_CXX_API_ENABLED import com.facebook.react.utils.PropertyUtils.SCOPED_USE_THIRD_PARTY_JSC +import com.facebook.react.utils.PropertyUtils.STRICT_CXX_API_ENABLED import com.facebook.react.utils.PropertyUtils.USE_THIRD_PARTY_JSC import org.gradle.api.Project import org.gradle.api.file.DirectoryProperty @@ -56,6 +58,13 @@ internal object ProjectUtils { (project.hasProperty(SCOPED_EDGE_TO_EDGE_ENABLED) && project.property(SCOPED_EDGE_TO_EDGE_ENABLED).toString().toBoolean()) + internal val Project.isStrictCxxApiEnabled: Boolean + get() = + (project.hasProperty(STRICT_CXX_API_ENABLED) && + project.property(STRICT_CXX_API_ENABLED).toString().toBoolean()) || + (project.hasProperty(SCOPED_STRICT_CXX_API_ENABLED) && + project.property(SCOPED_STRICT_CXX_API_ENABLED).toString().toBoolean()) + internal val Project.useThirdPartyJSC: Boolean get() = (project.hasProperty(USE_THIRD_PARTY_JSC) && diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt index a0c57e759734..3425aa4ac380 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/PropertyUtils.kt @@ -29,6 +29,13 @@ object PropertyUtils { const val EDGE_TO_EDGE_ENABLED = "edgeToEdgeEnabled" const val SCOPED_EDGE_TO_EDGE_ENABLED = "react.edgeToEdgeEnabled" + /** + * Public property that enforces the C++ stable API for the app's own native code by defining + * RN_STRICT_API + */ + const val STRICT_CXX_API_ENABLED = "strictCxxApiEnabled" + const val SCOPED_STRICT_CXX_API_ENABLED = "react.strictCxxApiEnabled" + /** Public property that excludes jsctooling from core */ const val USE_THIRD_PARTY_JSC = "useThirdPartyJSC" const val SCOPED_USE_THIRD_PARTY_JSC = "react.useThirdPartyJSC" diff --git a/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/ProjectUtilsTest.kt b/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/ProjectUtilsTest.kt index 491dd9b9545d..64fb15c42494 100644 --- a/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/ProjectUtilsTest.kt +++ b/packages/gradle-plugin/react-native-gradle-plugin/src/test/kotlin/com/facebook/react/utils/ProjectUtilsTest.kt @@ -15,6 +15,7 @@ import com.facebook.react.utils.ProjectUtils.getReactNativeArchitectures import com.facebook.react.utils.ProjectUtils.isEdgeToEdgeEnabled import com.facebook.react.utils.ProjectUtils.isHermesEnabled import com.facebook.react.utils.ProjectUtils.isNewArchEnabled +import com.facebook.react.utils.ProjectUtils.isStrictCxxApiEnabled import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson import java.io.File import org.assertj.core.api.Assertions.assertThat @@ -115,6 +116,39 @@ class ProjectUtilsTest { assertThat(project.isEdgeToEdgeEnabled).isFalse() } + @Test + fun isStrictCxxApiEnabled_returnsFalseByDefault() { + assertThat(createProject().isStrictCxxApiEnabled).isFalse() + } + + @Test + fun isStrictCxxApiEnabled_withEnabledViaProperty_returnsTrue() { + val project = createProject() + project.extensions.extraProperties.set("strictCxxApiEnabled", "true") + assertThat(project.isStrictCxxApiEnabled).isTrue() + } + + @Test + fun isStrictCxxApiEnabled_withEnabledViaScopedProperty_returnsTrue() { + val project = createProject() + project.extensions.extraProperties.set("react.strictCxxApiEnabled", "true") + assertThat(project.isStrictCxxApiEnabled).isTrue() + } + + @Test + fun isStrictCxxApiEnabled_withDisabledViaProperty_returnsFalse() { + val project = createProject() + project.extensions.extraProperties.set("strictCxxApiEnabled", "false") + assertThat(project.isStrictCxxApiEnabled).isFalse() + } + + @Test + fun isStrictCxxApiEnabled_withInvalidViaProperty_returnsFalse() { + val project = createProject() + project.extensions.extraProperties.set("strictCxxApiEnabled", "¯\\_(ツ)_/¯") + assertThat(project.isStrictCxxApiEnabled).isFalse() + } + @Test fun needsCodegenFromPackageJson_withCodegenConfigInPackageJson_returnsTrue() { val project = createProject() diff --git a/packages/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake b/packages/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake index 7201024de326..7791dacd6a96 100644 --- a/packages/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake +++ b/packages/react-native/ReactAndroid/cmake-utils/ReactNative-application.cmake @@ -89,6 +89,11 @@ target_link_libraries(${CMAKE_PROJECT_NAME} add_library(common_flags INTERFACE) target_compile_options(common_flags INTERFACE ${folly_FLAGS}) +if(RN_STRICT_API) + target_compile_definitions(common_flags INTERFACE RN_STRICT_API) + target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE RN_STRICT_API) +endif() + # Defines the `reactnative_pch` target and `target_reuse_reactnative_pch()`, so # the codegen targets below share a single precompiled header. Has to come after # `common_flags`, as the precompiled header is built with the same flags as its diff --git a/packages/react-native/ReactAndroid/cmake-utils/precompiled-header/pch.h b/packages/react-native/ReactAndroid/cmake-utils/precompiled-header/pch.h index e5ea7310a482..88e26e7285aa 100644 --- a/packages/react-native/ReactAndroid/cmake-utils/precompiled-header/pch.h +++ b/packages/react-native/ReactAndroid/cmake-utils/precompiled-header/pch.h @@ -7,15 +7,10 @@ #pragma once -#include -#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include