Skip to content

Commit 66f7717

Browse files
committed
llama : add xcframework build script
This commit adds a script to build an XCFramework for iOS. The script uses CMake to build the library for both the simulator and device and then creates an XCFramework that contains both builds. The XCFramework can then be added to a project and used in the same way as a regular framework. The llama.swiftui example project has been updated to use the XCFramework and can be started using the following command: ```console $ open examples/llama.swiftui/llama.swiftui.xcodeproj/ ``` Refs: #10747
1 parent c5d91a7 commit 66f7717

File tree

4 files changed

+182
-11
lines changed

4 files changed

+182
-11
lines changed

Package.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ let package = Package(
1414
.library(name: "llama", targets: ["llama"]),
1515
],
1616
targets: [
17-
.systemLibrary(name: "llama", pkgConfig: "llama"),
17+
.binaryTarget(
18+
name: "llama",
19+
path: "build-ios/llama.xcframework"
20+
),
21+
//.systemLibrary(name: "llama", pkgConfig: "llama"),
1822
]
1923
)

build-xcframework.sh

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
#!/bin/bash
2+
#
3+
# Options
4+
DEPLOYMENT_TARGET=14.0
5+
BUILD_SHARED_LIBS=ON
6+
LLAMA_STATIC=OFF
7+
LLAMA_BUILD_EXAMPLES=OFF
8+
LLAMA_BUILD_TESTS=OFF
9+
LLAMA_BUILD_SERVER=OFF
10+
GGML_METAL_EMBED_LIBRARY=ON
11+
GGML_METAL_USE_BF16=ON
12+
13+
set -xe
14+
15+
rm -rf build-ios
16+
rm -rf build-ios-sim
17+
rm -rf build-ios-device
18+
19+
# Function to setup framework structure for a given architecture
20+
setup_framework_structure() {
21+
local build_dir=$1
22+
local framework_name="llama"
23+
24+
# Create framework directory structure
25+
mkdir -p ${build_dir}/framework/${framework_name}.framework
26+
mkdir -p ${build_dir}/framework/${framework_name}.framework/Headers
27+
mkdir -p ${build_dir}/framework/${framework_name}.framework/Modules
28+
29+
# Copy all required headers.
30+
cp include/llama.h ${build_dir}/framework/${framework_name}.framework/Headers/
31+
cp ggml/include/ggml.h ${build_dir}/framework/${framework_name}.framework/Headers/
32+
cp ggml/include/ggml-alloc.h ${build_dir}/framework/${framework_name}.framework/Headers/
33+
cp ggml/include/ggml-backend.h ${build_dir}/framework/${framework_name}.framework/Headers/
34+
cp ggml/include/ggml-metal.h ${build_dir}/framework/${framework_name}.framework/Headers/
35+
cp ggml/include/ggml-cpu.h ${build_dir}/framework/${framework_name}.framework/Headers/
36+
cp ggml/include/ggml-blas.h ${build_dir}/framework/${framework_name}.framework/Headers/
37+
38+
# Create module map that describes how which headers are part of the llama module.
39+
# This enables swift code to import 'llama' and get access to all public interfaces
40+
# the headers with the usage of export *. For example, LibLlama.swift imports the
41+
# 'llama' module.
42+
cat > ${build_dir}/framework/${framework_name}.framework/Modules/module.modulemap << EOF
43+
framework module llama {
44+
header "llama.h"
45+
header "ggml.h"
46+
header "ggml-alloc.h"
47+
header "ggml-backend.h"
48+
header "ggml-metal.h"
49+
header "ggml-cpu.h"
50+
header "ggml-blas.h"
51+
52+
export *
53+
}
54+
EOF
55+
56+
# Create Info.plist (Information Property List) file that describes the app.
57+
# This will be a Framework bundle (FMWK).
58+
cat > ${build_dir}/framework/${framework_name}.framework/Info.plist << EOF
59+
<?xml version="1.0" encoding="UTF-8"?>
60+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
61+
<plist version="1.0">
62+
<dict>
63+
<key>CFBundleDevelopmentRegion</key>
64+
<string>en</string>
65+
<key>CFBundleExecutable</key>
66+
<string>llama</string>
67+
<key>CFBundleIdentifier</key>
68+
<string>ggml-org.llama.framework</string>
69+
<key>CFBundleInfoDictionaryVersion</key>
70+
<string>6.0</string>
71+
<key>CFBundleName</key>
72+
<string>llama</string>
73+
<key>CFBundlePackageType</key>
74+
<string>FMWK</string>
75+
<key>CFBundleShortVersionString</key>
76+
<string>1.0</string>
77+
<key>CFBundleVersion</key>
78+
<string>1</string>
79+
<key>MinimumOSVersion</key>
80+
<string>14.0</string>
81+
</dict>
82+
</plist>
83+
EOF
84+
}
85+
86+
# Common options for all builds.
87+
COMMON_CMAKE_ARGS=(
88+
-DIOS=ON
89+
-DCMAKE_SYSTEM_NAME=iOS
90+
-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOYMENT_TARGET}
91+
-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=NO
92+
-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY=""
93+
-DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED=NO
94+
-DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}
95+
-DLLAMA_STATIC=${LLAMA_STATIC}
96+
-DLLAMA_BUILD_EXAMPLES=${LLAMA_BUILD_EXAMPLES}
97+
-DLLAMA_BUILD_TESTS=${LLAMA_BUILD_TESTS}
98+
-DLLAMA_BUILD_SERVER=${LLAMA_BUILD_SERVER}
99+
-DGGML_METAL_EMBED_LIBRARY=${GGML_METAL_EMBED_LIBRARY}
100+
-DGGML_METAL_USE_BF16=${GGML_METAL_USE_BF16}
101+
-DCMAKE_INSTALL_NAME_DIR="@rpath/llama.framework/llama"
102+
)
103+
104+
# Build for iOS simulator.
105+
cmake -B build-ios-sim -G Xcode \
106+
"${COMMON_CMAKE_ARGS[@]}" \
107+
-DCMAKE_OSX_SYSROOT=iphonesimulator \
108+
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \
109+
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=iphonesimulator \
110+
-S .
111+
cmake --build build-ios-sim --config Release
112+
113+
# Build for iOS devices.
114+
cmake -B build-ios-device -G Xcode \
115+
"${COMMON_CMAKE_ARGS[@]}" \
116+
-DCMAKE_OSX_SYSROOT=iphoneos \
117+
-DCMAKE_OSX_ARCHITECTURES="arm64" \
118+
-DCMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS=iphoneos \
119+
-S .
120+
cmake --build build-ios-device --config Release
121+
122+
# Setup frameworks and copy binaries and headers.
123+
setup_framework_structure "build-ios-sim"
124+
setup_framework_structure "build-ios-device"
125+
126+
# Copy and rename the binaries.
127+
cp build-ios-sim/bin/Release/libllama.dylib build-ios-sim/framework/llama.framework/llama
128+
cp build-ios-device/bin/Release/libllama.dylib build-ios-device/framework/llama.framework/llama
129+
130+
# Fix the install name in the binaries
131+
install_name_tool -id "@rpath/llama.framework/llama" build-ios-sim/framework/llama.framework/llama
132+
install_name_tool -id "@rpath/llama.framework/llama" build-ios-device/framework/llama.framework/llama
133+
134+
# Create XCFramework with the two builds which will contain both simulator
135+
# and device versions in the same framework.
136+
xcodebuild -create-xcframework \
137+
-framework $(pwd)/build-ios-sim/framework/llama.framework \
138+
-framework $(pwd)/build-ios-device/framework/llama.framework \
139+
-output $(pwd)/build-ios/llama.xcframework
140+
141+
# The generated framework can be found in build-ios/llama.xcframework and
142+
# can be added to a projects "Frameworks, Libraries, and Embedded Content".

