/Source/iPhoneSimulator.m
Objective C | 572 lines | 461 code | 70 blank | 41 comment | 156 complexity | 0ade827c2d61f36bbedd0b36be2fa771 MD5 | raw file
Possible License(s): Apache-2.0
- /**
- * A simple DVTiPhoneSimulatorRemoteClient framework for launching app on iOS Simulator
- *
- * Copyright (c) 2009-2013 by Appcelerator, Inc. All Rights Reserved.
- *
- * Copyright (c) 2012 The Chromium Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- * (link : http://src.chromium.org/chrome/trunk/src/testing/iossim/)
- *
- * Original Author: Landon Fuller <landonf@plausiblelabs.com>
- * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc.
- * All rights reserved.
- *
- *
- * Headers for the DVTiPhoneSimulatorRemoteClient framework used in this tool are
- * generated by class-dump, via GYP.
- * (class-dump is available at http://www.codethecode.com/projects/class-dump/)
- *
- * See the LICENSE file for the license on the source code in this file.
- */
- #import "iPhoneSimulator.h"
- #import "NSString+expandPath.h"
- #import "nsprintf.h"
- #import <sys/types.h>
- #import <sys/stat.h>
- @class DTiPhoneSimulatorSystemRoot;
- NSString *simulatorAppId = @"com.apple.iphonesimulator";
- NSString *deviceProperty = @"SimulateDevice";
- NSString *deviceIphoneRetina3_5InchiOS7 = @"iPhone Retina (3.5-inch)";
- NSString *deviceIphoneRetina4_0InchiOS7 = @"iPhone Retina (4-inch)";
- NSString *deviceiPhoneRetine4_0InchiOS764bit = @"iPhone Retina (4-inch 64-bit)";
- NSString *deviceiPadRetinaiOS764bit = @"iPad Retina (64-bit)";
- NSString *deviceIpadRetinaiOS7 = @"iPad Retina";
- NSString *deviceIphone = @"iPhone";
- NSString *deviceIpad = @"iPad";
- // The path within the developer dir of the private Simulator frameworks.
- NSString* const kSimulatorFrameworkRelativePath = @"Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/DVTiPhoneSimulatorRemoteClient.framework";
- NSString* const kDVTFoundationRelativePath = @"../SharedFrameworks/DVTFoundation.framework";
- NSString* const kDevToolsFoundationRelativePath = @"../OtherFrameworks/DevToolsFoundation.framework";
- NSString* const kSimulatorRelativePath = @"Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app";
- @interface DVTPlatform : NSObject
- + (BOOL)loadAllPlatformsReturningError:(id*)arg1;
- @end
- @implementation iPhoneSimulator
- // Helper to find a class by name and die if it isn't found.
- -(Class) FindClassByName:(NSString*) nameOfClass {
- Class theClass = NSClassFromString(nameOfClass);
- if (!theClass) {
- nsfprintf(stderr,@"Failed to find class %@ at runtime.", nameOfClass);
- exit(EXIT_FAILURE);
- }
- return theClass;
- }
- // Loads the Simulator framework from the given developer dir.
- -(void) LoadSimulatorFramework:(NSString*) developerDir {
- // The Simulator framework depends on some of the other Xcode private
- // frameworks; manually load them first so everything can be linked up.
- NSString* dvtFoundationPath = [developerDir stringByAppendingPathComponent:kDVTFoundationRelativePath];
- NSBundle* dvtFoundationBundle =
- [NSBundle bundleWithPath:dvtFoundationPath];
- if (![dvtFoundationBundle load]){
- nsprintf(@"Unable to dvtFoundationBundle. Error: ");
- exit(EXIT_FAILURE);
- return ;
- }
- NSString* devToolsFoundationPath = [developerDir stringByAppendingPathComponent:kDevToolsFoundationRelativePath];
- NSBundle* devToolsFoundationBundle =
- [NSBundle bundleWithPath:devToolsFoundationPath];
- if (![devToolsFoundationBundle load]){
- nsprintf(@"Unable to devToolsFoundationPath. Error: ");
- return ;
- }
- // Prime DVTPlatform.
- NSError* error;
- Class DVTPlatformClass = [self FindClassByName:@"DVTPlatform"];
- if (![DVTPlatformClass loadAllPlatformsReturningError:&error]) {
- nsprintf(@"Unable to loadAllPlatformsReturningError. Error: %@",[error localizedDescription]);
- return ;
- }
- NSString* simBundlePath = [developerDir stringByAppendingPathComponent:kSimulatorFrameworkRelativePath];
- NSBundle* simBundle = [NSBundle bundleWithPath:simBundlePath];
- if (![simBundle load]){
- nsprintf(@"Unable to load simulator framework. Error: %@",[error localizedDescription]);
- return ;
- }
- return ;
- }
- // Finds the developer dir via xcode-select or the DEVELOPER_DIR environment
- // variable.
- NSString* FindDeveloperDir() {
- // Check the env first.
- NSDictionary* env = [[NSProcessInfo processInfo] environment];
- NSString* developerDir = [env objectForKey:@"DEVELOPER_DIR"];
- if ([developerDir length] > 0)
- return developerDir;
-
- // Go look for it via xcode-select.
- NSTask* xcodeSelectTask = [[[NSTask alloc] init] autorelease];
- [xcodeSelectTask setLaunchPath:@"/usr/bin/xcode-select"];
- [xcodeSelectTask setArguments:[NSArray arrayWithObject:@"-print-path"]];
-
- NSPipe* outputPipe = [NSPipe pipe];
- [xcodeSelectTask setStandardOutput:outputPipe];
- NSFileHandle* outputFile = [outputPipe fileHandleForReading];
-
- [xcodeSelectTask launch];
- NSData* outputData = [outputFile readDataToEndOfFile];
- [xcodeSelectTask terminate];
-
- NSString* output =
- [[[NSString alloc] initWithData:outputData
- encoding:NSUTF8StringEncoding] autorelease];
- output = [output stringByTrimmingCharactersInSet:
- [NSCharacterSet whitespaceAndNewlineCharacterSet]];
- if ([output length] == 0)
- output = nil;
- return output;
- }
- - (void) printUsage {
- fprintf(stderr, "Usage: ios-sim <command> <options> [--args ...]\n");
- fprintf(stderr, "\n");
- fprintf(stderr, "Commands:\n");
- fprintf(stderr, " showsdks List the available iOS SDK versions\n");
- fprintf(stderr, " launch <application path> Launch the application at the specified path on the iOS Simulator\n");
- fprintf(stderr, "\n");
- fprintf(stderr, "Options:\n");
- 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");
- fprintf(stderr, " --version Print the version of ios-sim\n");
- fprintf(stderr, " --help Show this help text\n");
- fprintf(stderr, " --verbose Set the output level to verbose\n");
- fprintf(stderr, " --exit Exit after startup\n");
- fprintf(stderr, " --retina Start as a retina device\n");
- fprintf(stderr, " --tall Start the tall version of the iPhone simulator(4-inch simulator), to be used in conjuction with retina flag\n");
- fprintf(stderr, " --sim-64bit Start 64 bit version of iOS 7 simulator\n");
- fprintf(stderr, " --timeout Set the timeout value for a new session from the Simulator. Default: 30 seconds \n");
- fprintf(stderr, " --sdk <sdkversion> The iOS SDK version to run the application on (defaults to the latest)\n");
- fprintf(stderr, " --family <device family> The device type that should be simulated (defaults to `iphone')\n");
- fprintf(stderr, " --uuid <uuid> A UUID identifying the session (is that correct?)\n");
- fprintf(stderr, " --env <environment file path> A plist file containing environment key-value pairs that should be set\n");
- fprintf(stderr, " --setenv NAME=VALUE Set an environment variable\n");
- fprintf(stderr, " --stdout <stdout file path> The path where stdout of the simulator will be redirected to (defaults to stdout of ios-sim)\n");
- fprintf(stderr, " --stderr <stderr file path> The path where stderr of the simulator will be redirected to (defaults to stderr of ios-sim)\n");
- fprintf(stderr, " --args <...> All following arguments will be passed on to the application\n");
- }
- - (NSString*) findDeviceType:(NSString *)family {
- NSString *devicePropertyValue;
-
- if (retinaDevice) {
- if (verbose) {
- nsprintf(@"using retina");
- }
- if ([family isEqualToString:@"ipad"]) {
- if (sim_64bit) {
- if (verbose) { nsprintf(@"using retina ipad ios 7 64-bit"); }
- devicePropertyValue = deviceiPadRetinaiOS764bit;
- } else {
- if (verbose) { nsprintf(@"using retina ipad ios 7"); }
- devicePropertyValue = deviceIpadRetinaiOS7;
- }
-
- }
- else {
- if (tallDevice) {
- if (sim_64bit) {
- if (verbose) { nsprintf(@"using iphone retina tall ios 7 64 bit"); }
- devicePropertyValue = deviceiPhoneRetine4_0InchiOS764bit;
- } else {
- if (verbose) { nsprintf(@"using iphone retina tall ios 7"); }
- devicePropertyValue = deviceIphoneRetina4_0InchiOS7;
- }
- } else {
- if (verbose) { nsprintf(@"using retina iphone retina ios 7"); }
- devicePropertyValue = deviceIphoneRetina3_5InchiOS7;
- }
- }
- } else {
- if ([family isEqualToString:@"ipad"]) {
- devicePropertyValue = deviceIpad;
- } else {
- devicePropertyValue = deviceIphone;
- }
- }
- if (verbose) {
- nsprintf(@"Simulated Device Name :: %@",devicePropertyValue);
- }
- return devicePropertyValue;
- }
- - (int) showSDKs {
- Class systemRootClass = [self FindClassByName:@"DTiPhoneSimulatorSystemRoot"];
-
- NSArray *roots = [systemRootClass knownRoots];
- nsprintf(@"Simulator SDK Roots:");
- for (DTiPhoneSimulatorSystemRoot *root in roots) {
- nsfprintf(stderr, @"'%@' (%@)\n\t%@", [root sdkDisplayName], [root sdkVersion], [root sdkRootPath]);
- }
- return EXIT_SUCCESS;
- }
- - (void)session:(DTiPhoneSimulatorSession *)session didEndWithError:(NSError *)error {
- if (verbose) {
- nsprintf(@"Session did end with error %@", error);
- }
- if (stderrFileHandle != nil) {
- NSString *stderrPath = [[session sessionConfig] simulatedApplicationStdErrPath];
- [self removeStdioFIFO:stderrFileHandle atPath:stderrPath];
- }
- if (stdoutFileHandle != nil) {
- NSString *stdoutPath = [[session sessionConfig] simulatedApplicationStdOutPath];
- [self removeStdioFIFO:stdoutFileHandle atPath:stdoutPath];
- }
- if (error != nil) {
- exit(EXIT_FAILURE);
- }
- exit(EXIT_SUCCESS);
- }
- - (void)session:(DTiPhoneSimulatorSession *)session didStart:(BOOL)started withError:(NSError *)error {
- if (startOnly && session) {
- nsprintf(@"Simulator started (no session)");
- exit(EXIT_SUCCESS);
- }
- if (started) {
- if (verbose) {
- nsprintf(@"Session started");
- }
- if (exitOnStartup) {
- exit(EXIT_SUCCESS);
- }
- } else {
- nsprintf(@"Session could not be started: %@", error);
- exit(EXIT_FAILURE);
- }
- }
- - (void)stdioDataIsAvailable:(NSNotification *)notification {
- [[notification object] readInBackgroundAndNotify];
- NSData *data = [[notification userInfo] valueForKey:NSFileHandleNotificationDataItem];
- NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
- if (!alreadyPrintedData) {
- if ([str length] == 0) {
- return;
- } else {
- alreadyPrintedData = YES;
- }
- }
- if ([notification object] == stdoutFileHandle) {
- printf("%s", [str UTF8String]);
- } else {
- nsprintf(str);
- }
- }
- - (void)createStdioFIFO:(NSFileHandle **)fileHandle ofType:(NSString *)type atPath:(NSString **)path {
- *path = [NSString stringWithFormat:@"%@/ios-sim-%@-pipe-%d", NSTemporaryDirectory(), type, (int)time(NULL)];
- if (mkfifo([*path UTF8String], S_IRUSR | S_IWUSR) == -1) {
- nsprintf(@"Unable to create %@ named pipe `%@'", type, *path);
- exit(EXIT_FAILURE);
- } else {
- if (verbose) {
- nsprintf(@"Creating named pipe at `%@'", *path);
- }
- int fd = open([*path UTF8String], O_RDONLY | O_NDELAY);
- *fileHandle = [[[NSFileHandle alloc] initWithFileDescriptor:fd] retain];
- [*fileHandle readInBackgroundAndNotify];
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(stdioDataIsAvailable:)
- name:NSFileHandleReadCompletionNotification
- object:*fileHandle];
- }
- }
- - (void)removeStdioFIFO:(NSFileHandle *)fileHandle atPath:(NSString *)path {
- if (verbose) {
- nsprintf(@"Removing named pipe at `%@'", path);
- }
- [fileHandle closeFile];
- [fileHandle release];
- if (![[NSFileManager defaultManager] removeItemAtPath:path error:NULL]) {
- nsprintf(@"Unable to remove named pipe `%@'", path);
- }
- }
- - (int)launchApp:(NSString *)path withFamily:(NSString *)family
- withTimeout:(NSTimeInterval)timeout
- uuid:(NSString *)uuid
- environment:(NSDictionary *)environment
- stdoutPath:(NSString *)stdoutPath
- stderrPath:(NSString *)stderrPath
- args:(NSArray *)args{
- DTiPhoneSimulatorApplicationSpecifier *appSpec;
- DTiPhoneSimulatorSessionConfig *config;
- DTiPhoneSimulatorSession *session;
- NSError *error;
- NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
- if (!startOnly && ![fileManager fileExistsAtPath:path]) {
- nsprintf(@"Could not load application specification for %s", path);
- exit(EXIT_FAILURE);
- }
- /* Create the app specifier */
- appSpec = startOnly ? nil : [[self FindClassByName:@"DTiPhoneSimulatorApplicationSpecifier"] specifierWithApplicationPath:path];
- if (verbose) {
- nsprintf(@"App Spec: %@", appSpec);
- nsprintf(@"SDK Root: %@", sdkRoot);
- for (id key in environment) {
- nsprintf(@"Env: %@ = %@", key, [environment objectForKey:key]);
- }
- }
- /* Set up the session configuration */
- config = [[[[self FindClassByName:@"DTiPhoneSimulatorSessionConfig"] alloc] init] autorelease];
- [config setApplicationToSimulateOnStart:appSpec];
- [config setSimulatedSystemRoot:sdkRoot];
- [config setSimulatedApplicationShouldWaitForDebugger: NO];
- [config setSimulatedApplicationLaunchArgs:args];
- [config setSimulatedApplicationLaunchEnvironment:environment];
- if (stderrPath) {
- stderrFileHandle = nil;
- } else if (!exitOnStartup) {
- [self createStdioFIFO:&stderrFileHandle ofType:@"stderr" atPath:&stderrPath];
- }
- [config setSimulatedApplicationStdErrPath:stderrPath];
- if (stdoutPath) {
- stdoutFileHandle = nil;
- } else if (!exitOnStartup) {
- [self createStdioFIFO:&stdoutFileHandle ofType:@"stdout" atPath:&stdoutPath];
- }
- [config setSimulatedApplicationStdOutPath:stdoutPath];
- [config setLocalizedClientName: @"ios-sim"];
- // this was introduced in 3.2 of SDK
- if ([config respondsToSelector:@selector(setSimulatedDeviceFamily:)]) {
- if (family == nil) {
- family = @"iphone";
- }
- if (verbose) {
- nsprintf(@"using device family %@",family);
- }
- if ([family isEqualToString:@"ipad"]) {
- [config setSimulatedDeviceFamily:[NSNumber numberWithInt:2]];
- } else{
- [config setSimulatedDeviceFamily:[NSNumber numberWithInt:1]];
- }
- }
-
- /* Figure out the type of simulator we need to open up.*/
- NSString *deviceInfoName = [self findDeviceType:family];
- [config setSimulatedDeviceInfoName:deviceInfoName];
- /* Start the session */
- session = [[[[self FindClassByName:@"DTiPhoneSimulatorSession"] alloc] init] autorelease];
- [session setDelegate:self];
- [session setSimulatedApplicationPID: [NSNumber numberWithInt:35]];
- if (uuid != nil){
- [session setUuid:uuid];
- }
- timeout = MIN(500, MAX(90, timeout));
- if (![session requestStartWithConfig:config timeout:timeout error:&error]) {
- nsprintf(@"Could not start simulator session: %@", error);
- return EXIT_FAILURE;
- }
- return EXIT_SUCCESS;
- }
- /**
- * Execute 'main'
- */
- - (void)runWithArgc:(int)argc argv:(char **)argv {
- if (argc < 2) {
- [self printUsage];
- exit(EXIT_FAILURE);
- }
- /* Initializing variables*/
- exitOnStartup = NO;
- alreadyPrintedData = NO;
- retinaDevice = NO;
- tallDevice = NO;
- sim_64bit = NO;
- startOnly = strcmp(argv[1], "start") == 0;
- launchFlag = strcmp(argv[1], "launch") == 0;
- NSTimeInterval timeout = 90;
- NSString* developerDir = FindDeveloperDir();
- if (!developerDir) {
- nsprintf(@"Unable to find developer directory.");
- exit(EXIT_FAILURE);
- }
-
- if (strcmp(argv[1], "showsdks") == 0) {
- [self LoadSimulatorFramework:developerDir];
- exit([self showSDKs]);
- } else if (launchFlag || startOnly) {
- if (launchFlag && argc < 3) {
- fprintf(stderr, "Missing application path argument\n");
- [self printUsage];
- exit(EXIT_FAILURE);
- }
- NSString *appPath = nil;
- int numOfArgs;
- if (startOnly) {
- numOfArgs = 2;
- } else {
- numOfArgs = 3;
- appPath = [[NSString stringWithUTF8String:argv[2]] expandPath];
- }
- NSString *family = nil;
- NSString *uuid = nil;
- NSString *stdoutPath = nil;
- NSString *stderrPath = nil;
- NSMutableDictionary *environment = [NSMutableDictionary dictionary];
- int i = numOfArgs;
- for (; i < argc; i++) {
- if (strcmp(argv[i], "--version") == 0) {
- printf("%s\n", IOS_SIM_VERSION);
- exit(EXIT_SUCCESS);
- } else if (strcmp(argv[i], "--help") == 0) {
- [self printUsage];
- exit(EXIT_SUCCESS);
- } else if (strcmp(argv[i], "--verbose") == 0) {
- verbose = YES;
- } else if (strcmp(argv[i], "--exit") == 0) {
- exitOnStartup = YES;
- }
- else if (strcmp(argv[i], "--sdk") == 0) {
- i++;
- [self LoadSimulatorFramework:developerDir];
- NSString* ver = [NSString stringWithCString:argv[i] encoding:NSUTF8StringEncoding];
- Class systemRootClass = [self FindClassByName:@"DTiPhoneSimulatorSystemRoot"];
- NSArray *roots = [systemRootClass knownRoots];
- for (DTiPhoneSimulatorSystemRoot *root in roots) {
- NSString *v = [root sdkVersion];
- if ([v isEqualToString:ver]) {
- sdkRoot = root;
- break;
- }
- }
- if (sdkRoot == nil) {
- fprintf(stderr,"Unknown or unsupported SDK version: %s\n",argv[i]);
- [self showSDKs];
- exit(EXIT_FAILURE);
- }
- } else if (strcmp(argv[i], "--family") == 0) {
- i++;
- family = [NSString stringWithUTF8String:argv[i]];
- } else if (strcmp(argv[i], "--uuid") == 0) {
- i++;
- uuid = [NSString stringWithUTF8String:argv[i]];
- } else if (strcmp(argv[i], "--setenv") == 0) {
- i++;
- NSArray *parts = [[NSString stringWithUTF8String:argv[i]] componentsSeparatedByString:@"="];
- [environment setObject:[parts objectAtIndex:1] forKey:[parts objectAtIndex:0]];
- } else if (strcmp(argv[i], "--env") == 0) {
- i++;
- NSString *envFilePath = [[NSString stringWithUTF8String:argv[i]] expandPath];
- environment = [NSDictionary dictionaryWithContentsOfFile:envFilePath];
- if (!environment) {
- fprintf(stderr, "Could not read environment from file: %s\n", argv[i]);
- [self printUsage];
- exit(EXIT_FAILURE);
- }
- } else if (strcmp(argv[i], "--stdout") == 0) {
- i++;
- stdoutPath = [[NSString stringWithUTF8String:argv[i]] expandPath];
- NSLog(@"stdoutPath: %@", stdoutPath);
- } else if (strcmp(argv[i], "--stderr") == 0) {
- i++;
- stderrPath = [[NSString stringWithUTF8String:argv[i]] expandPath];
- NSLog(@"stderrPath: %@", stderrPath);
- } else if (strcmp(argv[i], "--args") == 0) {
- i++;
- break;
- } else if (strcmp(argv[i], "--retina") == 0) {
- retinaDevice = YES;
- } else if (strcmp(argv[i], "--tall") == 0) {
- tallDevice = YES;
- } else if (strcmp(argv[i], "--xcode-dir") == 0) {
- i++;
- developerDir = [NSString stringWithCString:argv[i] encoding:NSUTF8StringEncoding];
- } else if (strcmp(argv[i], "--sim-64bit") == 0) {
- sim_64bit = YES;
- } else if (strcmp(argv[i], "--timeout") == 0){
- i++;
- timeout = [[NSString stringWithUTF8String:argv[i]] doubleValue];
- } else {
- fprintf(stderr, "unrecognized argument:%s\n", argv[i]);
- [self printUsage];
- exit(EXIT_FAILURE);
- }
- }
- i = MIN(argc, i);
- NSMutableArray *args = [NSMutableArray arrayWithCapacity:(argc - i)];
- for (; i < argc; i++) {
- [args addObject:[NSString stringWithUTF8String:argv[i]]];
- }
- if (sdkRoot == nil) {
- Class systemRootClass = [self FindClassByName:@"DTiPhoneSimulatorSystemRoot"];
- sdkRoot = [systemRootClass defaultRoot];
- }
- /* Don't exit, adds to runloop */
- [self launchApp:appPath
- withFamily:family
- withTimeout:timeout
- uuid:uuid
- environment:environment
- stdoutPath:stdoutPath
- stderrPath:stderrPath
- args:args];
- } else {
- if (argc == 2 && strcmp(argv[1], "--help") == 0) {
- [self printUsage];
- exit(EXIT_SUCCESS);
- } else if (argc == 2 && strcmp(argv[1], "--version") == 0) {
- printf("%s\n", IOS_SIM_VERSION);
- exit(EXIT_SUCCESS);
- } else {
- fprintf(stderr, "Unknown command\n");
- [self printUsage];
- exit(EXIT_FAILURE);
- }
- }
- }
- @end