Skip to content

Commit 40788e3

Browse files
committed
feat: compiles and runs on android
1 parent 998fe0d commit 40788e3

7 files changed

Lines changed: 400 additions & 73 deletions

File tree

android/CMakeLists.txt

Lines changed: 108 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,114 @@
1-
cmake_minimum_required(VERSION 3.4.1)
1+
cmake_minimum_required(VERSION 3.6.0-rc2)
22

33
set (CMAKE_VERBOSE_MAKEFILE ON)
4-
set (CMAKE_CXX_STANDARD 11)
4+
set (CMAKE_CXX_STANDARD 14)
55

6-
add_library(cpp
7-
SHARED
8-
../cpp/react-native-teaching-jsi.cpp
9-
cpp-adapter.cpp
10-
)
6+
set (PACKAGE_NAME "cpp")
7+
set (BUILD_DIR ${CMAKE_SOURCE_DIR}/build)
8+
set (RN_SO_DIR ${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/jni/first-party/react/jni)
9+
10+
if(${REACT_NATIVE_VERSION} LESS 66)
11+
set (
12+
INCLUDE_JSI_CPP
13+
"${NODE_MODULES_DIR}/react-native/ReactCommon/jsi/jsi/jsi.cpp"
14+
)
15+
set (
16+
INCLUDE_JSIDYNAMIC_CPP
17+
"${NODE_MODULES_DIR}/react-native/ReactCommon/jsi/jsi/JSIDynamic.cpp"
18+
)
19+
endif()
20+
21+
file (GLOB LIBFBJNI_INCLUDE_DIR "${BUILD_DIR}/fbjni-*-headers.jar/")
1122

12-
# Specifies a path to native header files.
1323
include_directories(
14-
../cpp
24+
../cpp
25+
"${NODE_MODULES_DIR}/react-native/React"
26+
"${NODE_MODULES_DIR}/react-native/React/Base"
27+
"${NODE_MODULES_DIR}/react-native/ReactCommon/jsi"
28+
"${NODE_MODULES_DIR}/react-native/ReactCommon/callinvoker"
29+
"${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni"
30+
"${NODE_MODULES_DIR}/react-native/ReactCommon"
31+
"${NODE_MODULES_DIR}/react-native/ReactCommon/callinvoker"
32+
"${NODE_MODULES_DIR}/react-native/ReactCommon/jsi"
33+
"${NODE_MODULES_DIR}/hermes-engine/android/include/"
34+
${INCLUDE_JSI_CPP} # only on older RN versions
35+
${INCLUDE_JSIDYNAMIC_CPP} # only on older RN versions
36+
)
37+
38+
add_library(
39+
${PACKAGE_NAME}
40+
SHARED
41+
../cpp/react-native-teaching-jsi.cpp
42+
cpp-adapter.cpp
43+
"${NODE_MODULES_DIR}/react-native/ReactCommon/jsi/jsi/jsi.cpp"
44+
)
45+
46+
# find fbjni package
47+
file (GLOB LIBFBJNI_INCLUDE_DIR "${BUILD_DIR}/fbjni-*-headers.jar/")
48+
49+
target_include_directories(
50+
${PACKAGE_NAME}
51+
PRIVATE
52+
# --- fbjni ---
53+
"${LIBFBJNI_INCLUDE_DIR}"
54+
# --- React Native ---
55+
"${NODE_MODULES_DIR}/react-native/React"
56+
"${NODE_MODULES_DIR}/react-native/React/Base"
57+
"${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/jni"
58+
"${NODE_MODULES_DIR}/react-native/ReactAndroid/src/main/java/com/facebook/react/turbomodule/core/jni"
59+
"${NODE_MODULES_DIR}/react-native/ReactCommon"
60+
"${NODE_MODULES_DIR}/react-native/ReactCommon/callinvoker"
61+
"${NODE_MODULES_DIR}/react-native/ReactCommon/jsi"
62+
"${NODE_MODULES_DIR}/hermes-engine/android/include/"
63+
${INCLUDE_JSI_CPP} # only on older RN versions
64+
${INCLUDE_JSIDYNAMIC_CPP} # only on older RN versions
65+
)
66+
67+
file (GLOB LIBRN_DIR "${BUILD_DIR}/react-native-0*/jni/${ANDROID_ABI}")
68+
69+
find_library(
70+
FBJNI_LIB
71+
fbjni
72+
PATHS ${LIBRN_DIR}
73+
NO_CMAKE_FIND_ROOT_PATH
1574
)
75+
76+
find_library(
77+
REACT_NATIVE_JNI_LIB
78+
reactnativejni
79+
PATHS ${LIBRN_DIR}
80+
NO_CMAKE_FIND_ROOT_PATH
81+
)
82+
83+
84+
if(${REACT_NATIVE_VERSION} LESS 66)
85+
# JSI lib didn't exist on RN 0.65 and before. Simply omit it.
86+
set (JSI_LIB "")
87+
else()
88+
# RN 0.66 distributes libjsi.so, can be used instead of compiling jsi.cpp manually.
89+
find_library(
90+
JSI_LIB
91+
jsi
92+
PATHS ${LIBRN_DIR}
93+
NO_CMAKE_FIND_ROOT_PATH
94+
)
95+
endif()
96+
97+
find_library(
98+
LOG_LIB
99+
log
100+
)
101+
102+
# target_link_libraries(sequel fbjni::fbjni android log)
103+
target_link_libraries(
104+
${PACKAGE_NAME}
105+
${LOG_LIB}
106+
${JSI_LIB}
107+
${REACT_NATIVE_JNI_LIB}
108+
${FBJNI_LIB}
109+
android
110+
)
111+
112+
113+
114+

android/build.gradle

Lines changed: 163 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import org.apache.tools.ant.filters.ReplaceTokens
2+
import java.nio.file.Paths
3+
14
buildscript {
25
if (project == rootProject) {
36
repositories {
@@ -18,6 +21,30 @@ def safeExtGet(prop, fallback) {
1821
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
1922
}
2023

24+
static def findNodeModules(baseDir) {
25+
def basePath = baseDir.toPath().normalize()
26+
// Node's module resolution algorithm searches up to the root directory,
27+
// after which the base path will be null
28+
while (basePath) {
29+
def nodeModulesPath = Paths.get(basePath.toString(), "node_modules")
30+
def reactNativePath = Paths.get(nodeModulesPath.toString(), "react-native")
31+
if (nodeModulesPath.toFile().exists() && reactNativePath.toFile().exists()) {
32+
return nodeModulesPath.toString()
33+
}
34+
basePath = basePath.getParent()
35+
}
36+
throw new GradleException("sample: Failed to find node_modules/ path!")
37+
}
38+
39+
def nodeModules = findNodeModules(projectDir);
40+
logger.warn("sampe: node_modules/ found at: ${nodeModules}");
41+
42+
def reactNative = new File("$nodeModules/react-native")
43+
44+
def reactProperties = new Properties()
45+
file("$nodeModules/react-native/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) }
46+
def REACT_NATIVE_VERSION = reactProperties.getProperty("VERSION_NAME").split("\\.")[1].toInteger()
47+
2148
android {
2249
compileSdkVersion safeExtGet('TeachingJsi_compileSdkVersion', 29)
2350
defaultConfig {
@@ -28,8 +55,11 @@ android {
2855

2956
externalNativeBuild {
3057
cmake {
31-
cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all"
58+
cppFlags "-O2", "-fexceptions", "-frtti", "-std=c++1y", "-DONANDROID"
3259
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
60+
arguments '-DANDROID_STL=c++_shared',
61+
"-DREACT_NATIVE_VERSION=${REACT_NATIVE_VERSION}",
62+
"-DNODE_MODULES_DIR=${nodeModules}"
3363
}
3464
}
3565

@@ -41,6 +71,12 @@ android {
4171
}
4272
}
4373

74+
packagingOptions {
75+
// Exclude all Libraries that are already present in the user's app (through React Native or by him installing REA)
76+
excludes = ["**/libc++_shared.so", "**/libfbjni.so", "**/libjsi.so", "**/libreactnativejni.so", "**/libfolly_json.so", "**/libjscexecutor.so", "**/libhermes.so"]
77+
exclude "META-INF/**"
78+
}
79+
4480
buildTypes {
4581
release {
4682
minifyEnabled false
@@ -53,6 +89,10 @@ android {
5389
sourceCompatibility JavaVersion.VERSION_1_8
5490
targetCompatibility JavaVersion.VERSION_1_8
5591
}
92+
configurations {
93+
extractHeaders
94+
extractJNI
95+
}
5696
}
5797

5898
repositories {
@@ -64,9 +104,131 @@ repositories {
64104
google()
65105
mavenCentral()
66106
jcenter()
107+
108+
def found = false
109+
def defaultDir = null
110+
def androidSourcesName = 'React Native sources'
111+
112+
if (rootProject.ext.has('reactNativeAndroidRoot')) {
113+
defaultDir = rootProject.ext.get('reactNativeAndroidRoot')
114+
} else {
115+
defaultDir = file("$nodeModules/react-native/android")
116+
}
117+
118+
if (defaultDir.exists()) {
119+
maven {
120+
url defaultDir.toString()
121+
name androidSourcesName
122+
}
123+
124+
logger.info(":${project.name}:reactNativeAndroidRoot ${defaultDir.canonicalPath}")
125+
found = true
126+
} else {
127+
def parentDir = rootProject.projectDir
128+
129+
1.upto(5, {
130+
if (found) return true
131+
parentDir = parentDir.parentFile
132+
133+
def androidSourcesDir = new File(
134+
parentDir,
135+
'node_modules/react-native'
136+
)
137+
138+
def androidPrebuiltBinaryDir = new File(
139+
parentDir,
140+
'node_modules/react-native/android'
141+
)
142+
143+
if (androidPrebuiltBinaryDir.exists()) {
144+
maven {
145+
url androidPrebuiltBinaryDir.toString()
146+
name androidSourcesName
147+
}
148+
149+
logger.info(":${project.name}:reactNativeAndroidRoot ${androidPrebuiltBinaryDir.canonicalPath}")
150+
found = true
151+
} else if (androidSourcesDir.exists()) {
152+
maven {
153+
url androidSourcesDir.toString()
154+
name androidSourcesName
155+
}
156+
157+
logger.info(":${project.name}:reactNativeAndroidRoot ${androidSourcesDir.canonicalPath}")
158+
found = true
159+
}
160+
})
161+
}
162+
163+
if (!found) {
164+
throw new GradleException(
165+
"${project.name}: unable to locate React Native android sources. " +
166+
"Ensure you have you installed React Native as a dependency in your project and try again."
167+
)
168+
}
67169
}
68170

69171
dependencies {
70172
//noinspection GradleDynamicVersion
71173
implementation "com.facebook.react:react-native:+" // From node_modules
174+
175+
//noinspection GradleDynamicVersion
176+
extractHeaders("com.facebook.fbjni:fbjni:+:headers")
177+
//noinspection GradleDynamicVersion
178+
extractJNI("com.facebook.fbjni:fbjni:+")
179+
180+
def rnAAR = fileTree("${nodeModules}/react-native/android").matching({ it.include "**/**/*.aar" }).singleFile
181+
def jscAAR = fileTree("${nodeModules}/jsc-android/dist/org/webkit/android-jsc").matching({ it.include "**/**/*.aar" }).singleFile
182+
183+
184+
extractJNI(files(rnAAR, jscAAR))
185+
}
186+
187+
def downloadsDir = new File("$buildDir/downloads")
188+
189+
task createNativeDepsDirectories {
190+
doLast {
191+
downloadsDir.mkdirs()
192+
}
193+
}
194+
195+
196+
task extractAARHeaders {
197+
doLast {
198+
configurations.extractHeaders.files.each {
199+
def file = it.absoluteFile
200+
copy {
201+
from zipTree(file)
202+
into "$buildDir/$file.name"
203+
include "**/*.h"
204+
}
205+
}
206+
}
207+
}
208+
209+
extractAARHeaders.mustRunAfter createNativeDepsDirectories
210+
211+
task extractJNIFiles {
212+
doLast {
213+
configurations.extractJNI.files.each {
214+
def file = it.absoluteFile
215+
216+
copy {
217+
from zipTree(file)
218+
into "$buildDir/$file.name"
219+
include "jni/**/*"
220+
}
221+
}
222+
}
223+
}
224+
225+
extractJNIFiles.mustRunAfter extractAARHeaders
226+
227+
// pre-native build pipeline
228+
229+
tasks.whenTaskAdded { task ->
230+
if (!task.name.contains('Clean') && (task.name.contains('externalNative') || task.name.contains('CMake'))) {
231+
task.dependsOn(extractAARHeaders)
232+
task.dependsOn(extractJNIFiles)
233+
}
72234
}

android/cpp-adapter.cpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,37 @@
11
#include <jni.h>
2-
#include "example.h"
32

4-
extern "C"
5-
JNIEXPORT jint JNICALL
6-
Java_com_reactnativeteachingjsi_TeachingJsiModule_nativeMultiply(JNIEnv *env, jclass type, jint a, jint b) {
7-
return example::multiply(a, b);
8-
}
3+
#include <jsi/jsi.h>
4+
#include <fbjni/fbjni.h>
5+
#include <ReactCommon/CallInvokerHolder.h>
6+
7+
#include <typeinfo>
8+
9+
#include <android/log.h>
10+
11+
#include "react-native-teaching-jsi.h"
12+
13+
using namespace facebook;
14+
15+
struct FirstModuleBridge: jni::JavaClass < FirstModuleBridge > {
16+
static constexpr auto kJavaDescriptor = "Lcom/reactnativeteachingjsi/AndroidNative;";
17+
18+
static void installNativeJsi(jni::alias_ref < jni::JObject > thiz, jlong jsiRuntimePtr) {
19+
auto jsiRuntime = reinterpret_cast < jsi::Runtime * > (jsiRuntimePtr);
20+
21+
installMath( * jsiRuntime);
22+
}
23+
24+
static void registerNatives() {
25+
javaClassStatic() -> registerNatives({
26+
makeNativeMethod("installNativeJsi", FirstModuleBridge::installNativeJsi)
27+
});
28+
}
29+
};
30+
31+
JNIEXPORT jint JNI_OnLoad(JavaVM * vm, void * ) {
32+
__android_log_write(ANDROID_LOG_ERROR, "Tag", "Loading Android CPP Code");
33+
34+
return jni::initialize(vm, [] {
35+
FirstModuleBridge::registerNatives();
36+
});
37+
}

0 commit comments

Comments
 (0)