From c9e6a6056d9ee24c70826e598ba7219da25ae10d Mon Sep 17 00:00:00 2001 From: Nicola Corti Date: Thu, 6 Oct 2022 06:40:22 -0700 Subject: [PATCH] Hide the C++/Cmake configuration from user space inside the framework Summary: This change encapsulates all the NDK configuration logic inside the React Native Gradle Plugin. The changes are additive so that the user can still specify a custom configuration if they wish. So far I've applied the changes to RN Tester. Changes to the template require a bump of the Gradle Plugin NPM package. Changelog: [Android] [Changed] - Hide the C++/Cmake configuration from user space inside the framework Reviewed By: cipolleschi Differential Revision: D40139557 fbshipit-source-id: 013220695791e3d0d458e118de16953e0545c3de --- .../default-app-setup/CMakeLists.txt | 31 ++++++++ .../cmake-utils/default-app-setup/OnLoad.cpp | 79 +++++++++++++++++++ .../kotlin/com/facebook/react/ReactPlugin.kt | 4 +- .../react/utils/NdkConfiguratorUtils.kt | 32 +++++++- packages/rn-tester/android/app/build.gradle | 17 +--- 5 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 ReactAndroid/cmake-utils/default-app-setup/CMakeLists.txt create mode 100644 ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp diff --git a/ReactAndroid/cmake-utils/default-app-setup/CMakeLists.txt b/ReactAndroid/cmake-utils/default-app-setup/CMakeLists.txt new file mode 100644 index 00000000000000..975567131ea461 --- /dev/null +++ b/ReactAndroid/cmake-utils/default-app-setup/CMakeLists.txt @@ -0,0 +1,31 @@ +# 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. + +# This CMake file is the default used by apps and is placed inside react-native +# to encapsulate it from user space (so you won't need to touch C++/Cmake code at all on Android). +# +# If you wish to customize it (because you want to manually link a C++ library or pass a custom +# compilation flag) you can: +# +# 1. Copy this CMake file inside the `android/app/src/main/jni` folder of your project +# 2. Copy the OnLoad.cpp (in this same folder) file inside the same folder as above. +# 3. Extend your `android/app/build.gradle` as follows +# +# android { +# // Other config here... +# externalNativeBuild { +# cmake { +# path "src/main/jni/CMakeLists.txt" +# } +# } +# } + +cmake_minimum_required(VERSION 3.13) + +# Define the library name here. +project(appmodules) + +# This file includes all the necessary to let you build your application with the New Architecture. +include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake) diff --git a/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp b/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp new file mode 100644 index 00000000000000..1eaa20836f7641 --- /dev/null +++ b/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp @@ -0,0 +1,79 @@ +/* + * 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. + */ + +// This C++ file is part of the default configuration used by apps and is placed +// inside react-native to encapsulate it from user space (so you won't need to +// touch C++/Cmake code at all on Android). +// +// If you wish to customize it (because you want to manually link a C++ library +// or pass a custom compilation flag) you can: +// +// 1. Copy this CMake file inside the `android/app/src/main/jni` folder of your +// project +// 2. Copy the OnLoad.cpp (in this same folder) file inside the same folder as +// above. +// 3. Extend your `android/app/build.gradle` as follows +// +// android { +// // Other config here... +// externalNativeBuild { +// cmake { +// path "src/main/jni/CMakeLists.txt" +// } +// } +// } + +#include +#include +#include +#include +#include + +namespace facebook { +namespace react { + +void registerComponents( + std::shared_ptr registry) { + // Custom Fabric Components go here. You can register custom + // components coming from your App or from 3rd party libraries here. + // + // providerRegistry->add(concreteComponentDescriptorProvider< + // AocViewerComponentDescriptor>()); + + // By default we just use the components autolinked by RN CLI + rncli_registerProviders(registry); +} + +std::shared_ptr provideModules( + const std::string &name, + const JavaTurboModule::InitParams ¶ms) { + // Here you can provide your own module provider for TurboModules coming from + // either your application or from external libraries. The approach to follow + // is similar to the following (for a library called `samplelibrary`): + // + // auto module = samplelibrary_ModuleProvider(moduleName, params); + // if (module != nullptr) { + // return module; + // } + // return rncore_ModuleProvider(moduleName, params); + + // By default we just use the module providers autolinked by RN CLI + return rncli_ModuleProvider(name, params); +} + +} // namespace react +} // namespace facebook + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { + return facebook::jni::initialize(vm, [] { + facebook::react::DefaultTurboModuleManagerDelegate:: + moduleProvidersFromEntryPoint = &facebook::react::provideModules; + facebook::react::DefaultComponentsRegistry:: + registerComponentDescriptorsFromEntryPoint = + &facebook::react::registerComponents; + }); +} diff --git a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt index aee077a66942d0..ab24aa43a4c75b 100644 --- a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt +++ b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/ReactPlugin.kt @@ -16,7 +16,7 @@ import com.facebook.react.tasks.BuildCodegenCLITask import com.facebook.react.tasks.GenerateCodegenArtifactsTask import com.facebook.react.tasks.GenerateCodegenSchemaTask import com.facebook.react.utils.JsonUtils -import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativePrefab +import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativeNdk import com.facebook.react.utils.findPackageJsonFile import java.io.File import kotlin.system.exitProcess @@ -54,7 +54,7 @@ class ReactPlugin : Plugin { } private fun applyAppPlugin(project: Project, config: ReactExtension) { - configureReactNativePrefab(project) + configureReactNativeNdk(project, config) project.afterEvaluate { if (config.applyAppPlugin.getOrElse(false)) { val androidConfiguration = project.extensions.getByType(BaseExtension::class.java) diff --git a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt index 22ebe92b538283..2cca27d547c5c6 100644 --- a/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt +++ b/packages/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/utils/NdkConfiguratorUtils.kt @@ -8,12 +8,14 @@ package com.facebook.react.utils import com.android.build.api.variant.AndroidComponentsExtension +import com.facebook.react.ReactExtension import com.facebook.react.utils.ProjectUtils.isNewArchEnabled +import java.io.File import org.gradle.api.Project internal object NdkConfiguratorUtils { @Suppress("UnstableApiUsage") - fun configureReactNativePrefab(project: Project) { + fun configureReactNativeNdk(project: Project, extension: ReactExtension) { if (!project.isNewArchEnabled) { return } @@ -54,6 +56,34 @@ internal object NdkConfiguratorUtils { // AGP will give priority of libc++_shared coming from App modules. "**/libc++_shared.so", )) + + // If the user has not provided a CmakeLists.txt path, let's provide + // the default one from the framework + if (ext.externalNativeBuild.cmake.path == null) { + System.err.println("NCOR: Patching cmake path file") + ext.externalNativeBuild.cmake.path = File("TODO") + } + + // Parameters should be provided in an additive manner (do not override what + // the user provided, but allow for sensible defaults). + val cmakeArgs = ext.defaultConfig.externalNativeBuild.cmake.arguments + if ("-DGENERATED_SRC_DIR" !in cmakeArgs) { + cmakeArgs.add("-DGENERATED_SRC_DIR=${File(project.buildDir, "generated/source")}") + } + if ("-DPROJECT_BUILD_DIR" !in cmakeArgs) { + cmakeArgs.add("-DPROJECT_BUILD_DIR=${project.buildDir}") + } + if ("-DREACT_ANDROID_DIR" !in cmakeArgs) { + cmakeArgs.add( + "-DREACT_ANDROID_DIR=${extension.reactNativeDir.file("ReactAndroid").get().asFile}") + } + if ("-DREACT_ANDROID_BUILD_DIR" !in cmakeArgs) { + cmakeArgs.add( + "-DREACT_ANDROID_BUILD_DIR=${extension.reactNativeDir.file("ReactAndroid/build").get().asFile}") + } + if ("-DANDROID_STL" !in cmakeArgs) { + cmakeArgs.add("-DANDROID_STL=c++_shared") + } } } } diff --git a/packages/rn-tester/android/app/build.gradle b/packages/rn-tester/android/app/build.gradle index bc1eead8261128..2fe27d3fa0cc5b 100644 --- a/packages/rn-tester/android/app/build.gradle +++ b/packages/rn-tester/android/app/build.gradle @@ -215,24 +215,11 @@ dependencies { androidTestImplementation 'junit:junit:4.12' } -// TODO: Move all this logic to CodegenPlugin.java. -def reactAndroidProjectDir = project(':ReactAndroid').projectDir; -def reactAndroidBuildDir = project(':ReactAndroid').buildDir; - android { - defaultConfig { - externalNativeBuild { - cmake { - arguments "-DGENERATED_SRC_DIR=$buildDir/generated/source", - "-DPROJECT_BUILD_DIR=$buildDir", - "-DREACT_ANDROID_DIR=$reactAndroidProjectDir", - "-DREACT_ANDROID_BUILD_DIR=$reactAndroidBuildDir", - "-DANDROID_STL=c++_shared" - } - } - } externalNativeBuild { cmake { + // RN Tester is doing custom linking of C++ libraries therefore needs + // a dedicated CMakeLists.txt file. path "src/main/jni/CMakeLists.txt" } }