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