Skip to main content

build-compilation-dependencies

Build system overview for react-native-audio-api across all platforms — CMakeLists.txt, android/build.gradle, RNAudioAPI.podspec, prebuilt external libraries, and the standalone C++ test build. Use this skill when adding a new source file, modifying CMakeLists or podspec, debugging compilation errors, integrating a new dependency, or understanding why includes work differently in tests vs the app. Trigger phrases: "add source file", "CMakeLists", "podspec", "build.gradle", "prebuilt binaries", "FFmpeg disabled", "pod install", "new architecture", "compile error", "undefined symbol", "SIMD", "worklets build flag", "C++ tests", "conditional compilation", "include path", "gradle build fails", "link error", "yarn catalog", "catalog:", "add a dependency", "bump a dependency", "yarn workspaces", "npmMinimalAgeGate", "shared dependency version".

Quellinformationen

Repository
software-mansion/react-native-audio-api
Letzte Quellaktivität
13. Oktober 2026 um 07:59
Erkannte Sprache von SKILL.md
Englisch
Sterne
848
Forks
96

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
3 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
build-compilation-dependencies
description
Build system overview for react-native-audio-api across all platforms — CMakeLists.txt, android/build.gradle, RNAudioAPI.podspec, Package.swift (SwiftPM), prebuilt external libraries, and the standalone C++ test build. Use this skill when adding a new source file, modifying CMakeLists or podspec, debugging compilation errors, integrating a new dependency, or understanding why includes work differently in tests vs the app. Trigger phrases: "add source file", "CMakeLists", "podspec", "build.gradle", "prebuilt binaries", "FFmpeg disabled", "pod install", "new architecture", "compile error", "undefined symbol", "SIMD", "worklets build flag", "C++ tests", "conditional compilation", "include path", "gradle build fails", "link error", "yarn catalog", "catalog:", "add a dependency", "bump a dependency", "yarn workspaces", "npmMinimalAgeGate", "shared dependency version", "Package.swift", "SwiftPM", "Swift Package Manager", "react-native spm".
# Skill: Build, Compilation & Dependencies For deep CMake/Gradle/podspec analysis see [build-details.md](build-details.md). --- ## Repository Build Overview ``` react-native-audio-api/ ├── package.json # Yarn 4 workspaces root ├── packages/react-native-audio-api/ # Main library │ ├── android/ │ │ ├── build.gradle # Android build config (Gradle) │ │ ├── CMakeLists.txt # Android CMake root (delegates to subdirectory) │ │ └── src/main/cpp/audioapi/ │ │ └── CMakeLists.txt # Actual Android C++ build target │ ├── common/cpp/audioapi/ # Shared C++ (used by all platforms) │ │ ├── decoding/ # Decoder factory, backends, SeekDecoderDaemon, AudioDecoding │ │ ├── encoding/ # AudioEncoder interface, EncoderCapabilities, OS encoder/remux selector headers, AudioFileConcatenator, AdtsHeader (ADTS framing) │ │ ├── libs/ # Third-party wrappers (FFmpeg, miniaudio, pffft, …) │ │ └── external/ # Prebuilt binaries per platform │ │ ├── android/ # .a static libs (Opus, Ogg, Vorbis, OpenSSL) │ │ ├── xcframeworks/ # Every iOS prebuilt as <lib>.xcframework (static Opus/Ogg/Vorbis, dynamic FFmpeg) │ │ ├── include/ # Headers for Opus/Ogg/Vorbis/OpenSSL │ │ └── include_ffmpeg/ # FFmpeg headers (downloaded, FFmpeg builds only) │ ├── common/cpp/test/ │ │ ├── CMakeLists.txt # Standalone test build (no Android/iOS) │ │ ├── run-tests.sh / filters.sh # smoke|extended|full (+ categories, CI path filters) │ │ └── src/ # Google Test files │ ├── RNAudioAPI.podspec # CocoaPods spec for iOS │ ├── Package.swift # SwiftPM manifest for RN's `react-native spm` mode │ ├── ios/spm/ # SwiftPM-only wrapper sources + empty public-header dirs │ └── scripts/ │ ├── download-prebuilt-binaries.sh # Downloads externals from GitHub Releases │ └── rnaa_utils.rb # Ruby helpers for podspec (path resolution) └── apps/ └── fabric-example/ └── ios/ └── Podfile # Consumer Podfile (new arch enabled) ``` --- ## Yarn Workspaces & Dependency Catalog Dependency versions live in a single catalog in the root `.yarnrc.yml`, referenced from manifests as `"<dep>": "catalog:"`. Bump once there and every workspace follows. Needs Yarn >= 4.10 (repo is on 4.18). **What belongs in the catalog** — anything that must move in lockstep, which is broader than "used more than once": - used by two or more workspaces, **or** - version-locked to something already catalogued even if only one workspace uses it — `react-test-renderer` must equal `react`; `@react-native-community/cli*` and `@babel/runtime` move with `react-native` / `@babel/core`. A dependency used in exactly one workspace with no such coupling stays a literal range: it has no second copy to drift from, and catalogue membership is meant to *signal* "keep this in lockstep". There is no CI check enforcing this — review is the enforcement, which is why membership has to carry meaning. **Workflow when touching dependencies:** 1. Changing the version of a catalogued dep → edit the catalog entry, not the manifest. 2. Adding a dep → check the catalog first. Already there? Use `"catalog:"`. Not there but lockstep-coupled or now used twice? Add the entry and switch both call sites over. Three kinds of dependency must **never** be given `catalog:`: | Never catalogue | Why | |---|---| | `peerDependencies` | Public API surface of the published packages, deliberately loose (`"*"`). Pinning narrows what consumers may install. | | Runtime `dependencies` of published packages (`semver`, `chalk`, `commander`, `fs-extra`) | The release pipeline packs with `npm pack` (`scripts/create-package.sh`, `npm-custom-node-generator-build.yml`) and npm does not understand `catalog:`. It publishes the literal string, producing an uninstallable package. Only `yarn pack` / `yarn npm publish` perform the swap. | | `workspace:*` ranges | Not version ranges — nothing to centralise. | Other constraints: - **`resolutions` does not accept `catalog:`** — install fails with `typescript@catalog: isn't supported by any available resolver`. Root `package.json` pins `typescript` literally; keep it in sync with the catalog entry. It is *not* redundant with the catalog: it forces `@commitlint/load`'s optional `typescript` peer (`^4.6.4 || ^5.2.2`) onto our version, and catalogs only rewrite workspace manifests, never transitive dependencies. - **`packages/audiodocs` is a separate Yarn 1 project** with its own lockfile and `node_modules`. It is not in root `workspaces`, so the catalog does not reach it. - **`yarn pack` is not a drop-in for `npm pack` here.** It drops `development/react/` (a nested `package.json` listed in `files[]`) and strips the executable bit from 29 prebuilt FFmpeg binaries. Do not switch the release pipeline to it without re-verifying the tarball contents. Supply-chain gate: `npmMinimalAgeGate: '1w'` refuses versions published in the last 7 days. Bypass one command with `yarn add --no-time-gate` / `yarn up --no-time-gate`, or exempt specific packages permanently via `npmPreapprovedPackages`. --- ## C++ Header File Extensions In `common/cpp/audioapi/`: `.hpp` = header-only templates; `.h` = non-template (usually with a `.cpp`). Vendored code is excluded. --- ## Prebuilt Binaries External libraries (Opus, Ogg, Vorbis, OpenSSL, FFmpeg) are **not compiled from source** — they are downloaded as prebuilt `.a` / `.so` / `.xcframework` archives from: ``` https://github.com/software-mansion-labs/rn-audio-libs/releases/download/<TAG>/ ``` Current tag: **v3.0.0** (see `scripts/download-prebuilt-binaries.sh`). The download script is triggered automatically: **FFmpeg is opt-in (off by default).** Both build options (`enableFFmpeg`, `disableStaticExternalLibs`) come from the `"react-native-audio-api"` section of the **app's package.json** on every path: podspec (`rnaa_utils.rb` `read_audio_api_build_options`, `<Podfile dir>/../package.json`), Gradle (`readAudioApiBuildOptions`, `rootProject.projectDir.parentFile`), `Package.swift` (walk from the autolinking symlink). The 0.13 inputs (Podfile `ENV[...]`, `gradle.properties` keys) are simply not read any more — no guard, by decision: it ships in a major. Non-boolean values are errors. The Expo plugin (`src/plugin/withAudioAPI.ts`) still accepts `enableFFmpeg` / `disableStaticExternalLibs`, but only as a *writer*: a `withDangerousMod` on both platforms (so a single-platform prebuild still runs it; idempotent) merges the given keys into the app package.json section during `expo prebuild`, keeping the file's indentation, before prebuild's `pod install`. Omitted options leave package.json alone; the builds never read the plugin options themselves. The pre-1.0 `DISABLE_AUDIOAPI_FFMPEG` / `disableAudioapiFFmpeg` / `disableFFmpeg` flags are **not** read any more and produce no warning either: a leftover disable flag asks for the default, so there is nothing to tell the user, and the switch is covered by the separate upgrade docs page (planned, not written yet). Migration notes belong on that page only — feature pages such as `prebuilt-libraries-support.mdx` describe the current behaviour without "Upgrading from…" notes. Internally every build file and source keys off the positive `$RN_AUDIO_API_FFMPEG_ENABLED` / `RN_AUDIO_API_FFMPEG_ENABLED`. The define is only emitted (`=1`) in an FFmpeg build; `#if RN_AUDIO_API_FFMPEG_ENABLED` reads an undefined macro as `0`, so the test, clangd and WPT builds do not mention it at all. The download script passes `skipffmpeg` in the default build, so neither the `.so`/xcframeworks nor (once published) the `include_ffmpeg.zip` header archive are fetched. - **iOS**: by podspec `prepare_command` during `pod install` **and** by a `script_phase` (`[CP-User] Download RNAudioAPI prebuilt binaries`, `:before_headers`, always-out-of-date) on every Xcode build — downloads one `<name>.xcframework.zip` per library (plus `include_ffmpeg.zip` with FFmpeg). `prepare_command` ensures all vendored `.xcframework`s exist when CocoaPods generates `[CP] Copy XCFrameworks`. The build-time phase handles CI setups that cache `Pods/` independently from `node_modules/` — it is a fast no-op when binaries are already present. **It must use `:before_headers`, not `:before_compile`** — CocoaPods inserts its own `[CP] Copy XCFrameworks` phase right after `Headers`; `:before_compile` would land *after* it. The phase's `output_files` declares every vendored xcframework. The whole phase is skipped only when `DISABLE_AUDIOAPI_STATIC_EXTERNAL_LIBS` is set *and* FFmpeg is not enabled. - **Android**: by `downloadPrebuiltBinaries` Gradle task, which runs before `preBuild` — downloads `android`, `jniLibs` The script is idempotent — it skips any archive whose expected artifacts already exist (`artifacts_present`), so the per-build invocations are fast no-ops once binaries are present. A failed download is a hard error (`exit 1`): a silent skip used to surface much later as a missing header or `undefined symbol`. **FFmpeg headers.** `external/include_ffmpeg/` is not tracked: it is gitignored, negated in `package.json` `files`, and fetched as `include_ffmpeg.zip` from the pinned rn-audio-libs release in the same pass as the FFmpeg binaries (`FFMPEG_HEADERS_ARCHIVE`, presence checked through `FFMPEG_HEADER_MARKERS`, skipped with `skipffmpeg`). The archive is produced by `yarn export` (`scripts/pack_outputs.sh`) in `rn-audio-libs`, which zips every `outputs/*` directory except `include`, so a new release of that repo ships the headers automatically — but every tag the download script pins must carry the asset or FFmpeg builds fail at the download step. Nothing else references the directory outside the gated include-path entries (podspec `pod_target_xcconfig`, Android CMake); `common/cpp/clangd/CMakeLists.txt` adds the path unconditionally, so in a checkout that never enabled FFmpeg clangd shows unresolved `libav*` includes only inside `FfmpegDecoder.cpp`. `FfmpegDecoder.h` includes `libav*` directly, but `DecoderFactory.cpp` is the only consumer and gates that include behind `RN_AUDIO_API_FFMPEG_ENABLED`, every disabled build also drops `FfmpegDecoder.cpp`, and the Gradle prefab header copy excludes `audioapi/external/**`. Downloaded artifacts land in: - `common/cpp/audioapi/external/android/<ABI>/` — `.a` static libs for Android ABIs - `android/src/main/jniLibs/<ABI>/` — FFmpeg `.so` shared libs for Android (loaded at runtime) - `common/cpp/audioapi/external/xcframeworks/` — every iOS prebuilt as `<lib>.xcframework` (static Opus/Ogg/Vorbis, dynamic FFmpeg) (each release zip unpacks to exactly that directory). Older checkouts may still hold the pre-v4.0.0 `ffmpeg_ios/`, `iphoneos/`, `iphonesimulator/`, `macosx/` — unused and gitignored **These directories are gitignored.** If they're missing, run `pod install` (iOS) or Gradle build (Android) to re-download them. Do not commit them. --- ## Android Build — high-level summary ### Files - `android/build.gradle` — Gradle library config - `android/fix-prefab.gradle` — prefab publication workaround (ported from react-native-worklets) - `android/CMakeLists.txt` — Android CMake root (SIMD detection, RN version flags, delegates to subdirectory) - `android/src/main/cpp/audioapi/CMakeLists.txt` — actual build target (sources, prebuilt libs, include paths) - `common/cpp/audioapi/EXTENSION_API.md` — stable C++ extension contract for dependent native modules - `common/cpp/audioapi/compatibility/StableAPI.h` — single public C++ compatibility header for extensions ### Key behaviors - `newArchEnabled` is read from the app's `gradle.properties` and defaults to **on**: only an explicit `newArchEnabled=false` builds the legacy path, because RN 0.82+ apps no longer declare the property (the podspec likewise treats only `RCT_NEW_ARCH_ENABLED=0` as legacy); `enableFFmpeg` / `disableStaticExternalLibs` from the app's `package.json` (`readAudioApiBuildOptions`); all are forwarded to both CMake and Kotlin `BuildConfig` - Source exclusion in `android/src/main/cpp/audioapi/CMakeLists.txt` must use `list(FILTER … EXCLUDE REGEX)` — `COMMON_CPP_SOURCES` holds paths relative to that directory, so a `list(REMOVE_ITEM …)` with an absolute path silently matches nothing (this is how the FFmpeg exclusion was a no-op for a long time) - Third-party include dirs (`external/include`, `external/include_ffmpeg`) are only added when the corresponding library set is enabled - DSP sources always compiled with `-O3` regardless of overall build type - Sources gathered with `GLOB_RECURSE CONFIGURE_DEPENDS` — CMake re-runs automatically when files are added/removed - 16KB page size alignment enabled for Android 15+ - **Extension API (prefab)**: single public C++ header `<audioapi/compatibility/StableAPI.h>`; prefab publishes transitive headers needed to compile it (`prepareAudioApiHeadersForPrefabs`); `fix-prefab.gradle` ensures the `.so` is in prefab metadata. Contract: `EXTENSION_API.md` - CMake exposes `COMMON_CPP_DIR` and `ANDROID_CPP_DIR` as **PUBLIC** include dirs so prefab consumers resolve `<audioapi/...>` - **React Native's transitive Kotlin/Java deps are usable without declaring them.** `ReactAndroid` declares several libraries with `api(...)` rather than `implementation(...)`, so they reach us through the existing `implementation "com.facebook.react:react-native:+"`. Fresco is the notable one (`com.facebook.fresco:fresco` — 3.2.0 on RN 0.76, 3.7.0 on RN 0.87), used by `system/notification/ArtworkLoader.kt`. Check the RN version's `ReactAndroid/build.gradle.kts` before adding a dependency that RN may already expose; adding it explicitly only risks a version conflict. - **Verify any such API at the RN floor, not just the checked-in version.** `android/build.gradle` asserts RN minor >= 76, so a Kotlin API that exists in `node_modules` today may not exist at 0.76. Fresco's API surface happens to be identical across 3.2.0-3.7.0, but the third `ResizeOptions` constructor parameter was renamed (`maxBitmapSize` -> `maxBitmapDimension`), so positional arguments are required. Sources for a given version are fetchable from `raw.githubusercontent.com/facebook/{react-native,fresco}/v<tag>/...`. For full per-line analysis see [build-details.md](build-details.md#android-androidcmakeliststxt-root--detailed-analysis). --- ## iOS Build (CocoaPods) — high-level summary ### Files - `RNAudioAPI.podspec` — library spec with subspecs - `scripts/rnaa_utils.rb` — Ruby helpers called by podspec - `apps/fabric-example/ios/Podfile` — consumer ### Key behaviors - Four subspecs split compilation: `audioapi` (core C++), `audioapi/ios` (ObjC++), `audioapi/audioapi_dsp` (DSP with `-O3`), `audioapi/miniaudio_impl` (compiled as `-x objective-c++`) - Every prebuilt (static Opus/Ogg/Vorbis, dynamic FFmpeg) is an `.xcframework` in `s.ios.vendored_frameworks` (named after the library: CocoaPods links a dynamic xcframework as `-framework <xcframework name>`) — the same release assets Package.swift downloads; CocoaPods picks the device/simulator/Catalyst slice and embeds the dynamic ones. The pre-v4.0.0 per-SDK `.a` dirs (`iphoneos/`, `iphonesimulator/`, `macosx/`) and their `-force_load …/$(PLATFORM_NAME)` flags are gone - **CocoaPods never links a pod's own vendored dynamic xcframeworks into the pod's own target** (only into targets that depend on it). Under `use_frameworks! :linkage => :dynamic` `RNAudioAPI` must link FFmpeg itself, so the podspec adds `ffmpeg_ldflags` to `pod_target_xcconfig` `OTHER_LDFLAGS` (FFmpeg builds only). Build the xcframework list and the flags from the same `ffmpeg_libs`; see [build-details.md](build-details.md#ffmpeg-link-flags-on-the-pod-target) - `Accelerate` framework linked, enabling `HAVE_ACCELERATE=1` for vDSP SIMD on iOS - Header search paths split: `pod_target_xcconfig` (library compilation) vs `xcconfig` (app consumers) - `rnaa_utils.rb` resolves dynamic paths at `pod install` time (not hardcoded) - JSI internals use distinct names (`HostObject`, `RuntimeInstanceCache`, `RuntimeObserver`) instead of generic names like `JsiHostObject` / `RuntimeAwareCache` / `RuntimeLifecycleMonitor` — `react-native-skia` publishes headers with those generic names and CocoaPods can resolve the wrong file when both libraries are in the same app For full per-line analysis see [build-details.md](build-details.md#ios-rnaudioapipodspec--detailed-analysis). **Include our own headers by full path** (`<audioapi/ios/system/AudioEngine.h>`), never bare (`<AudioEngine.h>`): CocoaPods' header map resolves bare names, SwiftPM does not. --- ## iOS Build (SwiftPM) — `Package.swift` React Native's experimental `npx react-native spm` mode (0.87+) symlinks each library to `<app>/ios/build/generated/autolinking/libs/<SwiftName>/` and resolves our hand-written `Package.swift` from there — hence the fixed relative paths `../../../../xcframeworks` (`ReactNative`
Auf GitHub ansehen
Diese SKILL.md ist sehr gross, daher zeigt SkillsMP hier nur den ersten Abschnitt. Auf GitHub ansehen