#!/usr/bin/env bash # # usage: ./build-xcframework.sh [BUILD ...] (default: all builds) # builds: ios-sim ios-device macos visionos visionos-sim tvos-sim tvos-device # # Options IOS_MIN_OS_VERSION=16.4 MACOS_MIN_OS_VERSION=13.3 VISIONOS_MIN_OS_VERSION=1.0 TVOS_MIN_OS_VERSION=16.4 BUILD_SHARED_LIBS=OFF LLAMA_BUILD_APP=OFF LLAMA_BUILD_COMMON=OFF LLAMA_BUILD_EXAMPLES=OFF LLAMA_BUILD_TOOLS=OFF LLAMA_BUILD_TESTS=OFF LLAMA_BUILD_SERVER=OFF LLAMA_BUILD_MTMD=ON GGML_METAL=ON GGML_METAL_EMBED_LIBRARY=ON GGML_BLAS_DEFAULT=ON GGML_OPENMP=OFF # Max number of concurrent platform builds MAX_PARALLEL_BUILDS=1 # Split the available cores between the concurrent builds (min 1) JOBS_PER_BUILD=$(( $(sysctl -n hw.logicalcpu) / MAX_PARALLEL_BUILDS )) if [[ "$JOBS_PER_BUILD" -lt 1 ]]; then JOBS_PER_BUILD=1 fi # echo "build_fn build_dir release_dir platform is_simulator min_os" for a build name build_spec() { case "$1" in ios-sim) echo "build_ios_sim build-ios-sim Release-iphonesimulator ios true ${IOS_MIN_OS_VERSION}" ;; ios-device) echo "build_ios_device build-ios-device Release-iphoneos ios false ${IOS_MIN_OS_VERSION}" ;; macos) echo "build_macos build-macos Release macos false ${MACOS_MIN_OS_VERSION}" ;; visionos) echo "build_visionos build-visionos Release-xros visionos false ${VISIONOS_MIN_OS_VERSION}" ;; visionos-sim) echo "build_visionos_sim build-visionos-sim Release-xrsimulator visionos true ${VISIONOS_MIN_OS_VERSION}" ;; tvos-sim) echo "build_tvos_sim build-tvos-sim Release-appletvsimulator tvos true ${TVOS_MIN_OS_VERSION}" ;; tvos-device) echo "build_tvos_device build-tvos-device Release-appletvos tvos false ${TVOS_MIN_OS_VERSION}" ;; *) return 1 ;; esac } # Default: build everything if [[ $# -eq 0 ]]; then BUILDS=(ios-sim ios-device macos visionos visionos-sim tvos-sim tvos-device) else BUILDS=("$@") fi for b in "${BUILDS[@]}"; do if ! build_spec "$b" >/dev/null; then echo "Error: unknown build '$b'" >&2 echo "Valid builds: ios-sim ios-device macos visionos visionos-sim tvos-sim tvos-device" >&2 exit 1 fi done COMMON_C_FLAGS="-Wno-macro-redefined -Wno-shorten-64-to-32 -Wno-unused-command-line-argument -g" COMMON_CXX_FLAGS="-Wno-macro-redefined -Wno-shorten-64-to-32 -Wno-unused-command-line-argument -g" # Common options for all builds COMMON_CMAKE_ARGS=( -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=NO -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY="" -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=NO -DCMAKE_XCODE_ATTRIBUTE_DEBUG_INFORMATION_FORMAT="dwarf-with-dsym" -DCMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS=YES -DCMAKE_XCODE_ATTRIBUTE_COPY_PHASE_STRIP=NO -DCMAKE_XCODE_ATTRIBUTE_STRIP_INSTALLED_PRODUCT=NO -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=ggml -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} -DLLAMA_BUILD_APP=${LLAMA_BUILD_APP} -DLLAMA_BUILD_COMMON=${LLAMA_BUILD_COMMON} -DLLAMA_BUILD_EXAMPLES=${LLAMA_BUILD_EXAMPLES} -DLLAMA_BUILD_TOOLS=${LLAMA_BUILD_TOOLS} -DLLAMA_BUILD_TESTS=${LLAMA_BUILD_TESTS} -DLLAMA_BUILD_SERVER=${LLAMA_BUILD_SERVER} -DLLAMA_BUILD_MTMD=${LLAMA_BUILD_MTMD} -DGGML_METAL_EMBED_LIBRARY=${GGML_METAL_EMBED_LIBRARY} -DGGML_BLAS_DEFAULT=${GGML_BLAS_DEFAULT} -DGGML_METAL=${GGML_METAL} -DGGML_NATIVE=OFF -DGGML_OPENMP=${GGML_OPENMP} ) check_required_tool() { local tool=$1 local install_message=$2 if ! command -v $tool &> /dev/null; then echo "Error: $tool is required but not found." echo "$install_message" exit 1 fi } echo "Checking for required tools..." check_required_tool "cmake" "Please install CMake 3.28.0 or later (brew install cmake)" check_required_tool "xcrun" "Please install Xcode and Xcode Command Line Tools (xcode-select --install)" XCODE_VERSION=$(xcrun xcodebuild -version 2>/dev/null | head -n1 | awk '{ print $2 }') MAJOR_VERSION=$(echo $XCODE_VERSION | cut -d. -f1) MINOR_VERSION=$(echo $XCODE_VERSION | cut -d. -f2) echo "Detected Xcode version: $XCODE_VERSION" set -e ## Clean up previous builds rm -rf build-apple rm -rf build-ios-sim rm -rf build-ios-device rm -rf build-macos rm -rf build-visionos rm -rf build-visionos-sim rm -rf build-tvos-sim rm -rf build-tvos-device # Setup the xcframework build directory structure setup_framework_structure() { local build_dir=$1 local min_os_version=$2 local platform=$3 # "ios", "macos", "visionos", or "tvos" local framework_name="llama" echo "Creating ${platform}-style framework structure for ${build_dir}" if [[ "$platform" == "macos" ]]; then # macOS versioned structure uses versioned directories mkdir -p ${build_dir}/framework/${framework_name}.framework/Versions/A/Headers mkdir -p ${build_dir}/framework/${framework_name}.framework/Versions/A/Modules mkdir -p ${build_dir}/framework/${framework_name}.framework/Versions/A/Resources # Create symbolic links ln -sf A ${build_dir}/framework/${framework_name}.framework/Versions/Current ln -sf Versions/Current/Headers ${build_dir}/framework/${framework_name}.framework/Headers ln -sf Versions/Current/Modules ${build_dir}/framework/${framework_name}.framework/Modules ln -sf Versions/Current/Resources ${build_dir}/framework/${framework_name}.framework/Resources ln -sf Versions/Current/${framework_name} ${build_dir}/framework/${framework_name}.framework/${framework_name} # Set header and module paths local header_path=${build_dir}/framework/${framework_name}.framework/Versions/A/Headers/ local module_path=${build_dir}/framework/${framework_name}.framework/Versions/A/Modules/ else # iOS/VisionOS/tvOS use a flat structure mkdir -p ${build_dir}/framework/${framework_name}.framework/Headers mkdir -p ${build_dir}/framework/${framework_name}.framework/Modules # Remove any existing structure to ensure clean build rm -rf ${build_dir}/framework/${framework_name}.framework/Versions # Set header and module paths local header_path=${build_dir}/framework/${framework_name}.framework/Headers/ local module_path=${build_dir}/framework/${framework_name}.framework/Modules/ fi # Copy all required headers (common for all platforms) cp include/llama.h ${header_path} cp ggml/include/ggml.h ${header_path} cp ggml/include/ggml-opt.h ${header_path} cp ggml/include/ggml-alloc.h ${header_path} cp ggml/include/ggml-backend.h ${header_path} cp ggml/include/ggml-metal.h ${header_path} cp ggml/include/ggml-cpu.h ${header_path} cp ggml/include/ggml-blas.h ${header_path} cp ggml/include/gguf.h ${header_path} cp tools/mtmd/mtmd.h ${header_path} cp tools/mtmd/mtmd-helper.h ${header_path} # Create module map (common for all platforms) cat > ${module_path}module.modulemap << EOF framework module llama { umbrella "Headers" link "c++" link framework "Accelerate" link framework "Metal" link framework "Foundation" export * } EOF # Platform-specific settings for Info.plist local platform_name="" local sdk_name="" local supported_platform="" case "$platform" in "ios") platform_name="iphoneos" sdk_name="iphoneos${min_os_version}" supported_platform="iPhoneOS" local plist_path="${build_dir}/framework/${framework_name}.framework/Info.plist" local device_family=' UIDeviceFamily 1 2 ' ;; "macos") platform_name="macosx" sdk_name="macosx${min_os_version}" supported_platform="MacOSX" local plist_path="${build_dir}/framework/${framework_name}.framework/Versions/A/Resources/Info.plist" local device_family="" ;; "visionos") platform_name="xros" sdk_name="xros${min_os_version}" supported_platform="XRPlatform" local plist_path="${build_dir}/framework/${framework_name}.framework/Info.plist" local device_family="" ;; "tvos") platform_name="appletvos" sdk_name="appletvos${min_os_version}" supported_platform="AppleTVOS" local plist_path="${build_dir}/framework/${framework_name}.framework/Info.plist" local device_family=' UIDeviceFamily 3 ' ;; esac # Create Info.plist cat > ${plist_path} << EOF CFBundleDevelopmentRegion en CFBundleExecutable llama CFBundleIdentifier org.ggml.llama CFBundleInfoDictionaryVersion 6.0 CFBundleName llama CFBundlePackageType FMWK CFBundleShortVersionString 1.0 CFBundleVersion 1 MinimumOSVersion ${min_os_version} CFBundleSupportedPlatforms ${supported_platform} ${device_family} DTPlatformName ${platform_name} DTSDKName ${sdk_name} EOF } # Create dynamic libraries from static libraries. combine_static_libraries() { local build_dir="$1" local release_dir="$2" local platform="$3" # "ios", "macos", "visionos", or "tvos" local is_simulator="$4" local base_dir="$(pwd)" local framework_name="llama" # Determine output path based on platform local output_lib="" if [[ "$platform" == "macos" ]]; then # macOS uses versioned structure output_lib="${build_dir}/framework/${framework_name}.framework/Versions/A/${framework_name}" else # iOS, visionOS, and tvOS use a directory flat structure output_lib="${build_dir}/framework/${framework_name}.framework/${framework_name}" fi local libs=( "${base_dir}/${build_dir}/src/${release_dir}/libllama.a" "${base_dir}/${build_dir}/ggml/src/${release_dir}/libggml.a" "${base_dir}/${build_dir}/ggml/src/${release_dir}/libggml-base.a" "${base_dir}/${build_dir}/ggml/src/${release_dir}/libggml-cpu.a" "${base_dir}/${build_dir}/ggml/src/ggml-metal/${release_dir}/libggml-metal.a" "${base_dir}/${build_dir}/ggml/src/ggml-blas/${release_dir}/libggml-blas.a" "${base_dir}/${build_dir}/tools/mtmd/${release_dir}/libmtmd.a" "${base_dir}/${build_dir}/vendor/hash/${release_dir}/libvendor-hash.a" ) # Create temporary directory for processing local temp_dir="${base_dir}/${build_dir}/temp" mkdir -p "${temp_dir}" # Since we have multiple architectures libtool will find object files that do not # match the target architecture. We suppress these warnings. xcrun libtool -static -o "${temp_dir}/combined.a" "${libs[@]}" 2> /dev/null # Determine SDK, architectures, and install_name based on platform and simulator flag. local sdk="" local archs="" local min_version_flag="" local install_name="" case "$platform" in "ios") if [[ "$is_simulator" == "true" ]]; then sdk="iphonesimulator" archs="arm64 x86_64" min_version_flag="-mios-simulator-version-min=${IOS_MIN_OS_VERSION}" else sdk="iphoneos" archs="arm64" min_version_flag="-mios-version-min=${IOS_MIN_OS_VERSION}" fi install_name="@rpath/llama.framework/llama" ;; "macos") sdk="macosx" archs="arm64 x86_64" min_version_flag="-mmacosx-version-min=${MACOS_MIN_OS_VERSION}" install_name="@rpath/llama.framework/Versions/Current/llama" ;; "visionos") if [[ "$is_simulator" == "true" ]]; then sdk="xrsimulator" archs="arm64 x86_64" min_version_flag="-mtargetos=xros${VISIONOS_MIN_OS_VERSION}-simulator" else sdk="xros" archs="arm64" min_version_flag="-mtargetos=xros${VISIONOS_MIN_OS_VERSION}" fi # Use flat structure for visionOS, same as iOS install_name="@rpath/llama.framework/llama" ;; "tvos") if [[ "$is_simulator" == "true" ]]; then sdk="appletvsimulator" archs="arm64 x86_64" min_version_flag="-mtvos-simulator-version-min=${TVOS_MIN_OS_VERSION}" else sdk="appletvos" archs="arm64" min_version_flag="-mtvos-version-min=${TVOS_MIN_OS_VERSION}" fi install_name="@rpath/llama.framework/llama" ;; esac # Build architecture flags local arch_flags="" for arch in $archs; do arch_flags+=" -arch $arch" done # Create dynamic library echo "Creating dynamic library for ${platform}." xcrun -sdk $sdk clang++ -dynamiclib \ -isysroot $(xcrun --sdk $sdk --show-sdk-path) \ $arch_flags \ $min_version_flag \ -Wl,-force_load,"${temp_dir}/combined.a" \ -framework Foundation -framework Metal -framework Accelerate \ -install_name "$install_name" \ -o "${base_dir}/${output_lib}" # Platform-specific post-processing for device builds if [[ "$is_simulator" == "false" ]]; then if xcrun -f vtool &>/dev/null; then case "$platform" in "ios") echo "Marking binary as a framework binary for iOS..." xcrun vtool -set-build-version ios ${IOS_MIN_OS_VERSION} ${IOS_MIN_OS_VERSION} -replace \ -output "${base_dir}/${output_lib}" "${base_dir}/${output_lib}" ;; "visionos") echo "Marking binary as a framework binary for visionOS..." if [[ "$MAJOR_VERSION" -gt 16 ]] || [[ "$MAJOR_VERSION" -eq 16 && "$MINOR_VERSION" -gt 2 ]]; then echo "Xcode version greater than 16.2, using visionOS." VISION_OS_BUILD_VERSION="visionos" else echo "Xcode version less than or equal to 16.2, using xros." VISION_OS_BUILD_VERSION="xros" fi xcrun vtool -set-build-version ${VISION_OS_BUILD_VERSION} ${VISIONOS_MIN_OS_VERSION} ${VISIONOS_MIN_OS_VERSION} -replace \ -output "${base_dir}/${output_lib}" "${base_dir}/${output_lib}" ;; "tvos") echo "Marking binary as a framework binary for tvOS..." xcrun vtool -set-build-version tvos ${TVOS_MIN_OS_VERSION} ${TVOS_MIN_OS_VERSION} -replace \ -output "${base_dir}/${output_lib}" "${base_dir}/${output_lib}" ;; esac else echo "Warning: vtool not found. Binary may not pass App Store validation." fi fi echo "Creating properly formatted dSYM..." # Create a separate directory for dSYMs for all platforms mkdir -p "${base_dir}/${build_dir}/dSYMs" # iOS and visionOS style dSYM (flat structure) if [[ "$platform" == "ios" || "$platform" == "visionos" || "$platform" == "tvos" ]]; then # Generate dSYM in the dSYMs directory xcrun dsymutil "${base_dir}/${output_lib}" -o "${base_dir}/${build_dir}/dSYMs/llama.dSYM" # Create a copy of the binary that will be stripped cp "${base_dir}/${output_lib}" "${temp_dir}/binary_to_strip" # Strip debug symbols from the copy xcrun strip -S "${temp_dir}/binary_to_strip" -o "${temp_dir}/stripped_lib" # Replace the original with the stripped version mv "${temp_dir}/stripped_lib" "${base_dir}/${output_lib}" else # macOS style dSYM # First strip debug info to a separate file xcrun strip -S "${base_dir}/${output_lib}" -o "${temp_dir}/stripped_lib" # Generate dSYM in the dSYMs directory xcrun dsymutil "${base_dir}/${output_lib}" -o "${base_dir}/${build_dir}/dSYMs/llama.dSYM" # Replace original binary with stripped version mv "${temp_dir}/stripped_lib" "${base_dir}/${output_lib}" fi # Remove any automatically generated dSYM files in the framework structure as they will # otherwise case Invalid Bundle Structure validation errors. if [ -d "${base_dir}/${output_lib}.dSYM" ]; then echo "Removing generated dSYM file in framework structure: ${base_dir}/${output_lib}.dSYM" rm -rf "${base_dir}/${output_lib}.dSYM" fi # Clean up rm -rf "${temp_dir}" } build_ios_sim() { echo "Building for iOS simulator..." cmake -B build-ios-sim -G Xcode \ "${COMMON_CMAKE_ARGS[@]}" \ -DCMAKE_OSX_DEPLOYMENT_TARGET=${IOS_MIN_OS_VERSION} \ -DIOS=ON \ -DCMAKE_SYSTEM_NAME=iOS \ -DCMAKE_OSX_SYSROOT=iphonesimulator \ -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=iphonesimulator \ -DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \ -DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \ -DLLAMA_OPENSSL=OFF \ -DMTMD_VIDEO=OFF \ -S . cmake --build build-ios-sim --config Release -j "${JOBS_PER_BUILD}" -- -quiet } build_ios_device() { echo "Building for iOS devices..." cmake -B build-ios-device -G Xcode \ "${COMMON_CMAKE_ARGS[@]}" \ -DCMAKE_OSX_DEPLOYMENT_TARGET=${IOS_MIN_OS_VERSION} \ -DCMAKE_SYSTEM_NAME=iOS \ -DCMAKE_OSX_SYSROOT=iphoneos \ -DCMAKE_OSX_ARCHITECTURES="arm64" \ -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=iphoneos \ -DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \ -DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \ -DLLAMA_OPENSSL=OFF \ -DMTMD_VIDEO=OFF \ -S . cmake --build build-ios-device --config Release -j "${JOBS_PER_BUILD}" -- -quiet } build_macos() { echo "Building for macOS..." cmake -B build-macos -G Xcode \ "${COMMON_CMAKE_ARGS[@]}" \ -DCMAKE_OSX_DEPLOYMENT_TARGET=${MACOS_MIN_OS_VERSION} \ -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \ -DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \ -DLLAMA_OPENSSL=OFF \ -S . cmake --build build-macos --config Release -j "${JOBS_PER_BUILD}" -- -quiet } build_visionos() { echo "Building for visionOS..." cmake -B build-visionos -G Xcode \ "${COMMON_CMAKE_ARGS[@]}" \ -DCMAKE_OSX_DEPLOYMENT_TARGET=${VISIONOS_MIN_OS_VERSION} \ -DCMAKE_OSX_ARCHITECTURES="arm64" \ -DCMAKE_SYSTEM_NAME=visionOS \ -DCMAKE_OSX_SYSROOT=xros \ -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=xros \ -DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \ -DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \ -DLLAMA_OPENSSL=OFF \ -DLLAMA_BUILD_SERVER=OFF \ -DMTMD_VIDEO=OFF \ -S . cmake --build build-visionos --config Release -j "${JOBS_PER_BUILD}" -- -quiet } build_visionos_sim() { echo "Building for visionOS simulator..." cmake -B build-visionos-sim -G Xcode \ "${COMMON_CMAKE_ARGS[@]}" \ -DCMAKE_OSX_DEPLOYMENT_TARGET=${VISIONOS_MIN_OS_VERSION} \ -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -DCMAKE_SYSTEM_NAME=visionOS \ -DCMAKE_OSX_SYSROOT=xrsimulator \ -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=xrsimulator \ -DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \ -DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \ -DLLAMA_OPENSSL=OFF \ -DLLAMA_BUILD_SERVER=OFF \ -DMTMD_VIDEO=OFF \ -S . cmake --build build-visionos-sim --config Release -j "${JOBS_PER_BUILD}" -- -quiet } # Add tvOS builds (might need the same u_int definitions as watchOS and visionOS) build_tvos_sim() { echo "Building for tvOS simulator..." cmake -B build-tvos-sim -G Xcode \ "${COMMON_CMAKE_ARGS[@]}" \ -DCMAKE_OSX_DEPLOYMENT_TARGET=${TVOS_MIN_OS_VERSION} \ -DCMAKE_SYSTEM_NAME=tvOS \ -DCMAKE_OSX_SYSROOT=appletvsimulator \ -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ -DGGML_METAL=ON \ -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=appletvsimulator \ -DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \ -DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \ -DLLAMA_OPENSSL=OFF \ -DMTMD_VIDEO=OFF \ -S . cmake --build build-tvos-sim --config Release -j "${JOBS_PER_BUILD}" -- -quiet } build_tvos_device() { echo "Building for tvOS devices..." cmake -B build-tvos-device -G Xcode \ "${COMMON_CMAKE_ARGS[@]}" \ -DCMAKE_OSX_DEPLOYMENT_TARGET=${TVOS_MIN_OS_VERSION} \ -DCMAKE_SYSTEM_NAME=tvOS \ -DCMAKE_OSX_SYSROOT=appletvos \ -DCMAKE_OSX_ARCHITECTURES="arm64" \ -DGGML_METAL=ON \ -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=appletvos \ -DCMAKE_C_FLAGS="${COMMON_C_FLAGS}" \ -DCMAKE_CXX_FLAGS="${COMMON_CXX_FLAGS}" \ -DLLAMA_OPENSSL=OFF \ -DMTMD_VIDEO=OFF \ -S . cmake --build build-tvos-device --config Release -j "${JOBS_PER_BUILD}" -- -quiet } run_builds_parallel() { local -a pids=() local -a names=() local name i for name in "$@"; do # Wait for the oldest running build to free a slot if [[ "${#pids[@]}" -ge "$MAX_PARALLEL_BUILDS" ]]; then if ! wait "${pids[0]}"; then echo "ERROR: build '${names[0]}' failed, log follows (${names[0]}.log):" >&2 kill "${pids[@]}" 2>/dev/null || true cat "${names[0]}.log" >&2 exit 1 fi pids=("${pids[@]:1}") names=("${names[@]:1}") fi echo "Starting build: $name (log: ${name}.log, -j ${JOBS_PER_BUILD})" "$name" > "${name}.log" 2>&1 & pids+=("$!") names+=("$name") done # Wait for the remaining builds for i in "${!pids[@]}"; do if ! wait "${pids[$i]}"; then echo "ERROR: build '${names[$i]}' failed, log follows (${names[$i]}.log):" >&2 kill "${pids[@]}" 2>/dev/null || true cat "${names[$i]}.log" >&2 exit 1 fi done } BUILD_FNS=() for b in "${BUILDS[@]}"; do read -r fn _ < <(build_spec "$b") BUILD_FNS+=("$fn") done echo "Building: ${BUILDS[*]} (max ${MAX_PARALLEL_BUILDS} at a time, -j ${JOBS_PER_BUILD} each)..." run_builds_parallel "${BUILD_FNS[@]}" # Setup frameworks and copy binaries and headers echo "Setting up framework structures..." for b in "${BUILDS[@]}"; do read -r _ bdir _ platform _ min_os < <(build_spec "$b") setup_framework_structure "$bdir" "$min_os" "$platform" done # Create dynamic libraries from static libraries echo "Creating dynamic libraries from static libraries..." for b in "${BUILDS[@]}"; do read -r _ bdir rdir platform is_sim _ < <(build_spec "$b") combine_static_libraries "$bdir" "$rdir" "$platform" "$is_sim" done # Create XCFramework with correct debug symbols paths echo "Creating XCFramework..." XCFW_ARGS=() for b in "${BUILDS[@]}"; do read -r _ bdir _ _ _ _ < <(build_spec "$b") XCFW_ARGS+=(-framework "$(pwd)/${bdir}/framework/llama.framework") XCFW_ARGS+=(-debug-symbols "$(pwd)/${bdir}/dSYMs/llama.dSYM") done xcrun xcodebuild -create-xcframework \ "${XCFW_ARGS[@]}" \ -output "$(pwd)/build-apple/llama.xcframework"