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.
Use -G Ninja instead of -G Xcode. This requires only one change to the upstream llama.cpp source:
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.
- CMake 4.x (
brew install cmake) - Ninja (
brew install ninja) - Xcode 26+ with all platform SDKs installed
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"
)cmake -B build-macos "${BASE_FLAGS[@]}" \
-DCMAKE_OSX_DEPLOYMENT_TARGET=13.3 \
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
cmake --build build-macoscmake -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-devicecmake -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-simcmake -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-visionoscmake -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-simcmake -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-devicecmake -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-simCatalyst 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
doneEach 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.
| 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.
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.
-G Ninjais 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.
commonandcpp-httplibremain static libraries regardless ofBUILD_SHARED_LIBS=ON. This is expected — they are internal-only.