From e11b67e709318b25ea3b618d0be075ff523c770a Mon Sep 17 00:00:00 2001 From: Marek Malek Date: Wed, 12 Aug 2026 17:47:18 +0200 Subject: [PATCH 1/7] feat: setup android and ios --- .../common/cpp/clangd/CMakeLists.txt | 75 ++++++++++++++++++- .../common/cpp/clangd/SETUP.md | 11 ++- .../common/cpp/clangd/generate-and-copy.sh | 44 +++++++++++ 3 files changed, 127 insertions(+), 3 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt b/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt index 38959d7d5..aebc60076 100644 --- a/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt +++ b/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt @@ -61,4 +61,77 @@ target_include_directories( target_include_directories(rnaudioapi_cursor PUBLIC ${INCLUDE_LIBRARIES}) -target_link_libraries(rnaudioapi_cursor ${LINK_LIBRARIES} gtest gmock gtest_main) \ No newline at end of file +target_link_libraries(rnaudioapi_cursor ${LINK_LIBRARIES} gtest gmock gtest_main) + +# Android JNI glue (android/src/main/cpp/audioapi) is intentionally NOT built +# here: those sources only compile correctly with the NDK's own clang++ given +# --target=...-linux-android and --sysroot=/sysroot. Bolting the NDK +# sysroot onto the host AppleClang via a plain -I mixes NDK libc headers with +# macOS's libc++, which breaks even /. generate-and-copy.sh +# instead merges in the real compile commands Gradle already produced under +# android/.cxx/ (see that script for details). + +# ============================================================================ +# iOS Objective-C++ glue (ios/audioapi/ios) — OBJECT library purely so clangd +# gets a compile command per file; never actually linked. +# ============================================================================ +if(APPLE) + enable_language(OBJCXX) + + set(IOS_DIR ${COMMON_CPP_DIR}/../../ios) + file(GLOB_RECURSE IOS_MM_SOURCES CONFIGURE_DEPENDS "${IOS_DIR}/audioapi/ios/*.mm") + + add_library(rnaudioapi_cursor_ios OBJECT ${IOS_MM_SOURCES}) + set_source_files_properties(${IOS_MM_SOURCES} PROPERTIES COMPILE_FLAGS "-fobjc-arc") + + target_compile_definitions(rnaudioapi_cursor_ios PRIVATE + RN_AUDIO_API_TEST=${RN_AUDIO_API_TEST} + ) + + # System frameworks (Foundation, AVFoundation, AudioToolbox, ...) are found + # automatically once -isysroot points at an SDK. + execute_process( + COMMAND xcrun --sdk iphonesimulator --show-sdk-path + OUTPUT_VARIABLE IOS_SDK_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + if(IOS_SDK_PATH) + target_compile_options(rnaudioapi_cursor_ios PRIVATE -isysroot "${IOS_SDK_PATH}") + endif() + + # React/Pod headers (React-Core, ReactCommon, ReactCodegen, ...) — rather + # than re-deriving every pod's header path by hand, reuse whatever + # `pod install` already resolved for the example app. Requires + # `cd apps/fabric-example/ios && pod install` to have been run at least once. + set(FABRIC_EXAMPLE_IOS_DIR ${COMMON_CPP_DIR}/../../../../apps/fabric-example/ios) + set(PODS_ROOT "${FABRIC_EXAMPLE_IOS_DIR}/Pods") + set(PODS_XCCONFIG "${FABRIC_EXAMPLE_IOS_DIR}/Pods/Target Support Files/Pods-FabricExample/Pods-FabricExample.debug.xcconfig") + + set(IOS_POD_INCLUDE_DIRS "") + if(EXISTS "${PODS_XCCONFIG}") + file(STRINGS "${PODS_XCCONFIG}" HEADER_SEARCH_LINE REGEX "^HEADER_SEARCH_PATHS") + string(REGEX REPLACE "^HEADER_SEARCH_PATHS *= *" "" HEADER_SEARCH_LINE "${HEADER_SEARCH_LINE}") + string(REPLACE "\"" "" HEADER_SEARCH_LINE "${HEADER_SEARCH_LINE}") + string(REPLACE "$(inherited)" "" HEADER_SEARCH_LINE "${HEADER_SEARCH_LINE}") + string(REPLACE "\${PODS_ROOT}" "${PODS_ROOT}" HEADER_SEARCH_LINE "${HEADER_SEARCH_LINE}") + string(REPLACE "$(PODS_ROOT)" "${PODS_ROOT}" HEADER_SEARCH_LINE "${HEADER_SEARCH_LINE}") + separate_arguments(IOS_POD_INCLUDE_DIRS UNIX_COMMAND "${HEADER_SEARCH_LINE}") + else() + message(WARNING "clangd/CMakeLists.txt: ${PODS_XCCONFIG} not found — run `pod install` in apps/fabric-example/ios to resolve React/Pod headers for iOS files.") + endif() + + target_include_directories( + rnaudioapi_cursor_ios + PRIVATE + "${COMMON_CPP_DIR}" + "${IOS_DIR}" + # CocoaPods' header_mappings_dir flattens ios/audioapi's own headers so + # Xcode can resolve bare `#import `-style includes + # via its headermap; a plain include path needs to point directly at the + # containing folder to reproduce that. + "${IOS_DIR}/audioapi/ios/core" + "${IOS_DIR}/audioapi/ios/system" + ${IOS_POD_INCLUDE_DIRS} + ) +endif() \ No newline at end of file diff --git a/packages/react-native-audio-api/common/cpp/clangd/SETUP.md b/packages/react-native-audio-api/common/cpp/clangd/SETUP.md index 5a45a67b4..5e20c862d 100644 --- a/packages/react-native-audio-api/common/cpp/clangd/SETUP.md +++ b/packages/react-native-audio-api/common/cpp/clangd/SETUP.md @@ -1,9 +1,16 @@ -`CMakeLists.txt` in this directory can be used to build the C++ side and generate `compile_commands.json` for the Cursor C++ extension. +`CMakeLists.txt` in this directory can be used to build the C++ side and generate `compile_commands.json` for clangd (VS Code, Cursor, etc.). **Generate compile_commands.json** -From this directory (`common/cpp/cursor`): +From this directory (`common/cpp/clangd`): ```bash ./generate-and-copy.sh ``` + +This generates entries for `common/cpp`, the Android JNI glue (`android/src/main/cpp/audioapi`), and the iOS Objective-C++ glue (`ios/audioapi/ios`), then copies the result to the repo root. + +**Prerequisites for full header resolution** + +- **Android**: `fbjni`/`oboe`/`react-android` headers are resolved from your local Gradle dependency cache (`~/.gradle/caches`, or `$GRADLE_USER_HOME`), and the NDK sysroot from `$ANDROID_HOME/ndk`. Build the Android app (or open it in Android Studio) at least once to populate the cache — a missing cache just means fewer include paths, not a hard failure. +- **iOS**: React/Pod headers are reused from whatever `apps/fabric-example/ios/Pods` already has, via its generated xcconfig. Run `pod install` there at least once (see the [build skill](../../../../.claude/skills/build-compilation-dependencies/SKILL.md)). diff --git a/packages/react-native-audio-api/common/cpp/clangd/generate-and-copy.sh b/packages/react-native-audio-api/common/cpp/clangd/generate-and-copy.sh index 0714f30d8..43d3da115 100755 --- a/packages/react-native-audio-api/common/cpp/clangd/generate-and-copy.sh +++ b/packages/react-native-audio-api/common/cpp/clangd/generate-and-copy.sh @@ -5,6 +5,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" REPO_ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)" +PACKAGE_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)" WORKLETS_DB="$REPO_ROOT/packages/react-native-audio-worklets/compile_commands.json" cmake -B build . @@ -28,3 +29,46 @@ root_db.write_text(json.dumps(list(by_file.values()), indent=2) + "\n") print(f"Preserved worklets entries in {root_db} (total {len(by_file)})") PY fi + +# Merge in Android compile commands Gradle already produced under android/.cxx. +# Those sources only compile correctly with the NDK's own clang++ plus +# --target=...-linux-android --sysroot=/sysroot; this CMakeLists.txt +# can't reproduce that safely with the host compiler (mixing NDK libc headers +# into macOS's libc++ breaks even /), so we reuse Gradle's real +# build instead of guessing flags. Requires having built the Android app (or +# opened it in Android Studio) at least once. +python3 - < Date: Wed, 12 Aug 2026 20:16:58 +0200 Subject: [PATCH 2/7] chore(ios): update Podfile --- apps/fabric-example/ios/Podfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/fabric-example/ios/Podfile.lock b/apps/fabric-example/ios/Podfile.lock index 0b311863e..55c377cf4 100644 --- a/apps/fabric-example/ios/Podfile.lock +++ b/apps/fabric-example/ios/Podfile.lock @@ -2523,7 +2523,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: FBLazyVector: c00c20551d40126351a6783c47ce75f5b374851b - hermes-engine: 91023181d4bc5948b457de5314623fbfe4f8604e + hermes-engine: 146211e12d60a1951d9eb0287be07211e86cf5d5 RCTDeprecation: 3bb167081b134461cfeb875ff7ae1945f8635257 RCTRequired: 74839f55d5058a133a0bc4569b0afec750957f64 RCTSwiftUI: 87a316382f3eab4dd13d2a0d0fd2adcce917361a @@ -2532,7 +2532,7 @@ SPEC CHECKSUMS: React: 1b1536b9099195944034e65b1830f463caaa8390 React-callinvoker: 6dff6d17d1d6cc8fdf85468a649bafed473c65f5 React-Core: 00faa4d038298089a1d5a5b21dde8660c4f0820d - React-Core-prebuilt: a6d614de037caff7898424dfc22915ec792de921 + React-Core-prebuilt: ef40616103ee11f8c2517697c3aa4f48ce790549 React-CoreModules: a17807f849bfd86045b0b9a75ec8c19373b482f6 React-cxxreact: c7b53ace5827be54048288bce5c55f337c41e95f React-debug: e1f00fcd2cef58a2897471a6d76a4ef5f5f90c74 @@ -2596,7 +2596,7 @@ SPEC CHECKSUMS: ReactAppDependencyProvider: 5787b37b8e2e51dfeab697ec031cc7c4080dcea2 ReactCodegen: d07ee3c8db75b43d1cbe479ae6affebf9925c733 ReactCommon: fe2a3af8975e63efa60f95fca8c34dc85deee360 - ReactNativeDependencies: 4d5ce2683b6d74f7c686bf90a88c7d381295cf3c + ReactNativeDependencies: 54189f1570b1308686cb21564e755e1daa77ea03 RNAudioAPI: 50957b72cc742b9aa1e05349be71b9db73c9cf74 RNAudioWorklets: ff0c53fd3c3bbffacb7dd3beb03ffe0ea9f1fd05 RNGestureHandler: 187c5c7936abf427bc4d22d6c3b1ac80ad1f63c0 From c54511c97b2be07520ceee04344f2ed0d78ea0d3 Mon Sep 17 00:00:00 2001 From: Marek Malek Date: Wed, 12 Aug 2026 20:18:12 +0200 Subject: [PATCH 3/7] fix: compile test commands by default --- .../common/cpp/clangd/CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt b/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt index aebc60076..a4d74dfa8 100644 --- a/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt +++ b/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt @@ -5,8 +5,11 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 20) set(COMMON_CPP_DIR ${CMAKE_SOURCE_DIR}/..) -# Toggle: set to 1 to enable test mode, 0 for normal mode -set(RN_AUDIO_API_TEST 0) +# common/cpp/test/*.cpp is globbed in below unconditionally, and the real +# test build (common/cpp/test/CMakeLists.txt) always compiles with this set +# to 1 — test-only members like AudioGraph::Node::test_node_identifier__ only +# exist under it, so 0 here would guarantee clangd errors on every test file. +set(RN_AUDIO_API_TEST 1) set(REACT_NATIVE_DIR ../../../../../node_modules/react-native) include(FetchContent) From 74b5d74817cff1dd8a9923e7b06612f936bcbcc1 Mon Sep 17 00:00:00 2001 From: Marek Malek Date: Wed, 12 Aug 2026 20:19:10 +0200 Subject: [PATCH 4/7] fix: objective c/c++ edgecases --- packages/react-native-audio-api/.clangd | 25 ++++++++++++++++++- .../common/cpp/clangd/CMakeLists.txt | 20 ++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/react-native-audio-api/.clangd b/packages/react-native-audio-api/.clangd index e735c6397..4348f02ec 100644 --- a/packages/react-native-audio-api/.clangd +++ b/packages/react-native-audio-api/.clangd @@ -3,9 +3,32 @@ Documentation: CompileFlags: Add: - - -std=c++20 - -Wall Diagnostics: ClangTidy: FastCheckFilter: Loose + +--- +# -std=c++20 is invalid for plain Objective-C (.m) sources — clang's driver +# rejects it outright. Scoped out here instead of left in the unconditional +# block above, which applied it to every file regardless of language. +If: + PathExclude: .*\.m +CompileFlags: + Add: + - -std=c++20 + +--- +# ios/audioapi/ios headers use `#ifndef __OBJC__` to fall back to opaque +# typedefs for AVFoundation types (AVAudioFormat, etc.) so they stay +# includable from plain C++. That means opening one directly (rather than via +# its owning .mm, which already #imports AVFoundation) parses with __OBJC__ +# defined but no AVFoundation import — force both here. +If: + # PathMatch fully-matches, and which root it's relative to isn't + # documented reliably — no leading anchor, so it matches whether clangd + # resolves this against the package dir, the repo root, or an absolute path. + PathMatch: .*ios/audioapi/ios/.*\.h +CompileFlags: + Add: [-x, objective-c++, -include, AVFoundation/AVFoundation.h] diff --git a/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt b/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt index a4d74dfa8..854430839 100644 --- a/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt +++ b/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt @@ -80,16 +80,27 @@ target_link_libraries(rnaudioapi_cursor ${LINK_LIBRARIES} gtest gmock gtest_main # ============================================================================ if(APPLE) enable_language(OBJCXX) + enable_language(OBJC) set(IOS_DIR ${COMMON_CPP_DIR}/../../ios) + # The podspec compiles both .mm (Objective-C++) and plain .m (Objective-C, + # e.g. NativeAudioPlayer.m/NativeAudioRecorder.m) — source_files = "ios/audioapi/**/*.{mm,h,m,hpp}". + # Kept as separate targets: a shared OBJECT library set CMAKE_CXX_STANDARD's + # "-std=c++20" on the .m (plain Objective-C) sources too, which clang + # rejects outright for that language. file(GLOB_RECURSE IOS_MM_SOURCES CONFIGURE_DEPENDS "${IOS_DIR}/audioapi/ios/*.mm") + file(GLOB_RECURSE IOS_M_SOURCES CONFIGURE_DEPENDS "${IOS_DIR}/audioapi/ios/*.m") add_library(rnaudioapi_cursor_ios OBJECT ${IOS_MM_SOURCES}) - set_source_files_properties(${IOS_MM_SOURCES} PROPERTIES COMPILE_FLAGS "-fobjc-arc") + add_library(rnaudioapi_cursor_ios_objc OBJECT ${IOS_M_SOURCES}) + set_source_files_properties(${IOS_MM_SOURCES} ${IOS_M_SOURCES} PROPERTIES COMPILE_FLAGS "-fobjc-arc") target_compile_definitions(rnaudioapi_cursor_ios PRIVATE RN_AUDIO_API_TEST=${RN_AUDIO_API_TEST} ) + target_compile_definitions(rnaudioapi_cursor_ios_objc PRIVATE + RN_AUDIO_API_TEST=${RN_AUDIO_API_TEST} + ) # System frameworks (Foundation, AVFoundation, AudioToolbox, ...) are found # automatically once -isysroot points at an SDK. @@ -101,6 +112,7 @@ if(APPLE) ) if(IOS_SDK_PATH) target_compile_options(rnaudioapi_cursor_ios PRIVATE -isysroot "${IOS_SDK_PATH}") + target_compile_options(rnaudioapi_cursor_ios_objc PRIVATE -isysroot "${IOS_SDK_PATH}") endif() # React/Pod headers (React-Core, ReactCommon, ReactCodegen, ...) — rather @@ -124,9 +136,7 @@ if(APPLE) message(WARNING "clangd/CMakeLists.txt: ${PODS_XCCONFIG} not found — run `pod install` in apps/fabric-example/ios to resolve React/Pod headers for iOS files.") endif() - target_include_directories( - rnaudioapi_cursor_ios - PRIVATE + set(IOS_EXTRA_INCLUDES "${COMMON_CPP_DIR}" "${IOS_DIR}" # CocoaPods' header_mappings_dir flattens ios/audioapi's own headers so @@ -137,4 +147,6 @@ if(APPLE) "${IOS_DIR}/audioapi/ios/system" ${IOS_POD_INCLUDE_DIRS} ) + target_include_directories(rnaudioapi_cursor_ios PRIVATE ${IOS_EXTRA_INCLUDES}) + target_include_directories(rnaudioapi_cursor_ios_objc PRIVATE ${IOS_EXTRA_INCLUDES}) endif() \ No newline at end of file From 65385ba22f28f0c4c3beb388f1526a2f8dc69c16 Mon Sep 17 00:00:00 2001 From: Marek Malek Date: Thu, 13 Aug 2026 11:13:32 +0200 Subject: [PATCH 5/7] fix: session-order resolution, standalone headers --- packages/react-native-audio-api/.clangd | 17 +-- .../common/cpp/clangd/CMakeLists.txt | 109 ++++++++++++------ 2 files changed, 76 insertions(+), 50 deletions(-) diff --git a/packages/react-native-audio-api/.clangd b/packages/react-native-audio-api/.clangd index 4348f02ec..3b578cbdb 100644 --- a/packages/react-native-audio-api/.clangd +++ b/packages/react-native-audio-api/.clangd @@ -10,25 +10,10 @@ Diagnostics: FastCheckFilter: Loose --- -# -std=c++20 is invalid for plain Objective-C (.m) sources — clang's driver -# rejects it outright. Scoped out here instead of left in the unconditional -# block above, which applied it to every file regardless of language. +# clang rejects -std=c++20 outright for plain Objective-C (.m). If: PathExclude: .*\.m CompileFlags: Add: - -std=c++20 ---- -# ios/audioapi/ios headers use `#ifndef __OBJC__` to fall back to opaque -# typedefs for AVFoundation types (AVAudioFormat, etc.) so they stay -# includable from plain C++. That means opening one directly (rather than via -# its owning .mm, which already #imports AVFoundation) parses with __OBJC__ -# defined but no AVFoundation import — force both here. -If: - # PathMatch fully-matches, and which root it's relative to isn't - # documented reliably — no leading anchor, so it matches whether clangd - # resolves this against the package dir, the repo root, or an absolute path. - PathMatch: .*ios/audioapi/ios/.*\.h -CompileFlags: - Add: [-x, objective-c++, -include, AVFoundation/AVFoundation.h] diff --git a/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt b/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt index 854430839..96fc1b904 100644 --- a/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt +++ b/packages/react-native-audio-api/common/cpp/clangd/CMakeLists.txt @@ -5,11 +5,9 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 20) set(COMMON_CPP_DIR ${CMAKE_SOURCE_DIR}/..) -# common/cpp/test/*.cpp is globbed in below unconditionally, and the real -# test build (common/cpp/test/CMakeLists.txt) always compiles with this set -# to 1 — test-only members like AudioGraph::Node::test_node_identifier__ only -# exist under it, so 0 here would guarantee clangd errors on every test file. -set(RN_AUDIO_API_TEST 1) +# =1 swaps real types for dummy stand-ins too (e.g. JsiBuffer), so production +# code needs =0; only test/*.cpp needs =1 — split via separate targets below. +set(RN_AUDIO_API_TEST 0) set(REACT_NATIVE_DIR ../../../../../node_modules/react-native) include(FetchContent) @@ -22,10 +20,11 @@ set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) # ------------------------------------- -file(GLOB_RECURSE COMMON_CPP_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/audioapi/*.cpp" "${COMMON_CPP_DIR}/audioapi/*.c" "${COMMON_CPP_DIR}/test/*.cpp") +file(GLOB_RECURSE COMMON_CPP_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/audioapi/*.cpp" "${COMMON_CPP_DIR}/audioapi/*.c") list(REMOVE_ITEM COMMON_CPP_SOURCES "${COMMON_CPP_DIR}/audioapi/HostObjects/inputs/AudioRecorderHostObject.cpp" ) +file(GLOB_RECURSE TEST_CPP_SOURCES CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/test/*.cpp") set(INCLUDE_DIR ${COMMON_CPP_DIR}/audioapi/external/include) set(FFMPEG_INCLUDE_DIR ${COMMON_CPP_DIR}/audioapi/external/include_ffmpeg) @@ -48,9 +47,7 @@ target_compile_definitions(rnaudioapi_cursor PRIVATE RN_AUDIO_API_TEST=${RN_AUDIO_API_TEST} ) -target_include_directories( - rnaudioapi_cursor - PRIVATE +set(COMMON_EXTRA_INCLUDES "${COMMON_CPP_DIR}" "${INCLUDE_DIR}" "${INCLUDE_DIR}/opus" @@ -61,33 +58,61 @@ target_include_directories( "${COMMON_CPP_DIR}/../../ios" "${googletest_SOURCE_DIR}/googletest/include" ) +target_include_directories(rnaudioapi_cursor PRIVATE ${COMMON_EXTRA_INCLUDES}) target_include_directories(rnaudioapi_cursor PUBLIC ${INCLUDE_LIBRARIES}) target_link_libraries(rnaudioapi_cursor ${LINK_LIBRARIES} gtest gmock gtest_main) -# Android JNI glue (android/src/main/cpp/audioapi) is intentionally NOT built -# here: those sources only compile correctly with the NDK's own clang++ given -# --target=...-linux-android and --sysroot=/sysroot. Bolting the NDK -# sysroot onto the host AppleClang via a plain -I mixes NDK libc headers with -# macOS's libc++, which breaks even /. generate-and-copy.sh -# instead merges in the real compile commands Gradle already produced under -# android/.cxx/ (see that script for details). - -# ============================================================================ -# iOS Objective-C++ glue (ios/audioapi/ios) — OBJECT library purely so clangd -# gets a compile command per file; never actually linked. -# ============================================================================ +# Mirrors common/cpp/test/CMakeLists.txt's definitions exactly. +add_library(rnaudioapi_cursor_test OBJECT ${TEST_CPP_SOURCES}) +target_compile_definitions(rnaudioapi_cursor_test PRIVATE + RN_AUDIO_API_TEST=1 + RN_AUDIO_API_FFMPEG_DISABLED=1 + RN_AUDIO_API_STATIC_EXTERNAL_LIBS_DISABLED=1 + MA_NO_LIBOPUS=1 + MA_NO_LIBVORBIS=1 +) +target_include_directories(rnaudioapi_cursor_test PRIVATE ${COMMON_EXTRA_INCLUDES}) +# OBJECT libs never link, but this still pulls in gtest/gmock's include dirs. +target_link_libraries(rnaudioapi_cursor_test PRIVATE gtest gmock gtest_main) + +# Headers have no compile command of their own, so clangd falls back to +# "inferred from " — session-order +# dependent and breaks badly across platforms (e.g. borrowed iOS flags can +# lose ). Force real per-header entries instead of relying on that. +file(GLOB_RECURSE COMMON_CPP_HEADERS CONFIGURE_DEPENDS "${COMMON_CPP_DIR}/audioapi/*.h" "${COMMON_CPP_DIR}/audioapi/*.hpp") +# Vendored code isn't self-contained (e.g. FFmpeg needs -D__STDC_CONSTANT_MACROS +# from its caller) and was never meant to compile standalone. +list(FILTER COMMON_CPP_HEADERS EXCLUDE REGEX "/audioapi/external/") +list(FILTER COMMON_CPP_HEADERS EXCLUDE REGEX "/audioapi/libs/") +list(FILTER COMMON_CPP_HEADERS EXCLUDE REGEX "/audioapi/dsp/r8brain/") + +add_library(rnaudioapi_cursor_headers OBJECT ${COMMON_CPP_HEADERS}) +set_source_files_properties(${COMMON_CPP_HEADERS} PROPERTIES + HEADER_FILE_ONLY OFF + LANGUAGE CXX +) +target_compile_definitions(rnaudioapi_cursor_headers PRIVATE + RN_AUDIO_API_TEST=${RN_AUDIO_API_TEST} +) +target_include_directories(rnaudioapi_cursor_headers PRIVATE ${COMMON_EXTRA_INCLUDES}) + +# Android JNI glue (android/src/main/cpp/audioapi) isn't built here: it only +# compiles correctly with the NDK's own clang++ (--target/--sysroot); bolting +# the NDK sysroot onto AppleClang breaks even /. +# generate-and-copy.sh merges in Gradle's real android/.cxx/ commands instead. + +# iOS Objective-C++ glue (ios/audioapi/ios) — OBJECT libs purely for compile +# commands; never actually linked. if(APPLE) enable_language(OBJCXX) enable_language(OBJC) set(IOS_DIR ${COMMON_CPP_DIR}/../../ios) - # The podspec compiles both .mm (Objective-C++) and plain .m (Objective-C, - # e.g. NativeAudioPlayer.m/NativeAudioRecorder.m) — source_files = "ios/audioapi/**/*.{mm,h,m,hpp}". - # Kept as separate targets: a shared OBJECT library set CMAKE_CXX_STANDARD's - # "-std=c++20" on the .m (plain Objective-C) sources too, which clang - # rejects outright for that language. + # Podspec compiles both .mm and plain .m (NativeAudioPlayer.m etc). Separate + # targets: a shared lib would leak CMAKE_CXX_STANDARD's -std=c++20 onto .m, + # which clang rejects outright for plain Objective-C. file(GLOB_RECURSE IOS_MM_SOURCES CONFIGURE_DEPENDS "${IOS_DIR}/audioapi/ios/*.mm") file(GLOB_RECURSE IOS_M_SOURCES CONFIGURE_DEPENDS "${IOS_DIR}/audioapi/ios/*.m") @@ -115,10 +140,8 @@ if(APPLE) target_compile_options(rnaudioapi_cursor_ios_objc PRIVATE -isysroot "${IOS_SDK_PATH}") endif() - # React/Pod headers (React-Core, ReactCommon, ReactCodegen, ...) — rather - # than re-deriving every pod's header path by hand, reuse whatever - # `pod install` already resolved for the example app. Requires - # `cd apps/fabric-example/ios && pod install` to have been run at least once. + # Reuse whatever `pod install` already resolved for the example app rather + # than re-deriving every pod's header path by hand. Requires having run it. set(FABRIC_EXAMPLE_IOS_DIR ${COMMON_CPP_DIR}/../../../../apps/fabric-example/ios) set(PODS_ROOT "${FABRIC_EXAMPLE_IOS_DIR}/Pods") set(PODS_XCCONFIG "${FABRIC_EXAMPLE_IOS_DIR}/Pods/Target Support Files/Pods-FabricExample/Pods-FabricExample.debug.xcconfig") @@ -139,14 +162,32 @@ if(APPLE) set(IOS_EXTRA_INCLUDES "${COMMON_CPP_DIR}" "${IOS_DIR}" - # CocoaPods' header_mappings_dir flattens ios/audioapi's own headers so - # Xcode can resolve bare `#import `-style includes - # via its headermap; a plain include path needs to point directly at the - # containing folder to reproduce that. + # Xcode's headermap resolves bare `#import ` via + # CocoaPods' header_mappings_dir; a plain -I needs the containing folder. "${IOS_DIR}/audioapi/ios/core" "${IOS_DIR}/audioapi/ios/system" ${IOS_POD_INCLUDE_DIRS} ) target_include_directories(rnaudioapi_cursor_ios PRIVATE ${IOS_EXTRA_INCLUDES}) target_include_directories(rnaudioapi_cursor_ios_objc PRIVATE ${IOS_EXTRA_INCLUDES}) + + # Same reasoning as the common headers above. These headers also rely on + # their .mm caller having already #imported AVFoundation (see the + # `#ifndef __OBJC__` guard in e.g. IOSRecorderCallback.h) — `-include` + # reproduces that here. + file(GLOB_RECURSE IOS_HEADERS CONFIGURE_DEPENDS "${IOS_DIR}/audioapi/ios/*.h") + + add_library(rnaudioapi_cursor_ios_headers OBJECT ${IOS_HEADERS}) + set_source_files_properties(${IOS_HEADERS} PROPERTIES + HEADER_FILE_ONLY OFF + LANGUAGE OBJCXX + COMPILE_FLAGS "-include AVFoundation/AVFoundation.h" + ) + target_compile_definitions(rnaudioapi_cursor_ios_headers PRIVATE + RN_AUDIO_API_TEST=${RN_AUDIO_API_TEST} + ) + target_include_directories(rnaudioapi_cursor_ios_headers PRIVATE ${IOS_EXTRA_INCLUDES}) + if(IOS_SDK_PATH) + target_compile_options(rnaudioapi_cursor_ios_headers PRIVATE -isysroot "${IOS_SDK_PATH}") + endif() endif() \ No newline at end of file From feb6673eee22d471d71b8eeea3451dc05f8dbe6b Mon Sep 17 00:00:00 2001 From: Marek Malek Date: Thu, 13 Aug 2026 11:18:07 +0200 Subject: [PATCH 6/7] feat: add yarn setup command --- package.json | 4 +++- .../common/cpp/clangd/SETUP.md | 20 ++++++++++------ .../common/cpp/clangd/generate-and-copy.sh | 24 ++++++++++--------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 7c4aff502..d96878f84 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,9 @@ "validate:graph": "bash scripts/validate.sh --graph", "validate:android": "bash scripts/validate.sh --android", "validate:ios": "bash scripts/validate.sh --ios", - "validate:full": "bash scripts/validate.sh --full" + "validate:full": "bash scripts/validate.sh --full", + "setup:clangd": "bash packages/react-native-audio-api/common/cpp/clangd/generate-and-copy.sh", + "setup:clangd:clean": "bash packages/react-native-audio-api/common/cpp/clangd/generate-and-copy.sh --clean" }, "devDependencies": { "@commitlint/config-conventional": "17.0.2", diff --git a/packages/react-native-audio-api/common/cpp/clangd/SETUP.md b/packages/react-native-audio-api/common/cpp/clangd/SETUP.md index 5e20c862d..6e72784be 100644 --- a/packages/react-native-audio-api/common/cpp/clangd/SETUP.md +++ b/packages/react-native-audio-api/common/cpp/clangd/SETUP.md @@ -2,15 +2,21 @@ **Generate compile_commands.json** -From this directory (`common/cpp/clangd`): - ```bash -./generate-and-copy.sh +yarn setup:clangd ``` -This generates entries for `common/cpp`, the Android JNI glue (`android/src/main/cpp/audioapi`), and the iOS Objective-C++ glue (`ios/audioapi/ios`), then copies the result to the repo root. +Generates entries for `common/cpp` (sources + headers), the iOS Objective-C++/Objective-C glue (`ios/audioapi/ios`), and merges in Android's real NDK-compiled entries from `android/.cxx` (see `generate-and-copy.sh`), then copies the result to the repo root. + +**Prerequisites** + +- **Android**: needs a real Gradle build's `android/.cxx` output — this CMakeLists.txt can't safely reproduce NDK cross-compile flags itself. +- **iOS**: needs `apps/fabric-example/ios/Pods` — reuses its generated xcconfig for React/Pod header paths. -**Prerequisites for full header resolution** +If either is missing or stale (e.g. after a dependency bump), refresh both first: + +```bash +yarn setup:clangd:clean +``` -- **Android**: `fbjni`/`oboe`/`react-android` headers are resolved from your local Gradle dependency cache (`~/.gradle/caches`, or `$GRADLE_USER_HOME`), and the NDK sysroot from `$ANDROID_HOME/ndk`. Build the Android app (or open it in Android Studio) at least once to populate the cache — a missing cache just means fewer include paths, not a hard failure. -- **iOS**: React/Pod headers are reused from whatever `apps/fabric-example/ios/Pods` already has, via its generated xcconfig. Run `pod install` there at least once (see the [build skill](../../../../.claude/skills/build-compilation-dependencies/SKILL.md)). +This runs `pod deintegrate && pod install` and a full Android build, then regenerates — slow, only needed when native deps/config actually change. diff --git a/packages/react-native-audio-api/common/cpp/clangd/generate-and-copy.sh b/packages/react-native-audio-api/common/cpp/clangd/generate-and-copy.sh index 43d3da115..a6ab1f134 100755 --- a/packages/react-native-audio-api/common/cpp/clangd/generate-and-copy.sh +++ b/packages/react-native-audio-api/common/cpp/clangd/generate-and-copy.sh @@ -8,6 +8,13 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)" PACKAGE_DIR="$(cd "$SCRIPT_DIR/../../.." && pwd)" WORKLETS_DB="$REPO_ROOT/packages/react-native-audio-worklets/compile_commands.json" +# --clean: refresh the real Pods/Gradle builds this script depends on before +# regenerating. Slow — only needed after native deps/config actually change. +if [[ "${1:-}" == "--clean" ]]; then + (cd "$REPO_ROOT/apps/fabric-example/ios" && pod deintegrate && pod install) + (cd "$REPO_ROOT" && yarn workspace react-native-audio-api run build:android) +fi + cmake -B build . cp build/compile_commands.json "$REPO_ROOT/compile_commands.json" @@ -30,13 +37,10 @@ print(f"Preserved worklets entries in {root_db} (total {len(by_file)})") PY fi -# Merge in Android compile commands Gradle already produced under android/.cxx. -# Those sources only compile correctly with the NDK's own clang++ plus -# --target=...-linux-android --sysroot=/sysroot; this CMakeLists.txt -# can't reproduce that safely with the host compiler (mixing NDK libc headers -# into macOS's libc++ breaks even /), so we reuse Gradle's real -# build instead of guessing flags. Requires having built the Android app (or -# opened it in Android Studio) at least once. +# Merge in Android compile commands Gradle already produced under android/.cxx +# — those sources need the NDK's own clang++ (--target/--sysroot), which this +# CMakeLists.txt can't reproduce safely with the host compiler. Requires +# having built the Android app (or opened it in Android Studio) once. python3 - < Date: Thu, 13 Aug 2026 16:29:07 +0200 Subject: [PATCH 7/7] ci: trigger