Skip to content

Commit 0c23fd8

Browse files
msluszniakclaude
andcommitted
feat: on-demand native lib download and optional feature splitting
Removes bundled native binaries from the npm package. Libraries are now downloaded at postinstall time from GitHub Releases, reducing npm install size and enabling users to opt out of unused dependencies (OpenCV, Phonemizer). - postinstall script downloads only needed artifacts based on user config ("react-native-executorch": { "extras": [...] } in app's package.json) - artifacts cached at ~/.cache/react-native-executorch/<version>/ with SHA256 verification - CMake and podspec conditionally compile/link OpenCV and Phonemizer based on rne-build-config.json written by the postinstall script - GlobalThreadPool.h guards cv::setNumThreads(0) with #ifdef RNE_ENABLE_OPENCV - index.ts startup check validates only core globals; optional feature globals are allowed absent when those features are disabled - scripts/package-release-artifacts.sh packages libs into split tarballs ready for manual upload to GitHub Releases (Phase 1 release flow) - package.json excludes third-party lib dirs from npm bundle - .gitignore excludes downloaded lib dirs and rne-build-config.json Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 1973c6f commit 0c23fd8

10 files changed

Lines changed: 658 additions & 97 deletions

File tree

.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ docs/docs/06-api-reference/
9898
# integration test model assets
9999
packages/react-native-executorch/common/rnexecutorch/tests/integration/assets/models/
100100

101+
# release artifact staging dir (produced by scripts/package-release-artifacts.sh)
102+
packages/react-native-executorch/dist-artifacts/
103+
104+
# on-demand native libs (downloaded at postinstall time, not committed)
105+
packages/react-native-executorch/third-party/android/libs/
106+
packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/
107+
packages/react-native-executorch/third-party/ios/libs/
108+
packages/react-native-executorch/rne-build-config.json
109+
101110
# custom
102111
*.tgz
103112
Makefile

packages/react-native-executorch/android/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ set(LIBS_DIR "${CMAKE_SOURCE_DIR}/../third-party/android/libs")
2424
set(TOKENIZERS_DIR "${CMAKE_SOURCE_DIR}/../third-party/include/executorch/extension/llm/tokenizers/include")
2525
set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/../third-party/include")
2626

27+
# Optional feature flags — driven by user config in package.json, passed via gradle cmake arguments
28+
option(RNE_ENABLE_OPENCV "Enable OpenCV-dependent computer vision features" ON)
29+
option(RNE_ENABLE_PHONEMIZER "Enable Phonemizer-dependent TTS features" ON)
30+
2731
# Treat third-party headers as system headers to suppress deprecation warnings
2832
include_directories(SYSTEM "${INCLUDE_DIR}")
2933

packages/react-native-executorch/android/build.gradle

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
import org.apache.tools.ant.taskdefs.condition.Os
22

3+
// Read the generated build config written by the postinstall script.
4+
// Falls back to enabling everything if the file doesn't exist (e.g. during CI
5+
// when libs are pre-cached and the postinstall script skipped writing config).
6+
def getRneBuildConfig() {
7+
def configFile = new File("${project.projectDir}/../rne-build-config.json")
8+
if (configFile.exists()) {
9+
try {
10+
return new groovy.json.JsonSlurper().parse(configFile)
11+
} catch (e) {
12+
logger.warn("[RnExecutorch] Failed to parse rne-build-config.json: ${e.message}. Defaulting to all features enabled.")
13+
}
14+
}
15+
return [enableOpencv: true, enablePhonemizer: true]
16+
}
17+
18+
def rneBuildConfig = getRneBuildConfig()
19+
320
buildscript {
421
ext {
522
agp_version = '8.4.2'
@@ -122,7 +139,9 @@ android {
122139
"-DREACT_NATIVE_DIR=${toPlatformFileString(reactNativeRootDir.path)}",
123140
"-DBUILD_DIR=${project.buildDir}",
124141
"-DANDROID_TOOLCHAIN=clang",
125-
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
142+
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON",
143+
"-DRNE_ENABLE_OPENCV=${rneBuildConfig.enableOpencv ? 'ON' : 'OFF'}",
144+
"-DRNE_ENABLE_PHONEMIZER=${rneBuildConfig.enablePhonemizer ? 'ON' : 'OFF'}"
126145
}
127146
}
128147
}

packages/react-native-executorch/android/src/main/cpp/CMakeLists.txt

Lines changed: 91 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,71 @@
11
cmake_minimum_required(VERSION 3.13)
22

33
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")
5-
file(GLOB_RECURSE COMMON_C_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/*.c")
4+
5+
# --- Source separation ---
6+
# Glob all common sources, then separate opencv-dependent and phonemizer-dependent
7+
# files so they can be conditionally included based on feature flags.
8+
9+
file(GLOB_RECURSE ALL_COMMON_CPP_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/*.cpp")
10+
file(GLOB_RECURSE ALL_COMMON_C_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/*.c")
11+
12+
# Exclude test sources unconditionally
613
file(GLOB_RECURSE TEST_CPP_SOURCES "${COMMON_CPP_DIR}/rnexecutorch/tests/*.cpp")
7-
list(REMOVE_ITEM COMMON_CPP_SOURCES ${TEST_CPP_SOURCES})
14+
list(REMOVE_ITEM ALL_COMMON_CPP_SOURCES ${TEST_CPP_SOURCES})
15+
16+
# OpenCV-dependent sources: CV models + frame utilities + image processing
17+
file(GLOB_RECURSE OPENCV_CPP_SOURCES CONFIGURE_DEPENDS
18+
"${COMMON_CPP_DIR}/rnexecutorch/models/classification/*.cpp"
19+
"${COMMON_CPP_DIR}/rnexecutorch/models/object_detection/*.cpp"
20+
"${COMMON_CPP_DIR}/rnexecutorch/models/semantic_segmentation/*.cpp"
21+
"${COMMON_CPP_DIR}/rnexecutorch/models/instance_segmentation/*.cpp"
22+
"${COMMON_CPP_DIR}/rnexecutorch/models/style_transfer/*.cpp"
23+
"${COMMON_CPP_DIR}/rnexecutorch/models/ocr/*.cpp"
24+
"${COMMON_CPP_DIR}/rnexecutorch/models/vertical_ocr/*.cpp"
25+
"${COMMON_CPP_DIR}/rnexecutorch/models/embeddings/image/*.cpp"
26+
"${COMMON_CPP_DIR}/rnexecutorch/models/text_to_image/*.cpp"
27+
"${COMMON_CPP_DIR}/rnexecutorch/models/VisionModel.cpp"
28+
"${COMMON_CPP_DIR}/rnexecutorch/data_processing/ImageProcessing.cpp"
29+
"${COMMON_CPP_DIR}/rnexecutorch/utils/FrameExtractor.cpp"
30+
"${COMMON_CPP_DIR}/rnexecutorch/utils/FrameProcessor.cpp"
31+
"${COMMON_CPP_DIR}/rnexecutorch/utils/FrameTransform.cpp"
32+
"${COMMON_CPP_DIR}/rnexecutorch/utils/computer_vision/*.cpp"
33+
)
34+
35+
# Phonemizer-dependent sources: Kokoro TTS (only user of phonemis)
36+
file(GLOB_RECURSE PHONEMIZER_CPP_SOURCES CONFIGURE_DEPENDS
37+
"${COMMON_CPP_DIR}/rnexecutorch/models/text_to_speech/*.cpp"
38+
)
39+
40+
# Core = everything minus optional sources
41+
set(CORE_COMMON_CPP_SOURCES ${ALL_COMMON_CPP_SOURCES})
42+
list(REMOVE_ITEM CORE_COMMON_CPP_SOURCES ${OPENCV_CPP_SOURCES} ${PHONEMIZER_CPP_SOURCES})
43+
44+
# Build final source list
45+
set(ENABLED_COMMON_SOURCES ${CORE_COMMON_CPP_SOURCES})
846

9-
add_library(react-native-executorch SHARED ${ANDROID_CPP_SOURCES} ${COMMON_CPP_SOURCES} ${COMMON_C_SOURCES})
47+
if(RNE_ENABLE_OPENCV)
48+
list(APPEND ENABLED_COMMON_SOURCES ${OPENCV_CPP_SOURCES})
49+
endif()
50+
51+
if(RNE_ENABLE_PHONEMIZER)
52+
list(APPEND ENABLED_COMMON_SOURCES ${PHONEMIZER_CPP_SOURCES})
53+
endif()
54+
55+
add_library(react-native-executorch SHARED
56+
${ANDROID_CPP_SOURCES}
57+
${ENABLED_COMMON_SOURCES}
58+
${ALL_COMMON_C_SOURCES}
59+
)
60+
61+
# Propagate feature flags as preprocessor defines so C++ code can guard includes
62+
if(RNE_ENABLE_OPENCV)
63+
target_compile_definitions(react-native-executorch PRIVATE RNE_ENABLE_OPENCV)
64+
endif()
65+
66+
if(RNE_ENABLE_PHONEMIZER)
67+
target_compile_definitions(react-native-executorch PRIVATE RNE_ENABLE_PHONEMIZER)
68+
endif()
1069

1170
find_package(ReactAndroid REQUIRED CONFIG)
1271
find_package(fbjni REQUIRED CONFIG)
@@ -34,63 +93,55 @@ set(RN_VERSION_LINK_LIBRARIES
3493
ReactAndroid::reactnative
3594
)
3695

37-
# Dependencies:
38-
39-
# ------- Executorch -------
96+
# ------- Executorch (always required) -------
4097

4198
add_library(executorch SHARED IMPORTED)
4299

43100
set_target_properties(executorch PROPERTIES
44101
IMPORTED_LOCATION "${LIBS_DIR}/executorch/${ANDROID_ABI}/libexecutorch.so")
45102

46-
47103
if(ANDROID_ABI STREQUAL "arm64-v8a")
48104
target_compile_definitions(react-native-executorch PRIVATE ARCH_ARM64)
49105

50-
# ------- pthreadpool -------
51106
add_library(pthreadpool SHARED IMPORTED)
52-
53107
set_target_properties(pthreadpool PROPERTIES
54108
IMPORTED_LOCATION "${LIBS_DIR}/pthreadpool/${ANDROID_ABI}/libpthreadpool.so")
55109

56-
# ------- cpuinfo -------
57110
add_library(cpuinfo SHARED IMPORTED)
58-
59111
set_target_properties(cpuinfo PROPERTIES
60112
IMPORTED_LOCATION "${LIBS_DIR}/cpuinfo/${ANDROID_ABI}/libcpuinfo.so")
61-
set(EXECUTORCH_LIBS
62-
"pthreadpool"
63-
"cpuinfo"
64-
)
113+
114+
set(EXECUTORCH_LIBS "pthreadpool" "cpuinfo")
65115
endif()
66116

67-
# ------- OpenCV -------
117+
# ------- OpenCV (optional) -------
68118

69-
set(OPENCV_LIBS
70-
"${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_core.a"
71-
"${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_features2d.a"
72-
"${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_highgui.a"
73-
"${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_imgproc.a"
74-
"${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_photo.a"
75-
"${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_video.a"
76-
)
77-
78-
if(ANDROID_ABI STREQUAL "arm64-v8a")
79-
set(OPENCV_THIRD_PARTY_LIBS
80-
"${LIBS_DIR}/opencv-third-party/${ANDROID_ABI}/libkleidicv_hal.a"
81-
"${LIBS_DIR}/opencv-third-party/${ANDROID_ABI}/libkleidicv_thread.a"
82-
"${LIBS_DIR}/opencv-third-party/${ANDROID_ABI}/libkleidicv.a"
119+
if(RNE_ENABLE_OPENCV)
120+
set(OPENCV_LINK_LIBS
121+
"${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_core.a"
122+
"${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_features2d.a"
123+
"${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_highgui.a"
124+
"${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_imgproc.a"
125+
"${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_photo.a"
126+
"${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_video.a"
83127
)
84-
elseif(ANDROID_ABI STREQUAL "x86_64")
85-
set(OPENCV_THIRD_PARTY_LIBS "")
86-
endif()
87128

129+
if(ANDROID_ABI STREQUAL "arm64-v8a")
130+
list(APPEND OPENCV_LINK_LIBS
131+
"${LIBS_DIR}/opencv-third-party/${ANDROID_ABI}/libkleidicv_hal.a"
132+
"${LIBS_DIR}/opencv-third-party/${ANDROID_ABI}/libkleidicv_thread.a"
133+
"${LIBS_DIR}/opencv-third-party/${ANDROID_ABI}/libkleidicv.a"
134+
)
135+
endif()
136+
endif()
88137

89-
# ------- phonemis -------
138+
# ------- Phonemizer (optional) -------
90139

91-
set(PHONEMIS_LIBS
92-
"${LIBS_DIR}/phonemis/${ANDROID_ABI}/libphonemis.a"
93-
)
140+
if(RNE_ENABLE_PHONEMIZER)
141+
set(PHONEMIZER_LINK_LIBS
142+
"${LIBS_DIR}/phonemis/${ANDROID_ABI}/libphonemis.a"
143+
)
144+
endif()
94145

95146
# --------------
96147

@@ -100,9 +151,8 @@ target_link_libraries(
100151
react-native-executorch
101152
${LINK_LIBRARIES}
102153
${RN_VERSION_LINK_LIBRARIES}
103-
${OPENCV_LIBS}
104-
${OPENCV_THIRD_PARTY_LIBS}
105-
${PHONEMIS_LIBS}
154+
${OPENCV_LINK_LIBS}
155+
${PHONEMIZER_LINK_LIBS}
106156
executorch
107157
${EXECUTORCH_LIBS}
108158
z

packages/react-native-executorch/common/rnexecutorch/threads/GlobalThreadPool.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
#include <executorch/extension/threadpool/cpuinfo_utils.h>
55
#include <memory>
66
#include <mutex>
7+
#ifdef RNE_ENABLE_OPENCV
78
#include <opencv2/opencv.hpp>
9+
#endif
810
#include <optional>
911
#include <rnexecutorch/Log.h>
1012
#include <rnexecutorch/threads/HighPerformanceThreadPool.h>
@@ -41,7 +43,9 @@ class GlobalThreadPool {
4143
config);
4244
// Disable OpenCV's internal threading to prevent it from overriding our
4345
// thread pool configuration, which would cause degraded performance
46+
#ifdef RNE_ENABLE_OPENCV
4447
cv::setNumThreads(0);
48+
#endif
4549
});
4650
}
4751

packages/react-native-executorch/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
"*.podspec",
1919
"third-party/include",
2020
"third-party",
21+
"!third-party/android/libs",
2122
"!third-party/ios/ExecutorchLib",
22-
"!third-party/ios/libs/executorch",
23+
"!third-party/ios/ExecutorchLib.xcframework",
24+
"!third-party/ios/libs",
2325
"!ios/build",
2426
"!android/build",
2527
"!android/gradle",
@@ -38,7 +40,8 @@
3840
"clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib",
3941
"prepare": "bob build",
4042
"prepack": "cp ../../README.md ./README.md",
41-
"postpack": "rm ./README.md"
43+
"postpack": "rm ./README.md",
44+
"postinstall": "node scripts/download-libs.js"
4245
},
4346
"keywords": [
4447
"react-native",

0 commit comments

Comments
 (0)