Skip to content

Commit 673d4e1

Browse files
committed
feat: Add ET library for direct use in C++
1 parent a245872 commit 673d4e1

File tree

102 files changed

+13865
-61
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+13865
-61
lines changed

android/CMakeLists.txt

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
cmake_minimum_required(VERSION 3.13)
2+
project(RnExecutorch)
3+
4+
set (CMAKE_VERBOSE_MAKEFILE ON)
5+
set (CMAKE_CXX_STANDARD 20)
6+
7+
include("${REACT_NATIVE_DIR}/ReactAndroid/cmake-utils/folly-flags.cmake")
8+
add_compile_options(${folly_FLAGS})
9+
10+
if(${IS_NEW_ARCHITECTURE_ENABLED})
11+
string(APPEND CMAKE_CXX_FLAGS " -DRCT_NEW_ARCH_ENABLED")
12+
endif()
13+
14+
set(ANDROID_CPP_DIR "${CMAKE_SOURCE_DIR}/src/main/cpp")
15+
set(COMMON_CPP_DIR "${CMAKE_SOURCE_DIR}/../common")
16+
set(ET_LIB_DIR "${CMAKE_SOURCE_DIR}/libs")
17+
set(ET_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/../third-party/include")
18+
19+
add_subdirectory("${ANDROID_CPP_DIR}")

android/build.gradle

+67-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import org.apache.tools.ant.taskdefs.condition.Os
2+
13
buildscript {
24
ext {
35
agp_version = '8.4.2'
@@ -21,7 +23,14 @@ buildscript {
2123

2224
def reactNativeArchitectures() {
2325
def value = rootProject.getProperties().get("reactNativeArchitectures")
24-
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
26+
def defaultArchitectures = ["x86_64", "arm64-v8a"]
27+
if(!value) {
28+
return defaultArchitectures
29+
}
30+
else {
31+
def architectures = value.split(",")
32+
return architectures.findAll { it in defaultArchitectures }
33+
}
2534
}
2635

2736
def isNewArchitectureEnabled() {
@@ -52,6 +61,38 @@ def supportsNamespace() {
5261
return (major == 7 && minor >= 3) || major >= 8
5362
}
5463

64+
def safeAppExtGet(prop, fallback) {
65+
def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') }
66+
appProject?.ext?.has(prop) ? appProject.ext.get(prop) : fallback
67+
}
68+
69+
def toPlatformFileString(String path) {
70+
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
71+
path = path.replace(File.separatorChar, '/' as char)
72+
}
73+
return path
74+
}
75+
76+
def resolveReactNativeDirectory() {
77+
def reactNativeLocation = safeAppExtGet("REACT_NATIVE_NODE_MODULES_DIR", null)
78+
79+
if (reactNativeLocation !== null) {
80+
return file(reactNativeLocation)
81+
}
82+
83+
// Fallback to node resolver for custom directory structures like monorepos.
84+
def reactNativePackage = file(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim())
85+
if(reactNativePackage.exists()) {
86+
return reactNativePackage.parentFile
87+
}
88+
89+
throw new GradleException(
90+
"[RnExecutorch] Unable to resolve react-native location in node_modules. You should project extension property (in `app/build.gradle`) `REACT_NATIVE_NODE_MODULES_DIR` with path to react-native."
91+
)
92+
}
93+
94+
def reactNativeRootDir = resolveReactNativeDirectory()
95+
5596
android {
5697
if (supportsNamespace()) {
5798
namespace "com.swmansion.rnexecutorch"
@@ -63,12 +104,35 @@ android {
63104
}
64105
}
65106

107+
buildFeatures {
108+
prefab true
109+
prefabPublishing true
110+
buildConfig true
111+
}
112+
66113
compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
67114

68115
defaultConfig {
69116
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
70117
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
71118
buildConfigField("boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString())
119+
externalNativeBuild {
120+
cmake {
121+
cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all"
122+
abiFilters (*reactNativeArchitectures())
123+
arguments "-DANDROID_STL=c++_shared",
124+
"-DREACT_NATIVE_DIR=${toPlatformFileString(reactNativeRootDir.path)}"
125+
"-DBUILD_DIR=${project.buildDir}"
126+
"-DANDROID_TOOLCHAIN=clang"
127+
}
128+
}
129+
}
130+
131+
132+
externalNativeBuild {
133+
cmake {
134+
path "CMakeLists.txt"
135+
}
72136
}
73137

74138
buildTypes {
@@ -101,6 +165,8 @@ dependencies {
101165
//noinspection GradleDynamicVersion
102166
implementation 'com.github.wendykierp:JTransforms:3.1'
103167
implementation "com.facebook.react:react-android:+"
168+
implementation "com.facebook.react:react-native:+"
169+
implementation 'com.facebook.fbjni:fbjni:0.6.0'
104170
implementation 'org.opencv:opencv:4.10.0'
105171
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
106172
implementation(files("libs/executorch.aar"))
17.9 MB
Binary file not shown.

android/libs/x86_64/libexecutorch.so

21.2 MB
Binary file not shown.

android/src/main/cpp/CMakeLists.txt

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
cmake_minimum_required(VERSION 3.13)
2+
3+
file(GLOB_RECURSE ANDROID_CPP_SOURCES CONFIGURE_DEPENDS "${ANDROID_CPP_DIR}/*.cpp")
4+
file(GLOB_RECURSE COMMON_CPP_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/*.cpp" "${COMMON_CPP_DIR}/*.c")
5+
6+
add_library(react-native-executorch SHARED ${ANDROID_CPP_SOURCES} ${COMMON_CPP_SOURCES})
7+
8+
find_package(ReactAndroid REQUIRED CONFIG)
9+
find_package(fbjni REQUIRED CONFIG)
10+
11+
target_include_directories(
12+
react-native-executorch
13+
PUBLIC
14+
"${COMMON_CPP_DIR}"
15+
"${ANDROID_CPP_DIR}"
16+
"${ET_INCLUDE_DIR}"
17+
"${REACT_NATIVE_DIR}/ReactCommon"
18+
"${REACT_NATIVE_DIR}/ReactAndroid/src/main/jni/react/turbomodule"
19+
"${REACT_NATIVE_DIR}/ReactCommon/callinvoker"
20+
"${BUILD_DIR}/generated/source/codegen/jni/react/renderer/components/RnExecutorchSpec"
21+
)
22+
23+
set(LINK_LIBRARIES
24+
ReactAndroid::jsi
25+
fbjni::fbjni
26+
android
27+
log
28+
)
29+
30+
if(ReactAndroid_VERSION_MINOR GREATER_EQUAL 76)
31+
set(RN_VERSION_LINK_LIBRARIES
32+
ReactAndroid::reactnative
33+
)
34+
else()
35+
set(RN_VERSION_LINK_LIBRARIES
36+
ReactAndroid::folly_runtime
37+
ReactAndroid::react_nativemodule_core
38+
ReactAndroid::glog
39+
ReactAndroid::reactnativejni
40+
)
41+
endif()
42+
43+
add_library(executorch SHARED IMPORTED)
44+
45+
set_target_properties(executorch PROPERTIES
46+
IMPORTED_LOCATION "${ET_LIB_DIR}/${ANDROID_ABI}/libexecutorch.so")
47+
48+
target_link_libraries(
49+
react-native-executorch
50+
${LINK_LIBRARIES}
51+
${RN_VERSION_LINK_LIBRARIES}
52+
executorch
53+
)
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include "ETInstallerModule.h"
2+
#include "RnExecutorchInstaller.h"
3+
4+
namespace rnexecutorch {
5+
6+
using namespace facebook::jni;
7+
8+
ETInstallerModule::ETInstallerModule(
9+
jni::alias_ref<ETInstallerModule::jhybridobject> &jThis,
10+
jsi::Runtime *jsiRuntime,
11+
const std::shared_ptr<facebook::react::CallInvoker> &jsCallInvoker)
12+
: javaPart_(make_global(jThis)), jsiRuntime_(jsiRuntime),
13+
jsCallInvoker_(jsCallInvoker) {}
14+
15+
jni::local_ref<ETInstallerModule::jhybriddata> ETInstallerModule::initHybrid(
16+
jni::alias_ref<jhybridobject> jThis, jlong jsContext,
17+
jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>
18+
jsCallInvokerHolder) {
19+
auto jsCallInvoker = jsCallInvokerHolder->cthis()->getCallInvoker();
20+
auto rnRuntime = reinterpret_cast<jsi::Runtime *>(jsContext);
21+
return makeCxxInstance(jThis, rnRuntime, jsCallInvoker);
22+
}
23+
24+
void ETInstallerModule::registerNatives() {
25+
registerHybrid({
26+
makeNativeMethod("initHybrid", ETInstallerModule::initHybrid),
27+
makeNativeMethod("injectJSIBindings",
28+
ETInstallerModule::injectJSIBindings),
29+
});
30+
}
31+
32+
void ETInstallerModule::injectJSIBindings() {
33+
RnExecutorchInstaller::injectJSIBindings(jsiRuntime_, jsCallInvoker_);
34+
}
35+
} // namespace rnexecutorch
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#pragma once
2+
3+
#include <ReactCommon/CallInvokerHolder.h>
4+
#include <fbjni/fbjni.h>
5+
#include <react/jni/CxxModuleWrapper.h>
6+
#include <react/jni/JMessageQueueThread.h>
7+
8+
#include <memory>
9+
#include <utility>
10+
11+
namespace rnexecutorch {
12+
13+
using namespace facebook;
14+
using namespace react;
15+
16+
class ETInstallerModule : public jni::HybridClass<ETInstallerModule> {
17+
public:
18+
static auto constexpr kJavaDescriptor =
19+
"Lcom/swmansion/rnexecutorch/ETInstaller;";
20+
21+
static jni::local_ref<ETInstallerModule::jhybriddata>
22+
initHybrid(jni::alias_ref<jhybridobject> jThis, jlong jsContext,
23+
jni::alias_ref<facebook::react::CallInvokerHolder::javaobject>
24+
jsCallInvokerHolder);
25+
26+
static void registerNatives();
27+
28+
void injectJSIBindings();
29+
30+
private:
31+
friend HybridBase;
32+
33+
jni::global_ref<ETInstallerModule::javaobject> javaPart_;
34+
jsi::Runtime *jsiRuntime_;
35+
std::shared_ptr<facebook::react::CallInvoker> jsCallInvoker_;
36+
37+
explicit ETInstallerModule(
38+
jni::alias_ref<ETInstallerModule::jhybridobject> &jThis,
39+
jsi::Runtime *jsiRuntime,
40+
const std::shared_ptr<facebook::react::CallInvoker> &jsCallInvoker);
41+
};
42+
43+
} // namespace rnexecutorch

android/src/main/cpp/OnLoad.cpp

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include <ETInstallerModule.h>
2+
3+
#include <fbjni/fbjni.h>
4+
5+
using namespace rnexecutorch;
6+
7+
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
8+
return facebook::jni::initialize(
9+
vm, [] { ETInstallerModule::registerNatives(); });
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.swmansion.rnexecutorch
2+
3+
import com.facebook.jni.HybridData
4+
import com.facebook.react.bridge.ReactApplicationContext
5+
import com.facebook.react.bridge.ReactMethod
6+
import com.facebook.react.common.annotations.FrameworkAPI
7+
import com.facebook.react.module.annotations.ReactModule
8+
import com.facebook.react.turbomodule.core.CallInvokerHolderImpl
9+
10+
@OptIn(FrameworkAPI::class)
11+
@ReactModule(name = ETInstaller.NAME)
12+
class ETInstaller(
13+
reactContext: ReactApplicationContext,
14+
) : NativeETInstallerSpec(reactContext) {
15+
companion object {
16+
const val NAME = NativeETInstallerSpec.NAME
17+
}
18+
19+
private val mHybridData: HybridData
20+
21+
external fun initHybrid(
22+
jsContext: Long,
23+
callInvoker: CallInvokerHolderImpl,
24+
): HybridData
25+
26+
private external fun injectJSIBindings()
27+
28+
init {
29+
try {
30+
System.loadLibrary("executorch")
31+
System.loadLibrary("react-native-executorch")
32+
val jsCallInvokerHolder = reactContext.jsCallInvokerHolder as CallInvokerHolderImpl
33+
mHybridData = initHybrid(reactContext.javaScriptContextHolder!!.get(), jsCallInvokerHolder)
34+
} catch (exception: UnsatisfiedLinkError) {
35+
throw RuntimeException("Could not load native module Install", exception)
36+
}
37+
}
38+
39+
@ReactMethod(isBlockingSynchronousMethod = true)
40+
override fun install(): Boolean {
41+
injectJSIBindings()
42+
return true
43+
}
44+
}

0 commit comments

Comments
 (0)