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 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/.clangd b/packages/react-native-audio-api/.clangd index e735c6397..3b578cbdb 100644 --- a/packages/react-native-audio-api/.clangd +++ b/packages/react-native-audio-api/.clangd @@ -3,9 +3,17 @@ Documentation: CompileFlags: Add: - - -std=c++20 - -Wall Diagnostics: ClangTidy: FastCheckFilter: Loose + +--- +# clang rejects -std=c++20 outright for plain Objective-C (.m). +If: + PathExclude: .*\.m +CompileFlags: + Add: + - -std=c++20 + 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..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,7 +5,8 @@ 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 +# =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) @@ -19,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) @@ -45,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" @@ -58,7 +58,136 @@ 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) \ No newline at end of file +target_link_libraries(rnaudioapi_cursor ${LINK_LIBRARIES} gtest gmock gtest_main) + +# 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) + # 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") + + add_library(rnaudioapi_cursor_ios OBJECT ${IOS_MM_SOURCES}) + 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. + 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}") + target_compile_options(rnaudioapi_cursor_ios_objc PRIVATE -isysroot "${IOS_SDK_PATH}") + endif() + + # 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") + + 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() + + set(IOS_EXTRA_INCLUDES + "${COMMON_CPP_DIR}" + "${IOS_DIR}" + # 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 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..6e72784be 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,22 @@ -`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`): +```bash +yarn setup:clangd +``` + +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. + +If either is missing or stale (e.g. after a dependency bump), refresh both first: ```bash -./generate-and-copy.sh +yarn setup:clangd:clean ``` + +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 0714f30d8..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 @@ -5,8 +5,16 @@ 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" +# --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" @@ -28,3 +36,41 @@ 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 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 - <