PageRenderTime 52ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/Source/iPhoneSimulator.m

https://github.com/eighteyes/ios-sim
Objective C | 355 lines | 292 code | 46 blank | 17 comment | 105 complexity | cbb4e81dcf0f37ab0bcd0889c5b4586c MD5 | raw file
  1. /* Author: Landon Fuller <landonf@plausiblelabs.com>
  2. * Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc.
  3. * All rights reserved.
  4. *
  5. * See the LICENSE file for the license on the source code in this file.
  6. */
  7. #import "iPhoneSimulator.h"
  8. #import "NSString+expandPath.h"
  9. #import "nsprintf.h"
  10. #import <sys/types.h>
  11. #import <sys/stat.h>
  12. /**
  13. * A simple iPhoneSimulatorRemoteClient framework.
  14. */
  15. @implementation iPhoneSimulator
  16. - (void) printUsage {
  17. fprintf(stderr, "Usage: ios-sim <command> <options> [--args ...]\n");
  18. fprintf(stderr, "\n");
  19. fprintf(stderr, "Commands:\n");
  20. fprintf(stderr, " showsdks List the available iOS SDK versions\n");
  21. fprintf(stderr, " launch <application path> Launch the application at the specified path on the iOS Simulator\n");
  22. fprintf(stderr, "\n");
  23. fprintf(stderr, "Options:\n");
  24. fprintf(stderr, " --version Print the version of ios-sim\n");
  25. fprintf(stderr, " --help Show this help text\n");
  26. fprintf(stderr, " --verbose Set the output level to verbose\n");
  27. fprintf(stderr, " --exit Exit after startup\n");
  28. fprintf(stderr, " --sdk <sdkversion> The iOS SDK version to run the application on (defaults to the latest)\n");
  29. fprintf(stderr, " --family <device family> The device type that should be simulated (defaults to `iphone')\n");
  30. fprintf(stderr, " --uuid <uuid> A UUID identifying the session (is that correct?)\n");
  31. fprintf(stderr, " --env <environment file path> A plist file containing environment key-value pairs that should be set\n");
  32. fprintf(stderr, " --setenv NAME=VALUE Set an environment variable\n");
  33. fprintf(stderr, " --stdout <stdout file path> The path where stdout of the simulator will be redirected to (defaults to stdout of ios-sim)\n");
  34. fprintf(stderr, " --stderr <stderr file path> The path where stderr of the simulator will be redirected to (defaults to stderr of ios-sim)\n");
  35. fprintf(stderr, " --args <...> All following arguments will be passed on to the application\n");
  36. }
  37. - (int) showSDKs {
  38. NSArray *roots = [DTiPhoneSimulatorSystemRoot knownRoots];
  39. nsprintf(@"Simulator SDK Roots:");
  40. for (DTiPhoneSimulatorSystemRoot *root in roots) {
  41. nsfprintf(stderr, @"'%@' (%@)\n\t%@", [root sdkDisplayName], [root sdkVersion], [root sdkRootPath]);
  42. }
  43. return EXIT_SUCCESS;
  44. }
  45. - (void)session:(DTiPhoneSimulatorSession *)session didEndWithError:(NSError *)error {
  46. if (verbose) {
  47. nsprintf(@"Session did end with error %@", error);
  48. }
  49. if (stderrFileHandle != nil) {
  50. NSString *stderrPath = [[session sessionConfig] simulatedApplicationStdErrPath];
  51. [self removeStdioFIFO:stderrFileHandle atPath:stderrPath];
  52. }
  53. if (stdoutFileHandle != nil) {
  54. NSString *stdoutPath = [[session sessionConfig] simulatedApplicationStdOutPath];
  55. [self removeStdioFIFO:stdoutFileHandle atPath:stdoutPath];
  56. }
  57. if (error != nil) {
  58. exit(EXIT_FAILURE);
  59. }
  60. exit(EXIT_SUCCESS);
  61. }
  62. - (void)session:(DTiPhoneSimulatorSession *)session didStart:(BOOL)started withError:(NSError *)error {
  63. if (started) {
  64. if (verbose) {
  65. nsprintf(@"Session started");
  66. }
  67. if (exitOnStartup) {
  68. exit(EXIT_SUCCESS);
  69. }
  70. } else {
  71. nsprintf(@"Session could not be started: %@", error);
  72. exit(EXIT_FAILURE);
  73. }
  74. }
  75. - (void)stdioDataIsAvailable:(NSNotification *)notification {
  76. [[notification object] readInBackgroundAndNotify];
  77. NSData *data = [[notification userInfo] valueForKey:NSFileHandleNotificationDataItem];
  78. NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  79. if (!alreadyPrintedData) {
  80. if ([str length] == 0) {
  81. return;
  82. } else {
  83. alreadyPrintedData = YES;
  84. }
  85. }
  86. if ([notification object] == stdoutFileHandle) {
  87. printf("%s", [str UTF8String]);
  88. } else {
  89. nsprintf(@"%@", str);
  90. }
  91. }
  92. - (void)createStdioFIFO:(NSFileHandle **)fileHandle ofType:(NSString *)type atPath:(NSString **)path {
  93. *path = [NSString stringWithFormat:@"%@/ios-sim-%@-pipe-%d", NSTemporaryDirectory(), type, (int)time(NULL)];
  94. if (mkfifo([*path UTF8String], S_IRUSR | S_IWUSR) == -1) {
  95. nsprintf(@"Unable to create %@ named pipe `%@'", type, *path);
  96. exit(EXIT_FAILURE);
  97. } else {
  98. if (verbose) {
  99. nsprintf(@"Creating named pipe at `%@'", *path);
  100. }
  101. int fd = open([*path UTF8String], O_RDONLY | O_NDELAY);
  102. *fileHandle = [[[NSFileHandle alloc] initWithFileDescriptor:fd] retain];
  103. [*fileHandle readInBackgroundAndNotify];
  104. [[NSNotificationCenter defaultCenter] addObserver:self
  105. selector:@selector(stdioDataIsAvailable:)
  106. name:NSFileHandleReadCompletionNotification
  107. object:*fileHandle];
  108. }
  109. }
  110. - (void)removeStdioFIFO:(NSFileHandle *)fileHandle atPath:(NSString *)path {
  111. if (verbose) {
  112. nsprintf(@"Removing named pipe at `%@'", path);
  113. }
  114. [fileHandle closeFile];
  115. [fileHandle release];
  116. if (![[NSFileManager defaultManager] removeItemAtPath:path error:NULL]) {
  117. nsprintf(@"Unable to remove named pipe `%@'", path);
  118. }
  119. }
  120. - (int)launchApp:(NSString *)path withFamily:(NSString *)family
  121. uuid:(NSString *)uuid
  122. environment:(NSDictionary *)environment
  123. stdoutPath:(NSString *)stdoutPath
  124. stderrPath:(NSString *)stderrPath
  125. args:(NSArray *)args {
  126. DTiPhoneSimulatorApplicationSpecifier *appSpec;
  127. DTiPhoneSimulatorSessionConfig *config;
  128. DTiPhoneSimulatorSession *session;
  129. NSError *error;
  130. NSFileManager *fileManager = [[[NSFileManager alloc] init] autorelease];
  131. if (![fileManager fileExistsAtPath:path]) {
  132. nsprintf(@"Application path %@ doesn't exist!", path);
  133. exit(EXIT_FAILURE);
  134. }
  135. /* Create the app specifier */
  136. appSpec = [DTiPhoneSimulatorApplicationSpecifier specifierWithApplicationPath:path];
  137. if (verbose) {
  138. nsprintf(@"App Spec: %@", appSpec);
  139. nsprintf(@"SDK Root: %@", sdkRoot);
  140. for (id key in environment) {
  141. nsprintf(@"Env: %@ = %@", key, [environment objectForKey:key]);
  142. }
  143. }
  144. /* Set up the session configuration */
  145. config = [[[DTiPhoneSimulatorSessionConfig alloc] init] autorelease];
  146. [config setApplicationToSimulateOnStart:appSpec];
  147. [config setSimulatedSystemRoot:sdkRoot];
  148. [config setSimulatedApplicationShouldWaitForDebugger: NO];
  149. [config setSimulatedApplicationLaunchArgs:args];
  150. [config setSimulatedApplicationLaunchEnvironment:environment];
  151. if (stderrPath) {
  152. stderrFileHandle = nil;
  153. } else if (!exitOnStartup) {
  154. [self createStdioFIFO:&stderrFileHandle ofType:@"stderr" atPath:&stderrPath];
  155. }
  156. [config setSimulatedApplicationStdErrPath:stderrPath];
  157. if (stdoutPath) {
  158. stdoutFileHandle = nil;
  159. } else if (!exitOnStartup) {
  160. [self createStdioFIFO:&stdoutFileHandle ofType:@"stdout" atPath:&stdoutPath];
  161. }
  162. [config setSimulatedApplicationStdOutPath:stdoutPath];
  163. [config setLocalizedClientName: @"ios-sim"];
  164. // this was introduced in 3.2 of SDK
  165. if ([config respondsToSelector:@selector(setSimulatedDeviceFamily:)]) {
  166. if (family == nil) {
  167. family = @"iphone";
  168. }
  169. if (verbose) {
  170. nsprintf(@"using device family %@",family);
  171. }
  172. if ([family isEqualToString:@"ipad"]) {
  173. [config setSimulatedDeviceFamily:[NSNumber numberWithInt:2]];
  174. } else{
  175. [config setSimulatedDeviceFamily:[NSNumber numberWithInt:1]];
  176. }
  177. }
  178. /* Start the session */
  179. session = [[[DTiPhoneSimulatorSession alloc] init] autorelease];
  180. [session setDelegate:self];
  181. [session setSimulatedApplicationPID: [NSNumber numberWithInt:35]];
  182. if (uuid != nil){
  183. [session setUuid:uuid];
  184. }
  185. if (![session requestStartWithConfig:config timeout:30 error:&error]) {
  186. nsprintf(@"Could not start simulator session: %@", error);
  187. return EXIT_FAILURE;
  188. }
  189. return EXIT_SUCCESS;
  190. }
  191. /**
  192. * Execute 'main'
  193. */
  194. - (void)runWithArgc:(int)argc argv:(char **)argv {
  195. if (argc < 2) {
  196. [self printUsage];
  197. exit(EXIT_FAILURE);
  198. }
  199. exitOnStartup = NO;
  200. alreadyPrintedData = NO;
  201. if (strcmp(argv[1], "showsdks") == 0) {
  202. exit([self showSDKs]);
  203. } else if (strcmp(argv[1], "launch") == 0) {
  204. if (argc < 3) {
  205. fprintf(stderr, "Missing application path argument\n");
  206. [self printUsage];
  207. exit(EXIT_FAILURE);
  208. }
  209. NSString *appPath = [[NSString stringWithUTF8String:argv[2]] expandPath];
  210. NSString *family = nil;
  211. NSString *uuid = nil;
  212. NSString *stdoutPath = nil;
  213. NSString *stderrPath = nil;
  214. NSMutableDictionary *environment = [NSMutableDictionary dictionary];
  215. int i = 3;
  216. for (; i < argc; i++) {
  217. if (strcmp(argv[i], "--version") == 0) {
  218. printf("%s\n", IOS_SIM_VERSION);
  219. exit(EXIT_SUCCESS);
  220. } else if (strcmp(argv[i], "--help") == 0) {
  221. [self printUsage];
  222. exit(EXIT_SUCCESS);
  223. } else if (strcmp(argv[i], "--verbose") == 0) {
  224. verbose = YES;
  225. } else if (strcmp(argv[i], "--exit") == 0) {
  226. exitOnStartup = YES;
  227. }
  228. else if (strcmp(argv[i], "--sdk") == 0) {
  229. i++;
  230. NSString* ver = [NSString stringWithCString:argv[i] encoding:NSUTF8StringEncoding];
  231. NSArray *roots = [DTiPhoneSimulatorSystemRoot knownRoots];
  232. for (DTiPhoneSimulatorSystemRoot *root in roots) {
  233. NSString *v = [root sdkVersion];
  234. if ([v isEqualToString:ver]) {
  235. sdkRoot = root;
  236. break;
  237. }
  238. }
  239. if (sdkRoot == nil) {
  240. fprintf(stderr,"Unknown or unsupported SDK version: %s\n",argv[i]);
  241. [self showSDKs];
  242. exit(EXIT_FAILURE);
  243. }
  244. } else if (strcmp(argv[i], "--family") == 0) {
  245. i++;
  246. family = [NSString stringWithUTF8String:argv[i]];
  247. } else if (strcmp(argv[i], "--uuid") == 0) {
  248. i++;
  249. uuid = [NSString stringWithUTF8String:argv[i]];
  250. } else if (strcmp(argv[i], "--setenv") == 0) {
  251. i++;
  252. NSArray *parts = [[NSString stringWithUTF8String:argv[i]] componentsSeparatedByString:@"="];
  253. [environment setObject:[parts objectAtIndex:1] forKey:[parts objectAtIndex:0]];
  254. } else if (strcmp(argv[i], "--env") == 0) {
  255. i++;
  256. NSString *envFilePath = [[NSString stringWithUTF8String:argv[i]] expandPath];
  257. environment = [NSDictionary dictionaryWithContentsOfFile:envFilePath];
  258. if (!environment) {
  259. fprintf(stderr, "Could not read environment from file: %s\n", argv[i]);
  260. [self printUsage];
  261. exit(EXIT_FAILURE);
  262. }
  263. } else if (strcmp(argv[i], "--stdout") == 0) {
  264. i++;
  265. stdoutPath = [[NSString stringWithUTF8String:argv[i]] expandPath];
  266. NSLog(@"stdoutPath: %@", stdoutPath);
  267. } else if (strcmp(argv[i], "--stderr") == 0) {
  268. i++;
  269. stderrPath = [[NSString stringWithUTF8String:argv[i]] expandPath];
  270. NSLog(@"stderrPath: %@", stderrPath);
  271. } else if (strcmp(argv[i], "--args") == 0) {
  272. i++;
  273. break;
  274. } else {
  275. fprintf(stderr, "unrecognized argument:%s\n", argv[i]);
  276. [self printUsage];
  277. exit(EXIT_FAILURE);
  278. }
  279. }
  280. NSMutableArray *args = [NSMutableArray arrayWithCapacity:(argc - i)];
  281. for (; i < argc; i++) {
  282. [args addObject:[NSString stringWithUTF8String:argv[i]]];
  283. }
  284. if (sdkRoot == nil) {
  285. sdkRoot = [DTiPhoneSimulatorSystemRoot defaultRoot];
  286. }
  287. /* Don't exit, adds to runloop */
  288. [self launchApp:appPath
  289. withFamily:family
  290. uuid:uuid
  291. environment:environment
  292. stdoutPath:stdoutPath
  293. stderrPath:stderrPath
  294. args:args];
  295. } else {
  296. if (argc == 2 && strcmp(argv[1], "--help") == 0) {
  297. [self printUsage];
  298. exit(EXIT_SUCCESS);
  299. } else if (argc == 2 && strcmp(argv[1], "--version") == 0) {
  300. printf("%s\n", IOS_SIM_VERSION);
  301. exit(EXIT_SUCCESS);
  302. } else {
  303. fprintf(stderr, "Unknown command\n");
  304. [self printUsage];
  305. exit(EXIT_FAILURE);
  306. }
  307. }
  308. }
  309. @end