Skip to content

Latest commit

 

History

History
231 lines (175 loc) · 7.84 KB

File metadata and controls

231 lines (175 loc) · 7.84 KB

Building llama.cpp for Apple Platforms with Ninja

Problem

CMake 4.x with -G Xcode fails to identify the C/CXX compiler when cross-compiling for Apple embedded platforms (iOS, tvOS, visionOS). The error manifests as:

The C compiler identification is unknown
target_compile_features no known features for C compiler "" version .

This is a known incompatibility between CMake 4.x's Xcode generator and Xcode 26's toolchain detection for cross-compilation SDKs.

Solution

Use -G Ninja instead of -G Xcode. This requires only one change to the upstream llama.cpp source:

Required Source Change

Bump cmake_minimum_required in both top-level files to accept CMake 4.x:

# CMakeLists.txt (root)
cmake_minimum_required(VERSION 3.5...4.2)

# ggml/CMakeLists.txt
cmake_minimum_required(VERSION 3.5...4.2)

The 3.5...4.2 range maintains backward compatibility with CMake 3.x while allowing CMake 4.x to run without policy warnings.

No other changes to llama.cpp source are needed.

Required Tools

  • CMake 4.x (brew install cmake)
  • Ninja (brew install ninja)
  • Xcode 26+ with all platform SDKs installed

Build Commands

All builds use the same base flags:

BASE_FLAGS=(
    -G Ninja
    -DCMAKE_BUILD_TYPE=Release
    -DBUILD_SHARED_LIBS=ON
    -DLLAMA_BUILD_EXAMPLES=OFF
    -DLLAMA_BUILD_TOOLS=OFF
    -DLLAMA_BUILD_TESTS=OFF
    -DLLAMA_BUILD_SERVER=OFF
    -DGGML_METAL=ON
    -DGGML_METAL_EMBED_LIBRARY=ON
    -DGGML_BLAS_DEFAULT=ON
    -DGGML_METAL_USE_BF16=ON
    -DGGML_OPENMP=OFF
    -DGGML_NATIVE=OFF
    -DLLAMA_OPENSSL=OFF
    -DCMAKE_C_FLAGS="-Wno-macro-redefined -Wno-shorten-64-to-32 -Wno-unused-command-line-argument"
    -DCMAKE_CXX_FLAGS="-Wno-macro-redefined -Wno-shorten-64-to-32 -Wno-unused-command-line-argument"
)

macOS (arm64 + x86_64)

cmake -B build-macos "${BASE_FLAGS[@]}" \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=13.3 \
    -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"

cmake --build build-macos

iOS Device (arm64)

cmake -B build-ios-device "${BASE_FLAGS[@]}" \
    -DCMAKE_SYSTEM_NAME=iOS \
    -DCMAKE_OSX_SYSROOT=iphoneos \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=16.4 \
    -DCMAKE_OSX_ARCHITECTURES="arm64"

cmake --build build-ios-device

iOS Simulator (arm64 + x86_64)

cmake -B build-ios-sim "${BASE_FLAGS[@]}" \
    -DCMAKE_SYSTEM_NAME=iOS \
    -DCMAKE_OSX_SYSROOT=iphonesimulator \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=16.4 \
    -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"

cmake --build build-ios-sim

visionOS Device (arm64)

cmake -B build-visionos "${BASE_FLAGS[@]}" \
    -DCMAKE_SYSTEM_NAME=visionOS \
    -DCMAKE_OSX_SYSROOT=xros \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=1.0 \
    -DCMAKE_OSX_ARCHITECTURES="arm64"

cmake --build build-visionos

visionOS Simulator (arm64)

cmake -B build-visionos-sim "${BASE_FLAGS[@]}" \
    -DCMAKE_SYSTEM_NAME=visionOS \
    -DCMAKE_OSX_SYSROOT=xrsimulator \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=1.0 \
    -DCMAKE_OSX_ARCHITECTURES="arm64"

cmake --build build-visionos-sim

tvOS Device (arm64)

cmake -B build-tvos-device "${BASE_FLAGS[@]}" \
    -DCMAKE_SYSTEM_NAME=tvOS \
    -DCMAKE_OSX_SYSROOT=appletvos \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=16.4 \
    -DCMAKE_OSX_ARCHITECTURES="arm64"

cmake --build build-tvos-device

tvOS Simulator (arm64 + x86_64)

cmake -B build-tvos-sim "${BASE_FLAGS[@]}" \
    -DCMAKE_SYSTEM_NAME=tvOS \
    -DCMAKE_OSX_SYSROOT=appletvsimulator \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=16.4 \
    -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"

cmake --build build-tvos-sim

Mac Catalyst (arm64 + x86_64)

Catalyst requires building each architecture separately and merging with lipo, because CMake's cross-compilation flags (-target and --sysroot) conflict when combined for two architectures:

