diff --git a/.cspell-wordlist.txt b/.cspell-wordlist.txt index 3837e96955..0c81a9cc22 100644 --- a/.cspell-wordlist.txt +++ b/.cspell-wordlist.txt @@ -203,6 +203,52 @@ fishjam Fishjam deinitialize Deinitialize +podspec +libexecutorch +libxnnpack +libvulkan +libbackend +libcpuinfo +flatcc +Werror +dlopen +msluszniak +DRNE +libopencv +Kleidi +libphonemis +libreact +CPLUSPLUSFLAGS +LDFLAGS +iphoneos +iphonesimulator +xcframework +xcframeworks +EEXIST +RNET +Unigram +SIGSEGV +memcmp +nullptr +DANDROID +venv +pyyaml +libkernels +libthreadpool +libkleidiai +kleidiai +xcodeproj +prebuilts +pthreadpool +libpthreadpool +DFLATCC +flatccrt +Wimplicit +Wunterminated +NSURL +certifi +zstd +lintrunner fastsam promptable topk @@ -212,6 +258,7 @@ Siwis SIWIS Mateusz MATEUSZ +gitmodules BLAZEFACE Blazeface blazeface diff --git a/.github/workflows/build-android-llm-example.yml b/.github/workflows/build-android-llm-example.yml index 3b4c95bf58..ad8bb29960 100644 --- a/.github/workflows/build-android-llm-example.yml +++ b/.github/workflows/build-android-llm-example.yml @@ -19,6 +19,8 @@ jobs: runs-on: ubuntu-latest env: WORKING_DIRECTORY: apps/llm + # TODO: drop once v0.9.0 is cut and tarballs are attached to the matching Release. + RNET_BASE_URL: https://github.com/software-mansion/react-native-executorch/releases/download/v0.9.0-libs-test concurrency: group: android-${{ github.ref }} cancel-in-progress: true diff --git a/.github/workflows/build-ios-llm-example.yml b/.github/workflows/build-ios-llm-example.yml index 4a2b2b9b99..8fa02fc6fa 100644 --- a/.github/workflows/build-ios-llm-example.yml +++ b/.github/workflows/build-ios-llm-example.yml @@ -22,6 +22,9 @@ jobs: concurrency: group: ios-${{ github.ref }} cancel-in-progress: true + env: + # TODO: drop once v0.9.0 is cut and tarballs are attached to the matching Release. + RNET_BASE_URL: https://github.com/software-mansion/react-native-executorch/releases/download/v0.9.0-libs-test steps: - uses: maxim-lobanov/setup-xcode@v1 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 96fd27ad65..58a3884652 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,6 +10,9 @@ on: types: - checks_requested workflow_dispatch: +env: + # TODO: drop once v0.9.0 is cut and tarballs are attached to the matching Release. + RNET_BASE_URL: https://github.com/software-mansion/react-native-executorch/releases/download/v0.9.0-libs-test jobs: lint: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 86ce3a9042..a52b810e9e 100644 --- a/.gitignore +++ b/.gitignore @@ -98,6 +98,17 @@ docs/docs/06-api-reference/ # integration test model assets packages/react-native-executorch/common/rnexecutorch/tests/integration/assets/models/ +# release artifact staging dir (produced by scripts/package-release-artifacts.sh) +packages/react-native-executorch/dist-artifacts/ + +# on-demand native libs (downloaded at postinstall time, not committed) +packages/react-native-executorch/third-party/android/libs/ +packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ +packages/react-native-executorch/third-party/ios/CoreMLBackend.xcframework/ +packages/react-native-executorch/third-party/ios/XnnpackBackend.xcframework/ +packages/react-native-executorch/third-party/ios/libs/ +packages/react-native-executorch/rne-build-config.json + # custom *.tgz Makefile diff --git a/apps/bare-rn/package.json b/apps/bare-rn/package.json index e9201874bd..0593a0030f 100644 --- a/apps/bare-rn/package.json +++ b/apps/bare-rn/package.json @@ -45,5 +45,10 @@ }, "installConfig": { "hoistingLimits": "workspaces" + }, + "react-native-executorch": { + "features": [ + "llm" + ] } } diff --git a/apps/computer-vision/package.json b/apps/computer-vision/package.json index 794bb1effd..ed6822331e 100644 --- a/apps/computer-vision/package.json +++ b/apps/computer-vision/package.json @@ -50,5 +50,20 @@ "babel-preset-expo": "~55.0.16", "react-refresh": "^0.18.0" }, - "private": true + "private": true, + "react-native-executorch": { + "features": [ + "classification", + "imageEmbeddings", + "instanceSegmentation", + "ocr", + "objectDetection", + "poseEstimation", + "semanticSegmentation", + "styleTransfer", + "textEmbeddings", + "textToImage", + "verticalOCR" + ] + } } diff --git a/apps/llm/package.json b/apps/llm/package.json index acdc2379e7..7a148f3373 100644 --- a/apps/llm/package.json +++ b/apps/llm/package.json @@ -50,5 +50,12 @@ "babel-preset-expo": "~55.0.16", "react-refresh": "^0.18.0" }, - "private": true + "private": true, + "react-native-executorch": { + "features": [ + "llm", + "multimodalLLM", + "privacyFilter" + ] + } } diff --git a/apps/speech/package.json b/apps/speech/package.json index f941a7cfed..10c5b00332 100644 --- a/apps/speech/package.json +++ b/apps/speech/package.json @@ -37,5 +37,13 @@ "babel-preset-expo": "~55.0.16", "react-refresh": "^0.18.0" }, - "private": true + "private": true, + "react-native-executorch": { + "features": [ + "llm", + "speechToText", + "textToSpeech", + "vad" + ] + } } diff --git a/apps/speech/screens/VoiceActivityDetectionScreen.tsx b/apps/speech/screens/VoiceActivityDetectionScreen.tsx index 724ea52500..e3e72caf87 100644 --- a/apps/speech/screens/VoiceActivityDetectionScreen.tsx +++ b/apps/speech/screens/VoiceActivityDetectionScreen.tsx @@ -8,10 +8,7 @@ import { Platform, } from 'react-native'; import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'; -import { - models, - useVAD -} from 'react-native-executorch'; +import { models, useVAD } from 'react-native-executorch'; import FontAwesome from '@expo/vector-icons/FontAwesome'; import { AudioManager, AudioRecorder } from 'react-native-audio-api'; import SWMIcon from '../assets/swm_icon.svg'; diff --git a/apps/text-embeddings/package.json b/apps/text-embeddings/package.json index 41590d1e68..c002a3b0d0 100644 --- a/apps/text-embeddings/package.json +++ b/apps/text-embeddings/package.json @@ -40,5 +40,11 @@ "babel-preset-expo": "~55.0.16", "react-refresh": "^0.18.0" }, - "private": true + "private": true, + "react-native-executorch": { + "features": [ + "textEmbeddings", + "imageEmbeddings" + ] + } } diff --git a/docs/docs/01-fundamentals/01-getting-started.md b/docs/docs/01-fundamentals/01-getting-started.md index ebc35608da..398c049d7d 100644 --- a/docs/docs/01-fundamentals/01-getting-started.md +++ b/docs/docs/01-fundamentals/01-getting-started.md @@ -122,6 +122,81 @@ Pick the adapter that matches your project. We recommend the Expo adapter when y +### Configuring backends, libs, and features + +On install, `react-native-executorch` runs a `postinstall` script that downloads prebuilt native libraries from the matching GitHub Release and unpacks them under `third-party/`. By default every optional feature is included — which keeps the app binary large. You can trim the install by declaring exactly what you need under `react-native-executorch` in your app's `package.json`. Three optional arrays are supported, and they're all merged into a single set: + +```json +{ + "react-native-executorch": { + "backends": ["xnnpack", "coreml", "vulkan"], + "libs": ["opencv", "phonemizer"], + "features": ["llm", "textToSpeech", "objectDetection"] + } +} +``` + +- **`features`** is the friendly opt-in. List the use\* hooks you'll use; the postinstall expands each one to the backends + libs it needs. +- **`backends` / `libs`** are the precise opt-in. List the underlying components directly. +- Omit the whole `react-native-executorch` block to enable everything (largest install, lowest friction). +- If you mix `features` with `backends`/`libs`, the result is their union. + +Recognized **backends**: `xnnpack`, `coreml` (iOS-only), `vulkan` (Android-only). +Recognized **libs**: `opencv`, `phonemizer`. +Recognized **features** (one per documented hook): + +Each row reflects the union of what at least one model in that family ships today; bump it when a new variant adds a backend. + +| Feature | Pulls in (backends · libs) | What it powers | +| ---------------------- | -------------------------- | --------------------------------------- | +| `llm` | xnnpack · — | Text-only `useLLM` | +| `multimodalLLM` | xnnpack, vulkan · opencv | Vision-language `useLLM` (image inputs) | +| `privacyFilter` | xnnpack · — | `usePrivacyFilter` | +| `speechToText` | xnnpack, coreml · — | `useSpeechToText` (Whisper) | +| `textToSpeech` | xnnpack · phonemizer | `useTextToSpeech` (Kokoro) | +| `vad` | xnnpack · — | `useVAD` | +| `textEmbeddings` | xnnpack · — | `useTextEmbeddings` | +| `imageEmbeddings` | xnnpack · opencv | `useImageEmbeddings` | +| `classification` | xnnpack, coreml · opencv | `useClassification` | +| `objectDetection` | xnnpack, coreml · opencv | `useObjectDetection` | +| `semanticSegmentation` | xnnpack · opencv | `useSemanticSegmentation` | +| `instanceSegmentation` | xnnpack, coreml · opencv | `useInstanceSegmentation` | +| `ocr` | xnnpack · opencv | `useOCR` | +| `verticalOCR` | xnnpack · opencv | `useVerticalOCR` | +| `poseEstimation` | xnnpack · opencv | `usePoseEstimation` | +| `styleTransfer` | xnnpack, coreml · opencv | `useStyleTransfer` | +| `textToImage` | xnnpack · opencv | `useTextToImage` | +| `segmentAnything` | xnnpack, coreml · opencv | FastSAM / Segment-Anything hooks | + +Backend platform map: + +| Backend | iOS | Android | +| --------- | ----------------------------------------------------------- | --------------------------------------------------------- | +| `xnnpack` | ✅ — `XnnpackBackend.xcframework` force-loaded into the app | ✅ — separately-loaded `libxnnpack_executorch_backend.so` | +| `coreml` | ✅ — `CoreMLBackend.xcframework` force-loaded into the app | n/a | +| `vulkan` | n/a | ✅ — separately-loaded `libvulkan_executorch_backend.so` | + +Lib platform map: + +| Lib | iOS | Android | +| ------------ | ---------------------------------- | -------------------------------------------------------- | +| `opencv` | ✅ (via the `opencv-rne` CocoaPod) | ✅ (static `libopencv_*.a` + KleidiCV HAL on arm64) | +| `phonemizer` | ✅ (compiled from in-tree source) | ✅ (compiled from in-tree source via `add_subdirectory`) | + +Source files and native libraries are excluded from compilation when a backend or lib is disabled, so builds that only need LLMs can skip OpenCV and cut tens of megabytes off the final binary. + +The postinstall step honors a few environment variables: + +| Variable | Purpose | +| ---------------------- | ------------------------------------------------------------------------- | +| `RNET_SKIP_DOWNLOAD=1` | Skip the download entirely (for CI with pre-cached libraries). | +| `RNET_LIBS_CACHE_DIR` | Custom cache directory (default: `~/.cache/react-native-executorch/`). | +| `RNET_TARGET` | Force a specific target, e.g. `android-arm64-v8a` or `ios`. | +| `RNET_NO_X86_64=1` | Skip the Android x86_64 tarball (handy when only building for a device). | +| `GITHUB_TOKEN` | Required to access draft releases while iterating on a new version. | + +After changing `backends` / `libs` / `features`, re-run `yarn install` (or the equivalent) so the postinstall script regenerates `rne-build-config.json` and re-extracts the right tarballs, then rebuild the native project. + :::warning Before using any other API, you must call `initExecutorch` with a resource fetcher adapter at the entry point of your app: diff --git a/packages/react-native-executorch/NATIVE_LIBS_PIPELINE.md b/packages/react-native-executorch/NATIVE_LIBS_PIPELINE.md new file mode 100644 index 0000000000..56357a3a0c --- /dev/null +++ b/packages/react-native-executorch/NATIVE_LIBS_PIPELINE.md @@ -0,0 +1,243 @@ +# Native libraries pipeline + +This document describes how native dependencies (ExecuTorch runtime, backends, OpenCV, phonemizer) are produced, shipped, and stitched into an app build. It is intended for maintainers — the user-facing summary lives in `docs/docs/01-fundamentals/01-getting-started.md`. + +## High-level flow + +``` + ┌──────────────────────┐ ┌────────────────────────┐ ┌───────────────────────┐ + │ ExecuTorch fork │ ───▶ │ GitHub Release v │ ───▶ │ postinstall script │ + │ + our patches │ │ .tar.gz │ │ download-libs.js │ + │ (separate repo) │ │ .tar.gz.256 │ │ │ + └──────────────────────┘ └────────────────────────┘ └───────────┬───────────┘ + │ + ▼ + ┌───────────────────────┐ + │ third-party/android │ + │ third-party/ios │ + │ rne-build-config.json│ + └───────────┬───────────┘ + │ + ┌───────────────────────────┴────────────────────────────┐ + ▼ ▼ + ┌───────────────────────┐ ┌─────────────────────────┐ + │ android/build.gradle │ │ react-native-executorch │ + │ + CMakeLists.txt │ │ .podspec │ + │ -DRNE_ENABLE_* │ │ -DRNE_ENABLE_* │ + └───────────────────────┘ │ force_load xcframeworks │ + └─────────────────────────┘ +``` + +## Install-time: `scripts/download-libs.js` + +Runs at `postinstall`. Responsibilities: + +1. Read `react-native-executorch.{backends, libs, features}` from the app's `package.json` (uses `INIT_CWD`). Each array is optional; `features` is expanded via `FEATURE_MAP` to (backends, libs) and merged with the explicit arrays. With no config, everything defaults to enabled. The legacy `extras` field is rejected with a migration error. +2. Write `rne-build-config.json` at the package root with boolean flags (`enableXnnpack`, `enableCoreml`, `enableVulkan`, `enableOpencv`, `enablePhonemizer`) — this file is the single source of truth consumed by both the Gradle build and the podspec. +3. Detect targets (`ios` on macOS; always `android-arm64-v8a` and, unless `RNET_NO_X86_64` is set, `android-x86_64`). +4. For each target × enabled backend/lib, fetch the corresponding `.tar.gz` from the GitHub Release tagged `v${PACKAGE_VERSION}`, verify the `.sha256`, and extract into `third-party/android/libs/` or `third-party/ios/`. (Phonemizer ships as in-tree source via the `third-party/common/phonemis` submodule, so no tarball is fetched for it.) +5. Cache validated tarballs under `~/.cache/react-native-executorch//` so subsequent installs skip the network. + +Environment overrides: `RNET_SKIP_DOWNLOAD`, `RNET_LIBS_CACHE_DIR`, `RNET_TARGET`, `RNET_BASE_URL` (useful with `python3 -m http.server` against `dist-artifacts/` for local iteration), `GITHUB_TOKEN` (needed for draft releases). + +The set of artifacts per target is defined in `getArtifacts()`: + +| Artifact name | Target | Produced by | Contents | +| ------------------------ | ------- | ---------------------------------------- | ------------------------------------------------------ | +| `core-android-arm64-v8a` | Android | ExecuTorch fork build | `libexecutorch.so` (no backends), headers | +| `core-android-x86_64` | Android | ExecuTorch fork build | x86_64 `libexecutorch.so` for the simulator | +| `xnnpack-android-*` | Android | ExecuTorch fork build | `libxnnpack_executorch_backend.so` (separately-loaded) | +| `vulkan-android-*` | Android | ExecuTorch fork build | `libvulkan_executorch_backend.so` (separately-loaded) | +| `core-ios` | iOS | `third-party/ios/ExecutorchLib/build.sh` | `ExecutorchLib.xcframework` | +| `xnnpack-ios` | iOS | `third-party/ios/ExecutorchLib/build.sh` | `XnnpackBackend.xcframework` | +| `coreml-ios` | iOS | `third-party/ios/ExecutorchLib/build.sh` | `CoreMLBackend.xcframework` | +| `opencv-android-*` | Android | OpenCV release process | Static OpenCV + KleidiCV HAL | + +(`opencv-ios` is not a tarball — iOS consumes OpenCV through the `opencv-rne` CocoaPod.) +(`phonemizer` has no tarball — phonemis is a git submodule at `third-party/common/phonemis` and is compiled from source on both Android and iOS when the extra is enabled.) + +## Build-time: Android + +`android/build.gradle` reads `rne-build-config.json` once and forwards the booleans to CMake: + +```groovy +"-DRNE_ENABLE_OPENCV=${rneBuildConfig.enableOpencv ? 'ON' : 'OFF'}", +"-DRNE_ENABLE_PHONEMIZER=${rneBuildConfig.enablePhonemizer ? 'ON' : 'OFF'}", +"-DRNE_ENABLE_XNNPACK=${rneBuildConfig.enableXnnpack ? 'ON' : 'OFF'}", +"-DRNE_ENABLE_VULKAN=${rneBuildConfig.enableVulkan ? 'ON' : 'OFF'}" +``` + +`android/CMakeLists.txt` and `android/src/main/cpp/CMakeLists.txt` respond by: + +- Adding `-DRNE_ENABLE_OPENCV` / `-DRNE_ENABLE_PHONEMIZER` compile definitions so C++ code can `#ifdef` around optional dependencies. +- Conditionally linking `libopencv_*.a` and KleidiCV HAL (arm64 only). +- When `RNE_ENABLE_PHONEMIZER=ON`, `add_subdirectory()`'ing the `third-party/common/phonemis` git submodule and linking the resulting `phonemis` CMake target into `libreact-native-executorch.so`. When off, the submodule is not entered and no phonemis code is compiled. +- Always linking against the prebuilt `libexecutorch.so` downloaded into `third-party/android/libs/executorch//`. +- When `RNE_ENABLE_XNNPACK=ON` / `RNE_ENABLE_VULKAN=ON`, importing the matching `libxnnpack_executorch_backend.so` / `libvulkan_executorch_backend.so` and linking `react-native-executorch.so` against it. Linking (rather than dynamic `dlopen`) lets Gradle bundle the `.so` into the APK and triggers the dynamic linker to load it whenever `libreact-native-executorch.so` is loaded — each `.so`'s load-time constructor then registers its backend with the runtime in `libexecutorch.so`. + +## Build-time: iOS + +`react-native-executorch.podspec` reads the same `rne-build-config.json` and: + +- Excludes opencv/phonemizer C++ sources from compilation when those libs are disabled. +- Conditionally adds `third-party/common/phonemis/src/**` to `s.source_files` (and excludes `phonemis/main.cpp`) so phonemis compiles into the pod when `enable_phonemizer` is true. The corresponding header path and `-DET_ON=1` flag are also gated. +- Appends `-DRNE_ENABLE_*` to `OTHER_CPLUSPLUSFLAGS`. +- Assembles `OTHER_LDFLAGS[sdk=iphoneos*]` and `OTHER_LDFLAGS[sdk=iphonesimulator*]` with `-force_load` entries for each enabled backend xcframework. +- Declares `ExecutorchLib.xcframework` in `vendored_frameworks` but _not_ the backend xcframeworks — backend xcframeworks only live on the linker command line, never in the CocoaPods vendoring list (see next section for why). +- Adds `sqlite3` and the `CoreML` system framework to linkage only when Core ML is enabled. + +## Why backends differ between platforms + +ExecuTorch registers kernels statically via `__attribute__((constructor))` functions inside each backend's `.a`/`.so`. Two design points fall out of this: + +1. **Force-load is required.** Linkers drop unreferenced object files. The registrar symbols have no external users (they run as global constructors at load time), so a plain link keeps the backend library on disk but strips the registration symbols — and the app then fails with `Missing operator: ...` at inference. Every backend library must be force-loaded (`-force_load` on iOS, `--whole-archive` on Android, or `executorch_target_link_options_shared_lib(...)` in ExecuTorch's own CMake helpers). + +2. **A single copy of each CPU-kernel registration must exist.** Multiple backend libraries that each whole-archive-link `optimized_native_cpu_ops_lib` cause duplicate kernel-registration aborts (`error 22 EEXIST`) when both get force-loaded into the same process. + +On **iOS**, each backend ships as its own static xcframework (`XnnpackBackend.xcframework`, `CoreMLBackend.xcframework`). The podspec force-loads only the ones the user opted into, and `ExecutorchLib.xcframework` itself does not whole-archive the CPU ops — so there is no duplicate registration. + +On **Android**, the first iteration of the split hit a duplicate-registration abort (`Error::RegistrationAlreadyRegistered`, 0x16) because the ExecuTorch Android build whole-archive-linked the CPU ops (`optimized_native_cpu_ops_lib`, `custom_ops`, `quantized_ops_lib`, `register_prim_ops`) into each backend shared library, AND `extension/llm/custom_ops` PUBLIC-linked `xnnpack_backend` so XNNCompiler/XNNExecutor were getting WHOLE_ARCHIVE-pulled into `libexecutorch_jni.so` via `custom_ops`. Two fixes in the ExecuTorch fork resolve this: + +1. New `EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED` and `EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED` switches build `libxnnpack_executorch_backend.so` / `libvulkan_executorch_backend.so` linking only the backend's own static archive (--whole-archive) + the backend's schema/third-party deps + `executorch_core` — no kernel-registration archives. Loading either on top of `libexecutorch.so` does not duplicate any registration. +2. When `EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED=ON`, `extension/llm/custom_ops` drops the `xnnpack_backend` link (custom_ops doesn't actually call into XNNPACK anyway) so the WHOLE_ARCHIVE on `custom_ops` no longer drags XNNPACK code into `libexecutorch_jni.so`. + +Result: each Android backend ships as its own opt-in tarball, mirroring the iOS xcframework setup. + +## Building artifacts from the ExecuTorch fork + +Patched sources live in a separate repo (see `executorch/` in the maintainer's machine, typically checked out next to `react-native-executorch/`). The fork branch is [`msluszniak/executorch@ms/separate-backends`](https://github.com/msluszniak/executorch/tree/@ms/separate-backends), based on the upstream `release/1.2` tag, and the artifacts attached to the matching GitHub Release are produced from the pinned commit [`3ce953dbde73035e733442f99c082f5b6fedff5b`](https://github.com/msluszniak/executorch/commit/3ce953dbde73035e733442f99c082f5b6fedff5b). Bumping the `react-native-executorch` package version requires re-rolling the Release artifacts from a (possibly newer) pinned commit and updating this SHA. The branch carries (oldest → newest): + +- **chore: remove version script from `executorch_jni`** — reverts the Feb 2026 symbol-hiding change so RNE's C++ layer can resolve `Module`, `threadpool`, etc. directly. +- **feat: build `vulkan_backend` as a separate shared library on Android** — adds the `EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED` CMake option. When ON, `libvulkan_executorch_backend.so` is produced alongside `libexecutorch_jni.so` instead of vulkan_backend being whole-archive-linked into the latter. Mirrors the QNN backend pattern. +- **build: disable `-Werror` for `flatcc_ep` on host clang** — Apple clang 21+ flags warnings flatcc has not yet cleaned up; needed to build on Xcode 26.4+. Adds `-DFLATCC_ALLOW_WERROR=OFF` to the host-side `flatcc_ep` ExternalProject_Add in `third-party/CMakeLists.txt`. +- **feat: build `xnnpack_backend` as a separate shared library on Android** — same idea for XNNPACK via `EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED`. Also patches `extension/llm/custom_ops/CMakeLists.txt` to drop the (transitive) `xnnpack_backend` link when the switch is ON, so XNNPACK code does not leak into `libexecutorch_jni.so` via `custom_ops`. +- **chore: point tokenizers submodule at `software-mansion-labs/pytorch-tokenizers@build`** — replaces the old `meta-pytorch/tokenizers` pin with the SWM-internal build branch (`56a30afb…`). That branch carries support for new tokenizer types (Unigram, WordLevel) plus more normalizers / pre-tokenizers / decoders / post-processors. Headers in `third-party/include/executorch/extension/llm/tokenizers/` must match this commit; otherwise `HFTokenizer::setup_padding` / `setup_truncation` SIGSEGV when loading a real tokenizer.json. +- **build(android): forward `BACKEND_SHARED` env vars to cmake** — `build_android_library.sh` declared the `EXECUTORCH_BUILD_*_BACKEND_SHARED` env vars in surrounding docs but never passed them to cmake configure, so a fresh CMake configure produced backends baked into `libexecutorch_jni.so` even when the env vars were set. +- **fix(xnnpack): tolerate null ptr in `XNNWeightsCache::look_up_or_insert`** — `memcmp(ptr=NULL, saved_ptr, size)` crashed when XNNPACK re-checked the cache. Guard with `if (ptr == nullptr) ptr = saved_ptr;` before the compare. +- **build(android): pass `-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON`** — required for Android 16 KB-page devices (Pixel 9 / upcoming releases); without it the `.so` fails to map on those targets. +- **build(ios): keep merged intermediate `.a` files after `create_frameworks.sh`** — comments out the cleanup loop at the end of `create_frameworks.sh`. `build_apple_frameworks.sh` calls `create_frameworks.sh` to merge per-target archives via `libtool`, then deletes them by default. RNE's `third-party/ios/ExecutorchLib/build.sh` repackages those `.a` files into its own xcframeworks (with consistent slice library names that CocoaPods requires), so we need them to survive long enough to be repackaged. +- **build(ios): disable `XNNPACK_ENABLE_ARM_SME{,2}` on macOS / iOS presets** — the Apple SME backends in upstream XNNPACK fail to compile under Xcode 26's clang. Disable both for the `macos` / `ios` / `ios-simulator` presets in `CMakePresets.json` so the iOS build succeeds. +- **build(ios): disable `FLATCC_ALLOW_WERROR` on iOS / macOS presets** — the host-side `flatcc_ep` patch only affects the generator binary. Inside the iOS / macOS builds, flatcc's runtime (`flatccrt`) is rebuilt as a regular cmake target via `add_subdirectory(third-party/flatcc)`, which honors a separate cache variable. Pin `FLATCC_ALLOW_WERROR=OFF` for each Apple preset in `CMakePresets.json` so the runtime build also escapes Apple clang 21's stricter warnings (`-Wimplicit-int-conversion-on-negation`, `-Wunterminated-string-initialization`). + +### iOS + +Two-stage: the fork produces the merged per-slice `.a` files; RNE's `build.sh` repackages them into xcframeworks. Requires Xcode 26.x + Python 3.10. + +> **iOS 26.4 simulator caveat.** Apps linked against the iOS 26.4 SDK fail `URLSessionConfiguration.background` downloads in the 26.4 simulator with `NSURLErrorUnknown`. Older SDK builds (e.g. 26.2) are unaffected, and physical devices behave normally. This is an Apple regression — there's no fix on our end. Either use an iOS 26.2 sim for `ExpoResourceFetcher`-driven testing, fall back to `FOREGROUND` session type temporarily, or test on a real device. + +**1. Set up the Python env in the fork** + +```bash +# from the executorch fork (with @ms/separate-backends checked out) +python3 -m venv .venv && source .venv/bin/activate +pip install certifi zstd # for tools/cmake/resolve_buck.py +pip install torch==2.11.0 # cmake's find_package_torch_headers needs the torch wheel +pip install -r requirements-dev.txt # pyyaml, cmake, lintrunner, click — for codegen scripts +``` + +`install_executorch.sh` is **not** used: `torch_pin.py` pins a torch nightly (`2.11.0.dev20260215`) that's been pruned from the PyTorch nightly index, and the iOS build only needs the wheel installed for `find_package_torch_headers`. Pinning the matching stable `torch==2.11.0` is enough. + +**2. Build merged `.a` files** + +```bash +rm -rf cmake-out # always start clean — partial builds cache stale CMake options +./scripts/build_apple_frameworks.sh --Release +``` + +This drives Buck2 + cmake for the `ios`, `ios-simulator`, and `macos` presets, then calls the patched `create_frameworks.sh` with the right `--directory` / `--framework` flags to merge per-target archives via `libtool`. Outputs land in `cmake-out/`: + +- `cmake-out/libexecutorch_{ios,simulator,macos}.a`, `libexecutorch_llm_*`, `libkernels_*`, `libbackend_{xnnpack,coreml,mps}_*`, `libthreadpool_*` — the merged static archives the patched cleanup loop keeps around. +- `cmake-out/.xcframework` — produced by `xcodebuild -create-xcframework`, **not** what RNE consumes (RNE builds its own slimmer set via `ExecutorchLib/build.sh`). + +> The Swift Package generation step at the very end of the script prints +> `error: local binary target '_debug' at 'cmake-out/_debug.xcframework' does not contain a binary artifact` for each framework. That's harmless — it only fires when `--Debug` artifacts are missing — and `--Release` exits 0 anyway. The `.a` files are produced before this step runs. + +**3. Stage `.a` files into RNE** + +Copy only the `_ios` and `_simulator` slices into `packages/react-native-executorch/third-party/ios/libs/executorch/`. Skip the `_macos` files (RNE doesn't ship a macOS slice). Per-slice list to copy: + +``` +libbackend_coreml_{ios,simulator}.a +libbackend_mps_{ios,simulator}.a +libbackend_xnnpack_{ios,simulator}.a +libexecutorch_{ios,simulator}.a +libexecutorch_llm_{ios,simulator}.a +libkernels_llm_{ios,simulator}.a +libkernels_optimized_{ios,simulator}.a +libkernels_quantized_{ios,simulator}.a +libkernels_torchao_{ios,simulator}.a +libthreadpool_{ios,simulator}.a +``` + +Keep the existing `libkleidiai_{ios,simulator}.a` — kleidiai is merged into `libexecutorch_llm_*.a` by upstream's framework definitions, but RNE's `ExecutorchLib.xcodeproj` still references the standalone libs explicitly. They're stable and don't need rebuilding. + +The non-executorch prebuilts (`libs/cpuinfo/libcpuinfo.a`, `libs/pthreadpool/{physical-arm64-release,simulator-arm64-debug}/libpthreadpool.a`) live in their existing directories and are not produced by the executorch fork build — they ship as-is from prior tarballs. + +Phonemis is now built from in-tree source (the `third-party/common/phonemis` git submodule, pinned via `.gitmodules` to ). Initialize it with `git submodule update --init --recursive` after cloning — the podspec and Android CMake will pick it up automatically when `enable_phonemizer` is true. + +**4. Build xcframeworks** + +```bash +# from RNE +rm -rf packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework +rm -rf packages/react-native-executorch/third-party/ios/CoreMLBackend.xcframework +rm -rf packages/react-native-executorch/third-party/ios/XnnpackBackend.xcframework +cd packages/react-native-executorch/third-party/ios/ExecutorchLib +./build.sh +``` + +The script drives Xcode to archive the Obj-C++ wrapper for device and simulator, then uses `xcodebuild -create-xcframework` to produce: + +- `output/ExecutorchLib.xcframework` — the high-level wrapper + ExecuTorch core + kernels + threadpool + MPS backend. Does **not** contain XNNPACK or CoreML code (those live in their own xcframeworks). +- `output/XnnpackBackend.xcframework` — repackaged from `third-party/ios/libs/executorch/libbackend_xnnpack_{ios,simulator}.a`. +- `output/CoreMLBackend.xcframework` — repackaged from `libbackend_coreml_{ios,simulator}.a`. + +Move each `.xcframework` from `output/` up one level into `third-party/ios/`, then delete `output/` and `build/`. + +CocoaPods constraint: inside an xcframework, the library file name must be identical across slices, which is why `build.sh` copies each slice into a temp directory and renames before calling `-create-xcframework`. Do not skip this step. + +### Android + +Use `scripts/build_android_library.sh` from the fork (with the `@ms/separate-backends` branch checked out). It already passes the right preset and flags. Just enable the two shared-backend builds we need: + +```bash +# from the executorch fork +export ANDROID_NDK=$HOME/Library/Android/sdk/ndk/27.1.12297006 +EXECUTORCH_BUILD_VULKAN=ON \ +EXECUTORCH_BUILD_VULKAN_BACKEND_SHARED=ON \ +EXECUTORCH_BUILD_XNNPACK_BACKEND_SHARED=ON \ +ANDROID_ABI=arm64-v8a ./scripts/build_android_library.sh # repeat with x86_64 +``` + +Outputs land in `cmake-out-android-/extension/android/`: + +- `libexecutorch_jni.so` → copy to `third-party/android/libs/executorch//libexecutorch.so` (note the rename). +- `libxnnpack_executorch_backend.so` → copy to the same directory under its own name. +- `libvulkan_executorch_backend.so` → copy to the same directory under its own name. + +Strip all three with `$ANDROID_NDK/toolchains/llvm/prebuilt/*/bin/llvm-strip` before committing. The headers under `third-party/include/` must match the fork commit that produced the binary — a mismatch shows up as runtime `dlopen` / symbol errors. + +### Packaging for a release + +For each `` tarball: + +```bash +tar -czf .tar.gz -C . +sha256sum .tar.gz > .tar.gz.sha256 # or shasum -a 256 +``` + +Staging-dir layout must mirror the destination (`download-libs.js` extracts with `tar -xzf` into `third-party/android/libs/` or `third-party/ios/` without any path stripping). So `core-android-arm64-v8a.tar.gz` contains a top-level `executorch/arm64-v8a/libexecutorch.so`, `cpuinfo/arm64-v8a/libcpuinfo.a`, etc. + +Upload every `.tar.gz` **and** its `.tar.gz.sha256` as release assets under the `v` tag on GitHub. Publishing the release (out of draft) makes them fetchable anonymously; until then, consumers need `GITHUB_TOKEN` with `repo:read`. + +### Iterating locally + +Drop built artifacts (plus `.sha256` files) into `packages/react-native-executorch/dist-artifacts/`, then run a static server and point the script at it: + +```bash +cd packages/react-native-executorch/dist-artifacts +python3 -m http.server 8080 & +RNET_BASE_URL=http://localhost:8080 yarn install +``` + +This skips GitHub entirely and re-extracts from the local tarballs — the same checksum verification still runs, so stale caches still get rejected. diff --git a/packages/react-native-executorch/android/CMakeLists.txt b/packages/react-native-executorch/android/CMakeLists.txt index 038335f7e2..44f70fe2dc 100644 --- a/packages/react-native-executorch/android/CMakeLists.txt +++ b/packages/react-native-executorch/android/CMakeLists.txt @@ -25,6 +25,12 @@ set(COMMON_THIRD_PARTY_DIR "${CMAKE_SOURCE_DIR}/../third-party/common") set(TOKENIZERS_DIR "${CMAKE_SOURCE_DIR}/../third-party/include/executorch/extension/llm/tokenizers/include") set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/../third-party/include") +# Optional feature flags — driven by user config in package.json, passed via gradle cmake arguments +option(RNE_ENABLE_OPENCV "Enable OpenCV-dependent computer vision features" ON) +option(RNE_ENABLE_PHONEMIZER "Enable Phonemizer-dependent TTS features" ON) +option(RNE_ENABLE_XNNPACK "Load the XNNPACK backend shared library" ON) +option(RNE_ENABLE_VULKAN "Load the Vulkan backend shared library" ON) + # Treat third-party headers as system headers to suppress deprecation warnings include_directories(SYSTEM "${INCLUDE_DIR}") include_directories(SYSTEM "${INCLUDE_DIR}/cpuinfo") diff --git a/packages/react-native-executorch/android/build.gradle b/packages/react-native-executorch/android/build.gradle index 5b1cfd2973..3dbae2efdb 100644 --- a/packages/react-native-executorch/android/build.gradle +++ b/packages/react-native-executorch/android/build.gradle @@ -1,5 +1,22 @@ import org.apache.tools.ant.taskdefs.condition.Os +// Read the generated build config written by the postinstall script. +// Falls back to enabling everything if the file doesn't exist (e.g. during CI +// when libs are pre-cached and the postinstall script skipped writing config). +def getRneBuildConfig() { + def configFile = new File("${project.projectDir}/../rne-build-config.json") + if (configFile.exists()) { + try { + return new groovy.json.JsonSlurper().parse(configFile) + } catch (e) { + logger.warn("[RnExecutorch] Failed to parse rne-build-config.json: ${e.message}. Defaulting to all features enabled.") + } + } + return [enableOpencv: true, enablePhonemizer: true, enableXnnpack: true, enableCoreml: true, enableVulkan: true] +} + +def rneBuildConfig = getRneBuildConfig() + buildscript { ext { agp_version = '8.4.2' @@ -122,7 +139,11 @@ android { "-DREACT_NATIVE_DIR=${toPlatformFileString(reactNativeRootDir.path)}", "-DBUILD_DIR=${project.buildDir}", "-DANDROID_TOOLCHAIN=clang", - "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON" + "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON", + "-DRNE_ENABLE_OPENCV=${rneBuildConfig.enableOpencv ? 'ON' : 'OFF'}", + "-DRNE_ENABLE_PHONEMIZER=${rneBuildConfig.enablePhonemizer ? 'ON' : 'OFF'}", + "-DRNE_ENABLE_XNNPACK=${rneBuildConfig.enableXnnpack ? 'ON' : 'OFF'}", + "-DRNE_ENABLE_VULKAN=${rneBuildConfig.enableVulkan ? 'ON' : 'OFF'}" } } } diff --git a/packages/react-native-executorch/android/libs/classes.jar b/packages/react-native-executorch/android/libs/classes.jar index be5ec2ee7f..6a50b05849 100644 Binary files a/packages/react-native-executorch/android/libs/classes.jar and b/packages/react-native-executorch/android/libs/classes.jar differ diff --git a/packages/react-native-executorch/android/src/main/cpp/CMakeLists.txt b/packages/react-native-executorch/android/src/main/cpp/CMakeLists.txt index dfbf74ae3e..c34533c147 100644 --- a/packages/react-native-executorch/android/src/main/cpp/CMakeLists.txt +++ b/packages/react-native-executorch/android/src/main/cpp/CMakeLists.txt @@ -1,12 +1,74 @@ cmake_minimum_required(VERSION 3.13) file(GLOB_RECURSE ANDROID_CPP_SOURCES CONFIGURE_DEPENDS "${ANDROID_CPP_DIR}/*.cpp") -file(GLOB_RECURSE COMMON_CPP_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/*.cpp") -file(GLOB_RECURSE COMMON_C_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/*.c") + +# --- Source separation --- +# Glob all common sources, then separate opencv-dependent and phonemizer-dependent +# files so they can be conditionally included based on feature flags. + +file(GLOB_RECURSE ALL_COMMON_CPP_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/*.cpp") +file(GLOB_RECURSE ALL_COMMON_C_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/*.c") + +# Exclude test sources unconditionally file(GLOB_RECURSE TEST_CPP_SOURCES "${COMMON_CPP_DIR}/rnexecutorch/tests/*.cpp") -list(REMOVE_ITEM COMMON_CPP_SOURCES ${TEST_CPP_SOURCES}) +list(REMOVE_ITEM ALL_COMMON_CPP_SOURCES ${TEST_CPP_SOURCES}) + +# OpenCV-dependent sources: CV models + frame utilities + image processing +file(GLOB_RECURSE OPENCV_CPP_SOURCES CONFIGURE_DEPENDS + "${COMMON_CPP_DIR}/rnexecutorch/models/classification/*.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/models/object_detection/*.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/models/semantic_segmentation/*.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/models/instance_segmentation/*.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/models/style_transfer/*.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/models/ocr/*.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/models/vertical_ocr/*.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/models/embeddings/image/*.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/models/text_to_image/*.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/models/VisionModel.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/data_processing/ImageProcessing.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/utils/FrameExtractor.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/utils/FrameProcessor.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/utils/FrameTransform.cpp" + "${COMMON_CPP_DIR}/rnexecutorch/utils/computer_vision/*.cpp" + "${COMMON_CPP_DIR}/runner/encoders/vision_encoder.cpp" + "${COMMON_CPP_DIR}/runner/multimodal_prefiller.cpp" + "${COMMON_CPP_DIR}/runner/multimodal_runner.cpp" +) -add_library(react-native-executorch SHARED ${ANDROID_CPP_SOURCES} ${COMMON_CPP_SOURCES} ${COMMON_C_SOURCES}) +# Phonemizer-dependent sources: Kokoro TTS (only user of phonemis) +file(GLOB_RECURSE PHONEMIZER_CPP_SOURCES CONFIGURE_DEPENDS + "${COMMON_CPP_DIR}/rnexecutorch/models/text_to_speech/*.cpp" +) + +# Core = everything minus optional sources +set(CORE_COMMON_CPP_SOURCES ${ALL_COMMON_CPP_SOURCES}) +list(REMOVE_ITEM CORE_COMMON_CPP_SOURCES ${OPENCV_CPP_SOURCES} ${PHONEMIZER_CPP_SOURCES}) + +# Build final source list +set(ENABLED_COMMON_SOURCES ${CORE_COMMON_CPP_SOURCES}) + +if(RNE_ENABLE_OPENCV) + list(APPEND ENABLED_COMMON_SOURCES ${OPENCV_CPP_SOURCES}) +endif() + +if(RNE_ENABLE_PHONEMIZER) + list(APPEND ENABLED_COMMON_SOURCES ${PHONEMIZER_CPP_SOURCES}) +endif() + +add_library(react-native-executorch SHARED + ${ANDROID_CPP_SOURCES} + ${ENABLED_COMMON_SOURCES} + ${ALL_COMMON_C_SOURCES} +) + +# Propagate feature flags as preprocessor defines so C++ code can guard includes +if(RNE_ENABLE_OPENCV) + target_compile_definitions(react-native-executorch PRIVATE RNE_ENABLE_OPENCV) +endif() + +if(RNE_ENABLE_PHONEMIZER) + target_compile_definitions(react-native-executorch PRIVATE RNE_ENABLE_PHONEMIZER) +endif() find_package(ReactAndroid REQUIRED CONFIG) find_package(fbjni REQUIRED CONFIG) @@ -34,66 +96,77 @@ set(RN_VERSION_LINK_LIBRARIES ReactAndroid::reactnative ) -# Dependencies: - -# ------- Executorch ------- +# ------- Executorch (always required) ------- add_library(executorch SHARED IMPORTED) set_target_properties(executorch PROPERTIES IMPORTED_LOCATION "${LIBS_DIR}/executorch/${ANDROID_ABI}/libexecutorch.so") +# Backends ship as separate .so files (libxnnpack_executorch_backend.so, +# libvulkan_executorch_backend.so). When enabled, CMake imports each .so and +# links libreact-native-executorch.so against it; Gradle packages the .so into +# the APK and the dynamic linker loads it whenever libreact-native-executorch.so +# loads. Each .so's load-time constructor registers its backend with +# libexecutorch.so's registry. if(ANDROID_ABI STREQUAL "arm64-v8a") target_compile_definitions(react-native-executorch PRIVATE ARCH_ARM64) +endif() - # ------- pthreadpool ------- - add_library(pthreadpool SHARED IMPORTED) - - set_target_properties(pthreadpool PROPERTIES - IMPORTED_LOCATION "${LIBS_DIR}/pthreadpool/${ANDROID_ABI}/libpthreadpool.so") +# pthreadpool and cpuinfo are statically linked into libexecutorch.so, +# no separate shared libraries needed. - # ------- cpuinfo ------- - add_library(cpuinfo SHARED IMPORTED) +# ------- XNNPACK backend (optional) ------- - set_target_properties(cpuinfo PROPERTIES - IMPORTED_LOCATION "${LIBS_DIR}/cpuinfo/${ANDROID_ABI}/libcpuinfo.so") - set(EXECUTORCH_LIBS - "pthreadpool" - "cpuinfo" - ) +if(RNE_ENABLE_XNNPACK) + add_library(xnnpack_executorch_backend SHARED IMPORTED) + set_target_properties(xnnpack_executorch_backend PROPERTIES + IMPORTED_LOCATION + "${LIBS_DIR}/executorch/${ANDROID_ABI}/libxnnpack_executorch_backend.so") endif() -# ------- OpenCV ------- +# ------- Vulkan backend (optional) ------- -set(OPENCV_LIBS - "${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_core.a" - "${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_features2d.a" - "${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_highgui.a" - "${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_imgproc.a" - "${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_photo.a" - "${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_video.a" -) +if(RNE_ENABLE_VULKAN) + add_library(vulkan_executorch_backend SHARED IMPORTED) + set_target_properties(vulkan_executorch_backend PROPERTIES + IMPORTED_LOCATION + "${LIBS_DIR}/executorch/${ANDROID_ABI}/libvulkan_executorch_backend.so") +endif() -if(ANDROID_ABI STREQUAL "arm64-v8a") - set(OPENCV_THIRD_PARTY_LIBS - "${LIBS_DIR}/opencv-third-party/${ANDROID_ABI}/libkleidicv_hal.a" - "${LIBS_DIR}/opencv-third-party/${ANDROID_ABI}/libkleidicv_thread.a" - "${LIBS_DIR}/opencv-third-party/${ANDROID_ABI}/libkleidicv.a" +# ------- OpenCV (optional) ------- + +if(RNE_ENABLE_OPENCV) + set(OPENCV_LINK_LIBS + "${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_core.a" + "${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_features2d.a" + "${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_highgui.a" + "${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_imgproc.a" + "${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_photo.a" + "${LIBS_DIR}/opencv/${ANDROID_ABI}/libopencv_video.a" ) -elseif(ANDROID_ABI STREQUAL "x86_64") - set(OPENCV_THIRD_PARTY_LIBS "") -endif() + if(ANDROID_ABI STREQUAL "arm64-v8a") + list(APPEND OPENCV_LINK_LIBS + "${LIBS_DIR}/opencv-third-party/${ANDROID_ABI}/libkleidicv_hal.a" + "${LIBS_DIR}/opencv-third-party/${ANDROID_ABI}/libkleidicv_thread.a" + "${LIBS_DIR}/opencv-third-party/${ANDROID_ABI}/libkleidicv.a" + ) + endif() +endif() -# ------- phonemis ------- +# ------- Phonemizer (optional) ------- +# Built from in-tree source (third-party/common/phonemis submodule) when enabled. -set(PHONEMIS_DIR "${COMMON_THIRD_PARTY_DIR}/phonemis") -add_subdirectory(${PHONEMIS_DIR} ${CMAKE_BINARY_DIR}/phonemis) +if(RNE_ENABLE_PHONEMIZER) + set(PHONEMIS_DIR "${COMMON_THIRD_PARTY_DIR}/phonemis") + add_subdirectory(${PHONEMIS_DIR} ${CMAKE_BINARY_DIR}/phonemis) -target_compile_definitions(phonemis PRIVATE ET_ON) # Phonemis uses ET_ON flag to detect available ExecuTorch build (NeuralPhonemizer) -target_include_directories(phonemis PRIVATE "${INCLUDE_DIR}") # ExecuTorch headers -target_include_directories(react-native-executorch PUBLIC "${PHONEMIS_DIR}/src") + target_compile_definitions(phonemis PRIVATE ET_ON) + target_include_directories(phonemis PRIVATE "${INCLUDE_DIR}") + target_include_directories(react-native-executorch PUBLIC "${PHONEMIS_DIR}/src") +endif() # -------------- @@ -103,10 +176,22 @@ target_link_libraries( react-native-executorch ${LINK_LIBRARIES} ${RN_VERSION_LINK_LIBRARIES} - ${OPENCV_LIBS} - ${OPENCV_THIRD_PARTY_LIBS} - phonemis + ${OPENCV_LINK_LIBS} executorch - ${EXECUTORCH_LIBS} z ) + +if(RNE_ENABLE_PHONEMIZER) + target_link_libraries(react-native-executorch phonemis) +endif() + +# Linking against backend SOs (when enabled) makes Gradle bundle them into the +# APK and instructs the dynamic linker to load them whenever +# libreact-native-executorch.so is loaded. Each .so's load-time constructor +# registers its backend with libexecutorch.so's backend registry. +if(RNE_ENABLE_XNNPACK) + target_link_libraries(react-native-executorch xnnpack_executorch_backend) +endif() +if(RNE_ENABLE_VULKAN) + target_link_libraries(react-native-executorch vulkan_executorch_backend) +endif() diff --git a/packages/react-native-executorch/android/src/main/java/com/swmansion/rnexecutorch/ETInstaller.kt b/packages/react-native-executorch/android/src/main/java/com/swmansion/rnexecutorch/ETInstaller.kt index acc43c0a9e..4dbb35676f 100644 --- a/packages/react-native-executorch/android/src/main/java/com/swmansion/rnexecutorch/ETInstaller.kt +++ b/packages/react-native-executorch/android/src/main/java/com/swmansion/rnexecutorch/ETInstaller.kt @@ -49,6 +49,10 @@ class ETInstaller( init { try { + // Each backend (XNNPACK, Vulkan) ships as its own .so. When an extra is + // enabled, libreact-native-executorch.so links against the matching .so, + // so the dynamic linker loads it automatically here and its load-time + // constructor registers the backend with libexecutorch.so's registry. System.loadLibrary("executorch") System.loadLibrary("react-native-executorch") val jsCallInvokerHolder = reactContext.jsCallInvokerHolder as CallInvokerHolderImpl diff --git a/packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.cpp b/packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.cpp index 53ee65a904..523b0bd8b3 100644 --- a/packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.cpp +++ b/packages/react-native-executorch/common/rnexecutorch/RnExecutorchInstaller.cpp @@ -2,24 +2,30 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + +#ifdef RNE_ENABLE_OPENCV #include #include -#include #include -#include #include #include #include -#include #include -#include #include #include -#include #include -#include -#include -#include +#endif + +#ifdef RNE_ENABLE_PHONEMIZER +#include +#endif #if defined(__ANDROID__) && defined(__aarch64__) #include @@ -42,6 +48,7 @@ void RnExecutorchInstaller::injectJSIBindings( jsiRuntime->global().setProperty(*jsiRuntime, "__rne_isEmulator", jsi::Value(isEmulator)); +#ifdef RNE_ENABLE_OPENCV jsiRuntime->global().setProperty( *jsiRuntime, "loadStyleTransfer", RnExecutorchInstaller::loadModel( @@ -79,6 +86,7 @@ void RnExecutorchInstaller::injectJSIBindings( *jsiRuntime, "loadPoseEstimation", RnExecutorchInstaller::loadModel( jsiRuntime, jsCallInvoker, "loadPoseEstimation")); +#endif // RNE_ENABLE_OPENCV jsiRuntime->global().setProperty( *jsiRuntime, "loadExecutorchModule", @@ -90,10 +98,12 @@ void RnExecutorchInstaller::injectJSIBindings( RnExecutorchInstaller::loadModel( jsiRuntime, jsCallInvoker, "loadTokenizerModule")); +#ifdef RNE_ENABLE_OPENCV jsiRuntime->global().setProperty( *jsiRuntime, "loadImageEmbeddings", RnExecutorchInstaller::loadModel( jsiRuntime, jsCallInvoker, "loadImageEmbeddings")); +#endif // RNE_ENABLE_OPENCV jsiRuntime->global().setProperty( *jsiRuntime, "loadTextEmbeddings", @@ -110,6 +120,7 @@ void RnExecutorchInstaller::injectJSIBindings( RnExecutorchInstaller::loadModel( jsiRuntime, jsCallInvoker, "loadPrivacyFilter")); +#ifdef RNE_ENABLE_OPENCV jsiRuntime->global().setProperty( *jsiRuntime, "loadOCR", RnExecutorchInstaller::loadModel( @@ -119,16 +130,19 @@ void RnExecutorchInstaller::injectJSIBindings( *jsiRuntime, "loadVerticalOCR", RnExecutorchInstaller::loadModel( jsiRuntime, jsCallInvoker, "loadVerticalOCR")); +#endif // RNE_ENABLE_OPENCV jsiRuntime->global().setProperty( *jsiRuntime, "loadSpeechToText", RnExecutorchInstaller::loadModel( jsiRuntime, jsCallInvoker, "loadSpeechToText")); +#ifdef RNE_ENABLE_PHONEMIZER jsiRuntime->global().setProperty( *jsiRuntime, "loadTextToSpeechKokoro", RnExecutorchInstaller::loadModel( jsiRuntime, jsCallInvoker, "loadTextToSpeechKokoro")); +#endif // RNE_ENABLE_PHONEMIZER jsiRuntime->global().setProperty( *jsiRuntime, "loadVAD", diff --git a/packages/react-native-executorch/common/rnexecutorch/host_objects/ModelHostObject.h b/packages/react-native-executorch/common/rnexecutorch/host_objects/ModelHostObject.h index cb8313598f..4dd4320fd4 100644 --- a/packages/react-native-executorch/common/rnexecutorch/host_objects/ModelHostObject.h +++ b/packages/react-native-executorch/common/rnexecutorch/host_objects/ModelHostObject.h @@ -22,7 +22,9 @@ #include #include #include +#ifdef RNE_ENABLE_PHONEMIZER #include +#endif #include #include #include @@ -219,6 +221,7 @@ template class ModelHostObject : public JsiHostObject { JSI_EXPORT_FUNCTION(ModelHostObject, unload, "unload")); } +#ifdef RNE_ENABLE_PHONEMIZER if constexpr (meta::SameAs) { addFunctions( JSI_EXPORT_FUNCTION(ModelHostObject, unload, "unload")); @@ -232,6 +235,7 @@ template class ModelHostObject : public JsiHostObject { ModelHostObject, synchronousHostFunction<&Model::streamInsert>, "streamInsert")); } +#endif if constexpr (meta::HasGenerateFromString) { addFunctions( diff --git a/packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.cpp b/packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.cpp index 7e0fa4b26e..b46eb7cadc 100644 --- a/packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.cpp +++ b/packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.cpp @@ -6,9 +6,11 @@ #include #include #include +#include +#ifdef RNE_ENABLE_OPENCV #include #include -#include +#endif namespace rnexecutorch::models::llm { namespace llm = ::executorch::extension::llm; @@ -22,10 +24,8 @@ LLM::LLM(const std::string &modelSource, const std::string &tokenizerSource, std::shared_ptr callInvoker) : BaseModel(modelSource, callInvoker, Module::LoadMode::Mmap) { - if (capabilities.empty()) { - runner_ = - std::make_unique(std::move(module_), tokenizerSource); - } else { +#ifdef RNE_ENABLE_OPENCV + if (!capabilities.empty()) { std::map> encoders; for (const auto &cap : capabilities) { if (cap == "vision") { @@ -35,7 +35,13 @@ LLM::LLM(const std::string &modelSource, const std::string &tokenizerSource, } runner_ = std::make_unique( std::move(module_), tokenizerSource, std::move(encoders)); + } else { +#endif + runner_ = + std::make_unique(std::move(module_), tokenizerSource); +#ifdef RNE_ENABLE_OPENCV } +#endif auto loadResult = runner_->load(); if (loadResult != Error::Ok) { diff --git a/packages/react-native-executorch/common/rnexecutorch/models/speech_to_text/SpeechToText.h b/packages/react-native-executorch/common/rnexecutorch/models/speech_to_text/SpeechToText.h index 9c084dcf6e..a1cb040472 100644 --- a/packages/react-native-executorch/common/rnexecutorch/models/speech_to_text/SpeechToText.h +++ b/packages/react-native-executorch/common/rnexecutorch/models/speech_to_text/SpeechToText.h @@ -43,8 +43,8 @@ class SpeechToText { // Stream void stream(std::shared_ptr callback, - std::string languageOption, bool verbose, - uint32_t timeout, bool useVAD, uint32_t vadDetectionMargin); + std::string languageOption, bool verbose, uint32_t timeout, + bool useVAD, uint32_t vadDetectionMargin); void streamStop(); void streamInsert(std::span waveform); diff --git a/packages/react-native-executorch/common/rnexecutorch/models/text_to_speech/kokoro/Params.h b/packages/react-native-executorch/common/rnexecutorch/models/text_to_speech/kokoro/Params.h index 5f4e7cfe2b..a30a7ba053 100644 --- a/packages/react-native-executorch/common/rnexecutorch/models/text_to_speech/kokoro/Params.h +++ b/packages/react-native-executorch/common/rnexecutorch/models/text_to_speech/kokoro/Params.h @@ -39,9 +39,16 @@ inline constexpr int32_t kStreamPause = 200; * (ms). */ inline const std::unordered_map kPauseValues = { - {U'.', 375}, {U'?', 500}, {U'!', 250}, {U';', 400}, {U'…', 600}, // Ellipsis - {U',', 130}, {U':', 250}, {U'-', 200}, {U'—', 250}, // Em Dash (slightly - // longer than hyphen) + {U'.', 375}, + {U'?', 500}, + {U'!', 250}, + {U';', 400}, + {U'…', 600}, // Ellipsis + {U',', 130}, + {U':', 250}, + {U'-', 200}, + {U'—', 250}, // Em Dash (slightly + // longer than hyphen) {U'|', 375}, // ASCII Pipe (treated as full stop) {U'।', 375}, // Hindi Purna Viram {U'॥', 500}, // Hindi Deergh Viram (typically longer than Purna Viram) diff --git a/packages/react-native-executorch/common/rnexecutorch/threads/GlobalThreadPool.h b/packages/react-native-executorch/common/rnexecutorch/threads/GlobalThreadPool.h index 50025eeeb7..ad955b29f4 100644 --- a/packages/react-native-executorch/common/rnexecutorch/threads/GlobalThreadPool.h +++ b/packages/react-native-executorch/common/rnexecutorch/threads/GlobalThreadPool.h @@ -4,7 +4,9 @@ #include #include #include +#ifdef RNE_ENABLE_OPENCV #include +#endif #include #include #include @@ -41,7 +43,9 @@ class GlobalThreadPool { config); // Disable OpenCV's internal threading to prevent it from overriding our // thread pool configuration, which would cause degraded performance +#ifdef RNE_ENABLE_OPENCV cv::setNumThreads(0); +#endif }); } diff --git a/packages/react-native-executorch/package.json b/packages/react-native-executorch/package.json index 2aceb63d1f..1e67c2fefa 100644 --- a/packages/react-native-executorch/package.json +++ b/packages/react-native-executorch/package.json @@ -18,8 +18,10 @@ "*.podspec", "third-party/include", "third-party", + "!third-party/android/libs", "!third-party/ios/ExecutorchLib", - "!third-party/ios/libs/executorch", + "!third-party/ios/ExecutorchLib.xcframework", + "!third-party/ios/libs", "!ios/*.xcodeproj", "!ios/build", "!android/build", @@ -39,7 +41,8 @@ "clean": "del-cli android/build example/android/build example/android/app/build example/ios/build lib", "prepare": "bob build", "prepack": "cp ../../README.md ./README.md", - "postpack": "rm ./README.md" + "postpack": "rm ./README.md", + "postinstall": "node scripts/download-libs.js" }, "keywords": [ "text-to-speech", diff --git a/packages/react-native-executorch/react-native-executorch.podspec b/packages/react-native-executorch/react-native-executorch.podspec index 849759243f..6643fcaf0c 100644 --- a/packages/react-native-executorch/react-native-executorch.podspec +++ b/packages/react-native-executorch/react-native-executorch.podspec @@ -2,6 +2,23 @@ require "json" package = JSON.parse(File.read(File.join(__dir__, "package.json"))) +# Read the build config written by the postinstall script. +# Falls back to all features enabled if the file doesn't exist. +rne_build_config_path = File.join(__dir__, "rne-build-config.json") +if File.exist?(rne_build_config_path) + require "json" + rne_build_config = JSON.parse(File.read(rne_build_config_path)) + enable_opencv = rne_build_config["enableOpencv"] != false + enable_phonemizer = rne_build_config["enablePhonemizer"] != false + enable_xnnpack = rne_build_config["enableXnnpack"] != false + enable_coreml = rne_build_config["enableCoreml"] != false +else + enable_opencv = true + enable_phonemizer = true + enable_xnnpack = true + enable_coreml = true +end + Pod::Spec.new do |s| s.name = "react-native-executorch" s.version = package["version"] @@ -13,55 +30,42 @@ Pod::Spec.new do |s| s.platforms = { :ios => '17.0' } s.source = { :git => "https://github.com/software-mansion/react-native-executorch.git", :tag => "#{s.version}" } - pthreadpool_binaries_path = File.expand_path('$(PODS_TARGET_SRCROOT)/third-party/ios/libs/pthreadpool', __dir__) - cpuinfo_binaries_path = File.expand_path('$(PODS_TARGET_SRCROOT)/third-party/ios/libs/cpuinfo', __dir__) + cpuinfo_binaries_path = File.expand_path('$(PODS_TARGET_SRCROOT)/third-party/ios/libs/cpuinfo', __dir__) - s.user_target_xcconfig = { - "HEADER_SEARCH_PATHS" => - '"$(PODS_TARGET_SRCROOT)/third-party/include" '+ - '"$(PODS_TARGET_SRCROOT)/third-party/include/cpuinfo" '+ - '"$(PODS_TARGET_SRCROOT)/third-party/include/pthreadpool"', - - "OTHER_LDFLAGS[sdk=iphoneos*]" => [ - '$(inherited)', - "\"#{pthreadpool_binaries_path}/physical-arm64-release/libpthreadpool.a\"", - "\"#{cpuinfo_binaries_path}/libcpuinfo.a\"", - - ].join(' '), - - "OTHER_LDFLAGS[sdk=iphonesimulator*]" => [ - '$(inherited)', - "\"#{pthreadpool_binaries_path}/simulator-arm64-debug/libpthreadpool.a\"", - "\"#{cpuinfo_binaries_path}/libcpuinfo.a\"", - ].join(' '), - - 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'x86_64', - } + # --- Core sources (always compiled) --- + opencv_source_dirs = [ + "common/rnexecutorch/models/classification", + "common/rnexecutorch/models/object_detection", + "common/rnexecutorch/models/semantic_segmentation", + "common/rnexecutorch/models/instance_segmentation", + "common/rnexecutorch/models/style_transfer", + "common/rnexecutorch/models/ocr", + "common/rnexecutorch/models/vertical_ocr", + "common/rnexecutorch/models/embeddings/image", + "common/rnexecutorch/models/text_to_image", + "common/rnexecutorch/utils/computer_vision", + ] + opencv_source_files = opencv_source_dirs.map { |d| "#{d}/**/*.{cpp,c,h,hpp}" } + opencv_source_files += [ + "common/rnexecutorch/models/VisionModel.{cpp,h}", + "common/rnexecutorch/data_processing/ImageProcessing.{cpp,h}", + "common/rnexecutorch/utils/FrameExtractor.{cpp,h}", + "common/rnexecutorch/utils/FrameProcessor.{cpp,h}", + "common/rnexecutorch/utils/FrameTransform.{cpp,h}", + ] - s.pod_target_xcconfig = { - "USE_HEADERMAP" => "YES", - "HEADER_SEARCH_PATHS" => - '"$(PODS_TARGET_SRCROOT)/ios" '+ - '"$(PODS_TARGET_SRCROOT)/third-party/include/executorch/extension/llm/tokenizers/include" '+ - '"$(PODS_TARGET_SRCROOT)/third-party/include" '+ - '"$(PODS_TARGET_SRCROOT)/third-party/include/cpuinfo" '+ - '"$(PODS_TARGET_SRCROOT)/third-party/include/pthreadpool" '+ - '"$(PODS_TARGET_SRCROOT)/common" ' + - '"$(PODS_TARGET_SRCROOT)/third-party/common/phonemis/src" ', - "GCC_PREPROCESSOR_DEFINITIONS" => '$(inherited) ET_ON=1', - "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", - 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'x86_64', - } + phonemizer_source_files = [ + "common/rnexecutorch/models/text_to_speech/**/*.{cpp,c,h,hpp}", + ] - s.source_files = [ + source_files = [ "ios/**/*.{m,mm,h}", "common/**/*.{cpp,c,h,hpp}", - "third-party/common/phonemis/src/**/*.{cpp,hpp,h}", ] + source_files << "third-party/common/phonemis/src/**/*.{cpp,hpp,h}" if enable_phonemizer + s.source_files = source_files - s.libraries = "z" - s.ios.vendored_frameworks = "third-party/ios/ExecutorchLib.xcframework" # Exclude file with tests to not introduce gtest dependency. # Do not include the headers from common/rnexecutorch/jsi/ as source files. # Xcode/Cocoapods leaks them to other pods that an app also depends on, so if @@ -69,16 +73,96 @@ Pod::Spec.new do |s| # #include "Header.h" we get a conflict. Here, headers in jsi/ collide with # react-native-skia. The headers are preserved by preserve_paths and # then made available by HEADER_SEARCH_PATHS. - s.exclude_files = [ + exclude_files = [ "common/rnexecutorch/tests/**/*", "common/rnexecutorch/jsi/*.{h,hpp}", - "third-party/common/phonemis/src/phonemis/main.cpp" # Exclude the phonemis runner ] + exclude_files += opencv_source_files unless enable_opencv + exclude_files += phonemizer_source_files unless enable_phonemizer + # phonemis ships a CLI runner that defines its own `main()`; exclude when compiling into the pod. + exclude_files << "third-party/common/phonemis/src/phonemis/main.cpp" if enable_phonemizer + s.exclude_files = exclude_files + + # --- Preprocessor flags --- + extra_compiler_flags = [] + extra_compiler_flags << "-DRNE_ENABLE_OPENCV" if enable_opencv + extra_compiler_flags << "-DRNE_ENABLE_PHONEMIZER" if enable_phonemizer + extra_compiler_flags << "-DRNE_ENABLE_XNNPACK" if enable_xnnpack + extra_compiler_flags << "-DRNE_ENABLE_COREML" if enable_coreml + # ET_ON tells phonemis to compile the NeuralPhonemizer path against ExecuTorch. + extra_compiler_flags << "-DET_ON=1" if enable_phonemizer + + # --- Link flags --- + physical_ldflags = [ + '$(inherited)', + "\"#{pthreadpool_binaries_path}/physical-arm64-release/libpthreadpool.a\"", + "\"#{cpuinfo_binaries_path}/libcpuinfo.a\"", + ] + simulator_ldflags = [ + '$(inherited)', + "\"#{pthreadpool_binaries_path}/simulator-arm64-debug/libpthreadpool.a\"", + "\"#{cpuinfo_binaries_path}/libcpuinfo.a\"", + ] + + xnnpack_xcframework_path = File.expand_path('$(PODS_TARGET_SRCROOT)/third-party/ios/XnnpackBackend.xcframework', __dir__) + coreml_xcframework_path = File.expand_path('$(PODS_TARGET_SRCROOT)/third-party/ios/CoreMLBackend.xcframework', __dir__) + + if enable_xnnpack + physical_ldflags << "-force_load \"#{xnnpack_xcframework_path}/ios-arm64/libXnnpackBackend.a\"" + simulator_ldflags << "-force_load \"#{xnnpack_xcframework_path}/ios-arm64-simulator/libXnnpackBackend.a\"" + end + + if enable_coreml + physical_ldflags << "-force_load \"#{coreml_xcframework_path}/ios-arm64/libCoreMLBackend.a\"" + simulator_ldflags << "-force_load \"#{coreml_xcframework_path}/ios-arm64-simulator/libCoreMLBackend.a\"" + end + + s.user_target_xcconfig = { + "HEADER_SEARCH_PATHS" => + '"$(PODS_TARGET_SRCROOT)/third-party/include" '+ + '"$(PODS_TARGET_SRCROOT)/third-party/include/cpuinfo" '+ + '"$(PODS_TARGET_SRCROOT)/third-party/include/pthreadpool"', + "OTHER_LDFLAGS[sdk=iphoneos*]" => physical_ldflags.join(' '), + "OTHER_LDFLAGS[sdk=iphonesimulator*]" => simulator_ldflags.join(' '), + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'x86_64', + } + + pod_header_search_paths = + '"$(PODS_TARGET_SRCROOT)/ios" ' + + '"$(PODS_TARGET_SRCROOT)/third-party/include/executorch/extension/llm/tokenizers/include" ' + + '"$(PODS_TARGET_SRCROOT)/third-party/include" ' + + '"$(PODS_TARGET_SRCROOT)/third-party/include/cpuinfo" ' + + '"$(PODS_TARGET_SRCROOT)/third-party/include/pthreadpool" ' + + '"$(PODS_TARGET_SRCROOT)/common" ' + pod_header_search_paths += '"$(PODS_TARGET_SRCROOT)/third-party/common/phonemis/src" ' if enable_phonemizer + + s.pod_target_xcconfig = { + "USE_HEADERMAP" => "YES", + "HEADER_SEARCH_PATHS" => pod_header_search_paths, + "CLANG_CXX_LANGUAGE_STANDARD" => "c++20", + "OTHER_CPLUSPLUSFLAGS" => extra_compiler_flags.join(' '), + 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'x86_64', + } + + libs = ["z"] + libs << "sqlite3" if enable_coreml + s.libraries = libs + + system_frameworks = ["Accelerate"] + system_frameworks << "CoreML" if enable_coreml + s.frameworks = system_frameworks + + # Backend xcframeworks are linked via force_load in OTHER_LDFLAGS (needed to + # preserve __attribute__((constructor)) registrations). Only ExecutorchLib goes + # in vendored_frameworks to avoid duplicate symbol errors. + s.ios.vendored_frameworks = ["third-party/ios/ExecutorchLib.xcframework"] + + s.header_mappings_dir = "common/rnexecutorch" s.header_dir = "rnexecutorch" s.preserve_paths = "common/rnexecutorch/jsi/*.{h,hpp}" - s.dependency "opencv-rne", "~> 4.11.0" + s.dependency "opencv-rne", "~> 4.11.0" if enable_opencv install_modules_dependencies(s) end diff --git a/packages/react-native-executorch/scripts/download-libs.js b/packages/react-native-executorch/scripts/download-libs.js new file mode 100644 index 0000000000..bb00d5cdde --- /dev/null +++ b/packages/react-native-executorch/scripts/download-libs.js @@ -0,0 +1,426 @@ +/** + * On-demand native library downloader + * + * Runs at postinstall time. Downloads prebuilt native artifacts from GitHub Releases + * and extracts them into third-party/ so the existing CMakeLists.txt / podspec + * can find them at build time without any other changes. + * + * Artifact layout on GitHub Releases (per version tag, e.g. v0.9.0): + * + * core-android-arm64-v8a.tar.gz -- executorch, pthreadpool, cpuinfo for arm64 + * core-android-x86_64.tar.gz -- executorch for x86_64 + * core-ios.tar.gz -- ExecutorchLib.xcframework (without xnnpack/coreml) + * opencv-android-arm64-v8a.tar.gz -- OpenCV for arm64 + * opencv-android-x86_64.tar.gz -- OpenCV for x86_64 + * opencv-ios.tar.gz -- OpenCV xcframework + * xnnpack-android-arm64-v8a.tar.gz -- libxnnpack_executorch_backend.so (Android) + * xnnpack-android-x86_64.tar.gz + * xnnpack-ios.tar.gz -- XnnpackBackend.xcframework (iOS) + * vulkan-android-arm64-v8a.tar.gz -- libvulkan_executorch_backend.so (Android only) + * vulkan-android-x86_64.tar.gz + * coreml-ios.tar.gz -- CoreMLBackend.xcframework (iOS only) + * + * Each tarball extracts into third-party/android/libs/ or third-party/ios/ + * preserving the existing directory structure so CMakeLists/podspec need no changes. + * + * User configuration (in the app's package.json) — three optional arrays, all merged into a single set: + * "react-native-executorch": { + * "backends": ["xnnpack", "coreml", "vulkan"], + * "libs": ["opencv", "phonemizer"], + * "features": ["llm", "textToSpeech", "objectDetection"] + * } + * + * `features` is sugar — each one expands to a set of backends + libs via FEATURE_MAP below. + * If no `react-native-executorch` block is present, every backend and lib defaults to ON. + * + * Recognized values: + * backends: xnnpack, coreml (iOS), vulkan (Android) + * libs: opencv, phonemizer + * features: llm, multimodalLLM, speechToText, textToSpeech, vad, privacyFilter, + * textEmbeddings, imageEmbeddings, + * classification, objectDetection, semanticSegmentation, instanceSegmentation, + * ocr, verticalOCR, poseEstimation, styleTransfer, textToImage, segmentAnything + * + * Platform applicability: + * opencv Android + iOS — downloaded artifact + * phonemizer Android + iOS — built from in-tree source at third-party/common/phonemis (git submodule); + * this toggle only gates compilation (RNE_ENABLE_PHONEMIZER), no download. + * xnnpack Android (libxnnpack_executorch_backend.so) + iOS (XnnpackBackend.xcframework) + * coreml iOS only — toggles CoreMLBackend.xcframework. + * vulkan Android only — toggles libvulkan_executorch_backend.so. + * + * Environment variables: + * RNET_SKIP_DOWNLOAD=1 -- skip download entirely (for CI with pre-cached libs) + * RNET_LIBS_CACHE_DIR=/path -- use custom cache dir instead of default + * RNET_TARGET=android-arm64 -- force specific target (skip auto-detection) + * RNET_BASE_URL=http://localhost:8080 -- override base URL (useful for local testing: + * cd dist-artifacts && python3 -m http.server 8080) + * GITHUB_TOKEN=ghp_xxx -- GitHub token for accessing draft releases + */ + +'use strict'; + +const https = require('https'); +const http = require('http'); +const fs = require('fs'); +const path = require('path'); +const { execSync } = require('child_process'); + +// ---- Config ---------------------------------------------------------------- + +const PACKAGE_VERSION = require('../package.json').version; +const GITHUB_REPO = 'software-mansion/react-native-executorch'; +const BASE_URL = + process.env.RNET_BASE_URL || + `https://github.com/${GITHUB_REPO}/releases/download/v${PACKAGE_VERSION}`; + +const PACKAGE_ROOT = path.resolve(__dirname, '..'); +const THIRD_PARTY_DIR = path.join(PACKAGE_ROOT, 'third-party'); + +const DEFAULT_CACHE_DIR = path.join( + require('os').homedir(), + '.cache', + 'react-native-executorch', + PACKAGE_VERSION +); +const CACHE_DIR = process.env.RNET_LIBS_CACHE_DIR || DEFAULT_CACHE_DIR; + +// ---- User config ----------------------------------------------------------- + +const ALL_BACKENDS = ['xnnpack', 'coreml', 'vulkan']; +const ALL_LIBS = ['opencv', 'phonemizer']; + +// features -> { backends, libs } +// Backend lists are the union of what at least one model in that family ships +// today (per src/constants/modelRegistry.ts). When a new variant lands for a +// model that adds e.g. coreml or vulkan support, bump the family here. +const FEATURE_MAP = { + // Text-only LLMs ship xnnpack only. + llm: { backends: ['xnnpack'], libs: [] }, + // Multimodal LLMs add vulkan (Gemma-3-multimodal ships a Vulkan export). + multimodalLLM: { backends: ['xnnpack', 'vulkan'], libs: ['opencv'] }, + // Privacy filter classifiers — xnnpack only. + privacyFilter: { backends: ['xnnpack'], libs: [] }, + // Whisper ships xnnpack + coreml. + speechToText: { backends: ['xnnpack', 'coreml'], libs: [] }, + // Kokoro ships xnnpack only. + textToSpeech: { backends: ['xnnpack'], libs: ['phonemizer'] }, + // FSMN VAD — xnnpack only. + vad: { backends: ['xnnpack'], libs: [] }, + textEmbeddings: { backends: ['xnnpack'], libs: [] }, + imageEmbeddings: { backends: ['xnnpack'], libs: ['opencv'] }, + // EfficientNet ships xnnpack + coreml. + classification: { backends: ['xnnpack', 'coreml'], libs: ['opencv'] }, + // YOLO is xnnpack-only, ssdlite/rf_detr add coreml → union. + objectDetection: { backends: ['xnnpack', 'coreml'], libs: ['opencv'] }, + // YOLO-pose only. + poseEstimation: { backends: ['xnnpack'], libs: ['opencv'] }, + // DeepLab/FCN/LR-ASPP/selfie — xnnpack only. + semanticSegmentation: { backends: ['xnnpack'], libs: ['opencv'] }, + // YOLO-seg xnnpack-only, rf_detr-seg/fastsam add coreml → union. + instanceSegmentation: { backends: ['xnnpack', 'coreml'], libs: ['opencv'] }, + // CRAFT + CRNN — xnnpack only. + ocr: { backends: ['xnnpack'], libs: ['opencv'] }, + verticalOCR: { backends: ['xnnpack'], libs: ['opencv'] }, + // All style-transfer presets ship xnnpack + coreml. + styleTransfer: { backends: ['xnnpack', 'coreml'], libs: ['opencv'] }, + // BK-SDM — xnnpack only. + textToImage: { backends: ['xnnpack'], libs: ['opencv'] }, + // FastSAM ships xnnpack + coreml. + segmentAnything: { backends: ['xnnpack', 'coreml'], libs: ['opencv'] }, +}; + +function readUserConfig() { + const allOn = () => ({ backends: [...ALL_BACKENDS], libs: [...ALL_LIBS] }); + + // npm/yarn set INIT_CWD to the directory where install was invoked (project root) + const projectRoot = + process.env.INIT_CWD || process.env.npm_config_local_prefix; + if (!projectRoot) { + console.warn( + '[react-native-executorch] Could not determine project root, enabling all backends + libs.' + ); + return allOn(); + } + + let rneConfig; + try { + const userPackageJson = JSON.parse( + fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8') + ); + rneConfig = userPackageJson['react-native-executorch']; + } catch { + console.warn( + '[react-native-executorch] Could not read app package.json, enabling all backends + libs.' + ); + return allOn(); + } + + if (rneConfig === undefined) return allOn(); + + if (rneConfig.extras !== undefined) { + throw new Error( + '[react-native-executorch] The legacy `extras` field is no longer supported. ' + + 'Use `backends`, `libs`, and/or `features` instead.' + ); + } + + const backends = new Set(rneConfig.backends ?? []); + const libs = new Set(rneConfig.libs ?? []); + + for (const feature of rneConfig.features ?? []) { + const expansion = FEATURE_MAP[feature]; + if (!expansion) { + const known = Object.keys(FEATURE_MAP).join(', '); + throw new Error( + `[react-native-executorch] Unknown feature "${feature}". Known features: ${known}.` + ); + } + expansion.backends.forEach((b) => backends.add(b)); + expansion.libs.forEach((l) => libs.add(l)); + } + + for (const b of backends) { + if (!ALL_BACKENDS.includes(b)) { + throw new Error( + `[react-native-executorch] Unknown backend "${b}". Known: ${ALL_BACKENDS.join(', ')}.` + ); + } + } + for (const l of libs) { + if (!ALL_LIBS.includes(l)) { + throw new Error( + `[react-native-executorch] Unknown lib "${l}". Known: ${ALL_LIBS.join(', ')}.` + ); + } + } + + return { backends: [...backends], libs: [...libs] }; +} + +function writeBuildConfig({ backends, libs }) { + const config = { + enableOpencv: libs.includes('opencv'), + enablePhonemizer: libs.includes('phonemizer'), + enableXnnpack: backends.includes('xnnpack'), + enableCoreml: backends.includes('coreml'), + enableVulkan: backends.includes('vulkan'), + }; + fs.writeFileSync( + path.join(PACKAGE_ROOT, 'rne-build-config.json'), + JSON.stringify(config, null, 2) + ); + return config; +} + +// Warn when a backend is opted in but the build targets only the platform where +// it has no effect (coreml=iOS-only, vulkan=Android-only). Surfacing it at install +// time is friendlier than the user wondering why an opt-out had no effect. +function warnAboutPlatformAsymmetry({ backends }, targets) { + const hasAndroid = targets.some((t) => t.startsWith('android')); + const hasIos = targets.includes('ios'); + if (hasAndroid && !hasIos && backends.includes('coreml')) { + console.warn( + '[react-native-executorch] coreml is enabled but the build targets only Android; CoreML is iOS-only and the flag has no effect here.' + ); + } + if (hasIos && !hasAndroid && backends.includes('vulkan')) { + console.warn( + '[react-native-executorch] vulkan is enabled but the build targets only iOS; the Vulkan backend is Android-only and the flag has no effect here.' + ); + } +} + +// ---- Target detection ------------------------------------------------------ + +function detectTargets() { + if (process.env.RNET_TARGET) { + return [process.env.RNET_TARGET]; + } + + const targets = []; + if (process.platform === 'darwin') { + targets.push('ios'); + } + targets.push('android-arm64-v8a'); + if (!process.env.RNET_NO_X86_64) { + targets.push('android-x86_64'); + } + return targets; +} + +// ---- Artifact metadata ----------------------------------------------------- + +// Core artifacts are always downloaded; optional ones only if the backend / lib is enabled. +function getArtifacts(targets, { backends, libs }) { + const artifacts = []; + + for (const target of targets) { + const destDir = target.startsWith('android') + ? path.join(THIRD_PARTY_DIR, 'android', 'libs') + : path.join(THIRD_PARTY_DIR, 'ios'); + + // Core is always needed + artifacts.push(makeArtifact(`core-${target}`, destDir)); + + // iOS OpenCV is provided via CocoaPods (opencv-rne dependency), not a tarball + if (libs.includes('opencv') && target !== 'ios') { + artifacts.push(makeArtifact(`opencv-${target}`, destDir)); + } + + // Phonemizer is built from in-tree source (third-party/common/phonemis submodule); + // no artifact download required. + + if (backends.includes('xnnpack')) { + artifacts.push(makeArtifact(`xnnpack-${target}`, destDir)); + } + + // CoreML is iOS only + if (backends.includes('coreml') && target === 'ios') { + artifacts.push(makeArtifact(`coreml-${target}`, destDir)); + } + + // Vulkan is Android only + if (backends.includes('vulkan') && target.startsWith('android')) { + artifacts.push(makeArtifact(`vulkan-${target}`, destDir)); + } + } + + return artifacts; +} + +function makeArtifact(name, destDir) { + return { + name, + url: `${BASE_URL}/${name}.tar.gz`, + checksumUrl: `${BASE_URL}/${name}.tar.gz.sha256`, + destDir, + cacheFile: path.join(CACHE_DIR, `${name}.tar.gz`), + cacheChecksumFile: path.join(CACHE_DIR, `${name}.tar.gz.sha256`), + }; +} + +// ---- Helpers --------------------------------------------------------------- + +function ensureDir(dir) { + fs.mkdirSync(dir, { recursive: true }); +} + +function download(url, dest) { + return new Promise((resolve, reject) => { + const file = fs.createWriteStream(dest); + const get = (url) => { + const client = url.startsWith('http://') ? http : https; + const headers = {}; + if (process.env.GITHUB_TOKEN && !url.startsWith('http://')) { + headers['Authorization'] = `Bearer ${process.env.GITHUB_TOKEN}`; + } + client.get(url, { headers }, (res) => { + if (res.statusCode === 301 || res.statusCode === 302) { + return get(res.headers.location); + } + if (res.statusCode !== 200) { + return reject(new Error(`HTTP ${res.statusCode} for ${url}`)); + } + res.pipe(file); + file.on('finish', () => file.close(resolve)); + }); + }; + get(url); + file.on('error', (err) => { + fs.unlinkSync(dest); + reject(err); + }); + }); +} + +function sha256(filePath) { + const result = execSync( + `sha256sum "${filePath}" || shasum -a 256 "${filePath}"` + ); + return result.toString().split(' ')[0].trim(); +} + +function isCacheValid(artifact) { + if (!fs.existsSync(artifact.cacheFile)) return false; + if (!fs.existsSync(artifact.cacheChecksumFile)) return false; + const expectedChecksum = fs + .readFileSync(artifact.cacheChecksumFile, 'utf8') + .trim(); + const actualChecksum = sha256(artifact.cacheFile); + return expectedChecksum === actualChecksum; +} + +function extract(tarball, destDir) { + ensureDir(destDir); + execSync(`tar -xzf "${tarball}" -C "${destDir}"`); +} + +// ---- Main ------------------------------------------------------------------ + +async function main() { + if (process.env.RNET_SKIP_DOWNLOAD) { + console.log( + '[react-native-executorch] Skipping native lib download (RNET_SKIP_DOWNLOAD set)' + ); + // Still write build config so the native build knows what's enabled + const config = readUserConfig(); + writeBuildConfig(config); + return; + } + + const config = readUserConfig(); + const buildConfig = writeBuildConfig(config); + console.log( + `[react-native-executorch] Backends: [${config.backends.join(', ') || '—'}]; Libs: [${config.libs.join(', ') || '—'}]` + ); + console.log( + `[react-native-executorch] Build flags: opencv=${buildConfig.enableOpencv}, phonemizer=${buildConfig.enablePhonemizer}, xnnpack=${buildConfig.enableXnnpack}, coreml=${buildConfig.enableCoreml}, vulkan=${buildConfig.enableVulkan}` + ); + + const targets = detectTargets(); + warnAboutPlatformAsymmetry(config, targets); + const artifacts = getArtifacts(targets, config); + + ensureDir(CACHE_DIR); + + for (const artifact of artifacts) { + console.log(`[react-native-executorch] Preparing ${artifact.name}...`); + + if (isCacheValid(artifact)) { + console.log(` ✓ Cache hit, skipping download`); + } else { + console.log(` ↓ Downloading ${artifact.url}`); + await download(artifact.checksumUrl, artifact.cacheChecksumFile); + await download(artifact.url, artifact.cacheFile); + + const expectedChecksum = fs + .readFileSync(artifact.cacheChecksumFile, 'utf8') + .trim(); + const actualChecksum = sha256(artifact.cacheFile); + if (expectedChecksum !== actualChecksum) { + throw new Error( + `Checksum mismatch for ${artifact.name}: expected ${expectedChecksum}, got ${actualChecksum}` + ); + } + console.log(` ✓ Downloaded and verified`); + } + + console.log(` ↓ Extracting to ${artifact.destDir}`); + extract(artifact.cacheFile, artifact.destDir); + console.log(` ✓ Done`); + } + + console.log('[react-native-executorch] Native libs ready.'); +} + +main().catch((err) => { + console.error( + '[react-native-executorch] Failed to download native libs:', + err.message + ); + console.error( + ' You can set RNET_SKIP_DOWNLOAD=1 to skip and provide libs manually.' + ); + process.exit(1); +}); diff --git a/packages/react-native-executorch/scripts/package-release-artifacts.sh b/packages/react-native-executorch/scripts/package-release-artifacts.sh new file mode 100755 index 0000000000..f4b7d214d8 --- /dev/null +++ b/packages/react-native-executorch/scripts/package-release-artifacts.sh @@ -0,0 +1,201 @@ +#!/usr/bin/env bash +# package-release-artifacts.sh +# +# Packages the currently committed native libs into release artifact tarballs +# ready to be uploaded to GitHub Releases. +# +# Run from the package root (packages/react-native-executorch/): +# ./scripts/package-release-artifacts.sh +# +# Output: dist-artifacts/ +# core-android-arm64-v8a.tar.gz + .sha256 +# core-android-x86_64.tar.gz + .sha256 +# opencv-android-arm64-v8a.tar.gz + .sha256 +# opencv-android-x86_64.tar.gz + .sha256 +# xnnpack-android-arm64-v8a.tar.gz + .sha256 +# xnnpack-android-x86_64.tar.gz + .sha256 +# vulkan-android-arm64-v8a.tar.gz + .sha256 +# vulkan-android-x86_64.tar.gz + .sha256 +# core-ios.tar.gz + .sha256 +# xnnpack-ios.tar.gz + .sha256 +# coreml-ios.tar.gz + .sha256 +# +# Note: phonemizer ships as in-tree source (third-party/common/phonemis submodule), +# not as a tarball. +# +# Note: iOS OpenCV is provided via CocoaPods (opencv-rne), not a tarball. +# +# Testing the download flow +# ------------------------- +# Option A — local HTTP server (no GitHub needed): +# cd dist-artifacts && python3 -m http.server 8080 +# RNET_BASE_URL=http://localhost:8080 INIT_CWD= node scripts/download-libs.js +# +# Option B — GitHub pre-release: +# gh release create v0.9.0-libs-test --prerelease --title "libs test" \ +# --notes "Test release, will be deleted." \ +# --repo software-mansion/react-native-executorch +# gh release upload v0.9.0-libs-test dist-artifacts/* \ +# --repo software-mansion/react-native-executorch +# RNET_BASE_URL=https://github.com/software-mansion/react-native-executorch/releases/download/v0.9.0-libs-test \ +# INIT_CWD= node scripts/download-libs.js +# # cleanup: +# gh release delete v0.9.0-libs-test --repo software-mansion/react-native-executorch --yes + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PACKAGE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +ANDROID_LIBS="$PACKAGE_ROOT/third-party/android/libs" +IOS_DIR="$PACKAGE_ROOT/third-party/ios" +OUT="$PACKAGE_ROOT/dist-artifacts" + +VERSION=$(node -p "require('$PACKAGE_ROOT/package.json').version") + +echo "Packaging release artifacts for v$VERSION" +mkdir -p "$OUT" + +# ---- Helpers ---------------------------------------------------------------- + +package() { + local name=$1 + local src_dir=$2 + local out_file="$OUT/$name.tar.gz" + + echo " → $name" + + if [ ! -d "$src_dir" ]; then + echo " ✗ Source directory not found: $src_dir" >&2 + exit 1 + fi + + tar -czf "$out_file" -C "$src_dir" . + shasum -a 256 "$out_file" | awk '{print $1}' > "$out_file.sha256" + echo " ✓ $(du -sh "$out_file" | cut -f1)" +} + +# Packages a single file into a tarball, placing it at the given relative path. +# package_file +package_file() { + local name=$1 + local rel_path=$2 # directory path inside the tarball + local src_file=$3 # full path to the source file + local out_file="$OUT/$name.tar.gz" + local tmp + tmp=$(mktemp -d) + + echo " → $name" + + if [ ! -f "$src_file" ]; then + echo " ✗ Source file not found: $src_file" >&2 + rm -rf "$tmp" + exit 1 + fi + + mkdir -p "$tmp/$rel_path" + cp "$src_file" "$tmp/$rel_path/" + + tar -czf "$out_file" -C "$tmp" . + shasum -a 256 "$out_file" | awk '{print $1}' > "$out_file.sha256" + echo " ✓ $(du -sh "$out_file" | cut -f1)" + rm -rf "$tmp" +} + +# Packages multiple source directories into a single tarball by staging them +# into a temp directory first, preserving relative paths. +package_merged() { + local name=$1 + shift + local out_file="$OUT/$name.tar.gz" + local tmp + tmp=$(mktemp -d) + + echo " → $name" + + while [[ $# -gt 0 ]]; do + local rel_path=$1 # relative path inside the tarball + local src=$2 # source directory to copy from + shift 2 + + if [ ! -d "$src" ]; then + echo " ✗ Source directory not found: $src" >&2 + rm -rf "$tmp" + exit 1 + fi + + mkdir -p "$tmp/$rel_path" + cp -r "$src/." "$tmp/$rel_path/" + done + + tar -czf "$out_file" -C "$tmp" . + shasum -a 256 "$out_file" | awk '{print $1}' > "$out_file.sha256" + echo " ✓ $(du -sh "$out_file" | cut -f1)" + rm -rf "$tmp" +} + +# ---- Android ---------------------------------------------------------------- + +echo "" +echo "Android:" + +package_merged "core-android-arm64-v8a" \ + "executorch/arm64-v8a" "$ANDROID_LIBS/executorch/arm64-v8a" \ + "pthreadpool/arm64-v8a" "$ANDROID_LIBS/pthreadpool/arm64-v8a" \ + "cpuinfo/arm64-v8a" "$ANDROID_LIBS/cpuinfo/arm64-v8a" + +package_merged "core-android-x86_64" \ + "executorch/x86_64" "$ANDROID_LIBS/executorch/x86_64" + +package_merged "opencv-android-arm64-v8a" \ + "opencv/arm64-v8a" "$ANDROID_LIBS/opencv/arm64-v8a" \ + "opencv-third-party/arm64-v8a" "$ANDROID_LIBS/opencv-third-party/arm64-v8a" + +package_merged "opencv-android-x86_64" \ + "opencv/x86_64" "$ANDROID_LIBS/opencv/x86_64" + +# Phonemizer is built from in-tree source (third-party/common/phonemis submodule); +# no Android tarball is produced. + +# XNNPACK and Vulkan each ship as standalone shared libraries (opt-in backends). +package_file "xnnpack-android-arm64-v8a" \ + "executorch/arm64-v8a" "$ANDROID_LIBS/executorch/arm64-v8a/libxnnpack_executorch_backend.so" + +package_file "xnnpack-android-x86_64" \ + "executorch/x86_64" "$ANDROID_LIBS/executorch/x86_64/libxnnpack_executorch_backend.so" + +package_file "vulkan-android-arm64-v8a" \ + "executorch/arm64-v8a" "$ANDROID_LIBS/executorch/arm64-v8a/libvulkan_executorch_backend.so" + +package_file "vulkan-android-x86_64" \ + "executorch/x86_64" "$ANDROID_LIBS/executorch/x86_64/libvulkan_executorch_backend.so" + +# ---- iOS -------------------------------------------------------------------- +# Note: OpenCV for iOS is provided by CocoaPods (opencv-rne dependency). +# No opencv-ios tarball is needed. + +echo "" +echo "iOS:" + +package_merged "core-ios" \ + "ExecutorchLib.xcframework" "$IOS_DIR/ExecutorchLib.xcframework" \ + "libs/executorch" "$IOS_DIR/libs/executorch" \ + "libs/pthreadpool" "$IOS_DIR/libs/pthreadpool" \ + "libs/cpuinfo" "$IOS_DIR/libs/cpuinfo" + +# Phonemizer is built from in-tree source (third-party/common/phonemis submodule); +# no iOS tarball is produced. + +package_merged "xnnpack-ios" \ + "XnnpackBackend.xcframework" "$IOS_DIR/XnnpackBackend.xcframework" + +package_merged "coreml-ios" \ + "CoreMLBackend.xcframework" "$IOS_DIR/CoreMLBackend.xcframework" + +# ---- Summary ---------------------------------------------------------------- + +echo "" +echo "Done. Artifacts written to dist-artifacts/:" +ls -lh "$OUT" +echo "" +echo "Upload these files to the GitHub Release for v$VERSION:" +echo " https://github.com/software-mansion/react-native-executorch/releases/tag/v$VERSION" diff --git a/packages/react-native-executorch/src/index.ts b/packages/react-native-executorch/src/index.ts index 1f190d41f5..f1e5f0cd69 100644 --- a/packages/react-native-executorch/src/index.ts +++ b/packages/react-native-executorch/src/index.ts @@ -123,27 +123,22 @@ declare global { } // eslint-disable no-var -if ( - global.loadStyleTransfer == null || - global.loadSemanticSegmentation == null || - global.loadInstanceSegmentation == null || - global.loadTextToImage == null || - global.loadExecutorchModule == null || - global.loadClassification == null || - global.loadObjectDetection == null || - global.loadPoseEstimation == null || - global.loadTokenizerModule == null || - global.loadTextEmbeddings == null || - global.loadImageEmbeddings == null || - global.loadVAD == null || - global.loadLLM == null || - global.loadPrivacyFilter == null || - global.loadSpeechToText == null || - global.loadTextToSpeechKokoro == null || - global.loadOCR == null || - global.loadVerticalOCR == null || - global.__rne_isEmulator == null -) { +// Core globals are always installed regardless of which backends/libs are enabled. +// Optional globals (opencv/phonemizer) may be absent if the library was built +// without those features — calling them at that point throws a runtime error +// from the native side with a clear message. +const CORE_GLOBALS = [ + 'loadExecutorchModule', + 'loadTokenizerModule', + 'loadLLM', + 'loadSpeechToText', + 'loadTextEmbeddings', + 'loadPrivacyFilter', + 'loadVAD', + '__rne_isEmulator', +] as const; + +if (CORE_GLOBALS.some((name) => global[name] == null)) { if (!ETInstallerNativeModule) { throw new Error( `Failed to install react-native-executorch: The native module could not be found.` diff --git a/packages/react-native-executorch/third-party/android/libs/cpuinfo/arm64-v8a/libcpuinfo.so b/packages/react-native-executorch/third-party/android/libs/cpuinfo/arm64-v8a/libcpuinfo.so deleted file mode 100755 index c97092dbce..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/cpuinfo/arm64-v8a/libcpuinfo.so and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/executorch/arm64-v8a/libexecutorch.so b/packages/react-native-executorch/third-party/android/libs/executorch/arm64-v8a/libexecutorch.so deleted file mode 100644 index 8c65aa5d85..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/executorch/arm64-v8a/libexecutorch.so and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/executorch/x86_64/libexecutorch.so b/packages/react-native-executorch/third-party/android/libs/executorch/x86_64/libexecutorch.so deleted file mode 100644 index a56a5d20ac..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/executorch/x86_64/libexecutorch.so and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv-third-party/arm64-v8a/libkleidicv.a b/packages/react-native-executorch/third-party/android/libs/opencv-third-party/arm64-v8a/libkleidicv.a deleted file mode 100644 index aafb96d52a..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv-third-party/arm64-v8a/libkleidicv.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv-third-party/arm64-v8a/libkleidicv_hal.a b/packages/react-native-executorch/third-party/android/libs/opencv-third-party/arm64-v8a/libkleidicv_hal.a deleted file mode 100644 index 2586c51da7..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv-third-party/arm64-v8a/libkleidicv_hal.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv-third-party/arm64-v8a/libkleidicv_thread.a b/packages/react-native-executorch/third-party/android/libs/opencv-third-party/arm64-v8a/libkleidicv_thread.a deleted file mode 100644 index 21b46fd532..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv-third-party/arm64-v8a/libkleidicv_thread.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_core.a b/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_core.a deleted file mode 100644 index dfa6cac97b..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_core.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_features2d.a b/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_features2d.a deleted file mode 100644 index 2d9285ff48..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_features2d.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_highgui.a b/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_highgui.a deleted file mode 100644 index f5935c0a49..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_highgui.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_imgproc.a b/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_imgproc.a deleted file mode 100644 index 1230670d37..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_imgproc.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_photo.a b/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_photo.a deleted file mode 100644 index 6fd7b5725c..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_photo.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_video.a b/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_video.a deleted file mode 100644 index 94afa14b0c..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv/arm64-v8a/libopencv_video.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_core.a b/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_core.a deleted file mode 100644 index 11dbb38c99..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_core.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_features2d.a b/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_features2d.a deleted file mode 100644 index 7dda0f00f4..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_features2d.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_highgui.a b/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_highgui.a deleted file mode 100644 index 074a6fbd7f..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_highgui.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_imgproc.a b/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_imgproc.a deleted file mode 100644 index af4e846586..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_imgproc.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_photo.a b/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_photo.a deleted file mode 100644 index d1cd183451..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_photo.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_video.a b/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_video.a deleted file mode 100644 index 6d76f3b5b5..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/opencv/x86_64/libopencv_video.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/android/libs/pthreadpool/arm64-v8a/libpthreadpool.so b/packages/react-native-executorch/third-party/android/libs/pthreadpool/arm64-v8a/libpthreadpool.so deleted file mode 100755 index 5b144aaa8c..0000000000 Binary files a/packages/react-native-executorch/third-party/android/libs/pthreadpool/arm64-v8a/libpthreadpool.so and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/Info.plist b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/Info.plist deleted file mode 100644 index 6a6c556899..0000000000 --- a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/Info.plist +++ /dev/null @@ -1,43 +0,0 @@ - - - - - AvailableLibraries - - - BinaryPath - ExecutorchLib.framework/ExecutorchLib - LibraryIdentifier - ios-arm64 - LibraryPath - ExecutorchLib.framework - SupportedArchitectures - - arm64 - - SupportedPlatform - ios - - - BinaryPath - ExecutorchLib.framework/ExecutorchLib - LibraryIdentifier - ios-arm64-simulator - LibraryPath - ExecutorchLib.framework - SupportedArchitectures - - arm64 - - SupportedPlatform - ios - SupportedPlatformVariant - simulator - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/ExecutorchLib b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/ExecutorchLib deleted file mode 100755 index f74ed53c6a..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/ExecutorchLib and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/Info.plist b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/Info.plist deleted file mode 100644 index b2b2aa2478..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64-simulator/ExecutorchLib.framework/Info.plist and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/ExecutorchLib b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/ExecutorchLib deleted file mode 100755 index 61193b77ef..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/ExecutorchLib and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/Info.plist b/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/Info.plist deleted file mode 100644 index a6f2d4a5dc..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/ExecutorchLib.xcframework/ios-arm64/ExecutorchLib.framework/Info.plist and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib/ExecutorchLib.xcodeproj/project.pbxproj b/packages/react-native-executorch/third-party/ios/ExecutorchLib/ExecutorchLib.xcodeproj/project.pbxproj index a4d139dcaa..a7c0ce85bc 100644 --- a/packages/react-native-executorch/third-party/ios/ExecutorchLib/ExecutorchLib.xcodeproj/project.pbxproj +++ b/packages/react-native-executorch/third-party/ios/ExecutorchLib/ExecutorchLib.xcodeproj/project.pbxproj @@ -334,13 +334,10 @@ "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkernels_llm_ios.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkernels_torchao_ios.a", + "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkleidiai_ios.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkernels_quantized_ios.a", "-force_load", - "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_xnnpack_ios.a", - "-force_load", - "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_coreml_ios.a", - "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_mps_ios.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libexecutorch_ios.a", @@ -357,13 +354,10 @@ "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkernels_llm_simulator.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkernels_torchao_simulator.a", + "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkleidiai_simulator.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkernels_quantized_simulator.a", "-force_load", - "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_xnnpack_simulator.a", - "-force_load", - "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_coreml_simulator.a", - "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_mps_simulator.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libexecutorch_simulator.a", @@ -426,13 +420,10 @@ "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkernels_llm_ios.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkernels_torchao_ios.a", + "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkleidiai_ios.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkernels_quantized_ios.a", "-force_load", - "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_xnnpack_ios.a", - "-force_load", - "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_coreml_ios.a", - "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_mps_ios.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libexecutorch_ios.a", @@ -449,13 +440,10 @@ "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkernels_llm_simulator.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkernels_torchao_simulator.a", + "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkleidiai_simulator.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libkernels_quantized_simulator.a", "-force_load", - "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_xnnpack_simulator.a", - "-force_load", - "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_coreml_simulator.a", - "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libbackend_mps_simulator.a", "-force_load", "$(PROJECT_DIR)/../../../third-party/ios/libs/executorch/libexecutorch_simulator.a", diff --git a/packages/react-native-executorch/third-party/ios/ExecutorchLib/build.sh b/packages/react-native-executorch/third-party/ios/ExecutorchLib/build.sh index 0a410e237d..bc4191f5f1 100755 --- a/packages/react-native-executorch/third-party/ios/ExecutorchLib/build.sh +++ b/packages/react-native-executorch/third-party/ios/ExecutorchLib/build.sh @@ -1,21 +1,30 @@ #!/bin/bash -# Builds ExecutorchLib.xcframework for iOS and iOS Simulator +# Builds ExecutorchLib.xcframework for iOS and iOS Simulator, plus separate +# static xcframeworks for optional backends (xnnpack, coreml). # # This script: # 1. Cleans previous builds # 2. Archives the framework for iOS device (arm64) # 3. Archives the framework for iOS Simulator (arm64) # 4. Combines both archives into a single .xcframework +# 5. Creates XnnpackBackend.xcframework from the backend .a files +# 6. Creates CoreMLBackend.xcframework from the backend .a files # -# Output: ./output/ExecutorchLib.xcframework +# Output: +# ./output/ExecutorchLib.xcframework +# ./output/XnnpackBackend.xcframework +# ./output/CoreMLBackend.xcframework # # Usage: ./build.sh +set -euo pipefail + # --- Configuration --- -PROJECT_NAME="ExecutorchLib" # Replace with your Xcode project name -SCHEME_NAME="ExecutorchLib" # Replace with your scheme name -OUTPUT_FOLDER="output" # Choose your desired output folder +PROJECT_NAME="ExecutorchLib" +SCHEME_NAME="ExecutorchLib" +OUTPUT_FOLDER="output" +LIBS_DIR="$(pwd)/../../../third-party/ios/libs/executorch" # --- Derived Variables --- BUILD_FOLDER="build" @@ -27,6 +36,7 @@ XCFRAMEWORK_PATH="$OUTPUT_FOLDER/$XCFRAMEWORK_NAME" # --- Script --- rm -rf "$BUILD_FOLDER" "$OUTPUT_FOLDER" +mkdir -p "$OUTPUT_FOLDER" xcodebuild clean -project "$PROJECT_NAME.xcodeproj" -scheme "$SCHEME_NAME" @@ -54,3 +64,28 @@ xcodebuild -create-xcframework \ -framework "$ARCHIVE_PATH_IOS.xcarchive/Products/Library/Frameworks/$FRAMEWORK_NAME" \ -framework "$ARCHIVE_PATH_SIMULATOR.xcarchive/Products/Library/Frameworks/$FRAMEWORK_NAME" \ -output "$XCFRAMEWORK_PATH" + +# --- Build XnnpackBackend.xcframework --- +# CocoaPods requires matching binary names across slices +STAGING=$(mktemp -d) +mkdir -p "$STAGING/ios" "$STAGING/sim" +cp "$LIBS_DIR/libbackend_xnnpack_ios.a" "$STAGING/ios/libXnnpackBackend.a" +cp "$LIBS_DIR/libbackend_xnnpack_simulator.a" "$STAGING/sim/libXnnpackBackend.a" +xcodebuild -create-xcframework \ + -library "$STAGING/ios/libXnnpackBackend.a" \ + -library "$STAGING/sim/libXnnpackBackend.a" \ + -output "$OUTPUT_FOLDER/XnnpackBackend.xcframework" + +# --- Build CoreMLBackend.xcframework --- +cp "$LIBS_DIR/libbackend_coreml_ios.a" "$STAGING/ios/libCoreMLBackend.a" +cp "$LIBS_DIR/libbackend_coreml_simulator.a" "$STAGING/sim/libCoreMLBackend.a" +xcodebuild -create-xcframework \ + -library "$STAGING/ios/libCoreMLBackend.a" \ + -library "$STAGING/sim/libCoreMLBackend.a" \ + -output "$OUTPUT_FOLDER/CoreMLBackend.xcframework" +rm -rf "$STAGING" + +echo "Done! Output:" +echo " $OUTPUT_FOLDER/ExecutorchLib.xcframework" +echo " $OUTPUT_FOLDER/XnnpackBackend.xcframework" +echo " $OUTPUT_FOLDER/CoreMLBackend.xcframework" diff --git a/packages/react-native-executorch/third-party/ios/libs/cpuinfo/libcpuinfo.a b/packages/react-native-executorch/third-party/ios/libs/cpuinfo/libcpuinfo.a deleted file mode 100644 index b5d3895694..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/cpuinfo/libcpuinfo.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_ios.a deleted file mode 100644 index 4e2a80a220..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_ios.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_simulator.a deleted file mode 100644 index c5b16beb50..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_coreml_simulator.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_ios.a deleted file mode 100644 index ce63bfd904..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_ios.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_simulator.a deleted file mode 100644 index 3679faaa46..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_mps_simulator.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_ios.a deleted file mode 100644 index 4049c0a640..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_ios.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_simulator.a deleted file mode 100644 index bd145d474f..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libbackend_xnnpack_simulator.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_ios.a deleted file mode 100644 index 221409be07..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_ios.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_ios.a deleted file mode 100644 index 40b74e9c7d..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_ios.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_simulator.a deleted file mode 100644 index bec9105acb..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_llm_simulator.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_simulator.a deleted file mode 100644 index 99449364f2..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libexecutorch_simulator.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_ios.a deleted file mode 100644 index 5217a54902..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_ios.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_simulator.a deleted file mode 100644 index 9add6b355c..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_llm_simulator.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_ios.a deleted file mode 100644 index 9cc9a42bd2..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_ios.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_simulator.a deleted file mode 100644 index cb06a86712..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_optimized_simulator.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_ios.a deleted file mode 100644 index d54805247e..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_ios.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_simulator.a deleted file mode 100644 index 41f06f64fb..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_quantized_simulator.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_ios.a deleted file mode 100644 index 19db3e80f4..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_ios.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_simulator.a deleted file mode 100644 index af26c633e1..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libkernels_torchao_simulator.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_ios.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_ios.a deleted file mode 100644 index 8c14cf924b..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_ios.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_simulator.a b/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_simulator.a deleted file mode 100644 index d6deeb5a00..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/executorch/libthreadpool_simulator.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/pthreadpool/physical-arm64-release/libpthreadpool.a b/packages/react-native-executorch/third-party/ios/libs/pthreadpool/physical-arm64-release/libpthreadpool.a deleted file mode 100644 index 652afff7bf..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/pthreadpool/physical-arm64-release/libpthreadpool.a and /dev/null differ diff --git a/packages/react-native-executorch/third-party/ios/libs/pthreadpool/simulator-arm64-debug/libpthreadpool.a b/packages/react-native-executorch/third-party/ios/libs/pthreadpool/simulator-arm64-debug/libpthreadpool.a deleted file mode 100644 index e3c3053a15..0000000000 Binary files a/packages/react-native-executorch/third-party/ios/libs/pthreadpool/simulator-arm64-debug/libpthreadpool.a and /dev/null differ