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