diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e6555eb479c28..bfc5dc39789e9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -702,12 +702,11 @@ jobs: -DLLAMA_BUILD_SERVER=OFF \ -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" cmake --build build --config Release -j $(sysctl -n hw.logicalcpu) - sudo cmake --install build --config Release - name: xcodebuild for swift package id: xcodebuild run: | - xcodebuild -scheme llama-Package -destination "${{ matrix.destination }}" + ./build-xcframework.sh windows-msys2: runs-on: windows-latest @@ -1328,15 +1327,14 @@ jobs: -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \ -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM=ggml cmake --build build --config Release -j $(sysctl -n hw.logicalcpu) -- CODE_SIGNING_ALLOWED=NO - sudo cmake --install build --config Release - name: xcodebuild for swift package id: xcodebuild run: | - xcodebuild -scheme llama-Package -destination 'generic/platform=iOS' + ./build-xcframework.sh - name: Build Xcode project - run: xcodebuild -project examples/llama.swiftui/llama.swiftui.xcodeproj -scheme llama.swiftui -sdk iphoneos CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= -destination 'generic/platform=iOS' build + run: xcodebuild -project examples/llama.swiftui/llama.swiftui.xcodeproj -scheme llama.swiftui -sdk iphoneos CODE_SIGNING_REQUIRED=NO CODE_SIGN_IDENTITY= -destination 'generic/platform=iOS' FRAMEWORK_FOLDER_PATH=./build-ios build android-build: runs-on: ubuntu-latest diff --git a/Package.swift b/Package.swift deleted file mode 100644 index 01c996d242037..0000000000000 --- a/Package.swift +++ /dev/null @@ -1,19 +0,0 @@ -// swift-tools-version:5.5 - -import PackageDescription - -let package = Package( - name: "llama", - platforms: [ - .macOS(.v12), - .iOS(.v14), - .watchOS(.v4), - .tvOS(.v14) - ], - products: [ - .library(name: "llama", targets: ["llama"]), - ], - targets: [ - .systemLibrary(name: "llama", pkgConfig: "llama"), - ] -) diff --git a/Sources/llama/llama.h b/Sources/llama/llama.h deleted file mode 100644 index 41725880ed8c0..0000000000000 --- a/Sources/llama/llama.h +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include - diff --git a/Sources/llama/module.modulemap b/Sources/llama/module.modulemap deleted file mode 100644 index d010555b1cb65..0000000000000 --- a/Sources/llama/module.modulemap +++ /dev/null @@ -1,5 +0,0 @@ -module llama [system] { - header "llama.h" - link "llama" - export * -} diff --git a/build-xcframework.sh b/build-xcframework.sh new file mode 100755 index 0000000000000..1a9dc02307042 --- /dev/null +++ b/build-xcframework.sh @@ -0,0 +1,142 @@ +#!/bin/bash +# +# Options +DEPLOYMENT_TARGET=14.0 +BUILD_SHARED_LIBS=ON +LLAMA_STATIC=OFF +LLAMA_BUILD_EXAMPLES=OFF +LLAMA_BUILD_TESTS=OFF +LLAMA_BUILD_SERVER=OFF +GGML_METAL_EMBED_LIBRARY=ON +GGML_METAL_USE_BF16=ON + +set -xe + +rm -rf build-ios +rm -rf build-ios-sim +rm -rf build-ios-device + +# Function to setup framework structure for a given architecture +setup_framework_structure() { + local build_dir=$1 + local framework_name="llama" + + # Create framework directory structure + mkdir -p ${build_dir}/framework/${framework_name}.framework + mkdir -p ${build_dir}/framework/${framework_name}.framework/Headers + mkdir -p ${build_dir}/framework/${framework_name}.framework/Modules + + # Copy all required headers. + cp include/llama.h ${build_dir}/framework/${framework_name}.framework/Headers/ + cp ggml/include/ggml.h ${build_dir}/framework/${framework_name}.framework/Headers/ + cp ggml/include/ggml-alloc.h ${build_dir}/framework/${framework_name}.framework/Headers/ + cp ggml/include/ggml-backend.h ${build_dir}/framework/${framework_name}.framework/Headers/ + cp ggml/include/ggml-metal.h ${build_dir}/framework/${framework_name}.framework/Headers/ + cp ggml/include/ggml-cpu.h ${build_dir}/framework/${framework_name}.framework/Headers/ + cp ggml/include/ggml-blas.h ${build_dir}/framework/${framework_name}.framework/Headers/ + + # Create module map that describes how which headers are part of the llama module. + # This enables swift code to import 'llama' and get access to all public interfaces + # the headers with the usage of export *. For example, LibLlama.swift imports the + # 'llama' module. + cat > ${build_dir}/framework/${framework_name}.framework/Modules/module.modulemap << EOF +framework module llama { + header "llama.h" + header "ggml.h" + header "ggml-alloc.h" + header "ggml-backend.h" + header "ggml-metal.h" + header "ggml-cpu.h" + header "ggml-blas.h" + + export * +} +EOF + + # Create Info.plist (Information Property List) file that describes the app. + # This will be a Framework bundle (FMWK). + cat > ${build_dir}/framework/${framework_name}.framework/Info.plist << EOF + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + llama + CFBundleIdentifier + ggml-org.llama.framework + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + llama + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + MinimumOSVersion + 14.0 + + +EOF +} + +# Common options for all builds. +COMMON_CMAKE_ARGS=( + -DIOS=ON + -DCMAKE_SYSTEM_NAME=iOS + -DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET} + -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=NO + -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY="" + -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=NO + -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} + -DLLAMA_STATIC=${LLAMA_STATIC} + -DLLAMA_BUILD_EXAMPLES=${LLAMA_BUILD_EXAMPLES} + -DLLAMA_BUILD_TESTS=${LLAMA_BUILD_TESTS} + -DLLAMA_BUILD_SERVER=${LLAMA_BUILD_SERVER} + -DGGML_METAL_EMBED_LIBRARY=${GGML_METAL_EMBED_LIBRARY} + -DGGML_METAL_USE_BF16=${GGML_METAL_USE_BF16} + -DCMAKE_INSTALL_NAME_DIR="@rpath/llama.framework/llama" +) + +# Build for iOS simulator. +cmake -B build-ios-sim -G Xcode \ + "${COMMON_CMAKE_ARGS[@]}" \ + -DCMAKE_OSX_SYSROOT=iphonesimulator \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=iphonesimulator \ + -S . +cmake --build build-ios-sim --config Release + +# Build for iOS devices. +cmake -B build-ios-device -G Xcode \ + "${COMMON_CMAKE_ARGS[@]}" \ + -DCMAKE_OSX_SYSROOT=iphoneos \ + -DCMAKE_OSX_ARCHITECTURES="arm64" \ + -DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=iphoneos \ + -S . +cmake --build build-ios-device --config Release + +# Setup frameworks and copy binaries and headers. +setup_framework_structure "build-ios-sim" +setup_framework_structure "build-ios-device" + +# Copy and rename the binaries. +cp build-ios-sim/bin/Release/libllama.dylib build-ios-sim/framework/llama.framework/llama +cp build-ios-device/bin/Release/libllama.dylib build-ios-device/framework/llama.framework/llama + +# Fix the install name in the binaries +install_name_tool -id "@rpath/llama.framework/llama" build-ios-sim/framework/llama.framework/llama +install_name_tool -id "@rpath/llama.framework/llama" build-ios-device/framework/llama.framework/llama + +# Create XCFramework with the two builds which will contain both simulator +# and device versions in the same framework. +xcodebuild -create-xcframework \ + -framework $(pwd)/build-ios-sim/framework/llama.framework \ + -framework $(pwd)/build-ios-device/framework/llama.framework \ + -output $(pwd)/build-ios/llama.xcframework + +# The generated framework can be found in build-ios/llama.xcframework and +# can be added to a projects "Frameworks, Libraries, and Embedded Content". diff --git a/examples/llama.swiftui/README.md b/examples/llama.swiftui/README.md index f717886d661ce..5b0ee947208db 100644 --- a/examples/llama.swiftui/README.md +++ b/examples/llama.swiftui/README.md @@ -5,6 +5,21 @@ point for more advanced projects. For usage instructions and performance stats, check the following discussion: https://github.com/ggml-org/llama.cpp/discussions/4508 + +### Building +First llama.cpp need to be built and a XCFramework needs to be created. This can be done by running +the following script from the llama.cpp project root: +```console +$ ./build-xcframework.sh +``` +Open `llama.swiftui.xcodeproj` project in Xcode and you should be able to build and run the app on +a simulator or a real device. + +To use the framework with a different project, the XCFramework can be added to the project by +adding `build-ios/llama.xcframework` by dragging and dropping it into the project navigator, or +by manually selecting the framework in the "Frameworks, Libraries, and Embedded Content" section +of the project settings. + ![image](https://github.com/ggml-org/llama.cpp/assets/1991296/2b40284f-8421-47a2-b634-74eece09a299) Video demonstration: diff --git a/examples/llama.swiftui/llama.swiftui.xcodeproj/project.pbxproj b/examples/llama.swiftui/llama.swiftui.xcodeproj/project.pbxproj index ff3d108b2a18c..bc5f9728f24de 100644 --- a/examples/llama.swiftui/llama.swiftui.xcodeproj/project.pbxproj +++ b/examples/llama.swiftui/llama.swiftui.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 1809696D2D05A39F00400EE8 /* llama in Frameworks */ = {isa = PBXBuildFile; productRef = 1809696C2D05A39F00400EE8 /* llama */; }; 549479CB2AC9E16000E0F78B /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 549479CA2AC9E16000E0F78B /* Metal.framework */; }; 79E1D9CD2B4CD16E005F8E46 /* InputButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79E1D9CC2B4CD16E005F8E46 /* InputButton.swift */; }; 7FA3D2B32B2EA2F600543F92 /* DownloadButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FA3D2B22B2EA2F600543F92 /* DownloadButton.swift */; }; @@ -18,9 +17,25 @@ 8A3F84242AC4C891005E2EE8 /* models in Resources */ = {isa = PBXBuildFile; fileRef = 8A3F84232AC4C891005E2EE8 /* models */; }; 8A907F332AC7138A006146EA /* LibLlama.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A907F322AC7134E006146EA /* LibLlama.swift */; }; 8A9F7C4D2AC332EE008AE1EA /* LlamaState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9F7C4C2AC332EE008AE1EA /* LlamaState.swift */; }; + DD0078922D6756A5003C187C /* llama.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0078912D6756A5003C187C /* llama.xcframework */; }; + DD0078932D6756A5003C187C /* llama.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DD0078912D6756A5003C187C /* llama.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; F1FE20E22B465ECA00B45541 /* LoadCustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FE20E12B465EC900B45541 /* LoadCustomButton.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXCopyFilesBuildPhase section */ + DD0078942D6756A5003C187C /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + DD0078932D6756A5003C187C /* llama.xcframework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 549479CA2AC9E16000E0F78B /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; 79E1D9CC2B4CD16E005F8E46 /* InputButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputButton.swift; sourceTree = ""; }; @@ -33,7 +48,7 @@ 8A3F84232AC4C891005E2EE8 /* models */ = {isa = PBXFileReference; lastKnownFileType = folder; name = models; path = llama.swiftui/Resources/models; sourceTree = ""; }; 8A907F322AC7134E006146EA /* LibLlama.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibLlama.swift; sourceTree = ""; }; 8A9F7C4C2AC332EE008AE1EA /* LlamaState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LlamaState.swift; sourceTree = ""; }; - DF2D2FE72B4A59BE00FCB72D /* llama.cpp */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = llama.cpp; path = ../..; sourceTree = ""; }; + DD0078912D6756A5003C187C /* llama.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = llama.xcframework; path = "../../build-ios/llama.xcframework"; sourceTree = ""; }; F1FE20E12B465EC900B45541 /* LoadCustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadCustomButton.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -42,9 +57,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1809696D2D05A39F00400EE8 /* llama in Frameworks */, 549479CB2AC9E16000E0F78B /* Metal.framework in Frameworks */, 8A39BE0A2AC7601100BFEB40 /* Accelerate.framework in Frameworks */, + DD0078922D6756A5003C187C /* llama.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -54,7 +69,6 @@ 8A1C836A2AC328BD0096AF73 = { isa = PBXGroup; children = ( - DF2D2FE72B4A59BE00FCB72D /* llama.cpp */, 8A907F312AC7134E006146EA /* llama.cpp.swift */, 8A3F84232AC4C891005E2EE8 /* models */, 8A1C83752AC328BD0096AF73 /* llama.swiftui */, @@ -86,6 +100,7 @@ 8A39BE082AC7601000BFEB40 /* Frameworks */ = { isa = PBXGroup; children = ( + DD0078912D6756A5003C187C /* llama.xcframework */, 549479CA2AC9E16000E0F78B /* Metal.framework */, 8A39BE092AC7601000BFEB40 /* Accelerate.framework */, ); @@ -144,6 +159,7 @@ 8A1C836F2AC328BD0096AF73 /* Sources */, 8A1C83702AC328BD0096AF73 /* Frameworks */, 8A1C83712AC328BD0096AF73 /* Resources */, + DD0078942D6756A5003C187C /* Embed Frameworks */, ); buildRules = ( ); @@ -151,7 +167,6 @@ ); name = llama.swiftui; packageProductDependencies = ( - 1809696C2D05A39F00400EE8 /* llama */, ); productName = llama.swiftui; productReference = 8A1C83732AC328BD0096AF73 /* llama.swiftui.app */; @@ -427,13 +442,6 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ - -/* Begin XCSwiftPackageProductDependency section */ - 1809696C2D05A39F00400EE8 /* llama */ = { - isa = XCSwiftPackageProductDependency; - productName = llama; - }; -/* End XCSwiftPackageProductDependency section */ }; rootObject = 8A1C836B2AC328BD0096AF73 /* Project object */; } diff --git a/spm-headers/ggml-alloc.h b/spm-headers/ggml-alloc.h deleted file mode 120000 index 0361ffc386a1f..0000000000000 --- a/spm-headers/ggml-alloc.h +++ /dev/null @@ -1 +0,0 @@ -../ggml/include/ggml-alloc.h \ No newline at end of file diff --git a/spm-headers/ggml-backend.h b/spm-headers/ggml-backend.h deleted file mode 120000 index 7295f0f0da742..0000000000000 --- a/spm-headers/ggml-backend.h +++ /dev/null @@ -1 +0,0 @@ -../ggml/include/ggml-backend.h \ No newline at end of file diff --git a/spm-headers/ggml-cpp.h b/spm-headers/ggml-cpp.h deleted file mode 120000 index 8a8604cc21bd6..0000000000000 --- a/spm-headers/ggml-cpp.h +++ /dev/null @@ -1 +0,0 @@ -../ggml/include/ggml-cpp.h \ No newline at end of file diff --git a/spm-headers/ggml-cpu.h b/spm-headers/ggml-cpu.h deleted file mode 120000 index 66e6296076f48..0000000000000 --- a/spm-headers/ggml-cpu.h +++ /dev/null @@ -1 +0,0 @@ -../ggml/include/ggml-cpu.h \ No newline at end of file diff --git a/spm-headers/ggml-metal.h b/spm-headers/ggml-metal.h deleted file mode 120000 index aefad5fa04ced..0000000000000 --- a/spm-headers/ggml-metal.h +++ /dev/null @@ -1 +0,0 @@ -../ggml/include/ggml-metal.h \ No newline at end of file diff --git a/spm-headers/ggml.h b/spm-headers/ggml.h deleted file mode 120000 index 0bdfeacbdbead..0000000000000 --- a/spm-headers/ggml.h +++ /dev/null @@ -1 +0,0 @@ -../ggml/include/ggml.h \ No newline at end of file diff --git a/spm-headers/llama.h b/spm-headers/llama.h deleted file mode 120000 index b31388f0dd652..0000000000000 --- a/spm-headers/llama.h +++ /dev/null @@ -1 +0,0 @@ -../include/llama.h \ No newline at end of file