From 0d1ebf7d45fdffb7a354aacf8e87f1809abf3179 Mon Sep 17 00:00:00 2001 From: Abraham Gonzalez Date: Wed, 15 Apr 2026 00:41:51 -0400 Subject: [PATCH] build: include libmtmd in Apple XCFramework Adds opt-in LLAMA_BUILD_MTMD CMake option so build-xcframework.sh can link libmtmd.a into the framework binary without pulling in the rest of tools/ (which doesn't cross-build cleanly to iOS/tvOS/visionOS). - CMakeLists.txt: new option, default OFF. When on with LLAMA_BUILD_TOOLS=OFF, only the tools/mtmd subdir is added. - tools/mtmd/CMakeLists.txt: gate the CLI exe targets on LLAMA_BUILD_TOOLS. Gating on LLAMA_BUILD_COMMON is not enough: it defaults ON in standalone builds and visionOS xcodebuild then fails with "install TARGETS given no BUNDLE DESTINATION for MACOSX_BUNDLE executable target 'llama-mtmd-cli'". - build-xcframework.sh: turn the option on, pass -DLLAMA_BUILD_MTMD, add libmtmd.a to combine_static_libraries. Module map is not updated to expose mtmd.h / mtmd-helper.h. Those headers use C++ constructs and reference mtmd_decoder_pos without the struct tag, which break clang's pure-C module precompilation path for Swift / Obj-C importers. Consumers declare the ABI via their own extern "C" shim. After this, nm on ios-arm64/llama.framework/llama shows 52 _mtmd_ symbols. AI-assisted: used Claude to help with CMake wording. Design, debugging, and on-device verification are mine. --- CMakeLists.txt | 9 +++++++++ build-xcframework.sh | 10 ++++++++++ tools/mtmd/CMakeLists.txt | 40 ++++++++++++++++++++++----------------- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index caea48c5060..864743bd2a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -215,6 +215,15 @@ if (LLAMA_BUILD_COMMON AND LLAMA_BUILD_TOOLS) add_subdirectory(tools) endif() +# Standalone libmtmd build (e.g. for Apple XCFramework packaging) without pulling +# in the rest of the tools/ tree. When the full tools build is already enabled, +# mtmd is built by the tools/ subdirectory above; this hook only fires when +# LLAMA_BUILD_TOOLS is OFF to avoid double-adding the target. +option(LLAMA_BUILD_MTMD "llama: build tools/mtmd library standalone" OFF) +if (LLAMA_BUILD_MTMD AND NOT (LLAMA_BUILD_COMMON AND LLAMA_BUILD_TOOLS)) + add_subdirectory(tools/mtmd) +endif() + # Automatically add all files from the 'licenses' directory file(GLOB EXTRA_LICENSES "${CMAKE_SOURCE_DIR}/licenses/LICENSE-*") diff --git a/build-xcframework.sh b/build-xcframework.sh index c25a1ef28c1..295236b90d4 100755 --- a/build-xcframework.sh +++ b/build-xcframework.sh @@ -11,6 +11,7 @@ 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 @@ -35,6 +36,7 @@ COMMON_CMAKE_ARGS=( -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} @@ -123,6 +125,13 @@ setup_framework_structure() { cp ggml/include/ggml-blas.h ${header_path} cp ggml/include/gguf.h ${header_path} + # Note on mtmd: libmtmd.a is combined into the framework binary (see + # combine_static_libraries) so the symbols are exported, but mtmd.h and + # mtmd-helper.h are NOT copied into the module because they contain C++ + # constructs (method-bearing structs, deleters) that break Clang's pure-C + # module-map precompilation path. Consumers that need the mtmd ABI should + # declare the symbols themselves via an extern C shim header. + # Create module map (common for all platforms) cat > ${module_path}module.modulemap << EOF framework module llama { @@ -250,6 +259,7 @@ combine_static_libraries() { "${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" ) # Create temporary directory for processing diff --git a/tools/mtmd/CMakeLists.txt b/tools/mtmd/CMakeLists.txt index 3bafde178de..4f9417c64a2 100644 --- a/tools/mtmd/CMakeLists.txt +++ b/tools/mtmd/CMakeLists.txt @@ -95,22 +95,28 @@ if (TARGET mtmd) endif() endif() -add_executable(llama-llava-cli deprecation-warning.cpp) -add_executable(llama-gemma3-cli deprecation-warning.cpp) -add_executable(llama-minicpmv-cli deprecation-warning.cpp) -add_executable(llama-qwen2vl-cli deprecation-warning.cpp) +# Gate CLI binaries on LLAMA_BUILD_TOOLS so that standalone library-only +# builds (LLAMA_BUILD_MTMD=ON with LLAMA_BUILD_TOOLS=OFF — e.g. Apple +# XCFramework packaging) skip the executables entirely. LLAMA_BUILD_COMMON +# defaults to ON in standalone builds, so we cannot rely on it for gating. +if (LLAMA_BUILD_TOOLS) + add_executable(llama-llava-cli deprecation-warning.cpp) + add_executable(llama-gemma3-cli deprecation-warning.cpp) + add_executable(llama-minicpmv-cli deprecation-warning.cpp) + add_executable(llama-qwen2vl-cli deprecation-warning.cpp) -set(TARGET llama-mtmd-cli) -add_executable (${TARGET} mtmd-cli.cpp) -set_target_properties (${TARGET} PROPERTIES OUTPUT_NAME llama-mtmd-cli) -if(LLAMA_TOOLS_INSTALL) - install(TARGETS ${TARGET} RUNTIME) -endif() -target_link_libraries (${TARGET} PRIVATE common mtmd Threads::Threads) -target_compile_features(${TARGET} PRIVATE cxx_std_17) + set(TARGET llama-mtmd-cli) + add_executable (${TARGET} mtmd-cli.cpp) + set_target_properties (${TARGET} PROPERTIES OUTPUT_NAME llama-mtmd-cli) + if(LLAMA_TOOLS_INSTALL) + install(TARGETS ${TARGET} RUNTIME) + endif() + target_link_libraries (${TARGET} PRIVATE common mtmd Threads::Threads) + target_compile_features(${TARGET} PRIVATE cxx_std_17) -# mtmd-debug tool -add_executable(llama-mtmd-debug debug/mtmd-debug.cpp) -set_target_properties(llama-mtmd-debug PROPERTIES OUTPUT_NAME llama-mtmd-debug) -target_link_libraries(llama-mtmd-debug PRIVATE common mtmd Threads::Threads) -target_compile_features(llama-mtmd-debug PRIVATE cxx_std_17) + # mtmd-debug tool + add_executable(llama-mtmd-debug debug/mtmd-debug.cpp) + set_target_properties(llama-mtmd-debug PROPERTIES OUTPUT_NAME llama-mtmd-debug) + target_link_libraries(llama-mtmd-debug PRIVATE common mtmd Threads::Threads) + target_compile_features(llama-mtmd-debug PRIVATE cxx_std_17) +endif()