diff options
Diffstat (limited to 'src/tools/3rdparty/iossim_1_8_2/iphonesimulator.mm')
-rw-r--r-- | src/tools/3rdparty/iossim_1_8_2/iphonesimulator.mm | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/src/tools/3rdparty/iossim_1_8_2/iphonesimulator.mm b/src/tools/3rdparty/iossim_1_8_2/iphonesimulator.mm new file mode 100644 index 0000000000..b06b54276d --- /dev/null +++ b/src/tools/3rdparty/iossim_1_8_2/iphonesimulator.mm @@ -0,0 +1,566 @@ +/* Author: Landon Fuller <landonf@plausiblelabs.com> + * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc. + * All rights reserved. + * + * See the IOSSIM_LICENSE file in this directory for the license on the source code in this file. + */ + +#import "iphonesimulator.h" +#import "nsstringexpandpath.h" +#import "nsprintf.h" +#import <sys/types.h> +#import <sys/stat.h> +#import <objc/runtime.h> +#import <AppKit/NSRunningApplication.h> + +NSString *simulatorPrefrencesName = @"com.apple.iphonesimulator"; +NSString *deviceProperty = @"SimulateDevice"; +NSString *deviceIphoneRetina3_5Inch = @"iPhone Retina (3.5-inch)"; +NSString *deviceIphoneRetina4_0Inch = @"iPhone Retina (4-inch)"; +NSString *deviceIphone = @"iPhone"; +NSString *deviceIpad = @"iPad"; +NSString *deviceIpadRetina = @"iPad (Retina)"; + +/** + * A simple iPhoneSimulatorRemoteClient framework. + */ +@implementation iPhoneSimulator + +- (id)init { + self = [super init]; + session = nil; + pidCheckingTimer = nil; + return self; +} + +- (void)dealloc { + [session release]; + [pidCheckingTimer release]; + [super dealloc]; +} + +- (void)doExit:(int)errorCode { + nsprintf(@"<exit code=\"%d\"/>", errorCode); + nsprintf(@"</query_result>"); + fflush(stdout); + fflush(stderr); + exit(errorCode); +} + +- (void) printUsage { + fprintf(stdout, "<msg>Usage: iossim <command> <options> [--args ...]\n"); + fprintf(stdout, "\n"); + fprintf(stdout, "Commands:\n"); + fprintf(stdout, " showsdks List the available iOS SDK versions\n"); + fprintf(stdout, " launch <application path> Launch the application at the specified path on the iOS Simulator\n"); + fprintf(stdout, " start Launch iOS Simulator without an app\n"); + fprintf(stdout, "\n"); + fprintf(stdout, "Options:\n"); + fprintf(stdout, " --version Print the version of ios-sim\n"); + fprintf(stdout, " --developer-path <developerDir> path to the developer directory (in Xcode)"); + fprintf(stdout, " --help Show this help text\n"); + fprintf(stdout, " --verbose Set the output level to verbose\n"); + fprintf(stdout, " --exit Exit after startup\n"); + fprintf(stdout, " --wait-for-debugger Wait for debugger to attach\n"); + fprintf(stdout, " --debug Attach LLDB to the application on startup\n"); + fprintf(stdout, " --use-gdb Use GDB instead of LLDB. (Requires --debug)\n"); + fprintf(stdout, " --sdk <sdkversion> The iOS SDK version to run the application on (defaults to the latest)\n"); + fprintf(stdout, " --family <device family> The device type that should be simulated (defaults to `iphone')\n"); + fprintf(stdout, " --retina Start a retina device\n"); + fprintf(stdout, " --tall In combination with --retina flag, start the tall version of the retina device (e.g. iPhone 5 (4-inch))\n"); + fprintf(stdout, " --uuid <uuid> A UUID identifying the session (is that correct?)\n"); + fprintf(stdout, " --env <environment file path> A plist file containing environment key-value pairs that should be set\n"); + fprintf(stdout, " --setenv NAME=VALUE Set an environment variable\n"); + fprintf(stdout, " --stdout <stdout file path> The path where stdout of the simulator will be redirected to (defaults to stdout of ios-sim)\n"); + fprintf(stdout, " --stderr <stderr file path> The path where stderr of the simulator will be redirected to (defaults to stderr of ios-sim)\n"); + fprintf(stdout, " --timeout <seconds> The timeout time to wait for a response from the Simulator. Default value: 30 seconds\n"); + fprintf(stdout, " --args <...> All following arguments will be passed on to the application</msg>\n"); + fflush(stdout); +} + + +- (int) showSDKs { + NSUInteger i; + id tClass = objc_getClass("DTiPhoneSimulatorSystemRoot"); + if (tClass == nil) { + nsprintf(@"<msg>DTiPhoneSimulatorSystemRoot class is nil.</msg>"); + return EXIT_FAILURE; + } + NSArray *roots = [tClass knownRoots]; + + nsprintf(@"<device_info>"); + for (i = 0; i < [roots count]; ++i) { + DTiPhoneSimulatorSystemRoot *root = [roots objectAtIndex:i]; + nsprintf(@"<item><key>sdk%d_name</key><value>%@</value></item>", i, [root sdkDisplayName]); + nsprintf(@"<item><key>sdk%d_version</key><value>%@</value></item>", i, [root sdkVersion]); + nsprintf(@"<item><key>sdk%d_sysroot</key><value>%@</value></item>", i, [root sdkRootPath]); + } + nsprintf(@"</device_info>"); + return EXIT_SUCCESS; +} + + +- (void)session:(DTiPhoneSimulatorSession *)mySession didEndWithError:(NSError *)error { + if (verbose) { + nsprintf(@"<msg>Session did end with error %@</msg>", error); + } + + if (stderrFileHandle != nil) { + NSString *stderrPath = [[mySession sessionConfig] simulatedApplicationStdErrPath]; + [self removeStdioFIFO:stderrFileHandle atPath:stderrPath]; + } + + if (stdoutFileHandle != nil) { + NSString *stdoutPath = [[mySession sessionConfig] simulatedApplicationStdOutPath]; + [self removeStdioFIFO:stdoutFileHandle atPath:stdoutPath]; + } + + if (error != nil) + [self doExit:EXIT_FAILURE]; + else + [self doExit:EXIT_SUCCESS]; +} + + +- (void)session:(DTiPhoneSimulatorSession *)mySession didStart:(BOOL)started withError:(NSError *)error { + if (startOnly && mySession) { + [NSTask launchedTaskWithLaunchPath:@"/usr/bin/osascript" + arguments:[NSArray arrayWithObjects:@"-e", @"tell application \"iPhone Simulator\" to activate", nil]]; + nsprintf(@"<msg>Simulator started (no session)</msg>"); + [self doExit:EXIT_SUCCESS]; + return; + } + if (started) { + [NSTask launchedTaskWithLaunchPath:@"/usr/bin/osascript" + arguments:[NSArray arrayWithObjects:@"-e", @"tell application \"iPhone Simulator\" to activate", nil]]; + if (shouldStartDebugger) { + char*args[4] = { NULL, NULL, (char*)[[[mySession simulatedApplicationPID] description] UTF8String], NULL }; + if (useGDB) { + args[0] = strdup("gdb"); + args[1] = strdup("program"); + } else { + args[0] = strdup("lldb"); + args[1] = strdup("--attach-pid"); + } + // The parent process must live on to process the stdout/stderr fifos, + // so start the debugger as a child process. + pid_t child_pid = fork(); + if (child_pid == 0) { + execvp(args[0], args); + } else if (child_pid < 0) { + nsprintf(@"<msg>Could not start debugger process: %@</msg>", errno); + [self doExit:EXIT_FAILURE]; + return; + } + } + if (verbose) { + nsprintf(@"<msg>Session started</msg>"); + } + nsprintf(@"<inferior_pid>%@</inferior_pid>", [session simulatedApplicationPID]); + fflush(stdout); + if (exitOnStartup) { + [self doExit:EXIT_SUCCESS]; + return; + } + pidCheckingTimer = [[NSTimer scheduledTimerWithTimeInterval:5.0 target:self + selector:@selector(checkPid:) userInfo:nil repeats: TRUE] retain]; + } else { + nsprintf(@"<msg>Session could not be started: %@</msg>", error); + [self doExit:EXIT_FAILURE]; + } +} + +- (void)stop { + if (session) + [session requestEndWithTimeout: 0.1]; +} + +- (void)checkPid:(NSTimer *)timer { + (void)timer; + if (session && [[session simulatedApplicationPID]intValue] > 0) { + if (kill((pid_t)[[session simulatedApplicationPID]intValue], 0) == -1) { + nsprintf(@"<msg>app stopped</msg>"); + [self doExit:EXIT_SUCCESS]; + return; + } + } +} +- (void)stdioDataIsAvailable:(NSNotification *)notification { + [[notification object] readInBackgroundAndNotify]; + NSData *data = [[notification userInfo] valueForKey:NSFileHandleNotificationDataItem]; + NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; /* dangerous if partially encoded data is at the boundary */ + if (!alreadyPrintedData) { + if ([str length] == 0) { + return; + } else { + alreadyPrintedData = YES; + } + } + if ([notification object] == stdoutFileHandle) { + printf("<app_output>%s</app_output>\n", [str UTF8String]); + } else { + nsprintf(@"<app_output>%@</app_output>", str); // handle stderr differently? + } + fflush(stdout); +} + + +- (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(@"<msg>Unable to create %@ named pipe `%@'</msg>", type, *path); + [self doExit:EXIT_FAILURE]; + } else { + if (verbose) { + nsprintf(@"<msg>Creating named pipe at `%@'</msg>", *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(@"<msg>Removing named pipe at `%@'</msg>", path); + } + [fileHandle closeFile]; + [fileHandle release]; + if (![[NSFileManager defaultManager] removeItemAtPath:path error:NULL]) { + nsprintf(@"<msg>Unable to remove named pipe `%@'</msg>", path); + } +} + + +- (int)launchApp:(NSString *)path withFamily:(NSString *)family + uuid:(NSString *)uuid + environment:(NSDictionary *)environment + stdoutPath:(NSString *)stdoutPath + stderrPath:(NSString *)stderrPath + timeout:(NSTimeInterval)timeout + args:(NSArray *)args { + DTiPhoneSimulatorApplicationSpecifier *appSpec; + DTiPhoneSimulatorSessionConfig *config; + NSError *error = 0; + id tClass; + + NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease]; + if (!startOnly && ![fileManager fileExistsAtPath:path]) { + nsprintf(@"<msg>Application path %@ doesn't exist!</msg>", path); + return EXIT_FAILURE; + } + + /* Create the app specifier */ + tClass = objc_getClass("DTiPhoneSimulatorApplicationSpecifier"); + if (tClass == nil) { + nsprintf(@"<msg>DTiPhoneSimulatorApplicationSpecifier class is nil.</msg>"); + return EXIT_FAILURE; + } + appSpec = startOnly ? nil : [tClass specifierWithApplicationPath:path]; + + if (verbose) { + nsprintf(@"<msg>App Spec: %@</msg>", appSpec); + nsprintf(@"SDK Root: %@", sdkRoot); + + for (id key in environment) { + nsprintf(@"<msg>Env: %@ = %@</msg>", key, [environment objectForKey:key]); + } + } + + + NSString *sdkVersion = [sdkRoot sdkVersion]; + NSString *appSupportDir = [NSString stringWithFormat:@"%@/Library/Application Support/iPhone Simulator/%@", + NSHomeDirectory(), sdkVersion]; + NSMutableDictionary *mutableEnv = [NSMutableDictionary dictionaryWithDictionary:environment]; + [mutableEnv setObject:appSupportDir forKey:@"CFFIXED_USER_HOME"]; + [mutableEnv setObject:appSupportDir forKey:@"IPHONE_SHARED_RESOURCES_DIRECTORY"]; + [mutableEnv setObject:appSupportDir forKey:@"HOME"]; + [mutableEnv setObject:[sdkRoot sdkRootPath] forKey:@"IPHONE_SIMULATOR_ROOT"]; + [mutableEnv setObject:[sdkRoot sdkRootPath] forKey:@"DYLD_ROOT_PATH"]; + [mutableEnv setObject:[sdkRoot sdkRootPath] forKey:@"DYLD_FRAMEWORK_PATH"]; + [mutableEnv setObject:[sdkRoot sdkRootPath] forKey:@"DYLD_LIBRARY_PATH"]; + [mutableEnv setObject:@"YES" forKey:@"NSUnbufferedIO"]; + environment = mutableEnv; + + /* Set up the session configuration */ + tClass = objc_getClass("DTiPhoneSimulatorSessionConfig"); + if (tClass == nil) { + nsprintf(@"<msg>DTiPhoneSimulatorApplicationSpecifier class is nil.</msg>"); + return EXIT_FAILURE; + } + config = [[[tClass alloc] init] autorelease]; + [config setApplicationToSimulateOnStart:appSpec]; + [config setSimulatedSystemRoot:sdkRoot]; + [config setSimulatedApplicationShouldWaitForDebugger:shouldWaitDebugger]; + + [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: @"iossim"]; + + // 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]]; + } + } + + [self changeDeviceType:family retina:retinaDevice isTallDevice:tallDevice]; + + /* Start the session */ + tClass = objc_getClass("DTiPhoneSimulatorSession"); + if (tClass == nil) { + nsprintf(@"<msg>DTiPhoneSimulatorSession class is nil.</msg>"); + return EXIT_FAILURE; + } + session = [[tClass alloc] init]; + [session setDelegate:self]; + if (uuid != nil){ + [session setUuid:uuid]; + } + + if (![session requestStartWithConfig:config timeout:timeout error:&error]) { + nsprintf(@"<msg>Could not start simulator session: %@</msg>", error); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +- (void) changeDeviceType:(NSString *)family retina:(BOOL)retina isTallDevice:(BOOL)isTallDevice { + NSString *devicePropertyValue; + if (retina) { + if (verbose) { + nsprintf(@"<msg>using retina</msg>"); + } + if ([family isEqualToString:@"ipad"]) { + devicePropertyValue = deviceIpadRetina; + } + else { + if (isTallDevice) { + devicePropertyValue = deviceIphoneRetina4_0Inch; + } else { + devicePropertyValue = deviceIphoneRetina3_5Inch; + } + } + } else { + if ([family isEqualToString:@"ipad"]) { + devicePropertyValue = deviceIpad; + } else { + devicePropertyValue = deviceIphone; + } + } + CFPreferencesSetAppValue((CFStringRef)deviceProperty, (CFPropertyListRef)devicePropertyValue, (CFStringRef)simulatorPrefrencesName); + CFPreferencesAppSynchronize((CFStringRef)simulatorPrefrencesName); +} + + +/** + * Execute 'main' + */ +- (void)runWithArgc:(int)argc argv:(char **)argv { + if (argc < 2) { + [self printUsage]; + exit(EXIT_FAILURE); + } + + retinaDevice = NO; + tallDevice = NO; + exitOnStartup = NO; + alreadyPrintedData = NO; + startOnly = strcmp(argv[1], "start") == 0; + nsprintf(@"<query_result>"); + + if (strcmp(argv[1], "showsdks") == 0) { + [self doExit:[self showSDKs]]; + return; + } else if (strcmp(argv[1], "launch") == 0 || startOnly) { + if (strcmp(argv[1], "launch") == 0 && argc < 3) { + nsprintf(@"<msg>Missing application path argument</msg>"); + [self printUsage]; + [self doExit:EXIT_FAILURE]; + return; + } + + NSString *appPath = nil; + int argOffset; + if (startOnly) { + argOffset = 2; + } + else { + argOffset = 3; + appPath = [[NSString stringWithUTF8String:argv[2]] expandPath]; + } + + NSString *family = nil; + NSString *uuid = nil; + NSString *stdoutPath = nil; + NSString *stderrPath = nil; + NSTimeInterval timeout = 30; + NSMutableDictionary *environment = [NSMutableDictionary dictionary]; + + int i = argOffset; + 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], "--wait-for-debugger") == 0) { + shouldWaitDebugger = YES; + } else if (strcmp(argv[i], "--debug") == 0) { + shouldWaitDebugger = YES; + shouldStartDebugger = YES; + } else if (strcmp(argv[i], "--use-gdb") == 0) { + useGDB = YES; + } else if (strcmp(argv[i], "--developer-path") == 0) { + ++i; + } else if (strcmp(argv[i], "--timeout") == 0) { + if (i + 1 < argc) { + timeout = [[NSString stringWithUTF8String:argv[++i]] doubleValue]; + NSLog(@"<msg>Timeout: %f second(s)</msg>", timeout); + } + } + else if (strcmp(argv[i], "--sdk") == 0) { + i++; + NSString* ver = [NSString stringWithCString:argv[i] encoding:NSUTF8StringEncoding]; + id tClass = objc_getClass("DTiPhoneSimulatorSystemRoot"); + NSArray *roots; + if (tClass == nil) { + nsprintf(@"<msg>DTiPhoneSimulatorSystemRoot class is nil.</msg>"); + [self doExit:EXIT_FAILURE]; + return; + } + roots = [tClass knownRoots]; + for (DTiPhoneSimulatorSystemRoot *root in roots) { + NSString *v = [root sdkVersion]; + if ([v isEqualToString:ver]) { + sdkRoot = root; + break; + } + } + if (sdkRoot == nil) { + fprintf(stdout,"<msg>Unknown or unsupported SDK version: %s</msg>\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(stdout, "<msg>Could not read environment from file: %s</msg>\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], "--retina") == 0) { + retinaDevice = YES; + } else if (strcmp(argv[i], "--tall") == 0) { + tallDevice = YES; + } else if (strcmp(argv[i], "--args") == 0) { + i++; + break; + } else { + printf("<msg>unrecognized argument:%s</msg>\n", argv[i]); + [self printUsage]; + [self doExit:EXIT_FAILURE]; + return; + } + } + NSMutableArray *args = [NSMutableArray arrayWithCapacity:MAX(argc - i,0)]; + for (; i < argc; i++) { + [args addObject:[NSString stringWithUTF8String:argv[i]]]; + } + + if (sdkRoot == nil) { + id tClass = objc_getClass("DTiPhoneSimulatorSystemRoot"); + if (tClass == nil) { + nsprintf(@"<msg>DTiPhoneSimulatorSystemRoot class is nil.</msg>"); + [self doExit:EXIT_FAILURE]; + return; + } + sdkRoot = [tClass defaultRoot]; + } + + /* Don't exit, adds to runloop */ + int res = [self launchApp:appPath + withFamily:family + uuid:uuid + environment:environment + stdoutPath:stdoutPath + stderrPath:stderrPath + timeout:timeout + args:args]; + nsprintf(@"<app_started status=\"%@\" />", ((res == 0) ? @"SUCCESS" : @"FAILURE")); + fflush(stdout); + fflush(stderr); + if (res != 0) + [self doExit:EXIT_FAILURE]; + } else { + if (argc == 2 && strcmp(argv[1], "--help") == 0) { + [self printUsage]; + [self doExit:EXIT_SUCCESS]; + } else if (argc == 2 && strcmp(argv[1], "--version") == 0) { + printf("%s\n", IOS_SIM_VERSION); + [self doExit:EXIT_SUCCESS]; + } else { + fprintf(stdout, "<msg>Unknown command</msg>\n"); + [self printUsage]; + [self doExit:EXIT_FAILURE]; + } + } +} + +@end |