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