PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/Source/iPhoneSimulator.m

https://github.com/jhaynie/iphonesim
Objective C | 572 lines | 461 code | 70 blank | 41 comment | 156 complexity | 0ade827c2d61f36bbedd0b36be2fa771 MD5 | raw file
Possible License(s): Apache-2.0
  1. /**
  2. * A simple DVTiPhoneSimulatorRemoteClient framework for launching app on iOS Simulator
  3. *
  4. * Copyright (c) 2009-2013 by Appcelerator, Inc. All Rights Reserved.
  5. *
  6. * Copyright (c) 2012 The Chromium Authors. All rights reserved.
  7. * Use of this source code is governed by a BSD-style license that can be
  8. * found in the LICENSE file.
  9. * (link : http://src.chromium.org/chrome/trunk/src/testing/iossim/)
  10. *
  11. * Original Author: Landon Fuller <landonf@plausiblelabs.com>
  12. * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc.
  13. * All rights reserved.
  14. *
  15. *
  16. * Headers for the DVTiPhoneSimulatorRemoteClient framework used in this tool are
  17. * generated by class-dump, via GYP.
  18. * (class-dump is available at http://www.codethecode.com/projects/class-dump/)
  19. *
  20. * See the LICENSE file for the license on the source code in this file.
  21. */
  22. #import "iPhoneSimulator.h"
  23. #import "NSString+expandPath.h"
  24. #import "nsprintf.h"
  25. #import <sys/types.h>
  26. #import <sys/stat.h>
  27. @class DTiPhoneSimulatorSystemRoot;
  28. NSString *simulatorAppId = @"com.apple.iphonesimulator";
  29. NSString *deviceProperty = @"SimulateDevice";
  30. NSString *deviceIphoneRetina3_5InchiOS7 = @"iPhone Retina (3.5-inch)";
  31. NSString *deviceIphoneRetina4_0InchiOS7 = @"iPhone Retina (4-inch)";
  32. NSString *deviceiPhoneRetine4_0InchiOS764bit = @"iPhone Retina (4-inch 64-bit)";
  33. NSString *deviceiPadRetinaiOS764bit = @"iPad Retina (64-bit)";
  34. NSString *deviceIpadRetinaiOS7 = @"iPad Retina";
  35. NSString *deviceIphone = @"iPhone";
  36. NSString *deviceIpad = @"iPad";
  37. // The path within the developer dir of the private Simulator frameworks.
  38. NSString* const kSimulatorFrameworkRelativePath = @"Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/DVTiPhoneSimulatorRemoteClient.framework";
  39. NSString* const kDVTFoundationRelativePath = @"../SharedFrameworks/DVTFoundation.framework";
  40. NSString* const kDevToolsFoundationRelativePath = @"../OtherFrameworks/DevToolsFoundation.framework";
  41. NSString* const kSimulatorRelativePath = @"Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app";
  42. @interface DVTPlatform : NSObject
  43. + (BOOL)loadAllPlatformsReturningError:(id*)arg1;
  44. @end
  45. @implementation iPhoneSimulator
  46. // Helper to find a class by name and die if it isn't found.
  47. -(Class) FindClassByName:(NSString*) nameOfClass {
  48. Class theClass = NSClassFromString(nameOfClass);
  49. if (!theClass) {
  50. nsfprintf(stderr,@"Failed to find class %@ at runtime.", nameOfClass);
  51. exit(EXIT_FAILURE);
  52. }
  53. return theClass;
  54. }
  55. // Loads the Simulator framework from the given developer dir.
  56. -(void) LoadSimulatorFramework:(NSString*) developerDir {
  57. // The Simulator framework depends on some of the other Xcode private
  58. // frameworks; manually load them first so everything can be linked up.
  59. NSString* dvtFoundationPath = [developerDir stringByAppendingPathComponent:kDVTFoundationRelativePath];
  60. NSBundle* dvtFoundationBundle =
  61. [NSBundle bundleWithPath:dvtFoundationPath];
  62. if (![dvtFoundationBundle load]){
  63. nsprintf(@"Unable to dvtFoundationBundle. Error: ");
  64. exit(EXIT_FAILURE);
  65. return ;
  66. }
  67. NSString* devToolsFoundationPath = [developerDir stringByAppendingPathComponent:kDevToolsFoundationRelativePath];
  68. NSBundle* devToolsFoundationBundle =
  69. [NSBundle bundleWithPath:devToolsFoundationPath];
  70. if (![devToolsFoundationBundle load]){
  71. nsprintf(@"Unable to devToolsFoundationPath. Error: ");
  72. return ;
  73. }
  74. // Prime DVTPlatform.
  75. NSError* error;
  76. Class DVTPlatformClass = [self FindClassByName:@"DVTPlatform"];
  77. if (![DVTPlatformClass loadAllPlatformsReturningError:&error]) {
  78. nsprintf(@"Unable to loadAllPlatformsReturningError. Error: %@",[error localizedDescription]);
  79. return ;
  80. }
  81. NSString* simBundlePath = [developerDir stringByAppendingPathComponent:kSimulatorFrameworkRelativePath];
  82. NSBundle* simBundle = [NSBundle bundleWithPath:simBundlePath];
  83. if (![simBundle load]){
  84. nsprintf(@"Unable to load simulator framework. Error: %@",[error localizedDescription]);
  85. return ;
  86. }
  87. return ;
  88. }
  89. // Finds the developer dir via xcode-select or the DEVELOPER_DIR environment
  90. // variable.
  91. NSString* FindDeveloperDir() {
  92. // Check the env first.
  93. NSDictionary* env = [[NSProcessInfo processInfo] environment];
  94. NSString* developerDir = [env objectForKey:@"DEVELOPER_DIR"];
  95. if ([developerDir length] > 0)
  96. return developerDir;
  97. // Go look for it via xcode-select.
  98. NSTask* xcodeSelectTask = [[[NSTask alloc] init] autorelease];
  99. [xcodeSelectTask setLaunchPath:@"/usr/bin/xcode-select"];
  100. [xcodeSelectTask setArguments:[NSArray arrayWithObject:@"-print-path"]];
  101. NSPipe* outputPipe = [NSPipe pipe];
  102. [xcodeSelectTask setStandardOutput:outputPipe];
  103. NSFileHandle* outputFile = [outputPipe fileHandleForReading];
  104. [xcodeSelectTask launch];
  105. NSData* outputData = [outputFile readDataToEndOfFile];
  106. [xcodeSelectTask terminate];
  107. NSString* output =
  108. [[[NSString alloc] initWithData:outputData
  109. encoding:NSUTF8StringEncoding] autorelease];
  110. output = [output stringByTrimmingCharactersInSet:
  111. [NSCharacterSet whitespaceAndNewlineCharacterSet]];
  112. if ([output length] == 0)
  113. output = nil;
  114. return output;
  115. }
  116. - (void) printUsage {
  117. fprintf(stderr, "Usage: ios-sim <command> <options> [--args ...]\n");
  118. fprintf(stderr, "\n");
  119. fprintf(stderr, "Commands:\n");
  120. fprintf(stderr, " showsdks List the available iOS SDK versions\n");
  121. fprintf(stderr, " launch <application path> Launch the application at the specified path on the iOS Simulator\n");
  122. fprintf(stderr, "\n");
  123. fprintf(stderr, "Options:\n");
  124. fprintf(stderr, "xcode-dir <custom DEVELOPER_DIR> Set the xcode to be used by ios-sim. (Should be passed in as the First argument. Defaults to `xcode-select --print-path` location\n");
  125. fprintf(stderr, " --version Print the version of ios-sim\n");
  126. fprintf(stderr, " --help Show this help text\n");
  127. fprintf(stderr, " --verbose Set the output level to verbose\n");
  128. fprintf(stderr, " --exit Exit after startup\n");
  129. fprintf(stderr, " --retina Start as a retina device\n");
  130. fprintf(stderr, " --tall Start the tall version of the iPhone simulator(4-inch simulator), to be used in conjuction with retina flag\n");
  131. fprintf(stderr, " --sim-64bit Start 64 bit version of iOS 7 simulator\n");
  132. fprintf(stderr, " --timeout Set the timeout value for a new session from the Simulator. Default: 30 seconds \n");
  133. fprintf(stderr, " --sdk <sdkversion> The iOS SDK version to run the application on (defaults to the latest)\n");
  134. fprintf(stderr, " --family <device family> The device type that should be simulated (defaults to `iphone')\n");
  135. fprintf(stderr, " --uuid <uuid> A UUID identifying the session (is that correct?)\n");
  136. fprintf(stderr, " --env <environment file path> A plist file containing environment key-value pairs that should be set\n");
  137. fprintf(stderr, " --setenv NAME=VALUE Set an environment variable\n");
  138. fprintf(stderr, " --stdout <stdout file path> The path where stdout of the simulator will be redirected to (defaults to stdout of ios-sim)\n");
  139. fprintf(stderr, " --stderr <stderr file path> The path where stderr of the simulator will be redirected to (defaults to stderr of ios-sim)\n");
  140. fprintf(stderr, " --args <...> All following arguments will be passed on to the application\n");
  141. }
  142. - (NSString*) findDeviceType:(NSString *)family {
  143. NSString *devicePropertyValue;
  144. if (retinaDevice) {
  145. if (verbose) {
  146. nsprintf(@"using retina");
  147. }
  148. if ([family isEqualToString:@"ipad"]) {
  149. if (sim_64bit) {
  150. if (verbose) { nsprintf(@"using retina ipad ios 7 64-bit"); }
  151. devicePropertyValue = deviceiPadRetinaiOS764bit;
  152. } else {
  153. if (verbose) { nsprintf(@"using retina ipad ios 7"); }
  154. devicePropertyValue = deviceIpadRetinaiOS7;
  155. }
  156. }
  157. else {
  158. if (tallDevice) {
  159. if (sim_64bit) {
  160. if (verbose) { nsprintf(@"using iphone retina tall ios 7 64 bit"); }
  161. devicePropertyValue = deviceiPhoneRetine4_0InchiOS764bit;
  162. } else {
  163. if (verbose) { nsprintf(@"using iphone retina tall ios 7"); }
  164. devicePropertyValue = deviceIphoneRetina4_0InchiOS7;
  165. }
  166. } else {
  167. if (verbose) { nsprintf(@"using retina iphone retina ios 7"); }
  168. devicePropertyValue = deviceIphoneRetina3_5InchiOS7;
  169. }
  170. }
  171. } else {
  172. if ([family isEqualToString:@"ipad"]) {
  173. devicePropertyValue = deviceIpad;
  174. } else {
  175. devicePropertyValue = deviceIphone;
  176. }
  177. }
  178. if (verbose) {
  179. nsprintf(@"Simulated Device Name :: %@",devicePropertyValue);
  180. }
  181. return devicePropertyValue;
  182. }
  183. - (int) showSDKs {
  184. Class systemRootClass = [self FindClassByName:@"DTiPhoneSimulatorSystemRoot"];
  185. NSArray *roots = [systemRootClass knownRoots];
  186. nsprintf(@"Simulator SDK Roots:");
  187. for (DTiPhoneSimulatorSystemRoot *root in roots) {
  188. nsfprintf(stderr, @"'%@' (%@)\n\t%@", [root sdkDisplayName], [root sdkVersion], [root sdkRootPath]);
  189. }
  190. return EXIT_SUCCESS;
  191. }
  192. - (void)session:(DTiPhoneSimulatorSession *)session didEndWithError:(NSError *)error {
  193. if (verbose) {
  194. nsprintf(@"Session did end with error %@", error);
  195. }
  196. if (stderrFileHandle != nil) {
  197. NSString *stderrPath = [[session sessionConfig] simulatedApplicationStdErrPath];
  198. [self removeStdioFIFO:stderrFileHandle atPath:stderrPath];
  199. }
  200. if (stdoutFileHandle != nil) {
  201. NSString *stdoutPath = [[session sessionConfig] simulatedApplicationStdOutPath];
  202. [self removeStdioFIFO:stdoutFileHandle atPath:stdoutPath];
  203. }
  204. if (error != nil) {
  205. exit(EXIT_FAILURE);
  206. }
  207. exit(EXIT_SUCCESS);
  208. }
  209. - (void)session:(DTiPhoneSimulatorSession *)session didStart:(BOOL)started withError:(NSError *)error {
  210. if (startOnly && session) {
  211. nsprintf(@"Simulator started (no session)");
  212. exit(EXIT_SUCCESS);
  213. }
  214. if (started) {
  215. if (verbose) {
  216. nsprintf(@"Session started");
  217. }
  218. if (exitOnStartup) {
  219. exit(EXIT_SUCCESS);
  220. }
  221. } else {
  222. nsprintf(@"Session could not be started: %@", error);
  223. exit(EXIT_FAILURE);
  224. }
  225. }
  226. - (void)stdioDataIsAvailable:(NSNotification *)notification {
  227. [[notification object] readInBackgroundAndNotify];
  228. NSData *data = [[notification userInfo] valueForKey:NSFileHandleNotificationDataItem];
  229. NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  230. if (!alreadyPrintedData) {
  231. if ([str length] == 0) {
  232. return;
  233. } else {
  234. alreadyPrintedData = YES;
  235. }
  236. }
  237. if ([notification object] == stdoutFileHandle) {
  238. printf("%s", [str UTF8String]);
  239. } else {
  240. nsprintf(str);
  241. }
  242. }
  243. - (void)createStdioFIFO:(NSFileHandle **)fileHandle ofType:(NSString *)type atPath:(NSString **)path {
  244. *path = [NSString stringWithFormat:@"%@/ios-sim-%@-pipe-%d", NSTemporaryDirectory(), type, (int)time(NULL)];
  245. if (mkfifo([*path UTF8String], S_IRUSR | S_IWUSR) == -1) {
  246. nsprintf(@"Unable to create %@ named pipe `%@'", type, *path);
  247. exit(EXIT_FAILURE);
  248. } else {
  249. if (verbose) {
  250. nsprintf(@"Creating named pipe at `%@'", *path);
  251. }
  252. int fd = open([*path UTF8String], O_RDONLY | O_NDELAY);
  253. *fileHandle = [[[NSFileHandle alloc] initWithFileDescriptor:fd] retain];
  254. [*fileHandle readInBackgroundAndNotify];
  255. [[NSNotificationCenter defaultCenter] addObserver:self
  256. selector:@selector(stdioDataIsAvailable:)
  257. name:NSFileHandleReadCompletionNotification
  258. object:*fileHandle];
  259. }
  260. }
  261. - (void)removeStdioFIFO:(NSFileHandle *)fileHandle atPath:(NSString *)path {
  262. if (verbose) {
  263. nsprintf(@"Removing named pipe at `%@'", path);
  264. }
  265. [fileHandle closeFile];
  266. [fileHandle release];
  267. if (![[NSFileManager defaultManager] removeItemAtPath:path error:NULL]) {
  268. nsprintf(@"Unable to remove named pipe `%@'", path);
  269. }
  270. }
  271. - (int)launchApp:(NSString *)path withFamily:(NSString *)family
  272. withTimeout:(NSTimeInterval)timeout
  273. uuid:(NSString *)uuid
  274. environment:(NSDictionary *)environment
  275. stdoutPath:(NSString *)stdoutPath
  276. stderrPath:(NSString *)stderrPath
  277. args:(NSArray *)args{
  278. DTiPhoneSimulatorApplicationSpecifier *appSpec;
  279. DTiPhoneSimulatorSessionConfig *config;
  280. DTiPhoneSimulatorSession *session;
  281. NSError *error;
  282. NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
  283. if (!startOnly && ![fileManager fileExistsAtPath:path]) {
  284. nsprintf(@"Could not load application specification for %s", path);
  285. exit(EXIT_FAILURE);
  286. }
  287. /* Create the app specifier */
  288. appSpec = startOnly ? nil : [[self FindClassByName:@"DTiPhoneSimulatorApplicationSpecifier"] specifierWithApplicationPath:path];
  289. if (verbose) {
  290. nsprintf(@"App Spec: %@", appSpec);
  291. nsprintf(@"SDK Root: %@", sdkRoot);
  292. for (id key in environment) {
  293. nsprintf(@"Env: %@ = %@", key, [environment objectForKey:key]);
  294. }
  295. }
  296. /* Set up the session configuration */
  297. config = [[[[self FindClassByName:@"DTiPhoneSimulatorSessionConfig"] alloc] init] autorelease];
  298. [config setApplicationToSimulateOnStart:appSpec];
  299. [config setSimulatedSystemRoot:sdkRoot];
  300. [config setSimulatedApplicationShouldWaitForDebugger: NO];
  301. [config setSimulatedApplicationLaunchArgs:args];
  302. [config setSimulatedApplicationLaunchEnvironment:environment];
  303. if (stderrPath) {
  304. stderrFileHandle = nil;
  305. } else if (!exitOnStartup) {
  306. [self createStdioFIFO:&stderrFileHandle ofType:@"stderr" atPath:&stderrPath];
  307. }
  308. [config setSimulatedApplicationStdErrPath:stderrPath];
  309. if (stdoutPath) {
  310. stdoutFileHandle = nil;
  311. } else if (!exitOnStartup) {
  312. [self createStdioFIFO:&stdoutFileHandle ofType:@"stdout" atPath:&stdoutPath];
  313. }
  314. [config setSimulatedApplicationStdOutPath:stdoutPath];
  315. [config setLocalizedClientName: @"ios-sim"];
  316. // this was introduced in 3.2 of SDK
  317. if ([config respondsToSelector:@selector(setSimulatedDeviceFamily:)]) {
  318. if (family == nil) {
  319. family = @"iphone";
  320. }
  321. if (verbose) {
  322. nsprintf(@"using device family %@",family);
  323. }
  324. if ([family isEqualToString:@"ipad"]) {
  325. [config setSimulatedDeviceFamily:[NSNumber numberWithInt:2]];
  326. } else{
  327. [config setSimulatedDeviceFamily:[NSNumber numberWithInt:1]];
  328. }
  329. }
  330. /* Figure out the type of simulator we need to open up.*/
  331. NSString *deviceInfoName = [self findDeviceType:family];
  332. [config setSimulatedDeviceInfoName:deviceInfoName];
  333. /* Start the session */
  334. session = [[[[self FindClassByName:@"DTiPhoneSimulatorSession"] alloc] init] autorelease];
  335. [session setDelegate:self];
  336. [session setSimulatedApplicationPID: [NSNumber numberWithInt:35]];
  337. if (uuid != nil){
  338. [session setUuid:uuid];
  339. }
  340. timeout = MIN(500, MAX(90, timeout));
  341. if (![session requestStartWithConfig:config timeout:timeout error:&error]) {
  342. nsprintf(@"Could not start simulator session: %@", error);
  343. return EXIT_FAILURE;
  344. }
  345. return EXIT_SUCCESS;
  346. }
  347. /**
  348. * Execute 'main'
  349. */
  350. - (void)runWithArgc:(int)argc argv:(char **)argv {
  351. if (argc < 2) {
  352. [self printUsage];
  353. exit(EXIT_FAILURE);
  354. }
  355. /* Initializing variables*/
  356. exitOnStartup = NO;
  357. alreadyPrintedData = NO;
  358. retinaDevice = NO;
  359. tallDevice = NO;
  360. sim_64bit = NO;
  361. startOnly = strcmp(argv[1], "start") == 0;
  362. launchFlag = strcmp(argv[1], "launch") == 0;
  363. NSTimeInterval timeout = 90;
  364. NSString* developerDir = FindDeveloperDir();
  365. if (!developerDir) {
  366. nsprintf(@"Unable to find developer directory.");
  367. exit(EXIT_FAILURE);
  368. }
  369. if (strcmp(argv[1], "showsdks") == 0) {
  370. [self LoadSimulatorFramework:developerDir];
  371. exit([self showSDKs]);
  372. } else if (launchFlag || startOnly) {
  373. if (launchFlag && argc < 3) {
  374. fprintf(stderr, "Missing application path argument\n");
  375. [self printUsage];
  376. exit(EXIT_FAILURE);
  377. }
  378. NSString *appPath = nil;
  379. int numOfArgs;
  380. if (startOnly) {
  381. numOfArgs = 2;
  382. } else {
  383. numOfArgs = 3;
  384. appPath = [[NSString stringWithUTF8String:argv[2]] expandPath];
  385. }
  386. NSString *family = nil;
  387. NSString *uuid = nil;
  388. NSString *stdoutPath = nil;
  389. NSString *stderrPath = nil;
  390. NSMutableDictionary *environment = [NSMutableDictionary dictionary];
  391. int i = numOfArgs;
  392. for (; i < argc; i++) {
  393. if (strcmp(argv[i], "--version") == 0) {
  394. printf("%s\n", IOS_SIM_VERSION);
  395. exit(EXIT_SUCCESS);
  396. } else if (strcmp(argv[i], "--help") == 0) {
  397. [self printUsage];
  398. exit(EXIT_SUCCESS);
  399. } else if (strcmp(argv[i], "--verbose") == 0) {
  400. verbose = YES;
  401. } else if (strcmp(argv[i], "--exit") == 0) {
  402. exitOnStartup = YES;
  403. }
  404. else if (strcmp(argv[i], "--sdk") == 0) {
  405. i++;
  406. [self LoadSimulatorFramework:developerDir];
  407. NSString* ver = [NSString stringWithCString:argv[i] encoding:NSUTF8StringEncoding];
  408. Class systemRootClass = [self FindClassByName:@"DTiPhoneSimulatorSystemRoot"];
  409. NSArray *roots = [systemRootClass knownRoots];
  410. for (DTiPhoneSimulatorSystemRoot *root in roots) {
  411. NSString *v = [root sdkVersion];
  412. if ([v isEqualToString:ver]) {
  413. sdkRoot = root;
  414. break;
  415. }
  416. }
  417. if (sdkRoot == nil) {
  418. fprintf(stderr,"Unknown or unsupported SDK version: %s\n",argv[i]);
  419. [self showSDKs];
  420. exit(EXIT_FAILURE);
  421. }
  422. } else if (strcmp(argv[i], "--family") == 0) {
  423. i++;
  424. family = [NSString stringWithUTF8String:argv[i]];
  425. } else if (strcmp(argv[i], "--uuid") == 0) {
  426. i++;
  427. uuid = [NSString stringWithUTF8String:argv[i]];
  428. } else if (strcmp(argv[i], "--setenv") == 0) {
  429. i++;
  430. NSArray *parts = [[NSString stringWithUTF8String:argv[i]] componentsSeparatedByString:@"="];
  431. [environment setObject:[parts objectAtIndex:1] forKey:[parts objectAtIndex:0]];
  432. } else if (strcmp(argv[i], "--env") == 0) {
  433. i++;
  434. NSString *envFilePath = [[NSString stringWithUTF8String:argv[i]] expandPath];
  435. environment = [NSDictionary dictionaryWithContentsOfFile:envFilePath];
  436. if (!environment) {
  437. fprintf(stderr, "Could not read environment from file: %s\n", argv[i]);
  438. [self printUsage];
  439. exit(EXIT_FAILURE);
  440. }
  441. } else if (strcmp(argv[i], "--stdout") == 0) {
  442. i++;
  443. stdoutPath = [[NSString stringWithUTF8String:argv[i]] expandPath];
  444. NSLog(@"stdoutPath: %@", stdoutPath);
  445. } else if (strcmp(argv[i], "--stderr") == 0) {
  446. i++;
  447. stderrPath = [[NSString stringWithUTF8String:argv[i]] expandPath];
  448. NSLog(@"stderrPath: %@", stderrPath);
  449. } else if (strcmp(argv[i], "--args") == 0) {
  450. i++;
  451. break;
  452. } else if (strcmp(argv[i], "--retina") == 0) {
  453. retinaDevice = YES;
  454. } else if (strcmp(argv[i], "--tall") == 0) {
  455. tallDevice = YES;
  456. } else if (strcmp(argv[i], "--xcode-dir") == 0) {
  457. i++;
  458. developerDir = [NSString stringWithCString:argv[i] encoding:NSUTF8StringEncoding];
  459. } else if (strcmp(argv[i], "--sim-64bit") == 0) {
  460. sim_64bit = YES;
  461. } else if (strcmp(argv[i], "--timeout") == 0){
  462. i++;
  463. timeout = [[NSString stringWithUTF8String:argv[i]] doubleValue];
  464. } else {
  465. fprintf(stderr, "unrecognized argument:%s\n", argv[i]);
  466. [self printUsage];
  467. exit(EXIT_FAILURE);
  468. }
  469. }
  470. i = MIN(argc, i);
  471. NSMutableArray *args = [NSMutableArray arrayWithCapacity:(argc - i)];
  472. for (; i < argc; i++) {
  473. [args addObject:[NSString stringWithUTF8String:argv[i]]];
  474. }
  475. if (sdkRoot == nil) {
  476. Class systemRootClass = [self FindClassByName:@"DTiPhoneSimulatorSystemRoot"];
  477. sdkRoot = [systemRootClass defaultRoot];
  478. }
  479. /* Don't exit, adds to runloop */
  480. [self launchApp:appPath
  481. withFamily:family
  482. withTimeout:timeout
  483. uuid:uuid
  484. environment:environment
  485. stdoutPath:stdoutPath
  486. stderrPath:stderrPath
  487. args:args];
  488. } else {
  489. if (argc == 2 && strcmp(argv[1], "--help") == 0) {
  490. [self printUsage];
  491. exit(EXIT_SUCCESS);
  492. } else if (argc == 2 && strcmp(argv[1], "--version") == 0) {
  493. printf("%s\n", IOS_SIM_VERSION);
  494. exit(EXIT_SUCCESS);
  495. } else {
  496. fprintf(stderr, "Unknown command\n");
  497. [self printUsage];
  498. exit(EXIT_FAILURE);
  499. }
  500. }
  501. }
  502. @end