| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <asl.h> | 5 #include <asl.h> |
| 6 #import <Foundation/Foundation.h> | 6 #import <Foundation/Foundation.h> |
| 7 #include <libgen.h> | 7 #include <libgen.h> |
| 8 #include <stdarg.h> | 8 #include <stdarg.h> |
| 9 #include <stdio.h> | 9 #include <stdio.h> |
| 10 | 10 |
| 11 // An executable (iossim) that runs an app in the iOS Simulator. | 11 // An executable (iossim) that runs an app in the iOS Simulator. |
| 12 // Run 'iossim -h' for usage information. | 12 // Run 'iossim -h' for usage information. |
| 13 // | 13 // |
| 14 // For best results, the iOS Simulator application should not be running when | 14 // For best results, the iOS Simulator application should not be running when |
| 15 // iossim is invoked. | 15 // iossim is invoked. |
| 16 // | 16 // |
| 17 // Headers for iPhoneSimulatorRemoteClient and other frameworks used in this | 17 // Headers for iPhoneSimulatorRemoteClient and other frameworks used in this |
| 18 // tool are generated by class-dump, via GYP. | 18 // tool are generated by class-dump, via GYP. |
| 19 // (class-dump is available at http://www.codethecode.com/projects/class-dump/) | 19 // (class-dump is available at http://www.codethecode.com/projects/class-dump/) |
| 20 // | 20 // |
| 21 // However, there are some forward declarations required to get things to | 21 // However, there are some forward declarations required to get things to |
| 22 // compile. | 22 // compile. |
| 23 | 23 |
| 24 // TODO(lliabraa): Once all builders are on Xcode 6 this ifdef can be removed | |
| 25 // (crbug.com/385030). | |
| 26 #if defined(IOSSIM_USE_XCODE_6) | |
| 27 @class DVTStackBacktrace; | 24 @class DVTStackBacktrace; |
| 28 #import "DVTFoundation.h" | 25 #import "DVTFoundation.h" |
| 29 #endif // IOSSIM_USE_XCODE_6 | |
| 30 | 26 |
| 31 // TODO(lliabraa): Once all builders are on Xcode 6 this ifdef can be removed | |
| 32 // (crbug.com/385030). | |
| 33 #if defined(IOSSIM_USE_XCODE_6) | |
| 34 @protocol SimBridge; | 27 @protocol SimBridge; |
| 35 @class DVTSimulatorApplication; | 28 @class DVTSimulatorApplication; |
| 36 @class SimDeviceSet; | 29 @class SimDeviceSet; |
| 37 @class SimDeviceType; | 30 @class SimDeviceType; |
| 38 @class SimProfilesPathMonitor; | 31 @class SimProfilesPathMonitor; |
| 39 @class SimRuntime; | 32 @class SimRuntime; |
| 40 @class SimServiceConnectionManager; | 33 @class SimServiceConnectionManager; |
| 41 #import "CoreSimulator.h" | 34 #import "CoreSimulator.h" |
| 42 #endif // IOSSIM_USE_XCODE_6 | |
| 43 | 35 |
| 44 @interface DVTPlatform : NSObject | 36 @interface DVTPlatform : NSObject |
| 45 + (BOOL)loadAllPlatformsReturningError:(id*)arg1; | 37 + (BOOL)loadAllPlatformsReturningError:(id*)arg1; |
| 46 @end | 38 @end |
| 47 @class DTiPhoneSimulatorApplicationSpecifier; | 39 @class DTiPhoneSimulatorApplicationSpecifier; |
| 48 @class DTiPhoneSimulatorSession; | 40 @class DTiPhoneSimulatorSession; |
| 49 @class DTiPhoneSimulatorSessionConfig; | 41 @class DTiPhoneSimulatorSessionConfig; |
| 50 @class DTiPhoneSimulatorSystemRoot; | 42 @class DTiPhoneSimulatorSystemRoot; |
| 51 @class DVTConfinementServiceConnection; | 43 @class DVTConfinementServiceConnection; |
| 52 @class DVTDispatchLock; | 44 @class DVTDispatchLock; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 70 // is the PID of the process the message is about (as opposed to launchd's PID). | 62 // is the PID of the process the message is about (as opposed to launchd's PID). |
| 71 #define ASL_KEY_REF_PID "RefPID" | 63 #define ASL_KEY_REF_PID "RefPID" |
| 72 | 64 |
| 73 namespace { | 65 namespace { |
| 74 | 66 |
| 75 // Name of environment variables that control the user's home directory in the | 67 // Name of environment variables that control the user's home directory in the |
| 76 // simulator. | 68 // simulator. |
| 77 const char* const kUserHomeEnvVariable = "CFFIXED_USER_HOME"; | 69 const char* const kUserHomeEnvVariable = "CFFIXED_USER_HOME"; |
| 78 const char* const kHomeEnvVariable = "HOME"; | 70 const char* const kHomeEnvVariable = "HOME"; |
| 79 | 71 |
| 80 // Device family codes for iPhone and iPad. | |
| 81 const int kIPhoneFamily = 1; | |
| 82 const int kIPadFamily = 2; | |
| 83 | |
| 84 // Max number of seconds to wait for the simulator session to start. | 72 // Max number of seconds to wait for the simulator session to start. |
| 85 // This timeout must allow time to start up iOS Simulator, install the app | 73 // This timeout must allow time to start up iOS Simulator, install the app |
| 86 // and perform any other black magic that is encoded in the | 74 // and perform any other black magic that is encoded in the |
| 87 // iPhoneSimulatorRemoteClient framework to kick things off. Normal start up | 75 // iPhoneSimulatorRemoteClient framework to kick things off. Normal start up |
| 88 // time is only a couple seconds but machine load, disk caches, etc., can all | 76 // time is only a couple seconds but machine load, disk caches, etc., can all |
| 89 // affect startup time in the wild so the timeout needs to be fairly generous. | 77 // affect startup time in the wild so the timeout needs to be fairly generous. |
| 90 // If this timeout occurs iossim will likely exit with non-zero status; the | 78 // If this timeout occurs iossim will likely exit with non-zero status; the |
| 91 // exception being if the app is invoked and completes execution before the | 79 // exception being if the app is invoked and completes execution before the |
| 92 // session is started (this case is handled in session:didStart:withError). | 80 // session is started (this case is handled in session:didStart:withError). |
| 93 const NSTimeInterval kDefaultSessionStartTimeoutSeconds = 30; | 81 const NSTimeInterval kDefaultSessionStartTimeoutSeconds = 30; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 111 | 99 |
| 112 const char* gToolName = "iossim"; | 100 const char* gToolName = "iossim"; |
| 113 | 101 |
| 114 // Exit status codes. | 102 // Exit status codes. |
| 115 const int kExitSuccess = EXIT_SUCCESS; | 103 const int kExitSuccess = EXIT_SUCCESS; |
| 116 const int kExitFailure = EXIT_FAILURE; | 104 const int kExitFailure = EXIT_FAILURE; |
| 117 const int kExitInvalidArguments = 2; | 105 const int kExitInvalidArguments = 2; |
| 118 const int kExitInitializationFailure = 3; | 106 const int kExitInitializationFailure = 3; |
| 119 const int kExitAppFailedToStart = 4; | 107 const int kExitAppFailedToStart = 4; |
| 120 const int kExitAppCrashed = 5; | 108 const int kExitAppCrashed = 5; |
| 121 const int kExitUnsupportedXcodeVersion = 6; | |
| 122 | 109 |
| 123 void LogError(NSString* format, ...) { | 110 void LogError(NSString* format, ...) { |
| 124 va_list list; | 111 va_list list; |
| 125 va_start(list, format); | 112 va_start(list, format); |
| 126 | 113 |
| 127 NSString* message = | 114 NSString* message = |
| 128 [[[NSString alloc] initWithFormat:format arguments:list] autorelease]; | 115 [[[NSString alloc] initWithFormat:format arguments:list] autorelease]; |
| 129 | 116 |
| 130 fprintf(stderr, "%s: ERROR: %s\n", gToolName, [message UTF8String]); | 117 fprintf(stderr, "%s: ERROR: %s\n", gToolName, [message UTF8String]); |
| 131 fflush(stderr); | 118 fflush(stderr); |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 LogError(@"Task '%@ %@' exited with return code %d.", | 165 LogError(@"Task '%@ %@' exited with return code %d.", |
| 179 toolPath, | 166 toolPath, |
| 180 [args componentsJoinedByString:@" "], | 167 [args componentsJoinedByString:@" "], |
| 181 [task terminationStatus]); | 168 [task terminationStatus]); |
| 182 return nil; | 169 return nil; |
| 183 } | 170 } |
| 184 return [[[NSString alloc] initWithData:outputData | 171 return [[[NSString alloc] initWithData:outputData |
| 185 encoding:NSUTF8StringEncoding] autorelease]; | 172 encoding:NSUTF8StringEncoding] autorelease]; |
| 186 } | 173 } |
| 187 | 174 |
| 188 // Finds the Xcode version via xcodebuild -version. Output from xcodebuild is | |
| 189 // expected to look like: | |
| 190 // Xcode <version> | |
| 191 // Build version 5B130a | |
| 192 // where <version> is the string returned by this function (e.g. 6.0). | |
| 193 NSString* FindXcodeVersion() { | |
| 194 NSString* output = GetOutputFromTask(@"/usr/bin/xcodebuild", | |
| 195 @[ @"-version" ]); | |
| 196 // Scan past the "Xcode ", then scan the rest of the line into |version|. | |
| 197 NSScanner* scanner = [NSScanner scannerWithString:output]; | |
| 198 BOOL valid = [scanner scanString:@"Xcode " intoString:NULL]; | |
| 199 NSString* version; | |
| 200 valid = | |
| 201 [scanner scanUpToCharactersFromSet:[NSCharacterSet newlineCharacterSet] | |
| 202 intoString:&version]; | |
| 203 if (!valid) { | |
| 204 LogError(@"Unable to find Xcode version. 'xcodebuild -version' " | |
| 205 @"returned \n%@", output); | |
| 206 return nil; | |
| 207 } | |
| 208 return version; | |
| 209 } | |
| 210 | |
| 211 // Returns true if iossim is running with Xcode 6 or later installed on the | |
| 212 // host. | |
| 213 BOOL IsRunningWithXcode6OrLater() { | |
| 214 static NSString* xcodeVersion = FindXcodeVersion(); | |
| 215 if (!xcodeVersion) { | |
| 216 return false; | |
| 217 } | |
| 218 NSArray* components = [xcodeVersion componentsSeparatedByString:@"."]; | |
| 219 if ([components count] < 1) { | |
| 220 return false; | |
| 221 } | |
| 222 NSInteger majorVersion = [[components objectAtIndex:0] integerValue]; | |
| 223 return majorVersion >= 6; | |
| 224 } | |
| 225 | |
| 226 // Prints supported devices and SDKs. | 175 // Prints supported devices and SDKs. |
| 227 void PrintSupportedDevices() { | 176 void PrintSupportedDevices() { |
| 228 if (IsRunningWithXcode6OrLater()) { | 177 printf("Supported device/SDK combinations:\n"); |
| 229 #if defined(IOSSIM_USE_XCODE_6) | 178 Class simDeviceSetClass = FindClassByName(@"SimDeviceSet"); |
| 230 printf("Supported device/SDK combinations:\n"); | 179 id deviceSet = |
| 231 Class simDeviceSetClass = FindClassByName(@"SimDeviceSet"); | 180 [simDeviceSetClass setForSetPath:[simDeviceSetClass defaultSetPath]]; |
| 232 id deviceSet = | 181 for (id simDevice in [deviceSet availableDevices]) { |
| 233 [simDeviceSetClass setForSetPath:[simDeviceSetClass defaultSetPath]]; | 182 NSString* deviceInfo = |
| 234 for (id simDevice in [deviceSet availableDevices]) { | 183 [NSString stringWithFormat:@" -d '%@' -s '%@'\n", |
| 235 NSString* deviceInfo = | 184 [simDevice name], [[simDevice runtime] versionString]]; |
| 236 [NSString stringWithFormat:@" -d '%@' -s '%@'\n", | 185 printf("%s", [deviceInfo UTF8String]); |
| 237 [simDevice name], [[simDevice runtime] versionString]]; | |
| 238 printf("%s", [deviceInfo UTF8String]); | |
| 239 } | |
| 240 #endif // IOSSIM_USE_XCODE_6 | |
| 241 } else { | |
| 242 printf("Supported SDK versions:\n"); | |
| 243 Class rootClass = FindClassByName(@"DTiPhoneSimulatorSystemRoot"); | |
| 244 for (id root in [rootClass knownRoots]) { | |
| 245 printf(" '%s'\n", [[root sdkVersion] UTF8String]); | |
| 246 } | |
| 247 // This is the list of devices supported on Xcode 5.1.x. | |
| 248 printf("Supported devices:\n"); | |
| 249 printf(" 'iPhone'\n"); | |
| 250 printf(" 'iPhone Retina (3.5-inch)'\n"); | |
| 251 printf(" 'iPhone Retina (4-inch)'\n"); | |
| 252 printf(" 'iPhone Retina (4-inch 64-bit)'\n"); | |
| 253 printf(" 'iPad'\n"); | |
| 254 printf(" 'iPad Retina'\n"); | |
| 255 printf(" 'iPad Retina (64-bit)'\n"); | |
| 256 } | 186 } |
| 257 } | 187 } |
| 258 } // namespace | 188 } // namespace |
| 259 | 189 |
| 260 // A delegate that is called when the simulated app is started or ended in the | 190 // A delegate that is called when the simulated app is started or ended in the |
| 261 // simulator. | 191 // simulator. |
| 262 @interface SimulatorDelegate : NSObject <DTiPhoneSimulatorSessionDelegate> { | 192 @interface SimulatorDelegate : NSObject <DTiPhoneSimulatorSessionDelegate> { |
| 263 @private | 193 @private |
| 264 NSString* stdioPath_; | 194 NSString* stdioPath_; |
| 265 NSString* developerDir_; | 195 NSString* developerDir_; |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 305 // Reads data from the simulated app's output and writes it to stdout. This | 235 // Reads data from the simulated app's output and writes it to stdout. This |
| 306 // method blocks, so it should be called in a separate thread. The iOS | 236 // method blocks, so it should be called in a separate thread. The iOS |
| 307 // Simulator takes a file path for the simulated app's stdout and stderr, but | 237 // Simulator takes a file path for the simulated app's stdout and stderr, but |
| 308 // this path isn't always available (e.g. when the stdout is Xcode's build | 238 // this path isn't always available (e.g. when the stdout is Xcode's build |
| 309 // window). As a workaround, iossim creates a temp file to hold output, which | 239 // window). As a workaround, iossim creates a temp file to hold output, which |
| 310 // this method reads and copies to stdout. | 240 // this method reads and copies to stdout. |
| 311 - (void)tailOutputForSession:(DTiPhoneSimulatorSession*)session { | 241 - (void)tailOutputForSession:(DTiPhoneSimulatorSession*)session { |
| 312 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; | 242 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
| 313 | 243 |
| 314 NSFileHandle* simio = [NSFileHandle fileHandleForReadingAtPath:stdioPath_]; | 244 NSFileHandle* simio = [NSFileHandle fileHandleForReadingAtPath:stdioPath_]; |
| 315 if (IsRunningWithXcode6OrLater()) { | 245 NSString* versionString = |
| 316 #if defined(IOSSIM_USE_XCODE_6) | 246 [[[session sessionConfig] simulatedSystemRoot] sdkVersion]; |
| 317 // With iOS 8 simulators on Xcode 6, the app output is relative to the | 247 NSInteger majorVersion = [[[versionString componentsSeparatedByString:@"."] |
| 318 // simulator's data directory. | 248 objectAtIndex:0] intValue]; |
| 319 NSString* versionString = | 249 if (majorVersion >= 8) { |
| 320 [[[session sessionConfig] simulatedSystemRoot] sdkVersion]; | 250 NSString* dataPath = session.sessionConfig.device.dataPath; |
| 321 NSInteger majorVersion = [[[versionString componentsSeparatedByString:@"."] | 251 NSString* appOutput = |
| 322 objectAtIndex:0] intValue]; | 252 [dataPath stringByAppendingPathComponent:stdioPath_]; |
| 323 if (majorVersion >= 8) { | 253 simio = [NSFileHandle fileHandleForReadingAtPath:appOutput]; |
| 324 NSString* dataPath = session.sessionConfig.device.dataPath; | |
| 325 NSString* appOutput = | |
| 326 [dataPath stringByAppendingPathComponent:stdioPath_]; | |
| 327 simio = [NSFileHandle fileHandleForReadingAtPath:appOutput]; | |
| 328 } | |
| 329 #endif // IOSSIM_USE_XCODE_6 | |
| 330 } | 254 } |
| 331 NSFileHandle* standardOutput = [NSFileHandle fileHandleWithStandardOutput]; | 255 NSFileHandle* standardOutput = [NSFileHandle fileHandleWithStandardOutput]; |
| 332 // Copy data to stdout/stderr while the app is running. | 256 // Copy data to stdout/stderr while the app is running. |
| 333 while (appRunning_) { | 257 while (appRunning_) { |
| 334 NSAutoreleasePool* innerPool = [[NSAutoreleasePool alloc] init]; | 258 NSAutoreleasePool* innerPool = [[NSAutoreleasePool alloc] init]; |
| 335 [standardOutput writeData:[simio readDataToEndOfFile]]; | 259 [standardOutput writeData:[simio readDataToEndOfFile]]; |
| 336 [NSThread sleepForTimeInterval:kOutputPollIntervalSeconds]; | 260 [NSThread sleepForTimeInterval:kOutputPollIntervalSeconds]; |
| 337 [innerPool drain]; | 261 [innerPool drain]; |
| 338 } | 262 } |
| 339 | 263 |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 470 LogWarning(@"Console message: %s", message); | 394 LogWarning(@"Console message: %s", message); |
| 471 // Some messages are harmless, so don't trigger a failure for them. | 395 // Some messages are harmless, so don't trigger a failure for them. |
| 472 if (strstr(message, "The following job tried to hijack the service")) | 396 if (strstr(message, "The following job tried to hijack the service")) |
| 473 continue; | 397 continue; |
| 474 badEntryFound = YES; | 398 badEntryFound = YES; |
| 475 } | 399 } |
| 476 } else { | 400 } else { |
| 477 // Otherwise, the iOS Simulator's system logging is sandboxed, so parse the | 401 // Otherwise, the iOS Simulator's system logging is sandboxed, so parse the |
| 478 // sandboxed system.log file for known errors. | 402 // sandboxed system.log file for known errors. |
| 479 NSString* path; | 403 NSString* path; |
| 480 if (IsRunningWithXcode6OrLater()) { | 404 NSString* dataPath = session.sessionConfig.device.dataPath; |
| 481 #if defined(IOSSIM_USE_XCODE_6) | 405 path = |
| 482 NSString* dataPath = session.sessionConfig.device.dataPath; | 406 [dataPath stringByAppendingPathComponent:@"Library/Logs/system.log"]; |
| 483 path = | |
| 484 [dataPath stringByAppendingPathComponent:@"Library/Logs/system.log"]; | |
| 485 #endif // IOSSIM_USE_XCODE_6 | |
| 486 } else { | |
| 487 NSString* relativePathToSystemLog = | |
| 488 [NSString stringWithFormat: | |
| 489 @"Library/Logs/iOS Simulator/%@/system.log", versionString]; | |
| 490 path = [simulatorHome_ | |
| 491 stringByAppendingPathComponent:relativePathToSystemLog]; | |
| 492 } | |
| 493 NSFileManager* fileManager = [NSFileManager defaultManager]; | 407 NSFileManager* fileManager = [NSFileManager defaultManager]; |
| 494 if ([fileManager fileExistsAtPath:path]) { | 408 if ([fileManager fileExistsAtPath:path]) { |
| 495 NSString* content = | 409 NSString* content = |
| 496 [NSString stringWithContentsOfFile:path | 410 [NSString stringWithContentsOfFile:path |
| 497 encoding:NSUTF8StringEncoding | 411 encoding:NSUTF8StringEncoding |
| 498 error:NULL]; | 412 error:NULL]; |
| 499 NSArray* lines = [content componentsSeparatedByCharactersInSet: | 413 NSArray* lines = [content componentsSeparatedByCharactersInSet: |
| 500 [NSCharacterSet newlineCharacterSet]]; | 414 [NSCharacterSet newlineCharacterSet]]; |
| 501 NSString* simulatedAppPID = | 415 NSString* simulatedAppPID = |
| 502 [NSString stringWithFormat:@"%d", session.simulatedApplicationPID]; | 416 [NSString stringWithFormat:@"%d", session.simulatedApplicationPID]; |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 579 NSError* error; | 493 NSError* error; |
| 580 Class DVTPlatformClass = FindClassByName(@"DVTPlatform"); | 494 Class DVTPlatformClass = FindClassByName(@"DVTPlatform"); |
| 581 if (![DVTPlatformClass loadAllPlatformsReturningError:&error]) { | 495 if (![DVTPlatformClass loadAllPlatformsReturningError:&error]) { |
| 582 LogError(@"Unable to loadAllPlatformsReturningError. Error: %@", | 496 LogError(@"Unable to loadAllPlatformsReturningError. Error: %@", |
| 583 [error localizedDescription]); | 497 [error localizedDescription]); |
| 584 return nil; | 498 return nil; |
| 585 } | 499 } |
| 586 | 500 |
| 587 // The path within the developer dir of the private Simulator frameworks. | 501 // The path within the developer dir of the private Simulator frameworks. |
| 588 NSString* simulatorFrameworkRelativePath; | 502 NSString* simulatorFrameworkRelativePath; |
| 589 if (IsRunningWithXcode6OrLater()) { | 503 simulatorFrameworkRelativePath = |
| 590 simulatorFrameworkRelativePath = | 504 @"../SharedFrameworks/DVTiPhoneSimulatorRemoteClient.framework"; |
| 591 @"../SharedFrameworks/DVTiPhoneSimulatorRemoteClient.framework"; | 505 NSString* const kCoreSimulatorRelativePath = |
| 592 NSString* const kCoreSimulatorRelativePath = | 506 @"Library/PrivateFrameworks/CoreSimulator.framework"; |
| 593 @"Library/PrivateFrameworks/CoreSimulator.framework"; | 507 NSString* coreSimulatorPath = [developerDir |
| 594 NSString* coreSimulatorPath = [developerDir | 508 stringByAppendingPathComponent:kCoreSimulatorRelativePath]; |
| 595 stringByAppendingPathComponent:kCoreSimulatorRelativePath]; | 509 NSBundle* coreSimulatorBundle = |
| 596 NSBundle* coreSimulatorBundle = | 510 [NSBundle bundleWithPath:coreSimulatorPath]; |
| 597 [NSBundle bundleWithPath:coreSimulatorPath]; | 511 if (![coreSimulatorBundle load]) |
| 598 if (![coreSimulatorBundle load]) | 512 return nil; |
| 599 return nil; | |
| 600 } else { | |
| 601 simulatorFrameworkRelativePath = | |
| 602 @"Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/" | |
| 603 @"DVTiPhoneSimulatorRemoteClient.framework"; | |
| 604 } | |
| 605 NSString* simBundlePath = [developerDir | 513 NSString* simBundlePath = [developerDir |
| 606 stringByAppendingPathComponent:simulatorFrameworkRelativePath]; | 514 stringByAppendingPathComponent:simulatorFrameworkRelativePath]; |
| 607 NSBundle* simBundle = [NSBundle bundleWithPath:simBundlePath]; | 515 NSBundle* simBundle = [NSBundle bundleWithPath:simBundlePath]; |
| 608 if (![simBundle load]) | 516 if (![simBundle load]) |
| 609 return nil; | 517 return nil; |
| 610 return simBundle; | 518 return simBundle; |
| 611 } | 519 } |
| 612 | 520 |
| 613 // Converts the given app path to an application spec, which requires an | 521 // Converts the given app path to an application spec, which requires an |
| 614 // absolute path. | 522 // absolute path. |
| (...skipping 11 matching lines...) Expand all Loading... |
| 626 exit(kExitInvalidArguments); | 534 exit(kExitInvalidArguments); |
| 627 } | 535 } |
| 628 return [applicationSpecifierClass specifierWithApplicationPath:appPath]; | 536 return [applicationSpecifierClass specifierWithApplicationPath:appPath]; |
| 629 } | 537 } |
| 630 | 538 |
| 631 // Returns the system root for the given SDK version. If sdkVersion is nil, the | 539 // Returns the system root for the given SDK version. If sdkVersion is nil, the |
| 632 // default system root is returned. Will return nil if the sdkVersion is not | 540 // default system root is returned. Will return nil if the sdkVersion is not |
| 633 // valid. | 541 // valid. |
| 634 DTiPhoneSimulatorSystemRoot* BuildSystemRoot(NSString* sdkVersion) { | 542 DTiPhoneSimulatorSystemRoot* BuildSystemRoot(NSString* sdkVersion) { |
| 635 Class systemRootClass = FindClassByName(@"DTiPhoneSimulatorSystemRoot"); | 543 Class systemRootClass = FindClassByName(@"DTiPhoneSimulatorSystemRoot"); |
| 636 #if defined(IOSSIM_USE_XCODE_6) | |
| 637 Class simRuntimeClass = FindClassByName(@"SimRuntime"); | 544 Class simRuntimeClass = FindClassByName(@"SimRuntime"); |
| 638 NSArray* sorted = | 545 NSArray* sorted = |
| 639 [[simRuntimeClass supportedRuntimes] sortedArrayUsingDescriptors:@[ | 546 [[simRuntimeClass supportedRuntimes] sortedArrayUsingDescriptors:@[ |
| 640 [NSSortDescriptor sortDescriptorWithKey:@"version" ascending:YES] | 547 [NSSortDescriptor sortDescriptorWithKey:@"version" ascending:YES] |
| 641 ]]; | 548 ]]; |
| 642 NSString* versionString = [[sorted lastObject] versionString]; | 549 NSString* versionString = [[sorted lastObject] versionString]; |
| 643 DTiPhoneSimulatorSystemRoot* systemRoot = | 550 DTiPhoneSimulatorSystemRoot* systemRoot = |
| 644 [systemRootClass rootWithSDKVersion:versionString]; | 551 [systemRootClass rootWithSDKVersion:versionString]; |
| 645 #else | |
| 646 DTiPhoneSimulatorSystemRoot* systemRoot = [systemRootClass defaultRoot]; | |
| 647 #endif | |
| 648 if (sdkVersion) | 552 if (sdkVersion) |
| 649 systemRoot = [systemRootClass rootWithSDKVersion:sdkVersion]; | 553 systemRoot = [systemRootClass rootWithSDKVersion:sdkVersion]; |
| 650 | 554 |
| 651 return systemRoot; | 555 return systemRoot; |
| 652 } | 556 } |
| 653 | 557 |
| 654 // Builds a config object for starting the specified app. | 558 // Builds a config object for starting the specified app. |
| 655 DTiPhoneSimulatorSessionConfig* BuildSessionConfig( | 559 DTiPhoneSimulatorSessionConfig* BuildSessionConfig( |
| 656 DTiPhoneSimulatorApplicationSpecifier* appSpec, | 560 DTiPhoneSimulatorApplicationSpecifier* appSpec, |
| 657 DTiPhoneSimulatorSystemRoot* systemRoot, | 561 DTiPhoneSimulatorSystemRoot* systemRoot, |
| 658 NSString* stdoutPath, | 562 NSString* stdoutPath, |
| 659 NSString* stderrPath, | 563 NSString* stderrPath, |
| 660 NSArray* appArgs, | 564 NSArray* appArgs, |
| 661 NSDictionary* appEnv, | 565 NSDictionary* appEnv, |
| 662 NSNumber* deviceFamily, | 566 NSNumber* deviceFamily, |
| 663 NSString* deviceName) { | 567 NSString* deviceName) { |
| 664 Class sessionConfigClass = FindClassByName(@"DTiPhoneSimulatorSessionConfig"); | 568 Class sessionConfigClass = FindClassByName(@"DTiPhoneSimulatorSessionConfig"); |
| 665 DTiPhoneSimulatorSessionConfig* sessionConfig = | 569 DTiPhoneSimulatorSessionConfig* sessionConfig = |
| 666 [[[sessionConfigClass alloc] init] autorelease]; | 570 [[[sessionConfigClass alloc] init] autorelease]; |
| 667 sessionConfig.applicationToSimulateOnStart = appSpec; | 571 sessionConfig.applicationToSimulateOnStart = appSpec; |
| 668 sessionConfig.simulatedSystemRoot = systemRoot; | 572 sessionConfig.simulatedSystemRoot = systemRoot; |
| 669 sessionConfig.localizedClientName = @"chromium"; | 573 sessionConfig.localizedClientName = @"chromium"; |
| 670 sessionConfig.simulatedApplicationStdErrPath = stderrPath; | 574 sessionConfig.simulatedApplicationStdErrPath = stderrPath; |
| 671 sessionConfig.simulatedApplicationStdOutPath = stdoutPath; | 575 sessionConfig.simulatedApplicationStdOutPath = stdoutPath; |
| 672 sessionConfig.simulatedApplicationLaunchArgs = appArgs; | 576 sessionConfig.simulatedApplicationLaunchArgs = appArgs; |
| 673 sessionConfig.simulatedApplicationLaunchEnvironment = appEnv; | 577 sessionConfig.simulatedApplicationLaunchEnvironment = appEnv; |
| 674 sessionConfig.simulatedDeviceInfoName = deviceName; | 578 sessionConfig.simulatedDeviceInfoName = deviceName; |
| 675 sessionConfig.simulatedDeviceFamily = deviceFamily; | 579 sessionConfig.simulatedDeviceFamily = deviceFamily; |
| 676 | 580 |
| 677 if (IsRunningWithXcode6OrLater()) { | 581 Class simDeviceTypeClass = FindClassByName(@"SimDeviceType"); |
| 678 #if defined(IOSSIM_USE_XCODE_6) | 582 id simDeviceType = |
| 679 Class simDeviceTypeClass = FindClassByName(@"SimDeviceType"); | 583 [simDeviceTypeClass supportedDeviceTypesByAlias][deviceName]; |
| 680 id simDeviceType = | 584 Class simRuntimeClass = FindClassByName(@"SimRuntime"); |
| 681 [simDeviceTypeClass supportedDeviceTypesByAlias][deviceName]; | 585 NSString* identifier = systemRoot.runtime.identifier; |
| 682 Class simRuntimeClass = FindClassByName(@"SimRuntime"); | 586 id simRuntime = [simRuntimeClass supportedRuntimesByIdentifier][identifier]; |
| 683 NSString* identifier = systemRoot.runtime.identifier; | |
| 684 id simRuntime = [simRuntimeClass supportedRuntimesByIdentifier][identifier]; | |
| 685 | 587 |
| 686 // Attempt to use an existing device, but create one if a suitable match | 588 // Attempt to use an existing device, but create one if a suitable match |
| 687 // can't be found. For example, if the simulator is running with a | 589 // can't be found. For example, if the simulator is running with a |
| 688 // non-default home directory (e.g. via iossim's -u command line arg) then | 590 // non-default home directory (e.g. via iossim's -u command line arg) then |
| 689 // there won't be any devices so one will have to be created. | 591 // there won't be any devices so one will have to be created. |
| 690 Class simDeviceSetClass = FindClassByName(@"SimDeviceSet"); | 592 Class simDeviceSetClass = FindClassByName(@"SimDeviceSet"); |
| 691 id deviceSet = | 593 id deviceSet = |
| 692 [simDeviceSetClass setForSetPath:[simDeviceSetClass defaultSetPath]]; | 594 [simDeviceSetClass setForSetPath:[simDeviceSetClass defaultSetPath]]; |
| 693 id simDevice = nil; | 595 id simDevice = nil; |
| 694 for (id device in [deviceSet availableDevices]) { | 596 for (id device in [deviceSet availableDevices]) { |
| 695 if ([device runtime] == simRuntime && | 597 if ([device runtime] == simRuntime && |
| 696 [device deviceType] == simDeviceType) { | 598 [device deviceType] == simDeviceType) { |
| 697 simDevice = device; | 599 simDevice = device; |
| 698 break; | 600 break; |
| 699 } | |
| 700 } | 601 } |
| 701 if (!simDevice) { | 602 } |
| 702 NSError* error = nil; | 603 if (!simDevice) { |
| 703 // n.b. only the device name is necessary because the iOS Simulator menu | 604 NSError* error = nil; |
| 704 // already splits devices by runtime version. | 605 // n.b. only the device name is necessary because the iOS Simulator menu |
| 705 NSString* name = [NSString stringWithFormat:@"iossim - %@ ", deviceName]; | 606 // already splits devices by runtime version. |
| 706 simDevice = [deviceSet createDeviceWithType:simDeviceType | 607 NSString* name = [NSString stringWithFormat:@"iossim - %@ ", deviceName]; |
| 707 runtime:simRuntime | 608 simDevice = [deviceSet createDeviceWithType:simDeviceType |
| 708 name:name | 609 runtime:simRuntime |
| 709 error:&error]; | 610 name:name |
| 710 if (error) { | 611 error:&error]; |
| 711 LogError(@"Failed to create device: %@", error); | 612 if (error) { |
| 712 exit(kExitInitializationFailure); | 613 LogError(@"Failed to create device: %@", error); |
| 713 } | 614 exit(kExitInitializationFailure); |
| 714 } | 615 } |
| 715 sessionConfig.device = simDevice; | |
| 716 #endif // IOSSIM_USE_XCODE_6 | |
| 717 } | 616 } |
| 617 sessionConfig.device = simDevice; |
| 718 return sessionConfig; | 618 return sessionConfig; |
| 719 } | 619 } |
| 720 | 620 |
| 721 // Builds a simulator session that will use the given delegate. | 621 // Builds a simulator session that will use the given delegate. |
| 722 DTiPhoneSimulatorSession* BuildSession(SimulatorDelegate* delegate) { | 622 DTiPhoneSimulatorSession* BuildSession(SimulatorDelegate* delegate) { |
| 723 Class sessionClass = FindClassByName(@"DTiPhoneSimulatorSession"); | 623 Class sessionClass = FindClassByName(@"DTiPhoneSimulatorSession"); |
| 724 DTiPhoneSimulatorSession* session = | 624 DTiPhoneSimulatorSession* session = |
| 725 [[[sessionClass alloc] init] autorelease]; | 625 [[[sessionClass alloc] init] autorelease]; |
| 726 session.delegate = delegate; | 626 session.delegate = delegate; |
| 727 return session; | 627 return session; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 788 // variable's value if it has already been set. | 688 // variable's value if it has already been set. |
| 789 if ((setenv(kUserHomeEnvVariable, [userHomePath UTF8String], YES) == -1) || | 689 if ((setenv(kUserHomeEnvVariable, [userHomePath UTF8String], YES) == -1) || |
| 790 (setenv(kHomeEnvVariable, [userHomePath UTF8String], YES) == -1)) { | 690 (setenv(kHomeEnvVariable, [userHomePath UTF8String], YES) == -1)) { |
| 791 LogError(@"Unable to set environment variables for home directory."); | 691 LogError(@"Unable to set environment variables for home directory."); |
| 792 return NO; | 692 return NO; |
| 793 } | 693 } |
| 794 | 694 |
| 795 return YES; | 695 return YES; |
| 796 } | 696 } |
| 797 | 697 |
| 798 // Performs a case-insensitive search to see if |stringToSearch| begins with | |
| 799 // |prefixToFind|. Returns true if a match is found. | |
| 800 BOOL CaseInsensitivePrefixSearch(NSString* stringToSearch, | |
| 801 NSString* prefixToFind) { | |
| 802 NSStringCompareOptions options = (NSAnchoredSearch | NSCaseInsensitiveSearch); | |
| 803 NSRange range = [stringToSearch rangeOfString:prefixToFind | |
| 804 options:options]; | |
| 805 return range.location != NSNotFound; | |
| 806 } | |
| 807 | |
| 808 // Prints the usage information to stderr. | 698 // Prints the usage information to stderr. |
| 809 void PrintUsage() { | 699 void PrintUsage() { |
| 810 fprintf(stderr, "Usage: iossim [-d device] [-s sdkVersion] [-u homeDir] " | 700 fprintf(stderr, "Usage: iossim [-d device] [-s sdkVersion] [-u homeDir] " |
| 811 "[-e envKey=value]* [-t startupTimeout] <appPath> [<appArgs>]\n" | 701 "[-e envKey=value]* [-t startupTimeout] <appPath> [<appArgs>]\n" |
| 812 " where <appPath> is the path to the .app directory and appArgs are any" | 702 " where <appPath> is the path to the .app directory and appArgs are any" |
| 813 " arguments to send the simulated app.\n" | 703 " arguments to send the simulated app.\n" |
| 814 "\n" | 704 "\n" |
| 815 "Options:\n" | 705 "Options:\n" |
| 816 " -d Specifies the device (must be one of the values from the iOS" | 706 " -d Specifies the device (must be one of the values from the iOS" |
| 817 " Simulator's Hardware -> Device menu. Defaults to 'iPhone'.\n" | 707 " Simulator's Hardware -> Device menu. Defaults to 'iPhone'.\n" |
| 818 " -s Specifies the SDK version to use (e.g '4.3')." | 708 " -s Specifies the SDK version to use (e.g '4.3')." |
| 819 " Will use system default if not specified.\n" | 709 " Will use system default if not specified.\n" |
| 820 " -u Specifies a user home directory for the simulator." | 710 " -u Specifies a user home directory for the simulator." |
| 821 " Will create a new directory if not specified.\n" | 711 " Will create a new directory if not specified.\n" |
| 822 " -e Specifies an environment key=value pair that will be" | 712 " -e Specifies an environment key=value pair that will be" |
| 823 " set in the simulated application's environment.\n" | 713 " set in the simulated application's environment.\n" |
| 824 " -t Specifies the session startup timeout (in seconds)." | 714 " -t Specifies the session startup timeout (in seconds)." |
| 825 " Defaults to %d.\n" | 715 " Defaults to %d.\n" |
| 826 " -l List supported devices and iOS versions.\n", | 716 " -l List supported devices and iOS versions.\n", |
| 827 static_cast<int>(kDefaultSessionStartTimeoutSeconds)); | 717 static_cast<int>(kDefaultSessionStartTimeoutSeconds)); |
| 828 } | 718 } |
| 829 } // namespace | 719 } // namespace |
| 830 | 720 |
| 831 void EnsureSupportForCurrentXcodeVersion() { | |
| 832 if (IsRunningWithXcode6OrLater()) { | |
| 833 #if !IOSSIM_USE_XCODE_6 | |
| 834 LogError(@"Running on Xcode 6, but Xcode 6 support was not compiled in."); | |
| 835 exit(kExitUnsupportedXcodeVersion); | |
| 836 #endif // IOSSIM_USE_XCODE_6 | |
| 837 } | |
| 838 } | |
| 839 | |
| 840 int main(int argc, char* const argv[]) { | 721 int main(int argc, char* const argv[]) { |
| 841 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; | 722 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
| 842 | 723 |
| 843 EnsureSupportForCurrentXcodeVersion(); | |
| 844 | |
| 845 // basename() may modify the passed in string and it returns a pointer to an | 724 // basename() may modify the passed in string and it returns a pointer to an |
| 846 // internal buffer. Give it a copy to modify, and copy what it returns. | 725 // internal buffer. Give it a copy to modify, and copy what it returns. |
| 847 char* worker = strdup(argv[0]); | 726 char* worker = strdup(argv[0]); |
| 848 char* toolName = basename(worker); | 727 char* toolName = basename(worker); |
| 849 if (toolName != NULL) { | 728 if (toolName != NULL) { |
| 850 toolName = strdup(toolName); | 729 toolName = strdup(toolName); |
| 851 if (toolName != NULL) | 730 if (toolName != NULL) |
| 852 gToolName = toolName; | 731 gToolName = toolName; |
| 853 } | 732 } |
| 854 if (worker != NULL) | 733 if (worker != NULL) |
| 855 free(worker); | 734 free(worker); |
| 856 | 735 |
| 857 NSString* appPath = nil; | 736 NSString* appPath = nil; |
| 858 NSString* appName = nil; | 737 NSString* appName = nil; |
| 859 NSString* sdkVersion = nil; | 738 NSString* sdkVersion = nil; |
| 860 NSString* deviceName = | 739 NSString* deviceName = @"iPhone 5s"; |
| 861 IsRunningWithXcode6OrLater() ? @"iPhone 5s" : @"iPhone"; | |
| 862 NSString* simHomePath = nil; | 740 NSString* simHomePath = nil; |
| 863 NSMutableArray* appArgs = [NSMutableArray array]; | 741 NSMutableArray* appArgs = [NSMutableArray array]; |
| 864 NSMutableDictionary* appEnv = [NSMutableDictionary dictionary]; | 742 NSMutableDictionary* appEnv = [NSMutableDictionary dictionary]; |
| 865 NSTimeInterval sessionStartTimeout = kDefaultSessionStartTimeoutSeconds; | 743 NSTimeInterval sessionStartTimeout = kDefaultSessionStartTimeoutSeconds; |
| 866 | 744 |
| 867 NSString* developerDir = FindDeveloperDir(); | 745 NSString* developerDir = FindDeveloperDir(); |
| 868 if (!developerDir) { | 746 if (!developerDir) { |
| 869 LogError(@"Unable to find developer directory."); | 747 LogError(@"Unable to find developer directory."); |
| 870 exit(kExitInitializationFailure); | 748 exit(kExitInitializationFailure); |
| 871 } | 749 } |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 958 exit(kExitInitializationFailure); | 836 exit(kExitInitializationFailure); |
| 959 } | 837 } |
| 960 | 838 |
| 961 // Get the paths for stdout and stderr so the simulated app's output will show | 839 // Get the paths for stdout and stderr so the simulated app's output will show |
| 962 // up in the caller's stdout/stderr. | 840 // up in the caller's stdout/stderr. |
| 963 NSString* outputDir = CreateTempDirectory(@"iossim-XXXXXX"); | 841 NSString* outputDir = CreateTempDirectory(@"iossim-XXXXXX"); |
| 964 NSString* stdioPath = [outputDir stringByAppendingPathComponent:@"stdio.txt"]; | 842 NSString* stdioPath = [outputDir stringByAppendingPathComponent:@"stdio.txt"]; |
| 965 | 843 |
| 966 // Determine the deviceFamily based on the deviceName | 844 // Determine the deviceFamily based on the deviceName |
| 967 NSNumber* deviceFamily = nil; | 845 NSNumber* deviceFamily = nil; |
| 968 if (IsRunningWithXcode6OrLater()) { | 846 Class simDeviceTypeClass = FindClassByName(@"SimDeviceType"); |
| 969 #if defined(IOSSIM_USE_XCODE_6) | 847 if ([simDeviceTypeClass supportedDeviceTypesByAlias][deviceName] == nil) { |
| 970 Class simDeviceTypeClass = FindClassByName(@"SimDeviceType"); | 848 LogError(@"Invalid device name: %@.", deviceName); |
| 971 if ([simDeviceTypeClass supportedDeviceTypesByAlias][deviceName] == nil) { | 849 PrintSupportedDevices(); |
| 972 LogError(@"Invalid device name: %@.", deviceName); | 850 exit(kExitInvalidArguments); |
| 973 PrintSupportedDevices(); | |
| 974 exit(kExitInvalidArguments); | |
| 975 } | |
| 976 #endif // IOSSIM_USE_XCODE_6 | |
| 977 } else { | |
| 978 if (!deviceName || CaseInsensitivePrefixSearch(deviceName, @"iPhone")) { | |
| 979 deviceFamily = [NSNumber numberWithInt:kIPhoneFamily]; | |
| 980 } else if (CaseInsensitivePrefixSearch(deviceName, @"iPad")) { | |
| 981 deviceFamily = [NSNumber numberWithInt:kIPadFamily]; | |
| 982 } | |
| 983 else { | |
| 984 LogError(@"Invalid device name: %@. Must begin with 'iPhone' or 'iPad'", | |
| 985 deviceName); | |
| 986 exit(kExitInvalidArguments); | |
| 987 } | |
| 988 } | 851 } |
| 989 | 852 |
| 990 // Set up the user home directory for the simulator only if a non-default | 853 // Set up the user home directory for the simulator only if a non-default |
| 991 // value was specified. | 854 // value was specified. |
| 992 if (simHomePath) { | 855 if (simHomePath) { |
| 993 if (!InitializeSimulatorUserHome(simHomePath)) { | 856 if (!InitializeSimulatorUserHome(simHomePath)) { |
| 994 LogError(@"Unable to initialize home directory for simulator: %@", | 857 LogError(@"Unable to initialize home directory for simulator: %@", |
| 995 simHomePath); | 858 simHomePath); |
| 996 exit(kExitInitializationFailure); | 859 exit(kExitInitializationFailure); |
| 997 } | 860 } |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1029 [error localizedDescription], | 892 [error localizedDescription], |
| 1030 [error domain], static_cast<long int>([error code])); | 893 [error domain], static_cast<long int>([error code])); |
| 1031 } | 894 } |
| 1032 | 895 |
| 1033 // Note that this code is only executed if the simulator fails to start | 896 // Note that this code is only executed if the simulator fails to start |
| 1034 // because once the main run loop is started, only the delegate calling | 897 // because once the main run loop is started, only the delegate calling |
| 1035 // exit() will end the program. | 898 // exit() will end the program. |
| 1036 [pool drain]; | 899 [pool drain]; |
| 1037 return kExitFailure; | 900 return kExitFailure; |
| 1038 } | 901 } |
| OLD | NEW |