# arm64
cmake -B build-catalyst-arm64 "${BASE_FLAGS[@]}" \
    -DCMAKE_SYSTEM_NAME=iOS \
    -DCMAKE_OSX_SYSROOT=macosx \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=16.4 \
    -DCMAKE_OSX_ARCHITECTURES="arm64" \
    -DCMAKE_C_FLAGS="${C_FLAGS} -target arm64-apple-ios16.4-macabi --sysroot $(xcrun --sdk macosx --show-sdk-path)" \
    -DCMAKE_CXX_FLAGS="${CXX_FLAGS} -target arm64-apple-ios16.4-macabi --sysroot $(xcrun --sdk macosx --show-sdk-path)"

cmake --build build-catalyst-arm64

# x86_64
cmake -B build-catalyst-x86_64 "${BASE_FLAGS[@]}" \
    -DCMAKE_SYSTEM_NAME=iOS \
    -DCMAKE_OSX_SYSROOT=macosx \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=16.4 \
    -DCMAKE_OSX_ARCHITECTURES="x86_64" \
    -DCMAKE_C_FLAGS="${C_FLAGS} -target x86_64-apple-ios16.4-macabi --sysroot $(xcrun --sdk macosx --show-sdk-path)" \
    -DCMAKE_CXX_FLAGS="${CXX_FLAGS} -target x86_64-apple-ios16.4-macabi --sysroot $(xcrun --sdk macosx --show-sdk-path)"

cmake --build build-catalyst-x86_64

# Merge into universal binaries
mkdir -p build-catalyst/bin
for lib in libllama libggml libggml-base libggml-cpu libggml-metal libggml-blas; do
    dylib=$(ls build-catalyst-arm64/bin/${lib}.*.*.dylib 2>/dev/null | head -1)
    name=$(basename "$dylib")
    lipo -create \
        "build-catalyst-arm64/bin/$name" \
        "build-catalyst-x86_64/bin/$name" \
        -output "build-catalyst/bin/$name"
    # Recreate symlinks
    for link in $(ls build-catalyst-arm64/bin/${lib}*.dylib | grep -v "$name"); do
        ln -sf "$name" "build-catalyst/bin/$(basename $link)"
    done
done

Output

Each build produces shared libraries in build-*/bin/:

Library Description
libllama.dylib Main llama.cpp library
libggml.dylib GGML core (registry, backend loader)
libggml-base.dylib GGML base (tensor ops, alloc, quants)
libggml-cpu.dylib CPU backend
libggml-metal.dylib Metal GPU backend (embedded shaders)
libggml-blas.dylib Accelerate/BLAS backend

Static libraries libcommon.a and libcpp-httplib.a are also produced but are not needed for framework embedding.

Build Matrix (verified 2026-04-15)

Platform Sysroot Architectures Status
macOS (default) arm64, x86_64 OK
iOS Device iphoneos arm64 OK
iOS Simulator iphonesimulator arm64, x86_64 OK
visionOS Device xros arm64 OK
visionOS Simulator xrsimulator arm64, x86_64 OK
tvOS Device appletvos arm64 OK
tvOS Simulator appletvsimulator arm64, x86_64 OK
Mac Catalyst macosx arm64, x86_64 (lipo) OK

Built with: Xcode 26.4, CMake 4.2.3, Ninja 1.13.2, upstream llama.cpp tag b8802.

build-xcframework.sh

The upstream build-xcframework.sh script has been patched to use -G Ninja instead of -G Xcode for the same reason documented above. It produces build-apple/llama.xcframework containing all 7 slices (iOS device/sim, macOS, visionOS device/sim, tvOS device/sim). Mac Catalyst is not included in the xcframework — the manual lipo workflow in the Mac Catalyst section above applies if you need that slice.

Notes

  • -G Ninja is the key fix. The Xcode generator (-G Xcode) cannot detect the compiler when cross-compiling with CMake 4.x. Ninja works with no issues.
  • GGML_NATIVE=OFF is required for cross-compilation (disables host-specific optimizations).
  • GGML_OPENMP=OFF — Apple platforms don't ship libomp; without this flag, cmake will warn but still succeed.
  • Warnings suppressed: -Wno-macro-redefined (duplicate macro defs across Metal headers), -Wno-shorten-64-to-32 (many narrowing conversions in llama.cpp), -Wno-unused-command-line-argument (flags passed to linker that aren't used).
  • iOS device build detects ARM but some feature tests fail (DOTPROD, FP16_VECTOR_ARITHMETIC, etc.) because CMake's try_compile doesn't run on-device. The library still works — these are compile-time feature probes, not runtime failures.
  • common and cpp-httplib remain static libraries regardless of BUILD_SHARED_LIBS=ON. This is expected — they are internal-only.