diff --git a/.gitignore b/.gitignore index e0918fde3..802574e66 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ apple/build-*/ apple/*.xcframework +apple/*.xcframework.zip apple/libcactus*.a android/libcactus.so android/libcactus.a @@ -20,6 +21,7 @@ android/libs/ android/arm64-v8a/ bindings/flutter/libcactus.so bindings/flutter/*.xcframework +bindings/swift/package .claude .vscode diff --git a/apple/CMakeLists.txt b/apple/CMakeLists.txt index 510dfa64a..53dcf8aae 100644 --- a/apple/CMakeLists.txt +++ b/apple/CMakeLists.txt @@ -15,9 +15,15 @@ set(_DEPS cactus_engine cactus_graph cactus_kernels) if(BUILD_SHARED_LIBS) file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/stub.cpp "") add_library(cactus_framework SHARED ${CMAKE_CURRENT_BINARY_DIR}/stub.cpp) - set_target_properties(cactus_framework PROPERTIES OUTPUT_NAME cactus) add_dependencies(cactus_framework ${_DEPS}) + find_library(ACCELERATE_FRAMEWORK Accelerate REQUIRED) + find_library(COREML_FRAMEWORK CoreML REQUIRED) + find_library(FOUNDATION_FRAMEWORK Foundation REQUIRED) + find_library(SECURITY_FRAMEWORK Security) + find_library(SYSTEMCONFIGURATION_FRAMEWORK SystemConfiguration) + find_library(CFNETWORK_FRAMEWORK CFNetwork) + target_link_options(cactus_framework PRIVATE -force_load $ -force_load $ @@ -26,25 +32,49 @@ if(BUILD_SHARED_LIBS) ) target_link_libraries(cactus_framework PUBLIC - "-framework Accelerate" "-framework CoreML" "-framework Foundation" - "-framework Security" "-framework SystemConfiguration" "-framework CFNetwork" + ${ACCELERATE_FRAMEWORK} ${COREML_FRAMEWORK} ${FOUNDATION_FRAMEWORK} ) - if(CMAKE_SYSTEM_NAME STREQUAL "iOS") - string(TOLOWER "${CMAKE_OSX_SYSROOT}" _sysroot_lower) - if(_sysroot_lower MATCHES "simulator") - set(_CURL "${CACTUS_CURL_ROOT}/ios/simulator/libcurl.a") - else() - set(_CURL "${CACTUS_CURL_ROOT}/ios/device/libcurl.a") + string(TOLOWER "${CMAKE_OSX_SYSROOT}" _sysroot_lower) + if(_sysroot_lower MATCHES "iphonesimulator") + set(_CURL "${CACTUS_CURL_ROOT}/ios/simulator/libcurl.a") + elseif(_sysroot_lower MATCHES "iphoneos") + set(_CURL "${CACTUS_CURL_ROOT}/ios/device/libcurl.a") + elseif(_sysroot_lower MATCHES "appletvsimulator") + set(_CURL "${CACTUS_CURL_ROOT}/tvos/simulator/libcurl.a") + elseif(_sysroot_lower MATCHES "appletvos") + set(_CURL "${CACTUS_CURL_ROOT}/tvos/device/libcurl.a") + elseif(_sysroot_lower MATCHES "watchsimulator") + set(_CURL "${CACTUS_CURL_ROOT}/watchos/simulator/libcurl.a") + elseif(_sysroot_lower MATCHES "watchos") + string(TOLOWER "${CMAKE_OSX_ARCHITECTURES}" _archs_lower) + if(_archs_lower MATCHES "arm64_32") + set(_CURL "${CACTUS_CURL_ROOT}/watchos/device-arm64_32/libcurl.a") + elseif(_archs_lower MATCHES "arm64") + set(_CURL "${CACTUS_CURL_ROOT}/watchos/device-arm64/libcurl.a") endif() + elseif(_sysroot_lower MATCHES "xrsimulator") + set(_CURL "${CACTUS_CURL_ROOT}/visionos/simulator/libcurl.a") + elseif(_sysroot_lower MATCHES "xros") + set(_CURL "${CACTUS_CURL_ROOT}/visionos/device/libcurl.a") else() set(_CURL "${CACTUS_CURL_ROOT}/macos/libcurl.a") endif() if(EXISTS "${_CURL}") + if(NOT SECURITY_FRAMEWORK OR NOT CFNETWORK_FRAMEWORK) + message(FATAL_ERROR "Required Apple networking frameworks were not found for vendored libcurl linking") + endif() + target_link_libraries(cactus_framework PUBLIC "${_CURL}") + target_link_libraries(cactus_framework PUBLIC ${SECURITY_FRAMEWORK} ${CFNETWORK_FRAMEWORK}) + + if(SYSTEMCONFIGURATION_FRAMEWORK) + target_link_libraries(cactus_framework PUBLIC ${SYSTEMCONFIGURATION_FRAMEWORK}) + endif() endif() set_target_properties(cactus_framework PROPERTIES + OUTPUT_NAME cactus FRAMEWORK TRUE FRAMEWORK_VERSION A MACOSX_FRAMEWORK_IDENTIFIER com.cactuscompute.cactus @@ -57,7 +87,11 @@ if(BUILD_SHARED_LIBS) COMMAND ${CMAKE_COMMAND} -E make_directory "$/Headers" COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/../cactus-engine/cactus_engine.h" - "$/Headers" + "$/Headers/cactus.h" + COMMAND ${CMAKE_COMMAND} -E make_directory "$/Modules" + COMMAND ${CMAKE_COMMAND} -E copy + "${CMAKE_CURRENT_SOURCE_DIR}/module.modulemap" + "$/Modules/module.modulemap" ) if(CMAKE_SYSTEM_NAME STREQUAL "iOS") diff --git a/apple/build.sh b/apple/build.sh index fddffaea7..953abe493 100755 --- a/apple/build.sh +++ b/apple/build.sh @@ -9,12 +9,22 @@ BUILD_STATIC=${BUILD_STATIC:-true} BUILD_XCFRAMEWORK=${BUILD_XCFRAMEWORK:-true} CACTUS_CURL_ROOT=${CACTUS_CURL_ROOT:-"$ROOT_DIR/cactus-engine/libs/curl"} -if ! command -v cmake &> /dev/null; then +IOS_DEPLOYMENT_TARGET=${IOS_DEPLOYMENT_TARGET:-13.0} +TVOS_DEPLOYMENT_TARGET=${TVOS_DEPLOYMENT_TARGET:-13.0} +WATCHOS_DEPLOYMENT_TARGET=${WATCHOS_DEPLOYMENT_TARGET:-9.0} +VISIONOS_DEPLOYMENT_TARGET=${VISIONOS_DEPLOYMENT_TARGET:-1.0} +MACOS_DEPLOYMENT_TARGET=${MACOS_DEPLOYMENT_TARGET:-13.0} + +XCFRAMEWORK_PATH="$APPLE_DIR/cactus.xcframework" +XCFRAMEWORK_ZIP_PATH="$APPLE_DIR/cactus.xcframework.zip" +LAST_FRAMEWORK_PATH="" + +if ! command -v cmake >/dev/null 2>&1; then echo "Error: cmake not found, please install it" exit 1 fi -if ! xcode-select -p &> /dev/null; then +if ! xcode-select -p >/dev/null 2>&1; then echo "Error: Xcode command line tools not found" echo "Install with: xcode-select --install" exit 1 @@ -29,225 +39,219 @@ echo "Static library: $BUILD_STATIC" echo "XCFramework: $BUILD_XCFRAMEWORK" echo "Vendored libcurl root: $CACTUS_CURL_ROOT" -function cp_headers() { - mkdir -p "$ROOT_DIR/apple/$1/$2/cactus.framework/Headers" - cp "$ROOT_DIR/cactus-engine/cactus_engine.h" "$ROOT_DIR/apple/$1/$2/cactus.framework/Headers/" -} - -function create_ios_xcframework_info_plist() { - cat > "$ROOT_DIR/apple/cactus-ios.xcframework/Info.plist" << 'EOF' - - - - - AvailableLibraries - - - LibraryIdentifier - ios-arm64 - LibraryPath - cactus.framework - SupportedArchitectures - - arm64 - - SupportedPlatform - ios - - - LibraryIdentifier - ios-arm64-simulator - LibraryPath - cactus.framework - SupportedArchitectures - - arm64 - - SupportedPlatform - ios - SupportedPlatformVariant - simulator - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - -EOF -} - -function create_macos_xcframework_info_plist() { - cat > "$ROOT_DIR/apple/cactus-macos.xcframework/Info.plist" << 'EOF' - - - - - AvailableLibraries - - - LibraryIdentifier - macos-arm64 - LibraryPath - cactus.framework - SupportedArchitectures - - arm64 - - SupportedPlatform - macos - - - CFBundlePackageType - XFWK - XCFrameworkFormatVersion - 1.0 - - -EOF -} - -function build_static_library() { +build_static_library() { echo "Building static library for iOS device..." - BUILD_DIR="$APPLE_DIR/build-static-device" - - IOS_SDK_PATH=$(xcrun --sdk iphoneos --show-sdk-path) - if [ -z "$IOS_SDK_PATH" ] || [ ! -d "$IOS_SDK_PATH" ]; then + local build_dir="$APPLE_DIR/build-static-device" + local ios_sdk_path + + ios_sdk_path=$(xcrun --sdk iphoneos --show-sdk-path) + if [ -z "$ios_sdk_path" ] || [ ! -d "$ios_sdk_path" ]; then echo "Error: iOS SDK not found. Make sure Xcode is installed." exit 1 fi - echo "Using iOS SDK: $IOS_SDK_PATH" - cmake -DCMAKE_SYSTEM_NAME=iOS \ -DCMAKE_OSX_ARCHITECTURES=arm64 \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \ - -DCMAKE_OSX_SYSROOT="$IOS_SDK_PATH" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET="$IOS_DEPLOYMENT_TARGET" \ + -DCMAKE_OSX_SYSROOT="$ios_sdk_path" \ -DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TYPE" \ -DBUILD_SHARED_LIBS=OFF \ -DCACTUS_CURL_ROOT="$CACTUS_CURL_ROOT" \ -S "$APPLE_DIR" \ - -B "$BUILD_DIR" >/dev/null + -B "$build_dir" >/dev/null - cmake --build "$BUILD_DIR" --config "$CMAKE_BUILD_TYPE" -j "$n_cpu" >/dev/null + cmake --build "$build_dir" --config "$CMAKE_BUILD_TYPE" -j "$n_cpu" >/dev/null - mkdir -p "$APPLE_DIR" - cp "$BUILD_DIR/libcactus.a" "$APPLE_DIR/libcactus-device.a" + cp "$build_dir/libcactus.a" "$APPLE_DIR/libcactus-device.a" echo "Device static library built: $APPLE_DIR/libcactus-device.a" - + echo "Building static library for iOS simulator..." - BUILD_DIR_SIM="$APPLE_DIR/build-static-simulator" - - IOS_SIM_SDK_PATH=$(xcrun --sdk iphonesimulator --show-sdk-path) - if [ -z "$IOS_SIM_SDK_PATH" ] || [ ! -d "$IOS_SIM_SDK_PATH" ]; then + local build_dir_sim="$APPLE_DIR/build-static-simulator" + local ios_sim_sdk_path + + ios_sim_sdk_path=$(xcrun --sdk iphonesimulator --show-sdk-path) + if [ -z "$ios_sim_sdk_path" ] || [ ! -d "$ios_sim_sdk_path" ]; then echo "Error: iOS Simulator SDK not found. Make sure Xcode is installed." exit 1 fi - echo "Using iOS Simulator SDK: $IOS_SIM_SDK_PATH" - cmake -DCMAKE_SYSTEM_NAME=iOS \ -DCMAKE_OSX_ARCHITECTURES=arm64 \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \ - -DCMAKE_OSX_SYSROOT="$IOS_SIM_SDK_PATH" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET="$IOS_DEPLOYMENT_TARGET" \ + -DCMAKE_OSX_SYSROOT="$ios_sim_sdk_path" \ -DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TYPE" \ -DBUILD_SHARED_LIBS=OFF \ -DCACTUS_CURL_ROOT="$CACTUS_CURL_ROOT" \ -S "$APPLE_DIR" \ - -B "$BUILD_DIR_SIM" >/dev/null + -B "$build_dir_sim" >/dev/null - cmake --build "$BUILD_DIR_SIM" --config "$CMAKE_BUILD_TYPE" -j "$n_cpu" >/dev/null + cmake --build "$build_dir_sim" --config "$CMAKE_BUILD_TYPE" -j "$n_cpu" >/dev/null - cp "$BUILD_DIR_SIM/libcactus.a" "$APPLE_DIR/libcactus-simulator.a" + cp "$build_dir_sim/libcactus.a" "$APPLE_DIR/libcactus-simulator.a" echo "Simulator static library built: $APPLE_DIR/libcactus-simulator.a" } -function build_framework() { - echo "Building framework for $4..." - cd "$5" +build_framework_slice() { + local platform_name="$1" + local system_name="$2" + local sdk_name="$3" + local arch="$4" + local deployment_target="$5" + local build_dir="$6" + local sdk_path + local framework_path="" + + sdk_path=$(xcrun --sdk "$sdk_name" --show-sdk-path) + if [ -z "$sdk_path" ] || [ ! -d "$sdk_path" ]; then + echo "Error: SDK $sdk_name not found. Make sure Xcode is installed." + exit 1 + fi + + echo "Building $platform_name ($system_name, $sdk_name, $arch)..." + + rm -rf "$build_dir" cmake -S "$ROOT_DIR/apple" \ - -B . \ + -B "$build_dir" \ -GXcode \ - -DCMAKE_SYSTEM_NAME=$1 \ - -DCMAKE_OSX_ARCHITECTURES="$2" \ - -DCMAKE_OSX_SYSROOT=$3 \ - -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0 \ + -DCMAKE_SYSTEM_NAME="$system_name" \ + -DCMAKE_OSX_ARCHITECTURES="$arch" \ + -DCMAKE_OSX_SYSROOT="$sdk_path" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET="$deployment_target" \ -DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TYPE" \ -DBUILD_SHARED_LIBS=ON \ -DCACTUS_CURL_ROOT="$CACTUS_CURL_ROOT" \ -DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO \ - -DCMAKE_IOS_INSTALL_COMBINED=YES >/dev/null - - cmake --build . --config "$CMAKE_BUILD_TYPE" -j "$n_cpu" >/dev/null 2>&1 - - DEST_DIR="$ROOT_DIR/apple/$6/$4" - - # Try different possible framework locations - FRAMEWORK_SRC="" - if [ -d "$CMAKE_BUILD_TYPE-$3/cactus.framework" ]; then - FRAMEWORK_SRC="$CMAKE_BUILD_TYPE-$3/cactus.framework" - elif [ -d "$CMAKE_BUILD_TYPE/cactus.framework" ]; then - FRAMEWORK_SRC="$CMAKE_BUILD_TYPE/cactus.framework" + -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=NO \ + -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=NO \ + -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY="" >/dev/null + + cmake --build "$build_dir" --config "$CMAKE_BUILD_TYPE" -j "$n_cpu" >/dev/null + + if [ -d "$build_dir/lib/$CMAKE_BUILD_TYPE/cactus.framework" ]; then + framework_path="$build_dir/lib/$CMAKE_BUILD_TYPE/cactus.framework" + elif [ -d "$build_dir/$CMAKE_BUILD_TYPE-$sdk_name/cactus.framework" ]; then + framework_path="$build_dir/$CMAKE_BUILD_TYPE-$sdk_name/cactus.framework" + elif [ -d "$build_dir/$CMAKE_BUILD_TYPE/cactus.framework" ]; then + framework_path="$build_dir/$CMAKE_BUILD_TYPE/cactus.framework" else - # Find the framework in any subdirectory - FRAMEWORK_SRC=$(find . -name "cactus.framework" -type d | head -n 1) + framework_path=$(find "$build_dir" -path "*cactus.framework" -not -path "*EagerLinkingTBDs*" | head -n 1) fi - - FRAMEWORK_DEST="$DEST_DIR/cactus.framework" - - rm -rf "$DEST_DIR" - mkdir -p "$DEST_DIR" - if [ -n "$FRAMEWORK_SRC" ] && [ -d "$FRAMEWORK_SRC" ]; then - cp -R "$FRAMEWORK_SRC" "$FRAMEWORK_DEST" - echo "Framework copied from $FRAMEWORK_SRC to $FRAMEWORK_DEST" - else - echo "Error: Framework not found in build directory" - echo "Available files:" - find . -name "*.framework" -o -name "libcactus*" 2>/dev/null || true + if [ -z "$framework_path" ] || [ ! -d "$framework_path" ]; then + echo "Error: cactus.framework not found for $platform_name" exit 1 fi - cp_headers $6 $4 - - rm -rf ./* - cd "$ROOT_DIR" + LAST_FRAMEWORK_PATH="$framework_path" } -function build_ios_xcframework() { - echo "Building iOS XCFramework..." - - rm -rf "$ROOT_DIR/apple/cactus-ios.xcframework" - rm -rf "$ROOT_DIR/apple/build-ios" "$ROOT_DIR/apple/build-ios-simulator" - mkdir -p "$ROOT_DIR/apple/build-ios" "$ROOT_DIR/apple/build-ios-simulator" +framework_binary_path() { + local framework_path="$1" - build_framework "iOS" "arm64" "iphoneos" "ios-arm64" "$ROOT_DIR/apple/build-ios" "cactus-ios.xcframework" - - build_framework "iOS" "arm64" "iphonesimulator" "ios-arm64-simulator" "$ROOT_DIR/apple/build-ios-simulator" "cactus-ios.xcframework" + if [ -f "$framework_path/cactus" ]; then + printf '%s\n' "$framework_path/cactus" + elif [ -f "$framework_path/Versions/A/cactus" ]; then + printf '%s\n' "$framework_path/Versions/A/cactus" + else + echo "Error: Framework binary not found in $framework_path" >&2 + exit 1 + fi +} - create_ios_xcframework_info_plist +create_universal_framework() { + local source_framework_a="$1" + local source_framework_b="$2" + local destination_framework="$3" + local binary_a + local binary_b + local destination_binary - rm -rf "$ROOT_DIR/apple/build-ios" "$ROOT_DIR/apple/build-ios-simulator" - - echo "iOS XCFramework built: $ROOT_DIR/apple/cactus-ios.xcframework" -} + mkdir -p "$(dirname "$destination_framework")" + rm -rf "$destination_framework" + cp -R "$source_framework_a" "$destination_framework" -function build_macos_xcframework() { - echo "Building macOS XCFramework..." - - rm -rf "$ROOT_DIR/apple/cactus-macos.xcframework" - rm -rf "$ROOT_DIR/apple/build-macos" - mkdir -p "$ROOT_DIR/apple/build-macos" + binary_a=$(framework_binary_path "$source_framework_a") + binary_b=$(framework_binary_path "$source_framework_b") + destination_binary=$(framework_binary_path "$destination_framework") - build_framework "Darwin" "arm64" "macosx" "macos-arm64" "$ROOT_DIR/apple/build-macos" "cactus-macos.xcframework" + lipo -create "$binary_a" "$binary_b" -output "$destination_binary" +} - create_macos_xcframework_info_plist +build_combined_xcframework() { + echo "Building combined Apple XCFramework..." + + rm -rf "$XCFRAMEWORK_PATH" "$XCFRAMEWORK_ZIP_PATH" + + local build_root="$APPLE_DIR/build-xcframework" + local ios_framework + local ios_sim_framework + local macos_framework + local tvos_framework + local tvos_sim_framework + local watchos_arm64_framework + local watchos_arm64_32_framework + local watchos_device_framework + local watchos_sim_framework + local visionos_framework + local visionos_sim_framework + + build_framework_slice "iOS" "iOS" "iphoneos" "arm64" "$IOS_DEPLOYMENT_TARGET" "$build_root/ios" + ios_framework="$LAST_FRAMEWORK_PATH" + + build_framework_slice "iOS Simulator" "iOS" "iphonesimulator" "arm64" "$IOS_DEPLOYMENT_TARGET" "$build_root/ios-sim" + ios_sim_framework="$LAST_FRAMEWORK_PATH" + + build_framework_slice "macOS" "Darwin" "macosx" "arm64" "$MACOS_DEPLOYMENT_TARGET" "$build_root/macos" + macos_framework="$LAST_FRAMEWORK_PATH" + + build_framework_slice "tvOS" "tvOS" "appletvos" "arm64" "$TVOS_DEPLOYMENT_TARGET" "$build_root/tvos" + tvos_framework="$LAST_FRAMEWORK_PATH" + + build_framework_slice "tvOS Simulator" "tvOS" "appletvsimulator" "arm64" "$TVOS_DEPLOYMENT_TARGET" "$build_root/tvos-sim" + tvos_sim_framework="$LAST_FRAMEWORK_PATH" + + build_framework_slice "watchOS arm64" "watchOS" "watchos" "arm64" "$WATCHOS_DEPLOYMENT_TARGET" "$build_root/watchos-arm64" + watchos_arm64_framework="$LAST_FRAMEWORK_PATH" + + build_framework_slice "watchOS arm64_32" "watchOS" "watchos" "arm64_32" "$WATCHOS_DEPLOYMENT_TARGET" "$build_root/watchos-arm64_32" + watchos_arm64_32_framework="$LAST_FRAMEWORK_PATH" + + watchos_device_framework="$build_root/watchos-device/cactus.framework" + create_universal_framework "$watchos_arm64_framework" "$watchos_arm64_32_framework" "$watchos_device_framework" + + build_framework_slice "watchOS Simulator" "watchOS" "watchsimulator" "arm64" "$WATCHOS_DEPLOYMENT_TARGET" "$build_root/watchos-sim" + watchos_sim_framework="$LAST_FRAMEWORK_PATH" + + build_framework_slice "visionOS" "visionOS" "xros" "arm64" "$VISIONOS_DEPLOYMENT_TARGET" "$build_root/visionos" + visionos_framework="$LAST_FRAMEWORK_PATH" + + build_framework_slice "visionOS Simulator" "visionOS" "xrsimulator" "arm64" "$VISIONOS_DEPLOYMENT_TARGET" "$build_root/visionos-sim" + visionos_sim_framework="$LAST_FRAMEWORK_PATH" + + xcodebuild -create-xcframework \ + -framework "$ios_framework" \ + -framework "$ios_sim_framework" \ + -framework "$macos_framework" \ + -framework "$tvos_framework" \ + -framework "$tvos_sim_framework" \ + -framework "$watchos_device_framework" \ + -framework "$watchos_sim_framework" \ + -framework "$visionos_framework" \ + -framework "$visionos_sim_framework" \ + -output "$XCFRAMEWORK_PATH" >/dev/null + + local macos_framework_dir="$XCFRAMEWORK_PATH/macos-arm64/cactus.framework" + if [ -d "$macos_framework_dir/Versions/A" ]; then + rm -rf "$macos_framework_dir/Headers" "$macos_framework_dir/Modules" + ln -s Versions/A/Headers "$macos_framework_dir/Headers" + ln -s Versions/A/Modules "$macos_framework_dir/Modules" + fi - rm -rf "$ROOT_DIR/apple/build-macos" - - echo "macOS XCFramework built: $ROOT_DIR/apple/cactus-macos.xcframework" + rm -rf "$XCFRAMEWORK_ZIP_PATH" + ditto -c -k --norsrc --keepParent "$XCFRAMEWORK_PATH" "$XCFRAMEWORK_ZIP_PATH" + echo "Combined XCFramework built: $XCFRAMEWORK_PATH" + echo "Combined XCFramework zip: $XCFRAMEWORK_ZIP_PATH" } t0=$(date +%s) @@ -257,8 +261,7 @@ if [ "$BUILD_STATIC" = "true" ]; then fi if [ "$BUILD_XCFRAMEWORK" = "true" ]; then - build_ios_xcframework - build_macos_xcframework + build_combined_xcframework fi t1=$(date +%s) @@ -267,14 +270,15 @@ echo "Build complete!" echo "Total time: $((t1 - t0)) seconds" if [ "$BUILD_STATIC" = "true" ]; then - rm -rf "$APPLE_DIR/build-static-device" "$APPLE_DIR/build-static-simulator" "$APPLE_DIR/build-static-macos" + rm -rf "$APPLE_DIR/build-static-device" "$APPLE_DIR/build-static-simulator" echo "Static libraries:" echo " Device: $APPLE_DIR/libcactus-device.a" echo " Simulator: $APPLE_DIR/libcactus-simulator.a" fi if [ "$BUILD_XCFRAMEWORK" = "true" ]; then - echo "XCFrameworks:" - echo " iOS: $APPLE_DIR/cactus-ios.xcframework" - echo " macOS: $APPLE_DIR/cactus-macos.xcframework" + rm -rf "$APPLE_DIR/build-xcframework" + echo "XCFramework:" + echo " Darwin: $XCFRAMEWORK_PATH" + echo " Darwin zip: $XCFRAMEWORK_ZIP_PATH" fi diff --git a/apple/module.modulemap b/apple/module.modulemap new file mode 100644 index 000000000..8670710ba --- /dev/null +++ b/apple/module.modulemap @@ -0,0 +1,5 @@ +framework module cactus { + umbrella header "cactus.h" + export * + module * { export * } +} diff --git a/bindings/README.md b/bindings/README.md index dd7d63599..84e464c6f 100644 --- a/bindings/README.md +++ b/bindings/README.md @@ -1,7 +1,7 @@ # Bindings - `python/` uses `ctypes` -- `swift/` uses a C module map +- `swift/` uses a generated Swift package - `kotlin/` uses JNI - `flutter/` uses Dart FFI - `rust/` uses raw `extern "C"` declarations diff --git a/bindings/swift/Cactus.swift b/bindings/swift/Cactus.swift deleted file mode 100644 index 44a4cbfe7..000000000 --- a/bindings/swift/Cactus.swift +++ /dev/null @@ -1 +0,0 @@ -@_exported import cactus diff --git a/bindings/swift/README.md b/bindings/swift/README.md index d84ab88d5..824ace83d 100644 --- a/bindings/swift/README.md +++ b/bindings/swift/README.md @@ -1,21 +1,37 @@ # Swift Bindings -C module map import of `cactus_engine.h`. Works on iOS and macOS. +Generated Swift package wrapping `cactus_engine.h`. Works on all Apple platforms, Android, and Linux. ## Integration +Run this command to generate the local Swift package in this directory. ```bash -cactus build --apple +cactus build --swift ``` -**XCFramework**: drag `apple/cactus-ios.xcframework` into Xcode (Embed & Sign). +**Xcode Project:** Add the generated package to you project from the menu `File > Add Package Dependencies > Add Local...` Then, select the generated package in the menu. -**Static library**: add `apple/libcactus-device.a` to Link Binary With Libraries, copy `module.modulemap` and `cactus_engine.h` into Header Search Paths. +**Swift Package:** Add the generated package as a local SPM dependency in your `Package.swift`. +```swift +dependencies: [ + .package(path: ""), + // ... +], +targets: [ + .target( + name: "YourTarget", + dependencies: [ + .product(name: "CactusShims", package: "CactusShims"), + // ... + ] + ) +] +``` ## Usage ```swift -import cactus +import CactusShims let model = cactus_init("/path/to/model", nil, false) var buf = [CChar](repeating: 0, count: 65536) diff --git a/bindings/swift/build.sh b/bindings/swift/build.sh new file mode 100755 index 000000000..bab3ce8f7 --- /dev/null +++ b/bindings/swift/build.sh @@ -0,0 +1,216 @@ +#!/bin/bash -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" +PACKAGE_DIR="$SCRIPT_DIR/package" +BUILD_DIR="$SCRIPT_DIR/build" +PACKAGE_BIN_DIR="$PACKAGE_DIR/bin" +ARTIFACT_BUNDLE_PATH="$PACKAGE_BIN_DIR/cactus_artifact.artifactbundle" +ARTIFACT_BUNDLE_ZIP_PATH="$PACKAGE_BIN_DIR/cactus_artifact.artifactbundle.zip" +DARWIN_XCFRAMEWORK_ZIP_SRC="$ROOT_DIR/apple/cactus.xcframework.zip" +DARWIN_XCFRAMEWORK_ZIP_DST="$PACKAGE_BIN_DIR/cactus.xcframework.zip" + +CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-Release} + +if ! command -v cmake >/dev/null 2>&1; then + echo "Error: cmake not found, please install it" + exit 1 +fi + +n_cpu=$(sysctl -n hw.logicalcpu 2>/dev/null || nproc 2>/dev/null || echo 4) + +VARIANT_PATHS=() +VARIANT_TRIPLES=() + +artifactbundle_init() { + rm -rf "$ARTIFACT_BUNDLE_PATH" "$ARTIFACT_BUNDLE_ZIP_PATH" + mkdir -p "$ARTIFACT_BUNDLE_PATH/dist" "$ARTIFACT_BUNDLE_PATH/include" + cp "$ROOT_DIR/cactus-engine/cactus_engine.h" "$ARTIFACT_BUNDLE_PATH/include/cactus.h" + + cat > "$ARTIFACT_BUNDLE_PATH/include/module.modulemap" << 'EOF' +module cactus_artifact { + header "cactus.h" + export * +} +EOF +} + +artifactbundle_add_variant() { + local relative_path="$1" + local supported_triple="$2" + VARIANT_PATHS+=("$relative_path") + VARIANT_TRIPLES+=("$supported_triple") +} + +artifactbundle_write_info_json() { + local info_file="$ARTIFACT_BUNDLE_PATH/info.json" + { + echo "{" + echo " \"schemaVersion\": \"1.0\"," + echo " \"artifacts\": {" + echo " \"cactus_artifact\": {" + echo " \"type\": \"staticLibrary\"," + echo " \"version\": \"1.0.0\"," + echo " \"variants\": [" + local i + for i in "${!VARIANT_PATHS[@]}"; do + local comma="," + if [ "$i" -eq $((${#VARIANT_PATHS[@]} - 1)) ]; then + comma="" + fi + echo " {" + echo " \"path\": \"${VARIANT_PATHS[$i]}\"," + echo " \"supportedTriples\": [\"${VARIANT_TRIPLES[$i]}\"]," + echo " \"staticLibraryMetadata\": {" + echo " \"headerPaths\": [\"include\"]," + echo " \"moduleMapPath\": \"include/module.modulemap\"" + echo " }" + echo " }$comma" + done + echo " ]" + echo " }" + echo " }" + echo "}" + } > "$info_file" +} + +artifactbundle_finalize() { + artifactbundle_write_info_json + ditto -c -k --norsrc --keepParent "$ARTIFACT_BUNDLE_PATH" "$ARTIFACT_BUNDLE_ZIP_PATH" + rm -rf "$ARTIFACT_BUNDLE_PATH" + echo "Artifact bundle zip: $ARTIFACT_BUNDLE_ZIP_PATH" +} + +build_android_variant() { + echo "Building Android artifact bundle variant..." + "$ROOT_DIR/android/build.sh" + + mkdir -p "$ARTIFACT_BUNDLE_PATH/dist/android" + cp "$ROOT_DIR/android/libcactus.a" "$ARTIFACT_BUNDLE_PATH/dist/android/libcactus.a" + artifactbundle_add_variant "dist/android/libcactus.a" "aarch64-unknown-linux-android" +} + +build_linux_arm_variant() { + echo "Building Linux ARM artifact bundle variant..." + + local out="$BUILD_DIR/linux-arm" + local host_os host_arch toolchain_file + host_os="$(uname -s)" + host_arch="$(uname -m)" + toolchain_file="" + + if [ "$host_os" = "Linux" ] && { [ "$host_arch" = "aarch64" ] || [ "$host_arch" = "arm64" ]; }; then + echo "Using native Linux ARM toolchain ($host_arch)." + elif [ "$host_os" = "Darwin" ] && [ "$host_arch" = "arm64" ]; then + toolchain_file="$SCRIPT_DIR/linux-arm/aarch64-macos-cross.toolchain.cmake" + if [ ! -f "$toolchain_file" ]; then + echo "Error: Missing Linux ARM toolchain file at $toolchain_file" + exit 1 + fi + echo "Using macOS cross toolchain file: $toolchain_file" + else + echo "Error: Unsupported host for Linux ARM build: $host_os/$host_arch" + echo "Linux ARM build is supported on native Linux ARM or Apple Silicon macOS." + exit 1 + fi + + local cmake_args=( + -S "$SCRIPT_DIR/linux-arm" + -B "$out" + -DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TYPE" + -DCMAKE_SYSTEM_PROCESSOR=aarch64 + -DCACTUS_LINUX_USE_SYSTEM_CURL=ON + ) + + if [ -n "$toolchain_file" ]; then + cmake_args+=( -DCMAKE_TOOLCHAIN_FILE="$toolchain_file" ) + fi + + cmake "${cmake_args[@]}" >/dev/null + cmake --build "$out" --config "$CMAKE_BUILD_TYPE" -j "$n_cpu" >/dev/null + + local linux_lib_path="$out/lib/libcactus.a" + if [ ! -f "$linux_lib_path" ]; then + linux_lib_path="$out/libcactus.a" + fi + + if [ ! -f "$linux_lib_path" ]; then + echo "Error: Could not find Linux ARM static library output." + exit 1 + fi + + mkdir -p "$ARTIFACT_BUNDLE_PATH/dist/linux-arm64" + cp "$linux_lib_path" "$ARTIFACT_BUNDLE_PATH/dist/linux-arm64/libcactus.a" + artifactbundle_add_variant "dist/linux-arm64/libcactus.a" "aarch64-unknown-linux-gnu" +} + +write_package_manifest() { + cat > "$PACKAGE_DIR/Package.swift" << 'EOF' +// swift-tools-version: 6.0 +import PackageDescription + +let package = Package( + name: "CactusShims", + products: [ + .library(name: "CactusShims", targets: ["CactusShims"]) + ], + targets: [ + .target( + name: "CactusShims", + dependencies: [ + .target(name: "cactus_artifact", condition: .when(platforms: [.android, .linux])), + .target( + name: "cactus", + condition: .when(platforms: [.iOS, .macOS, .visionOS, .tvOS, .watchOS, .macCatalyst]) + ) + ], + linkerSettings: [ + .linkedLibrary("curl", .when(platforms: [.linux])), + .linkedLibrary("c++_shared", .when(platforms: [.android])) + ] + ), + .binaryTarget(name: "cactus", path: "bin/cactus.xcframework.zip"), + .binaryTarget(name: "cactus_artifact", path: "bin/cactus_artifact.artifactbundle.zip") + ] +) +EOF +} + +write_package_sources() { + mkdir -p "$PACKAGE_DIR/Sources/CactusShims" + cat > "$PACKAGE_DIR/Sources/CactusShims/CactusShims.swift" << 'EOF' +#if canImport(Darwin) + @_exported import cactus +#else + @_exported import cactus_artifact +#endif +EOF +} + +prepare_package_dir() { + rm -rf "$PACKAGE_DIR" "$BUILD_DIR" + mkdir -p "$PACKAGE_BIN_DIR" "$BUILD_DIR" +} + +copy_darwin_xcframework() { + echo "Building Darwin XCFramework..." + BUILD_STATIC=false BUILD_XCFRAMEWORK=true "$ROOT_DIR/apple/build.sh" + + if [ ! -f "$DARWIN_XCFRAMEWORK_ZIP_SRC" ]; then + echo "Error: Darwin XCFramework zip not found at $DARWIN_XCFRAMEWORK_ZIP_SRC" + exit 1 + fi + + cp "$DARWIN_XCFRAMEWORK_ZIP_SRC" "$DARWIN_XCFRAMEWORK_ZIP_DST" +} + +prepare_package_dir +copy_darwin_xcframework +artifactbundle_init +build_android_variant +build_linux_arm_variant +artifactbundle_finalize +write_package_manifest +write_package_sources + +echo "Swift package generated at $PACKAGE_DIR" diff --git a/bindings/swift/ffi/cactus_ffi_shim.h b/bindings/swift/ffi/cactus_ffi_shim.h deleted file mode 100644 index ae623eeb8..000000000 --- a/bindings/swift/ffi/cactus_ffi_shim.h +++ /dev/null @@ -1 +0,0 @@ -#include "../../../cactus-engine/cactus_engine.h" diff --git a/bindings/swift/ffi/module.modulemap b/bindings/swift/ffi/module.modulemap deleted file mode 100644 index 0fc94f2dd..000000000 --- a/bindings/swift/ffi/module.modulemap +++ /dev/null @@ -1,4 +0,0 @@ -module cactus [system] { - header "cactus_ffi_shim.h" - export * -} diff --git a/bindings/swift/linux-arm/CMakeLists.txt b/bindings/swift/linux-arm/CMakeLists.txt new file mode 100644 index 000000000..f6ed2c410 --- /dev/null +++ b/bindings/swift/linux-arm/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.10) +project(CactusLinuxArm LANGUAGES CXX) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../../../cactus ${CMAKE_CURRENT_BINARY_DIR}/cactus) diff --git a/bindings/swift/linux-arm/aarch64-macos-cross.toolchain.cmake b/bindings/swift/linux-arm/aarch64-macos-cross.toolchain.cmake new file mode 100644 index 000000000..cf3ddb5ce --- /dev/null +++ b/bindings/swift/linux-arm/aarch64-macos-cross.toolchain.cmake @@ -0,0 +1,31 @@ +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR aarch64) + +set(TOOLCHAIN_ROOT_DEFAULT "/opt/homebrew/opt/aarch64-unknown-linux-gnu/toolchain") +set(AARCH64_LINUX_GNU_TOOLCHAIN_ROOT "${TOOLCHAIN_ROOT_DEFAULT}" CACHE PATH "Root path of the aarch64 Linux GNU toolchain") +set(LLVM_ROOT_DEFAULT "/opt/homebrew/opt/llvm") +set(HOMEBREW_LLVM_ROOT "${LLVM_ROOT_DEFAULT}" CACHE PATH "Root path of the Homebrew LLVM toolchain") + +set(CMAKE_C_COMPILER "${HOMEBREW_LLVM_ROOT}/bin/clang") +set(CMAKE_CXX_COMPILER "${HOMEBREW_LLVM_ROOT}/bin/clang++") + +set(CMAKE_C_COMPILER_TARGET aarch64-unknown-linux-gnu) +set(CMAKE_CXX_COMPILER_TARGET aarch64-unknown-linux-gnu) + +set(CMAKE_SYSROOT "${AARCH64_LINUX_GNU_TOOLCHAIN_ROOT}/aarch64-unknown-linux-gnu/sysroot") +set(CMAKE_C_FLAGS_INIT "--gcc-toolchain=${AARCH64_LINUX_GNU_TOOLCHAIN_ROOT} --sysroot=${CMAKE_SYSROOT}") +set(CMAKE_CXX_FLAGS_INIT "--gcc-toolchain=${AARCH64_LINUX_GNU_TOOLCHAIN_ROOT} --sysroot=${CMAKE_SYSROOT}") +set(CMAKE_EXE_LINKER_FLAGS_INIT "--gcc-toolchain=${AARCH64_LINUX_GNU_TOOLCHAIN_ROOT} --sysroot=${CMAKE_SYSROOT}") +set(CMAKE_SHARED_LINKER_FLAGS_INIT "--gcc-toolchain=${AARCH64_LINUX_GNU_TOOLCHAIN_ROOT} --sysroot=${CMAKE_SYSROOT}") +set(CMAKE_MODULE_LINKER_FLAGS_INIT "--gcc-toolchain=${AARCH64_LINUX_GNU_TOOLCHAIN_ROOT} --sysroot=${CMAKE_SYSROOT}") + +set(CMAKE_FIND_ROOT_PATH + "${CMAKE_SYSROOT}" + "${AARCH64_LINUX_GNU_TOOLCHAIN_ROOT}/aarch64-unknown-linux-gnu" + "${AARCH64_LINUX_GNU_TOOLCHAIN_ROOT}" +) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/bindings/swift/module.modulemap b/bindings/swift/module.modulemap deleted file mode 100644 index af75a51b2..000000000 --- a/bindings/swift/module.modulemap +++ /dev/null @@ -1,4 +0,0 @@ -module cactus { - header "cactus_engine.h" - export * -} diff --git a/cactus-engine/CMakeLists.txt b/cactus-engine/CMakeLists.txt index d8fe682b3..4df0a026e 100644 --- a/cactus-engine/CMakeLists.txt +++ b/cactus-engine/CMakeLists.txt @@ -13,13 +13,28 @@ set(CACTUS_CURL_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/libs/curl" CACHE PATH "Path to set(CACTUS_CURL_INCLUDE_DIR "${CACTUS_CURL_ROOT}/include") if(APPLE) - if(CMAKE_SYSTEM_NAME STREQUAL "iOS") - string(TOLOWER "${CMAKE_OSX_SYSROOT}" _sysroot_lower) - if(_sysroot_lower MATCHES "simulator") - set(CACTUS_CURL_LIBRARY "${CACTUS_CURL_ROOT}/ios/simulator/libcurl.a") - else() - set(CACTUS_CURL_LIBRARY "${CACTUS_CURL_ROOT}/ios/device/libcurl.a") + string(TOLOWER "${CMAKE_OSX_SYSROOT}" _sysroot_lower) + if(_sysroot_lower MATCHES "iphonesimulator") + set(CACTUS_CURL_LIBRARY "${CACTUS_CURL_ROOT}/ios/simulator/libcurl.a") + elseif(_sysroot_lower MATCHES "iphoneos") + set(CACTUS_CURL_LIBRARY "${CACTUS_CURL_ROOT}/ios/device/libcurl.a") + elseif(_sysroot_lower MATCHES "appletvsimulator") + set(CACTUS_CURL_LIBRARY "${CACTUS_CURL_ROOT}/tvos/simulator/libcurl.a") + elseif(_sysroot_lower MATCHES "appletvos") + set(CACTUS_CURL_LIBRARY "${CACTUS_CURL_ROOT}/tvos/device/libcurl.a") + elseif(_sysroot_lower MATCHES "watchsimulator") + set(CACTUS_CURL_LIBRARY "${CACTUS_CURL_ROOT}/watchos/simulator/libcurl.a") + elseif(_sysroot_lower MATCHES "watchos") + string(TOLOWER "${CMAKE_OSX_ARCHITECTURES}" _archs_lower) + if(_archs_lower MATCHES "arm64_32") + set(CACTUS_CURL_LIBRARY "${CACTUS_CURL_ROOT}/watchos/device-arm64_32/libcurl.a") + elseif(_archs_lower MATCHES "arm64") + set(CACTUS_CURL_LIBRARY "${CACTUS_CURL_ROOT}/watchos/device-arm64/libcurl.a") endif() + elseif(_sysroot_lower MATCHES "xrsimulator") + set(CACTUS_CURL_LIBRARY "${CACTUS_CURL_ROOT}/visionos/simulator/libcurl.a") + elseif(_sysroot_lower MATCHES "xros") + set(CACTUS_CURL_LIBRARY "${CACTUS_CURL_ROOT}/visionos/device/libcurl.a") else() set(CACTUS_CURL_LIBRARY "${CACTUS_CURL_ROOT}/macos/libcurl.a") endif() @@ -37,6 +52,15 @@ elseif(ANDROID) set(CACTUS_HAS_CURL FALSE) message(WARNING "Vendored libcurl not found for Android; cloud features disabled") endif() +elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CACTUS_LINUX_USE_SYSTEM_CURL) + if(EXISTS "${CACTUS_CURL_INCLUDE_DIR}/curl/curl.h") + set(CURL_INCLUDE_DIRS "${CACTUS_CURL_INCLUDE_DIR}") + set(CURL_LIBRARIES curl) + set(CACTUS_HAS_CURL TRUE) + else() + set(CACTUS_HAS_CURL FALSE) + message(WARNING "Vendored libcurl not found for Linux; cloud features disabled") + endif() else() find_package(CURL QUIET) set(CACTUS_HAS_CURL ${CURL_FOUND}) @@ -63,7 +87,8 @@ set(ENGINE_SOURCES src/transcribe.cpp src/tokenizer.cpp ) -if(APPLE) + +if(APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "watchOS") enable_language(OBJCXX) list(APPEND ENGINE_SOURCES src/npu_ane.mm) set_source_files_properties(src/npu_ane.mm PROPERTIES COMPILE_FLAGS "-fobjc-arc") @@ -103,13 +128,24 @@ endif() if(APPLE) find_library(COREML_FRAMEWORK CoreML REQUIRED) find_library(FOUNDATION_FRAMEWORK Foundation REQUIRED) - find_library(SECURITY_FRAMEWORK Security REQUIRED) - find_library(SYSTEMCONFIGURATION_FRAMEWORK SystemConfiguration REQUIRED) - find_library(CFNETWORK_FRAMEWORK CFNetwork REQUIRED) + find_library(SECURITY_FRAMEWORK Security) + find_library(SYSTEMCONFIGURATION_FRAMEWORK SystemConfiguration) + find_library(CFNETWORK_FRAMEWORK CFNetwork) target_link_libraries(cactus_engine PUBLIC ${COREML_FRAMEWORK} ${FOUNDATION_FRAMEWORK} - ${SECURITY_FRAMEWORK} ${SYSTEMCONFIGURATION_FRAMEWORK} ${CFNETWORK_FRAMEWORK} ) + + if(CACTUS_HAS_CURL) + if(NOT SECURITY_FRAMEWORK OR NOT CFNETWORK_FRAMEWORK) + message(FATAL_ERROR "Required Apple networking frameworks were not found for vendored libcurl linking") + endif() + + target_link_libraries(cactus_engine PUBLIC ${SECURITY_FRAMEWORK} ${CFNETWORK_FRAMEWORK}) + + if(SYSTEMCONFIGURATION_FRAMEWORK) + target_link_libraries(cactus_engine PUBLIC ${SYSTEMCONFIGURATION_FRAMEWORK}) + endif() + endif() endif() # --- Version --- diff --git a/cactus-engine/libs/curl/ios/device/libcurl.a b/cactus-engine/libs/curl/ios/device/libcurl.a index ee93a86b0..1fae379f5 100644 Binary files a/cactus-engine/libs/curl/ios/device/libcurl.a and b/cactus-engine/libs/curl/ios/device/libcurl.a differ diff --git a/cactus-engine/libs/curl/ios/simulator/libcurl.a b/cactus-engine/libs/curl/ios/simulator/libcurl.a index 13bde09d7..d8c9f7058 100644 Binary files a/cactus-engine/libs/curl/ios/simulator/libcurl.a and b/cactus-engine/libs/curl/ios/simulator/libcurl.a differ diff --git a/cactus-engine/libs/curl/macos/libcurl.a b/cactus-engine/libs/curl/macos/libcurl.a index 0d2583b2e..57ad6ea93 100644 Binary files a/cactus-engine/libs/curl/macos/libcurl.a and b/cactus-engine/libs/curl/macos/libcurl.a differ diff --git a/cactus-engine/libs/curl/tvos/device/libcurl.a b/cactus-engine/libs/curl/tvos/device/libcurl.a new file mode 100644 index 000000000..034638225 Binary files /dev/null and b/cactus-engine/libs/curl/tvos/device/libcurl.a differ diff --git a/cactus-engine/libs/curl/tvos/simulator/libcurl.a b/cactus-engine/libs/curl/tvos/simulator/libcurl.a new file mode 100644 index 000000000..7733351cc Binary files /dev/null and b/cactus-engine/libs/curl/tvos/simulator/libcurl.a differ diff --git a/cactus-engine/libs/curl/visionos/device/libcurl.a b/cactus-engine/libs/curl/visionos/device/libcurl.a new file mode 100644 index 000000000..1c8b783bf Binary files /dev/null and b/cactus-engine/libs/curl/visionos/device/libcurl.a differ diff --git a/cactus-engine/libs/curl/visionos/simulator/libcurl.a b/cactus-engine/libs/curl/visionos/simulator/libcurl.a new file mode 100644 index 000000000..89cf798ab Binary files /dev/null and b/cactus-engine/libs/curl/visionos/simulator/libcurl.a differ diff --git a/cactus-engine/libs/curl/watchos/device-arm64/libcurl.a b/cactus-engine/libs/curl/watchos/device-arm64/libcurl.a new file mode 100644 index 000000000..a331cacb0 Binary files /dev/null and b/cactus-engine/libs/curl/watchos/device-arm64/libcurl.a differ diff --git a/cactus-engine/libs/curl/watchos/device-arm64_32/libcurl.a b/cactus-engine/libs/curl/watchos/device-arm64_32/libcurl.a new file mode 100644 index 000000000..88bc2e851 Binary files /dev/null and b/cactus-engine/libs/curl/watchos/device-arm64_32/libcurl.a differ diff --git a/cactus-engine/libs/curl/watchos/simulator/libcurl.a b/cactus-engine/libs/curl/watchos/simulator/libcurl.a new file mode 100644 index 000000000..21998da3e Binary files /dev/null and b/cactus-engine/libs/curl/watchos/simulator/libcurl.a differ diff --git a/cactus/CMakeLists.txt b/cactus/CMakeLists.txt index 41167424c..ce90653f1 100644 --- a/cactus/CMakeLists.txt +++ b/cactus/CMakeLists.txt @@ -39,7 +39,31 @@ if(APPLE) -Wl,-force_load,$ -Wl,-force_load,$ ) - set(_CURL "${CACTUS_CURL_ROOT}/macos/libcurl.a") + string(TOLOWER "${CMAKE_OSX_SYSROOT}" _sysroot_lower) + if(_sysroot_lower MATCHES "iphonesimulator") + set(_CURL "${CACTUS_CURL_ROOT}/ios/simulator/libcurl.a") + elseif(_sysroot_lower MATCHES "iphoneos") + set(_CURL "${CACTUS_CURL_ROOT}/ios/device/libcurl.a") + elseif(_sysroot_lower MATCHES "appletvsimulator") + set(_CURL "${CACTUS_CURL_ROOT}/tvos/simulator/libcurl.a") + elseif(_sysroot_lower MATCHES "appletvos") + set(_CURL "${CACTUS_CURL_ROOT}/tvos/device/libcurl.a") + elseif(_sysroot_lower MATCHES "watchsimulator") + set(_CURL "${CACTUS_CURL_ROOT}/watchos/simulator/libcurl.a") + elseif(_sysroot_lower MATCHES "watchos") + string(TOLOWER "${CMAKE_OSX_ARCHITECTURES}" _archs_lower) + if(_archs_lower MATCHES "arm64_32") + set(_CURL "${CACTUS_CURL_ROOT}/watchos/device-arm64_32/libcurl.a") + elseif(_archs_lower MATCHES "arm64") + set(_CURL "${CACTUS_CURL_ROOT}/watchos/device-arm64/libcurl.a") + endif() + elseif(_sysroot_lower MATCHES "xrsimulator") + set(_CURL "${CACTUS_CURL_ROOT}/visionos/simulator/libcurl.a") + elseif(_sysroot_lower MATCHES "xros") + set(_CURL "${CACTUS_CURL_ROOT}/visionos/device/libcurl.a") + else() + set(_CURL "${CACTUS_CURL_ROOT}/macos/libcurl.a") + endif() if(EXISTS "${_CURL}") target_link_libraries(cactus_shared PRIVATE "${_CURL}") endif() diff --git a/python/cactus/cli/__init__.py b/python/cactus/cli/__init__.py index 562d7ab58..0a5404321 100644 --- a/python/cactus/cli/__init__.py +++ b/python/cactus/cli/__init__.py @@ -93,6 +93,7 @@ def create_parser(): Optional flags: --apple build for Apple (iOS/macOS) --android build for Android + --swift build Swift package --python build shared lib for Python FFI ----------------------------------------------------------------- @@ -156,6 +157,8 @@ def create_parser(): help="Build for Apple platforms (iOS/macOS)") build_group.add_argument("--android", action="store_true", help="Build for Android") + build_group.add_argument("--swift", action="store_true", + help="Build Swift package") build_group.add_argument("--python", action="store_true", help="Build shared library for Python FFI") diff --git a/python/cactus/cli/compile.py b/python/cactus/cli/compile.py index d3da4845e..a2ed38eef 100644 --- a/python/cactus/cli/compile.py +++ b/python/cactus/cli/compile.py @@ -153,6 +153,8 @@ def cmd_build(args): return _build_with_script("apple", "Building Cactus for Apple platforms") if args.android: return _build_with_script("android", "Building Cactus for Android") + if args.swift: + return _build_with_script("bindings/swift", "Building Cactus Swift package") if args.python: return cmd_build_python() @@ -214,8 +216,8 @@ def _build_with_script(subdir, title): """Run a platform build.sh script from the given subdirectory.""" print_color(BLUE, f"{title}...") - if subdir == "apple" and platform.system() != "Darwin": - print_color(RED, "Error: Apple builds require macOS") + if subdir in {"apple", "bindings/swift"} and platform.system() != "Darwin": + print_color(RED, "Error: Apple and Swift package builds require macOS") return 1 build_script = PROJECT_ROOT / subdir / "build.sh"