1#!/usr/bin/env bash
2# validate-macos.sh - Validate macOS Application with embedded llama.xcframework using SwiftUI
3
4# Authentication options (optional) (can be set via environment variables)
5# To use: export APPLE_ID=your.email@example.com
6# export APPLE_PASSWORD=your-app-specific-password
7# ./validate-macos.sh
8APPLE_ID=${APPLE_ID:-""}
9APPLE_PASSWORD=${APPLE_PASSWORD:-""}
10
11# Ensure the script exits on error
12set -e
13
14# Function to print usage instructions
15print_usage() {
16 echo "Usage: ./validate-macos.sh [OPTIONS]"
17 echo ""
18 echo "Options:"
19 echo " --help Show this help message"
20 echo " --apple-id EMAIL Apple ID email for validation"
21 echo " --apple-password PWD App-specific password for Apple ID"
22 echo ""
23 echo "Environment variables:"
24 echo " APPLE_ID Apple ID email for validation"
25 echo " APPLE_PASSWORD App-specific password for Apple ID"
26 echo ""
27 echo "Notes:"
28 echo " - Command line options take precedence over environment variables"
29 echo " - Authentication is optional. If not provided, alternative validation will be performed"
30 echo " - For APPLE_PASSWORD, use an app-specific password generated at https://appleid.apple.com/account/manage"
31}
32
33# Parse command line arguments
34while [[ $# -gt 0 ]]; do
35 case $1 in
36 --help)
37 print_usage
38 exit 0
39 ;;
40 --apple-id)
41 APPLE_ID="$2"
42 shift 2
43 ;;
44 --apple-password)
45 APPLE_PASSWORD="$2"
46 shift 2
47 ;;
48 *)
49 echo "Unknown option: $1"
50 print_usage
51 exit 1
52 ;;
53 esac
54done
55
56# Function to clean up in case of error
57cleanup() {
58 # Don't clean up temp files on error to help with debugging
59 echo "===== macOS Validation Process Failed ====="
60 exit 1
61}
62
63# Set up trap to call cleanup function on error
64trap cleanup ERR
65
66set -e # Exit on any error
67
68ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )"
69BUILD_DIR="${ROOT_DIR}/validation-builds/ios"
70
71# Configuration
72APP_NAME="MacOSLlamaTest"
73BUNDLE_ID="org.ggml.MacOSLlamaTest"
74XCFRAMEWORK_PATH="${ROOT_DIR}/build-apple/llama.xcframework"
75TEMP_DIR="${BUILD_DIR}/temp"
76ARCHIVE_PATH="${BUILD_DIR}/${APP_NAME}.xcarchive"
77APP_PATH="${BUILD_DIR}/${APP_NAME}.app"
78ZIP_PATH="${BUILD_DIR}/${APP_NAME}.zip"
79VALIDATION_DIR="${BUILD_DIR}/validation"
80
81# Create necessary directories
82mkdir -p "${BUILD_DIR}"
83mkdir -p "${TEMP_DIR}"
84mkdir -p "${VALIDATION_DIR}"
85
86echo "===== macOS Validation Process Started ====="
87
88# 1. Create a simple test app project
89echo "Creating test macOS app project..."
90mkdir -p "${TEMP_DIR}/${APP_NAME}/${APP_NAME}"
91cat > "${TEMP_DIR}/${APP_NAME}/${APP_NAME}/Info.plist" << EOF
92<?xml version="1.0" encoding="UTF-8"?>
93<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
94<plist version="1.0">
95<dict>
96 <key>CFBundleDevelopmentRegion</key>
97 <string>en</string>
98 <key>CFBundleExecutable</key>
99 <string>${APP_NAME}</string>
100 <key>CFBundleIdentifier</key>
101 <string>${BUNDLE_ID}</string>
102 <key>CFBundleInfoDictionaryVersion</key>
103 <string>6.0</string>
104 <key>CFBundleName</key>
105 <string>${APP_NAME}</string>
106 <key>CFBundlePackageType</key>
107 <string>APPL</string>
108 <key>CFBundleShortVersionString</key>
109 <string>1.0</string>
110 <key>CFBundleVersion</key>
111 <string>1</string>
112 <key>LSMinimumSystemVersion</key>
113 <string>12.0</string>
114 <key>NSHumanReadableCopyright</key>
115 <string>Copyright © 2025 GGML. All rights reserved.</string>
116 <key>NSPrincipalClass</key>
117 <string>NSApplication</string>
118</dict>
119</plist>
120EOF
121
122# Create SwiftUI app files
123mkdir -p "${TEMP_DIR}/${APP_NAME}/${APP_NAME}/Sources"
124
125# Create App.swift
126cat > "${TEMP_DIR}/${APP_NAME}/${APP_NAME}/Sources/App.swift" << EOF
127import SwiftUI
128import llama
129
130@main
131struct LlamaTestApp: App {
132 var body: some Scene {
133 WindowGroup {
134 ContentView()
135 }
136 }
137}
138EOF
139
140# Create ContentView.swift with macOS specific elements
141cat > "${TEMP_DIR}/${APP_NAME}/${APP_NAME}/Sources/ContentView.swift" << EOF
142import SwiftUI
143import llama
144
145struct ContentView: View {
146 // Test that we can initialize a llama context params struct
147 let params = llama_context_default_params()
148
149 var body: some View {
150 VStack(spacing: 20) {
151 Text("Llama Framework Test on macOS")
152 .font(.largeTitle)
153 .padding()
154
155 Text("llama_context_default_params() created successfully")
156 .font(.headline)
157 .multilineTextAlignment(.center)
158 .padding()
159
160 // Display some param values to confirm the framework is working
161 Text("n_ctx: \(params.n_ctx)")
162 .font(.body)
163
164 Text("n_batch: \(params.n_batch)")
165 .font(.body)
166
167 Spacer()
168 }
169 .padding()
170 .frame(width: 600, height: 400)
171 }
172}
173
174struct ContentView_Previews: PreviewProvider {
175 static var previews: some View {
176 ContentView()
177 }
178}
179EOF
180
181# Create project.pbxproj, fixing the framework search paths issues
182mkdir -p "${TEMP_DIR}/${APP_NAME}/${APP_NAME}.xcodeproj"
183cat > "${TEMP_DIR}/${APP_NAME}/${APP_NAME}.xcodeproj/project.pbxproj" << 'EOF'
184// !$*UTF8*$!
185{
186 archiveVersion = 1;
187 classes = {
188 };
189 objectVersion = 54;
190 objects = {
191
192/* Begin PBXBuildFile section */
193 11111111111111111111111 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22222222222222222222222; };
194 33333333333333333333333 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44444444444444444444444; };
195 55555555555555555555555 /* llama.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 66666666666666666666666; };
196 77777777777777777777777 /* llama.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 66666666666666666666666; };
197/* End PBXBuildFile section */
198
199/* Begin PBXCopyFilesBuildPhase section */
200 88888888888888888888888 /* Embed Frameworks */ = {
201 isa = PBXCopyFilesBuildPhase;
202 buildActionMask = 2147483647;
203 dstPath = "";
204 dstSubfolderSpec = 10;
205 files = (
206 77777777777777777777777 /* llama.xcframework in Embed Frameworks */,
207 );
208 name = "Embed Frameworks";
209 runOnlyForDeploymentPostprocessing = 0;
210 };
211/* End PBXCopyFilesBuildPhase section */
212
213/* Begin PBXFileReference section */
214EOF
215
216# Continue with the project.pbxproj file, using the APP_NAME variable appropriately
217cat >> "${TEMP_DIR}/${APP_NAME}/${APP_NAME}.xcodeproj/project.pbxproj" << EOF
218 99999999999999999999999 /* ${APP_NAME}.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "${APP_NAME}.app"; sourceTree = BUILT_PRODUCTS_DIR; };
219 22222222222222222222222 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
220 44444444444444444444444 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
221 AAAAAAAAAAAAAAAAAAAAAAA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
222 66666666666666666666666 /* llama.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = llama.xcframework; sourceTree = "<group>"; };
223/* End PBXFileReference section */
224EOF
225
226# Add the rest of the project file with fixed framework search paths
227cat >> "${TEMP_DIR}/${APP_NAME}/${APP_NAME}.xcodeproj/project.pbxproj" << 'EOF'
228/* Begin PBXFrameworksBuildPhase section */
229 BBBBBBBBBBBBBBBBBBBBBBBB /* Frameworks */ = {
230 isa = PBXFrameworksBuildPhase;
231 buildActionMask = 2147483647;
232 files = (
233 55555555555555555555555 /* llama.xcframework in Frameworks */,
234 );
235 runOnlyForDeploymentPostprocessing = 0;
236 };
237/* End PBXFrameworksBuildPhase section */
238
239/* Begin PBXGroup section */
240EOF
241
242# Continue with the project.pbxproj file, using the APP_NAME variable appropriately
243cat >> "${TEMP_DIR}/${APP_NAME}/${APP_NAME}.xcodeproj/project.pbxproj" << EOF
244 CCCCCCCCCCCCCCCCCCCCCCCC /* Products */ = {
245 isa = PBXGroup;
246 children = (
247 99999999999999999999999 /* ${APP_NAME}.app */,
248 );
249 name = Products;
250 sourceTree = "<group>";
251 };
252EOF
253
254cat >> "${TEMP_DIR}/${APP_NAME}/${APP_NAME}.xcodeproj/project.pbxproj" << 'EOF'
255 DDDDDDDDDDDDDDDDDDDDDDDD /* Frameworks */ = {
256 isa = PBXGroup;
257 children = (
258 66666666666666666666666 /* llama.xcframework */,
259 );
260 name = Frameworks;
261 sourceTree = "<group>";
262 };
263 EEEEEEEEEEEEEEEEEEEEEEEE = {
264 isa = PBXGroup;
265 children = (
266 FFFFFFFFFFFFFFFFFFFFFFFF /* MacOSLlamaTest */,
267 CCCCCCCCCCCCCCCCCCCCCCCC /* Products */,
268 DDDDDDDDDDDDDDDDDDDDDDDD /* Frameworks */,
269 );
270 sourceTree = "<group>";
271 };
272 FFFFFFFFFFFFFFFFFFFFFFFF /* MacOSLlamaTest */ = {
273 isa = PBXGroup;
274 children = (
275 1111111111111111111111AA /* Sources */,
276 AAAAAAAAAAAAAAAAAAAAAAA /* Info.plist */,
277 );
278 path = "MacOSLlamaTest";
279 sourceTree = "<group>";
280 };
281 1111111111111111111111AA /* Sources */ = {
282 isa = PBXGroup;
283 children = (
284 22222222222222222222222 /* App.swift */,
285 44444444444444444444444 /* ContentView.swift */,
286 );
287 path = Sources;
288 sourceTree = "<group>";
289 };
290/* End PBXGroup section */
291EOF
292
293# Continue with the project.pbxproj file, using the APP_NAME variable appropriately
294cat >> "${TEMP_DIR}/${APP_NAME}/${APP_NAME}.xcodeproj/project.pbxproj" << EOF
295/* Begin PBXNativeTarget section */
296 3333333333333333333333AA /* ${APP_NAME} */ = {
297 isa = PBXNativeTarget;
298 buildConfigurationList = 4444444444444444444444AA /* Build configuration list for PBXNativeTarget "${APP_NAME}" */;
299 buildPhases = (
300 5555555555555555555555AA /* Sources */,
301 BBBBBBBBBBBBBBBBBBBBBBBB /* Frameworks */,
302 6666666666666666666666AA /* Resources */,
303 88888888888888888888888 /* Embed Frameworks */,
304 );
305 buildRules = (
306 );
307 dependencies = (
308 );
309 name = "${APP_NAME}";
310 productName = "${APP_NAME}";
311 productReference = 99999999999999999999999 /* ${APP_NAME}.app */;
312 productType = "com.apple.product-type.application";
313 };
314/* End PBXNativeTarget section */
315
316/* Begin PBXProject section */
317 7777777777777777777777AA /* Project object */ = {
318 isa = PBXProject;
319 attributes = {
320 LastSwiftUpdateCheck = 1240;
321 LastUpgradeCheck = 1240;
322 TargetAttributes = {
323 3333333333333333333333AA = {
324 CreatedOnToolsVersion = 12.4;
325 };
326 };
327 };
328 buildConfigurationList = 8888888888888888888888AA /* Build configuration list for PBXProject "${APP_NAME}" */;
329 compatibilityVersion = "Xcode 12.0";
330 developmentRegion = en;
331 hasScannedForEncodings = 0;
332 knownRegions = (
333 en,
334 Base,
335 );
336 mainGroup = EEEEEEEEEEEEEEEEEEEEEEEE;
337 productRefGroup = CCCCCCCCCCCCCCCCCCCCCCCC /* Products */;
338 projectDirPath = "";
339 projectRoot = "";
340 targets = (
341 3333333333333333333333AA /* ${APP_NAME} */,
342 );
343 };
344/* End PBXProject section */
345EOF
346
347# Add the rest of the file with correct FRAMEWORK_SEARCH_PATHS and macOS settings
348cat >> "${TEMP_DIR}/${APP_NAME}/${APP_NAME}.xcodeproj/project.pbxproj" << 'EOF'
349/* Begin PBXResourcesBuildPhase section */
350 6666666666666666666666AA /* Resources */ = {
351 isa = PBXResourcesBuildPhase;
352 buildActionMask = 2147483647;
353 files = (
354 );
355 runOnlyForDeploymentPostprocessing = 0;
356 };
357/* End PBXResourcesBuildPhase section */
358
359/* Begin PBXSourcesBuildPhase section */
360 5555555555555555555555AA /* Sources */ = {
361 isa = PBXSourcesBuildPhase;
362 buildActionMask = 2147483647;
363 files = (
364 33333333333333333333333 /* ContentView.swift in Sources */,
365 11111111111111111111111 /* App.swift in Sources */,
366 );
367 runOnlyForDeploymentPostprocessing = 0;
368 };
369/* End PBXSourcesBuildPhase section */
370
371/* Begin XCBuildConfiguration section */
372 9999999999999999999999AA /* Debug */ = {
373 isa = XCBuildConfiguration;
374 buildSettings = {
375 ALWAYS_SEARCH_USER_PATHS = NO;
376 CLANG_ANALYZER_NONNULL = YES;
377 CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
378 CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
379 CLANG_CXX_LIBRARY = "libc++";
380 CLANG_ENABLE_MODULES = YES;
381 CLANG_ENABLE_OBJC_ARC = YES;
382 CLANG_ENABLE_OBJC_WEAK = YES;
383 CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
384 CLANG_WARN_BOOL_CONVERSION = YES;
385 CLANG_WARN_COMMA = YES;
386 CLANG_WARN_CONSTANT_CONVERSION = YES;
387 CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
388 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
389 CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
390 CLANG_WARN_EMPTY_BODY = YES;
391 CLANG_WARN_ENUM_CONVERSION = YES;
392 CLANG_WARN_INFINITE_RECURSION = YES;
393 CLANG_WARN_INT_CONVERSION = YES;
394 CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
395 CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
396 CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
397 CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
398 CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
399 CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
400 CLANG_WARN_STRICT_PROTOTYPES = YES;
401 CLANG_WARN_SUSPICIOUS_MOVE = YES;
402 CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
403 CLANG_WARN_UNREACHABLE_CODE = YES;
404 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
405 COPY_PHASE_STRIP = NO;
406 DEBUG_INFORMATION_FORMAT = dwarf;
407 ENABLE_STRICT_OBJC_MSGSEND = YES;
408 ENABLE_TESTABILITY = YES;
409 GCC_C_LANGUAGE_STANDARD = gnu11;
410 GCC_DYNAMIC_NO_PIC = NO;
411 GCC_NO_COMMON_BLOCKS = YES;
412 GCC_OPTIMIZATION_LEVEL = 0;
413 GCC_PREPROCESSOR_DEFINITIONS = (
414 "DEBUG=1",
415 "$(inherited)",
416 );
417 GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
418 GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
419 GCC_WARN_UNDECLARED_SELECTOR = YES;
420 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
421 GCC_WARN_UNUSED_FUNCTION = YES;
422 GCC_WARN_UNUSED_VARIABLE = YES;
423 MACOSX_DEPLOYMENT_TARGET = 12.0;
424 MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
425 MTL_FAST_MATH = YES;
426 ONLY_ACTIVE_ARCH = YES;
427 SDKROOT = macosx;
428 SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
429 SWIFT_OPTIMIZATION_LEVEL = "-Onone";
430 };
431 name = Debug;
432 };
433 AAAAAAAAAAAAAAAAAAAAABBB /* Release */ = {
434 isa = XCBuildConfiguration;
435 buildSettings = {
436 ALWAYS_SEARCH_USER_PATHS = NO;
437 CLANG_ANALYZER_NONNULL = YES;
438 CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
439 CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
440 CLANG_CXX_LIBRARY = "libc++";
441 CLANG_ENABLE_MODULES = YES;
442 CLANG_ENABLE_OBJC_ARC = YES;
443 CLANG_ENABLE_OBJC_WEAK = YES;
444 CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
445 CLANG_WARN_BOOL_CONVERSION = YES;
446 CLANG_WARN_COMMA = YES;
447 CLANG_WARN_CONSTANT_CONVERSION = YES;
448 CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
449 CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
450 CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
451 CLANG_WARN_EMPTY_BODY = YES;
452 CLANG_WARN_ENUM_CONVERSION = YES;
453 CLANG_WARN_INFINITE_RECURSION = YES;
454 CLANG_WARN_INT_CONVERSION = YES;
455 CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
456 CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
457 CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
458 CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
459 CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
460 CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
461 CLANG_WARN_STRICT_PROTOTYPES = YES;
462 CLANG_WARN_SUSPICIOUS_MOVE = YES;
463 CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
464 CLANG_WARN_UNREACHABLE_CODE = YES;
465 CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
466 COPY_PHASE_STRIP = NO;
467 DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
468 ENABLE_NS_ASSERTIONS = NO;
469 ENABLE_STRICT_OBJC_MSGSEND = YES;
470 GCC_C_LANGUAGE_STANDARD = gnu11;
471 GCC_NO_COMMON_BLOCKS = YES;
472 GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
473 GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
474 GCC_WARN_UNDECLARED_SELECTOR = YES;
475 GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
476 GCC_WARN_UNUSED_FUNCTION = YES;
477 GCC_WARN_UNUSED_VARIABLE = YES;
478 MACOSX_DEPLOYMENT_TARGET = 12.0;
479 MTL_ENABLE_DEBUG_INFO = NO;
480 MTL_FAST_MATH = YES;
481 SDKROOT = macosx;
482 SWIFT_COMPILATION_MODE = wholemodule;
483 SWIFT_OPTIMIZATION_LEVEL = "-O";
484 };
485 name = Release;
486 };
487 BBBBBBBBBBBBBBBBBBBBBBCCC /* Debug */ = {
488 isa = XCBuildConfiguration;
489 buildSettings = {
490 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
491 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
492 CODE_SIGN_STYLE = Manual;
493 COMBINE_HIDPI_IMAGES = YES;
494 DEVELOPMENT_TEAM = "";
495 ENABLE_HARDENED_RUNTIME = YES;
496 ENABLE_PREVIEWS = YES;
497 FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)";
498 INFOPLIST_FILE = "MacOSLlamaTest/Info.plist";
499 LD_RUNPATH_SEARCH_PATHS = (
500 "$(inherited)",
501 "@executable_path/../Frameworks",
502 );
503 PRODUCT_BUNDLE_IDENTIFIER = "org.ggml.MacOSLlamaTest";
504 PRODUCT_NAME = "$(TARGET_NAME)";
505 PROVISIONING_PROFILE_SPECIFIER = "";
506 SWIFT_VERSION = 5.0;
507 };
508 name = Debug;
509 };
510 CCCCCCCCCCCCCCCCCCCCCCDDD /* Release */ = {
511 isa = XCBuildConfiguration;
512 buildSettings = {
513 ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
514 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
515 CODE_SIGN_STYLE = Manual;
516 COMBINE_HIDPI_IMAGES = YES;
517 DEVELOPMENT_TEAM = "";
518 ENABLE_HARDENED_RUNTIME = YES;
519 ENABLE_PREVIEWS = YES;
520 FRAMEWORK_SEARCH_PATHS = (
521 "$(inherited)",
522 "$(PROJECT_DIR)",
523 );
524 INFOPLIST_FILE = "MacOSLlamaTest/Info.plist";
525 LD_RUNPATH_SEARCH_PATHS = (
526 "$(inherited)",
527 "@executable_path/../Frameworks",
528 );
529 PRODUCT_BUNDLE_IDENTIFIER = "org.ggml.MacOSLlamaTest";
530 PRODUCT_NAME = "$(TARGET_NAME)";
531 PROVISIONING_PROFILE_SPECIFIER = "";
532 SWIFT_VERSION = 5.0;
533 };
534 name = Release;
535 };
536/* End XCBuildConfiguration section */
537EOF
538
539# Finish the project.pbxproj file
540cat >> "${TEMP_DIR}/${APP_NAME}/${APP_NAME}.xcodeproj/project.pbxproj" << EOF
541/* Begin XCConfigurationList section */
542 8888888888888888888888AA /* Build configuration list for PBXProject "${APP_NAME}" */ = {
543 isa = XCConfigurationList;
544 buildConfigurations = (
545 9999999999999999999999AA /* Debug */,
546 AAAAAAAAAAAAAAAAAAAAABBB /* Release */,
547 );
548 defaultConfigurationIsVisible = 0;
549 defaultConfigurationName = Release;
550 };
551 4444444444444444444444AA /* Build configuration list for PBXNativeTarget "${APP_NAME}" */ = {
552 isa = XCConfigurationList;
553 buildConfigurations = (
554 BBBBBBBBBBBBBBBBBBBBBBCCC /* Debug */,
555 CCCCCCCCCCCCCCCCCCCCCCDDD /* Release */,
556 );
557 defaultConfigurationIsVisible = 0;
558 defaultConfigurationName = Release;
559 };
560/* End XCConfigurationList section */
561 };
562 rootObject = 7777777777777777777777AA /* Project object */;
563}
564EOF
565
566# 2. Copy XCFramework to test project
567echo "Copying XCFramework to test project..."
568cp -R "${XCFRAMEWORK_PATH}" "${TEMP_DIR}/${APP_NAME}/"
569
570# 3. Build and archive the app
571echo "Building and archiving test app..."
572cd "${TEMP_DIR}/${APP_NAME}"
573
574# Create a simple xcscheme file to avoid xcodebuild scheme issues
575mkdir -p "${APP_NAME}.xcodeproj/xcshareddata/xcschemes"
576cat > "${APP_NAME}.xcodeproj/xcshareddata/xcschemes/${APP_NAME}.xcscheme" << EOF
577<?xml version="1.0" encoding="UTF-8"?>
578<Scheme
579 LastUpgradeVersion = "1240"
580 version = "1.3">
581 <BuildAction
582 parallelizeBuildables = "YES"
583 buildImplicitDependencies = "YES">
584 <BuildActionEntries>
585 <BuildActionEntry
586 buildForTesting = "YES"
587 buildForRunning = "YES"
588 buildForProfiling = "YES"
589 buildForArchiving = "YES"
590 buildForAnalyzing = "YES">
591 <BuildableReference
592 BuildableIdentifier = "primary"
593 BlueprintIdentifier = "3333333333333333333333AA"
594 BuildableName = "${APP_NAME}.app"
595 BlueprintName = "${APP_NAME}"
596 ReferencedContainer = "container:${APP_NAME}.xcodeproj">
597 </BuildableReference>
598 </BuildActionEntry>
599 </BuildActionEntries>
600 </BuildAction>
601 <TestAction
602 buildConfiguration = "Debug"
603 selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
604 selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
605 shouldUseLaunchSchemeArgsEnv = "YES">
606 <Testables>
607 </Testables>
608 </TestAction>
609 <LaunchAction
610 buildConfiguration = "Debug"
611 selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
612 selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
613 launchStyle = "0"
614 useCustomWorkingDirectory = "NO"
615 ignoresPersistentStateOnLaunch = "NO"
616 debugDocumentVersioning = "YES"
617 debugServiceExtension = "internal"
618 allowLocationSimulation = "YES">
619 <BuildableProductRunnable
620 runnableDebuggingMode = "0">
621 <BuildableReference
622 BuildableIdentifier = "primary"
623 BlueprintIdentifier = "3333333333333333333333AA"
624 BuildableName = "${APP_NAME}.app"
625 BlueprintName = "${APP_NAME}"
626 ReferencedContainer = "container:${APP_NAME}.xcodeproj">
627 </BuildableReference>
628 </BuildableProductRunnable>
629 </LaunchAction>
630 <ProfileAction
631 buildConfiguration = "Release"
632 shouldUseLaunchSchemeArgsEnv = "YES"
633 savedToolIdentifier = ""
634 useCustomWorkingDirectory = "NO"
635 debugDocumentVersioning = "YES">
636 <BuildableProductRunnable
637 runnableDebuggingMode = "0">
638 <BuildableReference
639 BuildableIdentifier = "primary"
640 BlueprintIdentifier = "3333333333333333333333AA"
641 BuildableName = "${APP_NAME}.app"
642 BlueprintName = "${APP_NAME}"
643 ReferencedContainer = "container:${APP_NAME}.xcodeproj">
644 </BuildableReference>
645 </BuildableProductRunnable>
646 </ProfileAction>
647 <AnalyzeAction
648 buildConfiguration = "Debug">
649 </AnalyzeAction>
650 <ArchiveAction
651 buildConfiguration = "Release"
652 revealArchiveInOrganizer = "YES">
653 </ArchiveAction>
654</Scheme>
655EOF
656
657# Now use xcodebuild with an explicitly defined product name for macOS
658xcodebuild -project "${APP_NAME}.xcodeproj" -scheme "${APP_NAME}" -sdk macosx -configuration Release archive -archivePath "${ARCHIVE_PATH}" CODE_SIGN_IDENTITY="-" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO PRODUCT_NAME="${APP_NAME}" SWIFT_OPTIMIZATION_LEVEL="-Onone" -quiet
659
660# 4. Create a package for distribution
661echo "Creating distributable package from archive..."
662cp -R "${ARCHIVE_PATH}/Products/Applications/${APP_NAME}.app" "${APP_PATH}"
663
664# Check and log app structure
665echo "App structure:"
666ls -la "${APP_PATH}"
667echo "Frameworks:"
668ls -la "${APP_PATH}/Contents/Frameworks/" 2>/dev/null || echo "No Frameworks directory found"
669
670# Create a zip file for potential distribution
671cd "${BUILD_DIR}"
672zip -r "${ZIP_PATH}" "${APP_NAME}.app"
673
674# Check embedded provisioning profile
675echo "Checking provisioning profile (if any)..."
676PROVISIONING_PROFILE=$(find "${APP_PATH}/Contents" -name "embedded.provisionprofile" 2>/dev/null)
677if [ -n "$PROVISIONING_PROFILE" ]; then
678 echo "Found embedded provisioning profile:"
679 security cms -D -i "$PROVISIONING_PROFILE" || echo "Unable to decode provisioning profile"
680else
681 echo "No embedded provisioning profile found (expected for ad-hoc builds)"
682fi
683
684# 5. Validate the app
685echo "Validating macOS app..."
686VALIDATION_OUTPUT="${VALIDATION_DIR}/validation_output.txt"
687
688# Check if authentication credentials are provided
689AUTH_ARGS=""
690if [ -n "$APPLE_ID" ] && [ -n "$APPLE_PASSWORD" ]; then
691 echo "Using Apple ID authentication for validation..."
692 AUTH_ARGS="--username \"$APPLE_ID\" --password \"$APPLE_PASSWORD\""
693else
694 echo "No authentication credentials provided. Will perform basic validation."
695 echo "To use your personal developer account, you can run the script with:"
696 echo " APPLE_ID='your.email@example.com' APPLE_PASSWORD='your-app-specific-password' ./validate-macos.sh"
697 echo "Note: You need to create an app-specific password at https://appleid.apple.com/account/manage"
698fi
699
700# For macOS we need to use notarytool or alternative checks because altool doesn't support macOS apps in the same way
701echo "Note: For macOS, formal notarization process would require Apple Developer credentials."
702echo "Performing alternative validation checks..."
703
704# Final validation result
705FINAL_VALIDATION_RESULT=0
706
707# Check if app was created successfully
708if [ -d "${APP_PATH}" ] && [ -s "${APP_PATH}/Contents/MacOS/${APP_NAME}" ]; then
709 echo "✅ App package created successfully"
710else
711 echo "❌ App package not created or binary missing"
712 FINAL_VALIDATION_RESULT=1
713fi
714
715# Check if app binary exists and is executable
716if [ -f "${APP_PATH}/Contents/MacOS/${APP_NAME}" ] && [ -x "${APP_PATH}/Contents/MacOS/${APP_NAME}" ]; then
717 echo "✅ App binary exists and is executable"
718else
719 echo "❌ App binary missing or not executable"
720 FINAL_VALIDATION_RESULT=1
721fi
722
723# Check if framework was properly embedded
724if [ -d "${APP_PATH}/Contents/Frameworks/llama.framework" ]; then
725 echo "✅ llama.framework properly embedded"
726else
727 echo "❌ llama.framework not properly embedded"
728 FINAL_VALIDATION_RESULT=1
729fi
730
731# Check if framework binary exists
732if [ -f "${APP_PATH}/Contents/Frameworks/llama.framework/Versions/A/llama" ]; then
733 echo "✅ Framework binary exists"
734
735 # Further validate framework by checking architecture
736 ARCHS=$(lipo -info "${APP_PATH}/Contents/Frameworks/llama.framework/Versions/A/llama" 2>/dev/null | grep -o "arm64\\|x86_64" | tr '\n' ' ')
737 if [ -n "$ARCHS" ]; then
738 echo "✅ Framework architecture(s): $ARCHS"
739 else
740 echo "⚠️ Could not determine framework architecture"
741 fi
742else
743 echo "❌ Framework binary missing"
744 FINAL_VALIDATION_RESULT=1
745fi
746
747# Check code signing
748echo ""
749echo "==== CODE SIGNING INFO ===="
750codesign -vv -d "${APP_PATH}" 2>&1 || echo "Code signing verification not available (expected for ad-hoc builds)"
751
752if [ $FINAL_VALIDATION_RESULT -eq 0 ]; then
753 if [ -n "$AUTH_ARGS" ]; then
754 echo ""
755 echo "To notarize this app with Apple (requires Apple Developer account):"
756 echo "xcrun notarytool submit \"${ZIP_PATH}\" --apple-id \"your-apple-id\" --password \"your-app-specific-password\" --team-id \"your-team-id\" --wait"
757 echo ""
758 fi
759 echo "✅ Validation PASSED: macOS app built successfully with embedded framework"
760else
761 echo "❌ Validation FAILED: Issues found with the app or framework"
762fi
763
764# Don't clean up on error to allow inspection
765if [ $FINAL_VALIDATION_RESULT -ne 0 ]; then
766 echo ""
767 echo "Temporary files kept for inspection at: ${TEMP_DIR}"
768 echo "===== macOS Validation Process Failed ====="
769 exit 1
770fi
771
772# Clean up temporary files but keep build artifacts
773if [ $FINAL_VALIDATION_RESULT -eq 0 ]; then
774 echo "Cleaning up temporary files..."
775 #rm -rf "${TEMP_DIR}"
776fi
777
778echo "===== macOS Validation Process Completed ====="
779echo "App package available at: ${APP_PATH}"
780echo "Zipped app available at: ${ZIP_PATH}"
781exit $FINAL_VALIDATION_RESULT