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