examples/llama.swiftui/README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ point for more advanced projects.
55

66
For usage instructions and performance stats, check the following discussion: https://github.com/ggml-org/llama.cpp/discussions/4508
77

8+
9+
### Building
10+
First llama.cpp need to be built and a XCFramework needs to be created. This can be done by running
11+
the following script from the llama.cpp project root:
12+
```console
13+
$ ./build-xcframework.sh
14+
```
15+
Open `llama.swiftui.xcodeproj` project in Xcode and you should be able to build and run the app on
16+
a simulator or a real device.
17+
18+
To use the framework with a different project, the XCFramework can be added to the project by
19+
adding `build-ios/llama.xcframework` by dragging and dropping it into the project navigator, or
20+
by manually selecting the framework in the "Frameworks, Libraries, and Embedded Content" section
21+
of the project settings.
22+
823
![image](https://github.com/ggml-org/llama.cpp/assets/1991296/2b40284f-8421-47a2-b634-74eece09a299)
924

1025
Video demonstration:

examples/llama.swiftui/llama.swiftui.xcodeproj/project.pbxproj

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
1809696D2D05A39F00400EE8 /* llama in Frameworks */ = {isa = PBXBuildFile; productRef = 1809696C2D05A39F00400EE8 /* llama */; };
1110
549479CB2AC9E16000E0F78B /* Metal.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 549479CA2AC9E16000E0F78B /* Metal.framework */; };
1211
79E1D9CD2B4CD16E005F8E46 /* InputButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79E1D9CC2B4CD16E005F8E46 /* InputButton.swift */; };
1312
7FA3D2B32B2EA2F600543F92 /* DownloadButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FA3D2B22B2EA2F600543F92 /* DownloadButton.swift */; };
@@ -18,9 +17,25 @@
1817
8A3F84242AC4C891005E2EE8 /* models in Resources */ = {isa = PBXBuildFile; fileRef = 8A3F84232AC4C891005E2EE8 /* models */; };
1918
8A907F332AC7138A006146EA /* LibLlama.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A907F322AC7134E006146EA /* LibLlama.swift */; };
2019
8A9F7C4D2AC332EE008AE1EA /* LlamaState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9F7C4C2AC332EE008AE1EA /* LlamaState.swift */; };
20+
DD0078922D6756A5003C187C /* llama.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0078912D6756A5003C187C /* llama.xcframework */; };
21+
DD0078932D6756A5003C187C /* llama.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DD0078912D6756A5003C187C /* llama.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
2122
F1FE20E22B465ECA00B45541 /* LoadCustomButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1FE20E12B465EC900B45541 /* LoadCustomButton.swift */; };
2223
/* End PBXBuildFile section */
2324

