react-native-litert-lm 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +270 -186
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLM.kt +93 -37
- package/app.plugin.js +33 -0
- package/cpp/HybridLiteRTLM.cpp +571 -451
- package/cpp/HybridLiteRTLM.hpp +54 -23
- package/cpp/IOSDownloadHelper.h +24 -0
- package/cpp/cpp-adapter.cpp +2 -2
- package/cpp/include/litert_lm_engine.h +502 -0
- package/ios/IOSDownloadHelper.mm +129 -0
- package/ios/LiteRTLMAutolinking.mm +30 -0
- package/lib/hooks.d.ts +9 -4
- package/lib/hooks.js +34 -20
- package/lib/index.d.ts +1 -0
- package/lib/index.js +2 -5
- package/lib/memoryTracker.d.ts +1 -1
- package/lib/memoryTracker.js +1 -1
- package/lib/modelFactory.d.ts +11 -5
- package/lib/modelFactory.js +9 -4
- package/nitrogen/generated/android/LiteRTLMOnLoad.cpp +11 -4
- package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.cpp +31 -37
- package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.hpp +19 -22
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMSpec.kt +15 -18
- package/package.json +12 -5
- package/react-native-litert-lm.podspec +20 -7
- package/scripts/build-ios-engine.sh +283 -0
- package/scripts/download-ios-frameworks.sh +72 -0
- package/scripts/postinstall.js +116 -0
- package/scripts/stubs/cxx_bridge_stubs.cc +224 -0
- package/scripts/stubs/gemma_model_constraint_provider.cc +46 -0
- package/scripts/stubs/llguidance_stubs.c +101 -0
- package/src/hooks.ts +62 -39
- package/src/index.ts +4 -7
- package/src/memoryTracker.ts +1 -1
- package/src/modelFactory.ts +30 -5
|
@@ -18,34 +18,33 @@ namespace margelo::nitro::litertlm {
|
|
|
18
18
|
|
|
19
19
|
using namespace facebook;
|
|
20
20
|
|
|
21
|
-
class JHybridLiteRTLMSpec: public
|
|
22
|
-
public virtual HybridLiteRTLMSpec {
|
|
21
|
+
class JHybridLiteRTLMSpec: public virtual HybridLiteRTLMSpec, public virtual JHybridObject {
|
|
23
22
|
public:
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
HybridBase
|
|
33
|
-
|
|
23
|
+
struct JavaPart: public jni::JavaClass<JavaPart, JHybridObject::JavaPart> {
|
|
24
|
+
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMSpec;";
|
|
25
|
+
std::shared_ptr<JHybridLiteRTLMSpec> getJHybridLiteRTLMSpec();
|
|
26
|
+
};
|
|
27
|
+
struct CxxPart: public jni::HybridClass<CxxPart, JHybridObject::CxxPart> {
|
|
28
|
+
static auto constexpr kJavaDescriptor = "Lcom/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMSpec$CxxPart;";
|
|
29
|
+
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
|
|
30
|
+
static void registerNatives();
|
|
31
|
+
using HybridBase::HybridBase;
|
|
32
|
+
protected:
|
|
33
|
+
std::shared_ptr<JHybridObject> createHybridObject(const jni::local_ref<JHybridObject::JavaPart>& javaPart) override;
|
|
34
|
+
};
|
|
34
35
|
|
|
35
36
|
public:
|
|
37
|
+
explicit JHybridLiteRTLMSpec(const jni::local_ref<JHybridLiteRTLMSpec::JavaPart>& javaPart):
|
|
38
|
+
HybridObject(HybridLiteRTLMSpec::TAG),
|
|
39
|
+
JHybridObject(javaPart),
|
|
40
|
+
_javaPart(jni::make_global(javaPart)) {}
|
|
36
41
|
~JHybridLiteRTLMSpec() override {
|
|
37
42
|
// Hermes GC can destroy JS objects on a non-JNI Thread.
|
|
38
43
|
jni::ThreadScope::WithClassLoader([&] { _javaPart.reset(); });
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
public:
|
|
42
|
-
|
|
43
|
-
bool equals(const std::shared_ptr<HybridObject>& other) override;
|
|
44
|
-
void dispose() noexcept override;
|
|
45
|
-
std::string toString() override;
|
|
46
|
-
|
|
47
|
-
public:
|
|
48
|
-
inline const jni::global_ref<JHybridLiteRTLMSpec::javaobject>& getJavaPart() const noexcept {
|
|
47
|
+
inline const jni::global_ref<JHybridLiteRTLMSpec::JavaPart>& getJavaPart() const noexcept {
|
|
49
48
|
return _javaPart;
|
|
50
49
|
}
|
|
51
50
|
|
|
@@ -70,9 +69,7 @@ namespace margelo::nitro::litertlm {
|
|
|
70
69
|
void close() override;
|
|
71
70
|
|
|
72
71
|
private:
|
|
73
|
-
|
|
74
|
-
using HybridBase::HybridBase;
|
|
75
|
-
jni::global_ref<JHybridLiteRTLMSpec::javaobject> _javaPart;
|
|
72
|
+
jni::global_ref<JHybridLiteRTLMSpec::JavaPart> _javaPart;
|
|
76
73
|
};
|
|
77
74
|
|
|
78
75
|
} // namespace margelo::nitro::litertlm
|
|
@@ -25,23 +25,6 @@ import com.margelo.nitro.core.HybridObject
|
|
|
25
25
|
"LocalVariableName", "PropertyName", "PrivatePropertyName", "FunctionName"
|
|
26
26
|
)
|
|
27
27
|
abstract class HybridLiteRTLMSpec: HybridObject() {
|
|
28
|
-
@DoNotStrip
|
|
29
|
-
private var mHybridData: HybridData = initHybrid()
|
|
30
|
-
|
|
31
|
-
init {
|
|
32
|
-
super.updateNative(mHybridData)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
override fun updateNative(hybridData: HybridData) {
|
|
36
|
-
mHybridData = hybridData
|
|
37
|
-
super.updateNative(hybridData)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Default implementation of `HybridObject.toString()`
|
|
41
|
-
override fun toString(): String {
|
|
42
|
-
return "[HybridObject LiteRTLM]"
|
|
43
|
-
}
|
|
44
|
-
|
|
45
28
|
// Properties
|
|
46
29
|
|
|
47
30
|
|
|
@@ -108,7 +91,21 @@ abstract class HybridLiteRTLMSpec: HybridObject() {
|
|
|
108
91
|
@Keep
|
|
109
92
|
abstract fun close(): Unit
|
|
110
93
|
|
|
111
|
-
|
|
94
|
+
// Default implementation of `HybridObject.toString()`
|
|
95
|
+
override fun toString(): String {
|
|
96
|
+
return "[HybridObject LiteRTLM]"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// C++ backing class
|
|
100
|
+
@DoNotStrip
|
|
101
|
+
@Keep
|
|
102
|
+
protected open class CxxPart(javaPart: HybridLiteRTLMSpec): HybridObject.CxxPart(javaPart) {
|
|
103
|
+
// C++ JHybridLiteRTLMSpec::CxxPart::initHybrid(...)
|
|
104
|
+
external override fun initHybrid(): HybridData
|
|
105
|
+
}
|
|
106
|
+
override fun createCxxPart(): CxxPart {
|
|
107
|
+
return CxxPart(this)
|
|
108
|
+
}
|
|
112
109
|
|
|
113
110
|
companion object {
|
|
114
111
|
protected const val TAG = "HybridLiteRTLMSpec"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-litert-lm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "High-performance LLM inference for React Native using LiteRT-LM. Optimized for Gemma 3n and other on-device language models.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Hugh Chen (https://github.com/hung-yueh)",
|
|
@@ -37,9 +37,14 @@
|
|
|
37
37
|
"android/src",
|
|
38
38
|
"android/build.gradle",
|
|
39
39
|
"android/CMakeLists.txt",
|
|
40
|
-
"ios",
|
|
40
|
+
"ios/*.mm",
|
|
41
|
+
"ios/*.m",
|
|
41
42
|
"cpp",
|
|
42
43
|
"nitrogen/generated",
|
|
44
|
+
"scripts/postinstall.js",
|
|
45
|
+
"scripts/download-ios-frameworks.sh",
|
|
46
|
+
"scripts/build-ios-engine.sh",
|
|
47
|
+
"scripts/stubs",
|
|
43
48
|
"react-native.config.js",
|
|
44
49
|
"react-native-litert-lm.podspec",
|
|
45
50
|
"app.plugin.js",
|
|
@@ -50,12 +55,14 @@
|
|
|
50
55
|
"node": ">=22"
|
|
51
56
|
},
|
|
52
57
|
"scripts": {
|
|
58
|
+
"postinstall": "node scripts/postinstall.js",
|
|
53
59
|
"build": "tsc",
|
|
54
60
|
"typecheck": "tsc --noEmit",
|
|
55
61
|
"lint": "eslint \"**/*.{js,ts,tsx}\" --fix",
|
|
56
62
|
"prepack": "npm run build",
|
|
57
63
|
"specs": "npx nitrogen",
|
|
58
|
-
"clean": "rm -rf lib android/build ios/build nitrogen/generated",
|
|
64
|
+
"clean": "rm -rf lib android/build ios/build ios/Frameworks nitrogen/generated",
|
|
65
|
+
"download-frameworks": "scripts/download-ios-frameworks.sh",
|
|
59
66
|
"android": "expo run:android",
|
|
60
67
|
"android:clean": "cd android && ./gradlew clean",
|
|
61
68
|
"ios": "expo run:ios",
|
|
@@ -65,7 +72,7 @@
|
|
|
65
72
|
"@expo/config-plugins": "~54.0.4",
|
|
66
73
|
"@types/react": "~19.1.10",
|
|
67
74
|
"expo": "^54.0.31",
|
|
68
|
-
"nitrogen": "^0.
|
|
75
|
+
"nitrogen": "^0.35.0",
|
|
69
76
|
"react": "19.1.0",
|
|
70
77
|
"react-native": "0.81.5",
|
|
71
78
|
"release-it": "^19.2.4",
|
|
@@ -82,6 +89,6 @@
|
|
|
82
89
|
}
|
|
83
90
|
},
|
|
84
91
|
"dependencies": {
|
|
85
|
-
"react-native-nitro-modules": "^0.
|
|
92
|
+
"react-native-nitro-modules": "^0.35.0"
|
|
86
93
|
}
|
|
87
94
|
}
|
|
@@ -16,36 +16,49 @@ Pod::Spec.new do |s|
|
|
|
16
16
|
|
|
17
17
|
s.source_files = [
|
|
18
18
|
# Implementation (C++)
|
|
19
|
-
"cpp/**/*.{hpp,cpp}",
|
|
19
|
+
"cpp/**/*.{hpp,cpp,h}",
|
|
20
20
|
# Autolinking (Objective-C++)
|
|
21
21
|
"ios/**/*.{m,mm}",
|
|
22
22
|
# Nitrogen generated iOS bridge
|
|
23
23
|
"nitrogen/generated/ios/**/*.{mm,swift}",
|
|
24
24
|
]
|
|
25
25
|
|
|
26
|
+
# Exclude Android-only JNI files from iOS build
|
|
27
|
+
s.exclude_files = [
|
|
28
|
+
"cpp/cpp-adapter.cpp",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
# Prebuilt LiteRT-LM C engine (static library built from Bazel //c:engine target).
|
|
32
|
+
# Downloaded from GitHub releases by postinstall.js, or built locally via:
|
|
33
|
+
# scripts/build-ios-engine.sh
|
|
34
|
+
s.vendored_frameworks = 'ios/Frameworks/LiteRTLM.xcframework'
|
|
35
|
+
|
|
26
36
|
s.pod_target_xcconfig = {
|
|
27
37
|
'CLANG_CXX_LANGUAGE_STANDARD' => 'c++20',
|
|
28
38
|
'CLANG_CXX_LIBRARY' => 'libc++',
|
|
29
39
|
'HEADER_SEARCH_PATHS' => [
|
|
30
40
|
'"$(PODS_TARGET_SRCROOT)/cpp"',
|
|
41
|
+
'"$(PODS_TARGET_SRCROOT)/cpp/include"',
|
|
31
42
|
'"$(PODS_TARGET_SRCROOT)/nitrogen/generated/shared/c++"',
|
|
32
43
|
'"$(PODS_TARGET_SRCROOT)/nitrogen/generated/ios"',
|
|
33
44
|
].join(' '),
|
|
34
|
-
|
|
35
|
-
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) LITERT_LM_IOS_STUB=1',
|
|
45
|
+
'OTHER_LDFLAGS' => '$(inherited) -ObjC',
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
# Load nitrogen autolinking
|
|
39
49
|
load 'nitrogen/generated/ios/LiteRTLM+autolinking.rb'
|
|
40
50
|
add_nitrogen_files(s)
|
|
41
51
|
|
|
42
|
-
# Core
|
|
52
|
+
# Core React Native dependencies
|
|
43
53
|
s.dependency 'React-jsi'
|
|
44
54
|
s.dependency 'React-callinvoker'
|
|
45
55
|
s.dependency 'ReactCommon/turbomodule/core'
|
|
46
|
-
|
|
47
|
-
#
|
|
48
|
-
#
|
|
56
|
+
|
|
57
|
+
# Apple frameworks needed by LiteRT-LM engine
|
|
58
|
+
# Metal/MPS: GPU inference, Accelerate: BLAS/LAPACK, CoreML: delegate
|
|
59
|
+
s.frameworks = ['Metal', 'MetalPerformanceShaders', 'Accelerate', 'CoreML', 'CoreGraphics']
|
|
60
|
+
s.libraries = ['c++']
|
|
49
61
|
|
|
50
62
|
install_modules_dependencies(s)
|
|
51
63
|
end
|
|
64
|
+
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# build-ios-engine.sh
|
|
3
|
+
#
|
|
4
|
+
# Builds the LiteRT-LM C engine as a static library for iOS (device + simulator)
|
|
5
|
+
# using Bazel, then packages it into an XCFramework for CocoaPods.
|
|
6
|
+
#
|
|
7
|
+
# Prerequisites:
|
|
8
|
+
# - Bazel 7.6.1+ (via Bazelisk recommended)
|
|
9
|
+
# - Xcode command line tools
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# ./scripts/build-ios-engine.sh
|
|
13
|
+
#
|
|
14
|
+
# Output:
|
|
15
|
+
# ios/Frameworks/LiteRTLM.xcframework/ (static library + headers)
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
LITERT_LM_VERSION="v0.9.0"
|
|
20
|
+
LITERT_LM_REPO="https://github.com/google-ai-edge/LiteRT-LM.git"
|
|
21
|
+
FRAMEWORK_NAME="LiteRTLM"
|
|
22
|
+
|
|
23
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
24
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
25
|
+
OUTPUT_DIR="$PROJECT_ROOT/ios/Frameworks"
|
|
26
|
+
C_API_HEADER_DIR="$PROJECT_ROOT/cpp/include"
|
|
27
|
+
BUILD_DIR="$PROJECT_ROOT/.litert-lm-build"
|
|
28
|
+
|
|
29
|
+
echo "==> Building LiteRT-LM ${LITERT_LM_VERSION} C engine for iOS..."
|
|
30
|
+
echo ""
|
|
31
|
+
|
|
32
|
+
# ---- 1. Clone / update the LiteRT-LM repo --------------------------------
|
|
33
|
+
echo "==> Step 1: Preparing LiteRT-LM source..."
|
|
34
|
+
if [ -f "$BUILD_DIR/LiteRT-LM/.bazelrc" ] && [ -f "$BUILD_DIR/LiteRT-LM/requirements.txt" ]; then
|
|
35
|
+
echo " Source already exists, checking out ${LITERT_LM_VERSION}..."
|
|
36
|
+
cd "$BUILD_DIR/LiteRT-LM"
|
|
37
|
+
git fetch --tags 2>/dev/null || true
|
|
38
|
+
git checkout "$LITERT_LM_VERSION" 2>/dev/null || git checkout "tags/$LITERT_LM_VERSION"
|
|
39
|
+
else
|
|
40
|
+
# Clean up any failed previous clone
|
|
41
|
+
rm -rf "$BUILD_DIR/LiteRT-LM"
|
|
42
|
+
mkdir -p "$BUILD_DIR"
|
|
43
|
+
echo " Cloning LiteRT-LM (shallow, skipping LFS)..."
|
|
44
|
+
|
|
45
|
+
# Clone without LFS filter to avoid requiring git-lfs installation.
|
|
46
|
+
# The prebuilt/ directory will contain LFS pointer files but we don't
|
|
47
|
+
# need those — we're building the engine from source.
|
|
48
|
+
GIT_LFS_SKIP_SMUDGE=1 git clone --depth 1 --branch "$LITERT_LM_VERSION" \
|
|
49
|
+
-c filter.lfs.smudge=cat \
|
|
50
|
+
-c filter.lfs.process= \
|
|
51
|
+
-c filter.lfs.clean=cat \
|
|
52
|
+
-c filter.lfs.required=false \
|
|
53
|
+
"$LITERT_LM_REPO" "$BUILD_DIR/LiteRT-LM"
|
|
54
|
+
|
|
55
|
+
cd "$BUILD_DIR/LiteRT-LM"
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
LITERT_SRC="$BUILD_DIR/LiteRT-LM"
|
|
59
|
+
|
|
60
|
+
# ---- 2. Verify Bazel is available -----------------------------------------
|
|
61
|
+
echo ""
|
|
62
|
+
echo "==> Step 2: Checking Bazel..."
|
|
63
|
+
if command -v bazelisk &>/dev/null; then
|
|
64
|
+
BAZEL="bazelisk"
|
|
65
|
+
elif command -v bazel &>/dev/null; then
|
|
66
|
+
BAZEL="bazel"
|
|
67
|
+
else
|
|
68
|
+
echo "Error: Bazel is not installed."
|
|
69
|
+
echo "Install via: brew install bazelisk"
|
|
70
|
+
echo "Or download from: https://github.com/bazelbuild/bazelisk"
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
BAZEL_VERSION=$($BAZEL --version 2>&1 | head -1)
|
|
75
|
+
echo " Using: $BAZEL ($BAZEL_VERSION)"
|
|
76
|
+
|
|
77
|
+
# ---- 3. Build the C engine static library for iOS -------------------------
|
|
78
|
+
echo ""
|
|
79
|
+
echo "==> Step 3: Building //c:engine for iOS..."
|
|
80
|
+
|
|
81
|
+
cd "$LITERT_SRC"
|
|
82
|
+
|
|
83
|
+
# Get Bazel output base (where all .o files live in the actual filesystem)
|
|
84
|
+
BAZEL_OUTPUT_BASE=$($BAZEL info output_base 2>/dev/null)
|
|
85
|
+
BAZEL_EXECROOT="$BAZEL_OUTPUT_BASE/execroot"
|
|
86
|
+
|
|
87
|
+
STAGE_DIR="$BUILD_DIR/staged-libs"
|
|
88
|
+
mkdir -p "$STAGE_DIR"
|
|
89
|
+
|
|
90
|
+
# Helper: build for a config, then merge ALL transitive .o files into one .a
|
|
91
|
+
# Bazel's cc_library produces thin archives — the engine's transitive deps
|
|
92
|
+
# (absl, protobuf, runtime, KleidiAI, etc.) are separate .o files that must
|
|
93
|
+
# be merged into a single self-contained static library for Xcode.
|
|
94
|
+
build_fat_static_lib() {
|
|
95
|
+
local CONFIG="$1"
|
|
96
|
+
local CONFIG_DIR="$2" # e.g. "ios_arm64-opt" or "ios_sim_arm64-opt"
|
|
97
|
+
local OUTPUT_PATH="$3"
|
|
98
|
+
|
|
99
|
+
echo " Building for $CONFIG..."
|
|
100
|
+
# Build both the engine AND all cc_proto_library targets in a single Bazel
|
|
101
|
+
# invocation. Bazel's cc_proto_library compiles proto-generated code, but
|
|
102
|
+
# the .pb.o files only appear in the output tree if these targets are
|
|
103
|
+
# explicitly requested alongside the engine.
|
|
104
|
+
$BAZEL build \
|
|
105
|
+
//c:engine \
|
|
106
|
+
@sentencepiece//:sentencepiece_cc_proto \
|
|
107
|
+
@sentencepiece//:sentencepiece_model_cc_proto \
|
|
108
|
+
//runtime/proto:engine_cc_proto \
|
|
109
|
+
//runtime/proto:sampler_params_cc_proto \
|
|
110
|
+
//runtime/proto:llm_metadata_cc_proto \
|
|
111
|
+
//runtime/proto:token_cc_proto \
|
|
112
|
+
//runtime/proto:llm_model_type_cc_proto \
|
|
113
|
+
//runtime/util:external_file_cc_proto \
|
|
114
|
+
@com_google_protobuf//:protobuf \
|
|
115
|
+
@com_googlesource_code_re2//:re2 \
|
|
116
|
+
--config=$CONFIG 2>&1 | tail -5
|
|
117
|
+
|
|
118
|
+
echo " Collecting transitive object files from $CONFIG_DIR..."
|
|
119
|
+
local OBJ_LIST="$STAGE_DIR/${CONFIG}-objects.txt"
|
|
120
|
+
find "$BAZEL_EXECROOT" -path "*/${CONFIG_DIR}/bin/*" -name "*.o" \
|
|
121
|
+
! -name "*.h.processed" 2>/dev/null | sort > "$OBJ_LIST"
|
|
122
|
+
|
|
123
|
+
# ---- Compile stubs for Rust/llguidance deps (unavailable on iOS) ----------
|
|
124
|
+
local EXTRA_OBJS="$STAGE_DIR/${CONFIG}-extra-objs"
|
|
125
|
+
rm -rf "$EXTRA_OBJS"
|
|
126
|
+
mkdir -p "$EXTRA_OBJS"
|
|
127
|
+
|
|
128
|
+
local STUBS_DIR="$PROJECT_ROOT/scripts/stubs"
|
|
129
|
+
local STUB_FILES=$(find "$STUBS_DIR" \( -name "*.cc" -o -name "*.c" \) 2>/dev/null)
|
|
130
|
+
if [ -n "$STUB_FILES" ]; then
|
|
131
|
+
echo " Compiling stubs for unavailable dependencies..."
|
|
132
|
+
local SDK_NAME="iphoneos"
|
|
133
|
+
local TARGET_TRIPLE="arm64-apple-ios15.0"
|
|
134
|
+
if [[ "$CONFIG_DIR" == *"sim"* ]]; then
|
|
135
|
+
SDK_NAME="iphonesimulator"
|
|
136
|
+
TARGET_TRIPLE="arm64-apple-ios15.0-simulator"
|
|
137
|
+
fi
|
|
138
|
+
local SDK_PATH=$(xcrun --sdk "$SDK_NAME" --show-sdk-path)
|
|
139
|
+
|
|
140
|
+
for STUB_SRC in $STUB_FILES; do
|
|
141
|
+
local STUB_BASE=$(basename "$STUB_SRC")
|
|
142
|
+
local STUB_NAME="${STUB_BASE%.*}"
|
|
143
|
+
local STUB_EXT="${STUB_BASE##*.}"
|
|
144
|
+
echo " → $STUB_NAME ($STUB_EXT)"
|
|
145
|
+
|
|
146
|
+
if [ "$STUB_EXT" = "cc" ]; then
|
|
147
|
+
xcrun clang++ -c -std=c++20 \
|
|
148
|
+
-target "$TARGET_TRIPLE" \
|
|
149
|
+
-isysroot "$SDK_PATH" \
|
|
150
|
+
-DNDEBUG \
|
|
151
|
+
-o "$EXTRA_OBJS/${STUB_NAME}.o" \
|
|
152
|
+
"$STUB_SRC" 2>&1 || true
|
|
153
|
+
else
|
|
154
|
+
xcrun clang -c \
|
|
155
|
+
-target "$TARGET_TRIPLE" \
|
|
156
|
+
-isysroot "$SDK_PATH" \
|
|
157
|
+
-DNDEBUG \
|
|
158
|
+
-o "$EXTRA_OBJS/${STUB_NAME}.o" \
|
|
159
|
+
"$STUB_SRC" 2>&1 || true
|
|
160
|
+
fi
|
|
161
|
+
done
|
|
162
|
+
|
|
163
|
+
# Add successfully compiled stubs to the object list
|
|
164
|
+
find "$EXTRA_OBJS" -name "*.o" -size +0c >> "$OBJ_LIST"
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
local OBJ_COUNT=$(wc -l < "$OBJ_LIST" | tr -d ' ')
|
|
168
|
+
echo " Found $OBJ_COUNT total object files (including proto + stubs)"
|
|
169
|
+
|
|
170
|
+
if [ "$OBJ_COUNT" -eq 0 ]; then
|
|
171
|
+
echo "Error: No object files found for $CONFIG in $CONFIG_DIR"
|
|
172
|
+
exit 1
|
|
173
|
+
fi
|
|
174
|
+
|
|
175
|
+
# Merge all .o files into a single fat static library using libtool
|
|
176
|
+
echo " Merging into fat static library..."
|
|
177
|
+
xcrun libtool -static -o "$OUTPUT_PATH" -filelist "$OBJ_LIST" 2>&1 | grep -v "has no symbols" || true
|
|
178
|
+
|
|
179
|
+
local LIB_SIZE=$(du -h "$OUTPUT_PATH" | cut -f1)
|
|
180
|
+
echo " ✅ $CONFIG: $LIB_SIZE ($OBJ_COUNT objects)"
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
# Build for device (arm64)
|
|
184
|
+
DEVICE_LIB="$STAGE_DIR/libengine-device.a"
|
|
185
|
+
build_fat_static_lib "ios_arm64" "ios_arm64-opt" "$DEVICE_LIB"
|
|
186
|
+
|
|
187
|
+
echo ""
|
|
188
|
+
|
|
189
|
+
# Build for simulator (sim_arm64)
|
|
190
|
+
SIM_LIB="$STAGE_DIR/libengine-sim.a"
|
|
191
|
+
build_fat_static_lib "ios_sim_arm64" "ios_sim_arm64-opt" "$SIM_LIB"
|
|
192
|
+
|
|
193
|
+
echo ""
|
|
194
|
+
echo " Device lib: $DEVICE_LIB ($(du -h "$DEVICE_LIB" | cut -f1))"
|
|
195
|
+
echo " Simulator lib: $SIM_LIB ($(du -h "$SIM_LIB" | cut -f1))"
|
|
196
|
+
|
|
197
|
+
# ---- 4. Copy the C API header ---------------------------------------------
|
|
198
|
+
echo ""
|
|
199
|
+
echo "==> Step 4: Vendoring C API header..."
|
|
200
|
+
mkdir -p "$C_API_HEADER_DIR"
|
|
201
|
+
cp "$LITERT_SRC/c/engine.h" "$C_API_HEADER_DIR/litert_lm_engine.h"
|
|
202
|
+
echo " ✅ Copied engine.h → cpp/include/litert_lm_engine.h"
|
|
203
|
+
|
|
204
|
+
# ---- 5. Create XCFramework from static libraries --------------------------
|
|
205
|
+
echo ""
|
|
206
|
+
echo "==> Step 5: Creating XCFramework..."
|
|
207
|
+
|
|
208
|
+
rm -rf "$OUTPUT_DIR"
|
|
209
|
+
mkdir -p "$OUTPUT_DIR"
|
|
210
|
+
|
|
211
|
+
TMP_DIR="$(mktemp -d)"
|
|
212
|
+
cleanup() { rm -rf "$TMP_DIR"; }
|
|
213
|
+
trap cleanup EXIT
|
|
214
|
+
|
|
215
|
+
# Create framework bundles from static libraries
|
|
216
|
+
for ARCH_NAME in "device" "simulator"; do
|
|
217
|
+
if [ "$ARCH_NAME" = "device" ]; then
|
|
218
|
+
LIB_PATH="$DEVICE_LIB"
|
|
219
|
+
else
|
|
220
|
+
LIB_PATH="$SIM_LIB"
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
FW_DIR="$TMP_DIR/$ARCH_NAME/$FRAMEWORK_NAME.framework"
|
|
224
|
+
mkdir -p "$FW_DIR/Headers"
|
|
225
|
+
|
|
226
|
+
# Copy static lib as the framework binary
|
|
227
|
+
cp "$LIB_PATH" "$FW_DIR/$FRAMEWORK_NAME"
|
|
228
|
+
|
|
229
|
+
# Copy headers
|
|
230
|
+
cp "$C_API_HEADER_DIR/litert_lm_engine.h" "$FW_DIR/Headers/"
|
|
231
|
+
|
|
232
|
+
# Create Info.plist
|
|
233
|
+
cat > "$FW_DIR/Info.plist" << PLIST
|
|
234
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
235
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
236
|
+
<plist version="1.0">
|
|
237
|
+
<dict>
|
|
238
|
+
<key>CFBundleDevelopmentRegion</key>
|
|
239
|
+
<string>en</string>
|
|
240
|
+
<key>CFBundleExecutable</key>
|
|
241
|
+
<string>${FRAMEWORK_NAME}</string>
|
|
242
|
+
<key>CFBundleIdentifier</key>
|
|
243
|
+
<string>com.google.ai.edge.litert-lm</string>
|
|
244
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
|
245
|
+
<string>6.0</string>
|
|
246
|
+
<key>CFBundleName</key>
|
|
247
|
+
<string>${FRAMEWORK_NAME}</string>
|
|
248
|
+
<key>CFBundlePackageType</key>
|
|
249
|
+
<string>FMWK</string>
|
|
250
|
+
<key>CFBundleShortVersionString</key>
|
|
251
|
+
<string>0.9.0</string>
|
|
252
|
+
<key>CFBundleVersion</key>
|
|
253
|
+
<string>1</string>
|
|
254
|
+
<key>MinimumOSVersion</key>
|
|
255
|
+
<string>15.0</string>
|
|
256
|
+
</dict>
|
|
257
|
+
</plist>
|
|
258
|
+
PLIST
|
|
259
|
+
done
|
|
260
|
+
|
|
261
|
+
# Create XCFramework
|
|
262
|
+
xcodebuild -create-xcframework \
|
|
263
|
+
-framework "$TMP_DIR/device/$FRAMEWORK_NAME.framework" \
|
|
264
|
+
-framework "$TMP_DIR/simulator/$FRAMEWORK_NAME.framework" \
|
|
265
|
+
-output "$OUTPUT_DIR/$FRAMEWORK_NAME.xcframework" 2>&1
|
|
266
|
+
|
|
267
|
+
echo " ✅ XCFramework created at: ios/Frameworks/${FRAMEWORK_NAME}.xcframework"
|
|
268
|
+
|
|
269
|
+
# ---- 6. Create zip for release asset --------------------------------------
|
|
270
|
+
echo ""
|
|
271
|
+
echo "==> Step 6: Creating release asset zip..."
|
|
272
|
+
cd "$OUTPUT_DIR"
|
|
273
|
+
zip -r "$PROJECT_ROOT/LiteRTLM-ios-frameworks.zip" . -x ".*" 2>&1
|
|
274
|
+
ZIP_SIZE=$(du -h "$PROJECT_ROOT/LiteRTLM-ios-frameworks.zip" | cut -f1)
|
|
275
|
+
echo " ✅ Created LiteRTLM-ios-frameworks.zip (${ZIP_SIZE})"
|
|
276
|
+
|
|
277
|
+
echo ""
|
|
278
|
+
echo "==> Done! iOS engine built and packaged."
|
|
279
|
+
echo ""
|
|
280
|
+
echo "Contents:"
|
|
281
|
+
find "$OUTPUT_DIR" -type f | head -20 | while read f; do
|
|
282
|
+
echo " $(echo "$f" | sed "s|$PROJECT_ROOT/||") ($(du -h "$f" | cut -f1))"
|
|
283
|
+
done
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# download-ios-frameworks.sh
|
|
3
|
+
#
|
|
4
|
+
# Downloads prebuilt LiteRT-LM iOS static engine from this project's GitHub
|
|
5
|
+
# releases. If the prebuilt asset is not available, falls back to building
|
|
6
|
+
# from source via Bazel (see build-ios-engine.sh).
|
|
7
|
+
#
|
|
8
|
+
# The XCFramework contains a static library compiled from the LiteRT-LM
|
|
9
|
+
# C engine (//c:engine Bazel target) for both device (arm64) and simulator
|
|
10
|
+
# (sim_arm64).
|
|
11
|
+
#
|
|
12
|
+
# Usage:
|
|
13
|
+
# ./scripts/download-ios-frameworks.sh
|
|
14
|
+
|
|
15
|
+
set -euo pipefail
|
|
16
|
+
|
|
17
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
18
|
+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
19
|
+
OUTPUT_DIR="$PROJECT_ROOT/ios/Frameworks"
|
|
20
|
+
C_API_HEADER_DIR="$PROJECT_ROOT/cpp/include"
|
|
21
|
+
|
|
22
|
+
LITERT_LM_VERSION="v0.9.0"
|
|
23
|
+
GITHUB_RAW="https://github.com/google-ai-edge/LiteRT-LM/raw/${LITERT_LM_VERSION}"
|
|
24
|
+
|
|
25
|
+
# Read version from package.json
|
|
26
|
+
PACKAGE_VERSION=$(node -e "console.log(require('$PROJECT_ROOT/package.json').version)" 2>/dev/null || echo "0.0.0")
|
|
27
|
+
GITHUB_REPO="hung-yueh/react-native-litert-lm"
|
|
28
|
+
ASSET_NAME="LiteRTLM-ios-frameworks.zip"
|
|
29
|
+
|
|
30
|
+
# Skip if already present
|
|
31
|
+
if [ -d "$OUTPUT_DIR" ] && [ "$(find "$OUTPUT_DIR" -name "*.xcframework" 2>/dev/null | wc -l)" -gt 0 ]; then
|
|
32
|
+
echo "[LiteRT-LM] iOS frameworks already present at ios/Frameworks/, skipping."
|
|
33
|
+
exit 0
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# ---- Ensure C API header is vendored --------------------------------------
|
|
37
|
+
echo "[LiteRT-LM] Vendoring C API header..."
|
|
38
|
+
mkdir -p "$C_API_HEADER_DIR"
|
|
39
|
+
curl -fsSL -o "$C_API_HEADER_DIR/litert_lm_engine.h" \
|
|
40
|
+
"${GITHUB_RAW}/c/engine.h" 2>/dev/null || true
|
|
41
|
+
|
|
42
|
+
# ---- Try downloading prebuilt from our GitHub releases --------------------
|
|
43
|
+
RELEASE_URL="https://github.com/${GITHUB_REPO}/releases/download/v${PACKAGE_VERSION}/${ASSET_NAME}"
|
|
44
|
+
|
|
45
|
+
echo "[LiteRT-LM] Attempting to download prebuilt iOS engine from:"
|
|
46
|
+
echo " ${RELEASE_URL}"
|
|
47
|
+
|
|
48
|
+
TMP_ZIP="$PROJECT_ROOT/.ios-frameworks-tmp.zip"
|
|
49
|
+
if curl -fsSL -o "$TMP_ZIP" "$RELEASE_URL" 2>/dev/null; then
|
|
50
|
+
echo "[LiteRT-LM] Download successful, extracting..."
|
|
51
|
+
rm -rf "$OUTPUT_DIR"
|
|
52
|
+
mkdir -p "$OUTPUT_DIR"
|
|
53
|
+
unzip -o -q "$TMP_ZIP" -d "$OUTPUT_DIR"
|
|
54
|
+
rm -f "$TMP_ZIP"
|
|
55
|
+
echo "[LiteRT-LM] ✅ iOS frameworks installed from prebuilt release."
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
rm -f "$TMP_ZIP"
|
|
60
|
+
echo "[LiteRT-LM] Prebuilt not available for v${PACKAGE_VERSION}."
|
|
61
|
+
|
|
62
|
+
# ---- Fall back to building from source ------------------------------------
|
|
63
|
+
echo "[LiteRT-LM] Falling back to building from source via Bazel..."
|
|
64
|
+
echo ""
|
|
65
|
+
|
|
66
|
+
if [ -x "$SCRIPT_DIR/build-ios-engine.sh" ]; then
|
|
67
|
+
exec "$SCRIPT_DIR/build-ios-engine.sh"
|
|
68
|
+
else
|
|
69
|
+
echo "Error: build-ios-engine.sh not found or not executable."
|
|
70
|
+
echo "Run manually: ./scripts/build-ios-engine.sh"
|
|
71
|
+
exit 1
|
|
72
|
+
fi
|