25+
/* Begin PBXCopyFilesBuildPhase section */
26+
DD0078942D6756A5003C187C /* Embed Frameworks */ = {
27+
isa = PBXCopyFilesBuildPhase;
28+
buildActionMask = 2147483647;
29+
dstPath = "";
30+
dstSubfolderSpec = 10;
31+
files = (
32+
DD0078932D6756A5003C187C /* llama.xcframework in Embed Frameworks */,
33+
);
34+
name = "Embed Frameworks";
35+
runOnlyForDeploymentPostprocessing = 0;
36+
};
37+
/* End PBXCopyFilesBuildPhase section */
38+
2439
/* Begin PBXFileReference section */
2540
549479CA2AC9E16000E0F78B /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; };
2641
79E1D9CC2B4CD16E005F8E46 /* InputButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputButton.swift; sourceTree = "<group>"; };
@@ -33,6 +48,7 @@
3348
8A3F84232AC4C891005E2EE8 /* models */ = {isa = PBXFileReference; lastKnownFileType = folder; name = models; path = llama.swiftui/Resources/models; sourceTree = "<group>"; };
3449
8A907F322AC7134E006146EA /* LibLlama.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LibLlama.swift; sourceTree = "<group>"; };
3550
8A9F7C4C2AC332EE008AE1EA /* LlamaState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LlamaState.swift; sourceTree = "<group>"; };
51+
DD0078912D6756A5003C187C /* llama.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = llama.xcframework; path = "../../build-ios/llama.xcframework"; sourceTree = "<group>"; };
3652
DF2D2FE72B4A59BE00FCB72D /* llama.cpp */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = llama.cpp; path = ../..; sourceTree = "<group>"; };
3753
F1FE20E12B465EC900B45541 /* LoadCustomButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadCustomButton.swift; sourceTree = "<group>"; };
3854
/* End PBXFileReference section */
@@ -42,9 +58,9 @@
4258
isa = PBXFrameworksBuildPhase;
4359
buildActionMask = 2147483647;
4460
files = (
45-
1809696D2D05A39F00400EE8 /* llama in Frameworks */,
4661
549479CB2AC9E16000E0F78B /* Metal.framework in Frameworks */,
4762
8A39BE0A2AC7601100BFEB40 /* Accelerate.framework in Frameworks */,
63+
DD0078922D6756A5003C187C /* llama.xcframework in Frameworks */,
4864
);
4965
runOnlyForDeploymentPostprocessing = 0;
5066
};
@@ -86,6 +102,7 @@
86102
8A39BE082AC7601000BFEB40 /* Frameworks */ = {
87103
isa = PBXGroup;
88104
children = (
105+
DD0078912D6756A5003C187C /* llama.xcframework */,
89106
549479CA2AC9E16000E0F78B /* Metal.framework */,
90107
8A39BE092AC7601000BFEB40 /* Accelerate.framework */,
91108
);
@@ -144,14 +161,14 @@
144161
8A1C836F2AC328BD0096AF73 /* Sources */,
145162
8A1C83702AC328BD0096AF73 /* Frameworks */,
146163
8A1C83712AC328BD0096AF73 /* Resources */,
164+
DD0078942D6756A5003C187C /* Embed Frameworks */,
147165
);
148166
buildRules = (
149167
);
150168
dependencies = (
151169
);
152170
name = llama.swiftui;
153171
packageProductDependencies = (
154-
1809696C2D05A39F00400EE8 /* llama */,
155172
);
156173
productName = llama.swiftui;
157174
productReference = 8A1C83732AC328BD0096AF73 /* llama.swiftui.app */;
@@ -427,13 +444,6 @@
427444
defaultConfigurationName = Release;
428445
};
429446
/* End XCConfigurationList section */
430-
431-
/* Begin XCSwiftPackageProductDependency section */
432-
1809696C2D05A39F00400EE8 /* llama */ = {
433-
isa = XCSwiftPackageProductDependency;
434-
productName = llama;
435-
};
436-
/* End XCSwiftPackageProductDependency section */
437447
};
438448
rootObject = 8A1C836B2AC328BD0096AF73 /* Project object */;
439449
}

0 commit comments

Comments
 (0)