OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #import <Foundation/Foundation.h> | |
6 #include <asl.h> | |
7 #include <libgen.h> | |
8 #include <stdarg.h> | |
9 #include <stdio.h> | |
10 | |
11 // An executable (iossim) that runs an app in the iOS Simulator. | |
12 // Run 'iossim -h' for usage information. | |
13 // | |
14 // For best results, the iOS Simulator application should not be running when | |
15 // iossim is invoked. | |
16 // | |
17 // Headers for the iPhoneSimulatorRemoteClient framework used in this tool are | |
18 // generated by class-dump, via GYP. | |
19 // (class-dump is available at http://www.codethecode.com/projects/class-dump/) | |
20 // | |
21 // However, there are some forward declarations required to get things to | |
22 // compile. Also, the DTiPhoneSimulatorSessionDelegate protocol is referenced | |
23 // by the iPhoneSimulatorRemoteClient framework, but not defined in the object | |
24 // file, so it must be defined here before we import the generated | |
stuartmorgan
2012/07/20 07:45:44
s/we import/importing/
lliabraa
2012/07/26 19:14:11
Done.
| |
25 // iPhoneSimulatorRemoteClient.h file. | |
26 | |
27 @class DTiPhoneSimulatorSession; | |
28 @class DTiPhoneSimulatorSessionConfig; | |
29 @class DTiPhoneSimulatorSystemRoot; | |
30 @class DTiPhoneSimulatorApplicationSpecifier; | |
stuartmorgan
2012/07/20 07:45:44
Move this up to the top so it's alphabetical.
lliabraa
2012/07/26 19:14:11
Done.
| |
31 @protocol DTiPhoneSimulatorSessionDelegate | |
stuartmorgan
2012/07/20 07:45:44
Add a blank line before this.
lliabraa
2012/07/26 19:14:11
Done.
| |
32 - (void)session:(DTiPhoneSimulatorSession*)session | |
33 didEndWithError:(NSError*)error; | |
34 - (void)session:(DTiPhoneSimulatorSession*)session | |
35 didStart:(BOOL)started | |
36 withError:(NSError*)error; | |
37 @end | |
38 | |
39 #import "iPhoneSimulatorRemoteClient.h" | |
40 | |
41 // Name of environment variables that control the user's home directory in the | |
42 // simulator. | |
43 #define kUserHomeEnvVariable "CFFIXED_USER_HOME" | |
44 #define kHomeEnvVariable "HOME" | |
stuartmorgan
2012/07/20 07:45:44
Let's change these to const char* const, and the o
lliabraa
2012/07/26 19:14:11
Done.
| |
45 | |
46 // Device family codes for iPhone and iPad. | |
47 #define kIPhoneFamily 1 | |
48 #define kIPadFamily 2 | |
49 | |
50 // Max number of seconds to wait for the simulator session to start. | |
51 // This timeout must allow time to start up iOS Simulator, install the app | |
52 // and perform any other black magic that is encoded in the | |
53 // iPhoneSimulatorRemoteClient framework to kick things off. Normal start up | |
54 // time is only a couple seconds but machine load, disk caches, etc., can all | |
55 // affect startup time in the wild so the timeout needs to be fairly generous. | |
56 // If this timeout occurs iossim will likely exit with non-zero status; the | |
57 // exception being if the app is invoked and completes execution before the | |
58 // session is started (this case is handled in session:didStart:withError). | |
59 #define kSessionStartTimeout 30 // seconds | |
stuartmorgan
2012/07/20 07:45:44
This one would be const NSTimeInterval I guess (si
lliabraa
2012/07/26 19:14:11
Done.
| |
60 | |
61 // An undocumented system log key included in messages from launchd. The value | |
62 // is the PID of the process the message is about (as opposed to launchd's PID). | |
63 #define ASL_KEY_REF_PID "RefPID" | |
stuartmorgan
2012/07/20 07:45:44
This one we can leave as-is for consistency with t
lliabraa
2012/07/26 19:14:11
Done.
| |
64 | |
65 namespace { | |
66 | |
67 static const char* gToolName = "iossim"; | |
stuartmorgan
2012/07/20 07:45:44
static is redundant.
lliabraa
2012/07/26 19:14:11
Done.
| |
68 | |
69 void LogError(NSString* format, ...) { | |
70 va_list list; | |
71 va_start(list, format); | |
72 | |
73 NSString* str = | |
stuartmorgan
2012/07/20 07:45:44
s/str/message/ in these functions
lliabraa
2012/07/26 19:14:11
Done.
| |
74 [[[NSString alloc] initWithFormat:format arguments:list] autorelease]; | |
75 | |
76 fprintf(stderr, "%s: ERROR: %s\n", gToolName, [str UTF8String]); | |
77 fflush(stderr); | |
78 | |
79 va_end(list); | |
80 } | |
81 | |
82 void LogWarning(NSString* format, ...) { | |
83 va_list list; | |
84 va_start(list, format); | |
85 | |
86 NSString* str = | |
87 [[[NSString alloc] initWithFormat:format arguments:list] autorelease]; | |
88 | |
89 fprintf(stderr, "%s: WARNING: %s\n", gToolName, [str UTF8String]); | |
90 fflush(stderr); | |
91 | |
92 va_end(list); | |
93 } | |
94 | |
95 } // namespace | |
96 | |
97 // A delegate that is called when the simulated app is started or ended in the | |
98 // simulator. | |
99 @interface SimulatorDelegate : NSObject <DTiPhoneSimulatorSessionDelegate> { | |
100 @private | |
101 NSString* stdioPath_; // weak | |
stuartmorgan
2012/07/20 07:45:44
Two spaces before // rather than one.
lliabraa
2012/07/26 19:14:11
Done.
| |
102 NSThread* outputThread_; | |
103 BOOL appRunning_; | |
104 } | |
105 @end | |
106 | |
107 // An implementation that copies the simulated app's stdio to stdout of this | |
108 // executable. While it would be nice to get stdout and stderr independently | |
109 // from the Simulator; anything from io buffering to output interleaved | |
stuartmorgan
2012/07/20 07:45:44
s/;/,/
lliabraa
2012/07/26 19:14:11
Done.
| |
110 // between the two mean they will show up out of onder here. Putting them | |
stuartmorgan
2012/07/20 07:45:44
s/onder/order/
lliabraa
2012/07/26 19:14:11
Done.
| |
111 // together keeps the order correct. The class should be initialized with the | |
112 // location of the simulated app's output file. When the simulated app starts, | |
113 // a thread is started which handles copying data from the simulated app's | |
114 // output file to the stdout of this executable. | |
115 @implementation SimulatorDelegate | |
116 | |
117 // Specifies the file locations of the simulated app's stdout and stderr. | |
118 - (SimulatorDelegate*)initWithStdioPath:(NSString*)stdioPath { | |
119 self = [super init]; | |
120 if (self) { | |
121 stdioPath_ = stdioPath; | |
stuartmorgan
2012/07/20 07:45:44
The only reason this doesn't eventually crash is t
lliabraa
2012/07/26 19:14:11
Done.
| |
122 } | |
stuartmorgan
2012/07/20 07:45:44
No {} for the one-line condition
lliabraa
2012/07/26 19:14:11
Done.
| |
123 return self; | |
124 } | |
125 | |
126 // Reads data from the simulated app's output and writes it to stdout. This | |
127 // method blocks, so it should be called in a separate thread. The iOS | |
128 // Simulator takes a file path for the simulated app's stdout and stderr, but | |
129 // this path isn't always available (e.g. when the stdout is Xcode's build | |
130 // window). As a workaround, iossim creates temp file to hold output, which | |
stuartmorgan
2012/07/20 07:45:44
s/temp file/a temp file/
lliabraa
2012/07/26 19:14:11
Done.
| |
131 // this method reads and copies to stdout. | |
132 - (void)tailOutput { | |
133 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; | |
134 | |
135 // Copy data to stdout/stderr while the app is running. | |
136 NSFileHandle* simio = [NSFileHandle fileHandleForReadingAtPath:stdioPath_]; | |
137 NSFileHandle* standardOutput = [NSFileHandle fileHandleWithStandardOutput]; | |
138 while (appRunning_) { | |
139 NSAutoreleasePool* innerPool = [[NSAutoreleasePool alloc] init]; | |
140 [standardOutput writeData:[simio readDataToEndOfFile]]; | |
141 [NSThread sleepForTimeInterval:0.1]; // seconds | |
stuartmorgan
2012/07/20 07:45:44
Remove the // seconds since TimeInterval is always
lliabraa
2012/07/26 19:14:11
Is this a hard convention? I don't see how it hurt
stuartmorgan
2012/07/26 19:47:05
Yes, see
https://developer.apple.com/library/mac/#
lliabraa
2012/07/27 13:08:58
I meant is it a hard convention not to document un
| |
142 [innerPool drain]; | |
143 } | |
144 | |
145 // Once the app is no longer running, copy any data that was written during | |
146 // the last sleep cycle. | |
stuartmorgan
2012/07/20 07:45:44
Could we just swap the sleep and the print in the
lliabraa
2012/07/26 19:14:11
I think it is better to leave this final read/writ
| |
147 [standardOutput writeData:[simio readDataToEndOfFile]]; | |
148 | |
149 [pool drain]; | |
150 } | |
151 | |
152 - (void)session:(DTiPhoneSimulatorSession*)session | |
153 didStart:(BOOL)started | |
154 withError:(NSError*)error { | |
155 if (!started) { | |
156 // If the test executes very quickly (<30ms), the SimulatorDelegate may not | |
157 // get the initial session:started:withError: message indicating successful | |
158 // startup of the simulated app. Instead the delegate will get a | |
159 // session:started:withError: message after the timeout has elapsed. To | |
160 // account for this case, check if the simulated app's stdio file was | |
161 // ever created and if it exists dump it to stdout and return success. | |
162 NSFileManager* fileManager = [NSFileManager defaultManager]; | |
163 if ([fileManager fileExistsAtPath:stdioPath_ isDirectory:NO]) { | |
164 appRunning_ = NO; | |
165 [self tailOutput]; | |
166 // Note that exiting in this state leaves a process running | |
167 // (i.e. /.../iPhoneSimulator4.3.sdk/usr/libexec/installd -t 30) that will | |
168 // prevent future simulator sessions from being started for 30 seconds | |
169 // unless the iOS Simulator application is killed altogether. | |
170 [self session:session didEndWithError:nil]; | |
171 | |
172 // session:didEndWithError should not return (because it exits) so | |
173 // the execution path should never get here. | |
174 exit(EXIT_FAILURE); | |
175 } | |
176 | |
177 LogError(@"Simulator failed to start: %@", [error localizedDescription]); | |
178 exit(EXIT_FAILURE); | |
179 } | |
180 | |
181 // Start a thread to write contents of outputPath to stdout. | |
182 appRunning_ = YES; | |
183 outputThread_ = [[NSThread alloc] initWithTarget:self | |
184 selector:@selector(tailOutput) | |
185 object:nil]; | |
186 [outputThread_ start]; | |
187 } | |
188 | |
189 - (void)session:(DTiPhoneSimulatorSession*)session | |
190 didEndWithError:(NSError*)error { | |
191 appRunning_ = NO; | |
192 // Wait for the output thread to finish copying data to stdout. | |
193 if (outputThread_) { | |
194 while (![outputThread_ isFinished]) { | |
195 [NSThread sleepForTimeInterval:0.1]; // seconds | |
stuartmorgan
2012/07/20 07:45:44
Remove the comment.
lliabraa
2012/07/26 19:14:11
See above.
| |
196 } | |
197 [outputThread_ release]; | |
198 outputThread_ = nil; | |
199 } | |
200 | |
201 if (error) { | |
202 LogError(@"Simulator ended with error: %@", [error localizedDescription]); | |
203 exit(EXIT_FAILURE); | |
204 } | |
205 | |
206 // Check if the simulated app exited abnormally by looking for system log | |
207 // messages from launchd that refer to the simulated app's PID. Limit query | |
208 // to messages in the last minute since PIDs are cyclical. | |
209 aslmsg query = asl_new(ASL_TYPE_QUERY); | |
210 asl_set_query(query, ASL_KEY_SENDER, "launchd", | |
211 ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_SUBSTRING); | |
212 asl_set_query(query, ASL_KEY_REF_PID, | |
213 [[[session simulatedApplicationPID] stringValue] UTF8String], | |
214 ASL_QUERY_OP_EQUAL); | |
215 asl_set_query(query, ASL_KEY_TIME, "-1m", ASL_QUERY_OP_GREATER_EQUAL); | |
216 | |
217 // Log any messages found. | |
218 aslresponse response = asl_search(NULL, query); | |
219 BOOL entryFound = NO; | |
220 aslmsg entry; | |
221 while ((entry = aslresponse_next(response)) != NULL) { | |
222 entryFound = YES; | |
223 LogWarning(@"Console message: %s", asl_get(entry, ASL_KEY_MSG)); | |
224 } | |
225 | |
226 // launchd only sends messages if the process crashed or exits with a | |
227 // non-zero status, so if the query returned any results iossim should exit | |
228 // with non-zero status. | |
229 if (entryFound) { | |
230 LogError(@"Simulated app crashed or exited with non-zero status"); | |
231 exit(EXIT_FAILURE); | |
232 } | |
233 exit(EXIT_SUCCESS); | |
234 } | |
235 @end | |
236 | |
237 namespace { | |
238 | |
239 // Converts the given app path to an application spec, which requires an | |
240 // absolute path. | |
241 DTiPhoneSimulatorApplicationSpecifier* BuildAppSpec(NSString* appPath) { | |
242 if (![appPath isAbsolutePath]) { | |
243 NSString* cwd = [[NSFileManager defaultManager] currentDirectoryPath]; | |
244 appPath = [cwd stringByAppendingPathComponent:appPath]; | |
245 } | |
246 appPath = [appPath stringByStandardizingPath]; | |
247 return [DTiPhoneSimulatorApplicationSpecifier | |
248 specifierWithApplicationPath:appPath]; | |
249 } | |
250 | |
251 // Returns the system root for the given SDK version. If sdkVersion is nil, the | |
252 // default system root is returned. Will return nil if the sdkVersion is not | |
253 // valid. | |
254 DTiPhoneSimulatorSystemRoot* BuildSystemRoot(NSString* sdkVersion) { | |
255 DTiPhoneSimulatorSystemRoot* systemRoot = | |
256 [DTiPhoneSimulatorSystemRoot defaultRoot]; | |
257 if (sdkVersion) { | |
258 systemRoot = [DTiPhoneSimulatorSystemRoot rootWithSDKVersion:sdkVersion]; | |
259 } | |
stuartmorgan
2012/07/20 07:45:44
No {}
lliabraa
2012/07/26 19:14:11
Done.
| |
260 return systemRoot; | |
261 } | |
262 | |
263 // Builds a config object for starting the specified app. | |
264 DTiPhoneSimulatorSessionConfig* BuildSessionConfig( | |
265 DTiPhoneSimulatorApplicationSpecifier* appSpec, | |
266 DTiPhoneSimulatorSystemRoot* systemRoot, | |
267 NSString* stdoutPath, | |
268 NSString* stderrPath, | |
269 NSArray* appArgs, | |
270 NSNumber* deviceFamily) { | |
271 DTiPhoneSimulatorSessionConfig* sessionConfig = | |
272 [[[DTiPhoneSimulatorSessionConfig alloc] init] autorelease]; | |
273 sessionConfig.applicationToSimulateOnStart = appSpec; | |
274 sessionConfig.simulatedSystemRoot = systemRoot; | |
275 sessionConfig.localizedClientName = @"chromium"; | |
276 sessionConfig.simulatedApplicationStdErrPath = stderrPath; | |
277 sessionConfig.simulatedApplicationStdOutPath = stdoutPath; | |
278 sessionConfig.simulatedApplicationLaunchArgs = appArgs; | |
279 // Note: This environment doesn't work for setting the app's user home | |
280 // directory via CFFIXED_USER_HOME. Instead the user home directory is set via | |
281 // the environment that the simuator runs in. | |
282 // sessionConfig.simulatedApplicationLaunchEnvironment = | |
283 // [NSDictionary dictionary]; | |
stuartmorgan
2012/07/20 07:45:44
If this doesn't work, let's not leave the code her
lliabraa
2012/07/26 19:14:11
I changed this to a more generic TODO for supporti
| |
284 sessionConfig.simulatedDeviceFamily = deviceFamily; | |
285 return sessionConfig; | |
286 } | |
287 | |
288 // Builds a simulator session that will use the given delegate. | |
289 DTiPhoneSimulatorSession* BuildSession(SimulatorDelegate* delegate) { | |
290 DTiPhoneSimulatorSession* session = | |
291 [[[DTiPhoneSimulatorSession alloc] init] autorelease]; | |
292 session.delegate = delegate; | |
293 return session; | |
294 } | |
295 | |
296 // Creates a temporary directory with a unique name based on the provided | |
297 // template. The template should not contain any path separators and be suffixed | |
298 // with X's, which will be substituted with a unique alphanumeric string (see | |
299 // 'man mkdtemp' for details). The directory will be created as a subdirectory | |
300 // of NSTemporaryDirectory(). For example, if dirNameTemplate is 'test-XXX', | |
301 // this method would return something like '/path/to/tempdir/test-3n2'. | |
302 // | |
303 // Returns the absolute path of the newly-created directory, or nill if unable | |
304 // to create a unique directory. | |
305 NSString* CreateTempDirectory(NSString* dirNameTemplate) { | |
306 NSString* fullPathTemplate = [NSTemporaryDirectory() | |
307 stringByAppendingPathComponent:dirNameTemplate]; | |
stuartmorgan
2012/07/20 07:45:44
Just indent 4 from the start of the previous line.
lliabraa
2012/07/26 19:14:11
I've changed the formatting to put the entire call
stuartmorgan
2012/07/26 19:47:05
Nope, both are fine. I tend toward what you did as
lliabraa
2012/07/27 13:08:58
Ack.
| |
308 char* fullPath = mkdtemp(const_cast<char*>([fullPathTemplate UTF8String])); | |
309 if (fullPath == NULL) { | |
310 return nil; | |
311 } | |
stuartmorgan
2012/07/20 07:45:44
No {}
lliabraa
2012/07/26 19:14:11
Done.
| |
312 return [NSString stringWithUTF8String:fullPath]; | |
313 } | |
314 | |
315 // Creates the necessary directory structure under the given user home directory | |
316 // path. | |
317 // Returns YES if successful, NO if unable to create the directories. | |
318 BOOL CreateHomeDirSubDirs(NSString* userHomePath) { | |
319 NSFileManager* fileManager = [NSFileManager defaultManager]; | |
320 | |
321 // Create user home and subdirectories. | |
322 NSArray* subDirsToCreate = [NSArray arrayWithObjects: | |
323 @"Documents", | |
324 @"Library/Caches", | |
325 nil]; | |
326 for (NSString* subDir in subDirsToCreate) { | |
327 NSString* path = [userHomePath stringByAppendingPathComponent:subDir]; | |
328 NSError* error; | |
329 if (![fileManager createDirectoryAtPath:path | |
330 withIntermediateDirectories:YES | |
331 attributes:nil | |
332 error:&error]) { | |
333 LogError(@"Unable to create directory: %@. Error: %@", | |
334 path, [error localizedDescription]); | |
335 return NO; | |
336 } | |
337 } | |
338 | |
339 return YES; | |
340 } | |
341 | |
342 // Creates the necessary directory structure under the given user home directory | |
343 // path, then sets the path in the appropriate environment variable. | |
344 // Returns YES if successful, NO if unable to create or initialize the given | |
345 // directory. | |
346 BOOL InitializeSimulatorUserHome(NSString* userHomePath) { | |
347 if (!CreateHomeDirSubDirs(userHomePath)) { | |
348 return NO; | |
349 } | |
stuartmorgan
2012/07/20 07:45:44
No {}
lliabraa
2012/07/26 19:14:11
Done.
| |
350 | |
351 // Update the environment to use the specified directory as the user home | |
352 // directory. | |
353 // Note: the third param of setenv specifies whether or not to overwrite the | |
354 // variable's value if it has already been set. | |
355 if ((setenv(kUserHomeEnvVariable, [userHomePath UTF8String], YES) == -1) || | |
356 (setenv(kHomeEnvVariable, [userHomePath UTF8String], YES) == -1)) { | |
357 LogError(@"Unable to set %s environment variables."); | |
stuartmorgan
2012/07/20 07:45:44
This will crash if it's ever reached; there's no a
lliabraa
2012/07/26 19:14:11
Done.
| |
358 return NO; | |
359 } | |
360 | |
361 return YES; | |
362 } | |
363 | |
364 // Prints the usage information to stderr. | |
365 void PrintUsage() { | |
366 fprintf(stderr, "Usage: iossim [-d device] [-s sdkVersion] [-u homeDir] " | |
367 "<appPath> [<appArgs>]\n"); | |
368 fprintf(stderr, " where <appPath> is the path to the .app directory and " | |
stuartmorgan
2012/07/20 07:45:44
Why all the separate fprintf statements instead of
lliabraa
2012/07/26 19:14:11
No idea. Changed to string continuations
| |
369 "appArgs are any arguments to send the simulated app.\n"); | |
370 fprintf(stderr, "\n"); | |
371 fprintf(stderr, "Options:\n"); | |
372 fprintf(stderr, " -d Specifies the device (either 'iPhone' or 'iPad')." | |
373 " Defaults to 'iPhone'.\n"); | |
374 fprintf(stderr, " -s Specifies the SDK version to use (e.g '4.3')." | |
375 " Will use system default if not specified.\n"); | |
376 fprintf(stderr, " -u Specifies a user home directory for the simulator." | |
377 " Will create a new directory if not specified.\n"); | |
378 } | |
379 | |
380 } // namespace | |
stuartmorgan
2012/07/20 07:45:44
One more space before //
lliabraa
2012/07/26 19:14:11
Done.
| |
381 | |
382 | |
383 int main(int argc, char* const argv[]) { | |
384 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; | |
385 | |
386 char* toolName = basename(argv[0]); | |
387 if (toolName != NULL) | |
388 gToolName = toolName; | |
389 | |
390 NSString* appPath = nil; | |
391 NSString* appName = nil; | |
392 NSString* sdkVersion = nil; | |
393 NSString* deviceName = @"iPhone"; | |
394 NSString* simHomePath = nil; | |
395 NSMutableArray* appArgs = [NSMutableArray array]; | |
396 | |
397 // Parse the optional arguments | |
398 int c; | |
399 while ((c = getopt(argc, argv, "hs:d:u:")) != -1) { | |
400 switch (c) { | |
401 case 's': | |
402 sdkVersion = [NSString stringWithUTF8String:optarg]; | |
403 break; | |
404 case 'd': | |
405 deviceName = [NSString stringWithUTF8String:optarg]; | |
406 break; | |
407 case 'u': | |
408 simHomePath = [NSString stringWithUTF8String:optarg]; | |
stuartmorgan
2012/07/20 07:45:44
This should be using NSFileManager's stringWithFil
lliabraa
2012/07/26 19:14:11
Done.
| |
409 break; | |
410 case 'h': | |
411 PrintUsage(); | |
412 exit(EXIT_SUCCESS); | |
413 default: | |
414 PrintUsage(); | |
415 exit(EXIT_FAILURE); | |
416 } | |
417 } | |
418 | |
419 // There should be at least one arg left, specifying the app path. Any | |
420 // additional args are passed as arguments to the app. | |
421 if (optind < argc) { | |
422 appPath = [NSString stringWithUTF8String:argv[optind++]]; | |
stuartmorgan
2012/07/20 07:45:44
As should this.
lliabraa
2012/07/26 19:14:11
Done.
| |
423 appName = [appPath lastPathComponent]; | |
424 while (optind < argc) { | |
425 [appArgs addObject:[NSString stringWithUTF8String:argv[optind++]]]; | |
426 } | |
427 } else { | |
428 LogError(@"Unable to parse command line arguments."); | |
429 PrintUsage(); | |
430 exit(EXIT_FAILURE); | |
431 } | |
432 | |
433 // Make sure the app path provided is legit. | |
434 DTiPhoneSimulatorApplicationSpecifier* appSpec = BuildAppSpec(appPath); | |
435 if (!appSpec) { | |
436 LogError(@"Invalid app path: %@", appPath); | |
437 exit(EXIT_FAILURE); | |
438 } | |
439 | |
440 // Make sure the SDK path provided is legit (or nil). | |
441 DTiPhoneSimulatorSystemRoot* systemRoot = BuildSystemRoot(sdkVersion); | |
442 if (!systemRoot) { | |
443 LogError(@"Invalid SDK version: %@", sdkVersion); | |
444 exit(EXIT_FAILURE); | |
445 } | |
446 | |
447 // Get the paths for stdout and stderr so the simulated app's output will show | |
448 // up in the caller's stdout/stderr. | |
449 NSString* outputDir = CreateTempDirectory(@"iossim-XXXXXX"); | |
450 NSString* stdioPath = [outputDir stringByAppendingPathComponent:@"stdio.txt"]; | |
451 | |
452 // Make sure the device name is legit. | |
453 NSNumber* deviceFamily = nil; | |
454 if (!deviceName || | |
455 [@"iPhone" caseInsensitiveCompare:deviceName] == NSOrderedSame) { | |
456 deviceFamily = [NSNumber numberWithInt:kIPhoneFamily]; | |
457 } else if ([@"iPad" caseInsensitiveCompare:deviceName] == NSOrderedSame) { | |
458 deviceFamily = [NSNumber numberWithInt:kIPadFamily]; | |
459 } else { | |
460 LogError(@"Invalid device name: %@", deviceName); | |
461 exit(EXIT_FAILURE); | |
462 } | |
463 | |
464 // Set up the user home directory for the simulator | |
465 if (!simHomePath) { | |
466 NSString* dirNameTemplate = | |
467 [NSString stringWithFormat:@"iossim-%@-%@-XXXXXX", appName, deviceName]; | |
468 simHomePath = CreateTempDirectory(dirNameTemplate); | |
469 if (!simHomePath) { | |
470 LogError(@"Unable to create unique directory for template %@", | |
471 dirNameTemplate); | |
stuartmorgan
2012/07/20 07:45:44
Indent 1 more
lliabraa
2012/07/26 19:14:11
Done.
| |
472 exit(EXIT_FAILURE); | |
473 } | |
474 } | |
475 if (!InitializeSimulatorUserHome(simHomePath)) { | |
476 LogError(@"Unable to initialize home directory for simulator: %@", | |
477 simHomePath); | |
stuartmorgan
2012/07/20 07:45:44
Indent 1 more
lliabraa
2012/07/26 19:14:11
Done.
| |
478 exit(EXIT_FAILURE); | |
479 } | |
480 | |
481 // Create the config and simulator session. | |
482 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec, | |
483 systemRoot, | |
484 stdioPath, | |
485 stdioPath, | |
486 appArgs, | |
487 deviceFamily); | |
488 SimulatorDelegate* delegate = | |
489 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath] autorelease]; | |
490 DTiPhoneSimulatorSession* session = BuildSession(delegate); | |
491 | |
492 // Start the simulator session. | |
493 NSError* error; | |
494 BOOL started = [session requestStartWithConfig:config | |
495 timeout:kSessionStartTimeout | |
496 error:&error]; | |
497 | |
498 // Spin the runtime indefinitely. When the delegate gets the message that the | |
499 // app has quit it will exit this program. | |
500 if (started) { | |
501 [[NSRunLoop mainRunLoop] run]; | |
502 } else { | |
503 LogError(@"Simulator failed to start: %@", [error localizedDescription]); | |
504 } | |
stuartmorgan
2012/07/20 07:45:44
Remove {}s on both
lliabraa
2012/07/26 19:14:11
Done.
| |
505 | |
506 // Note that this code is only executed if the simulator fails to start | |
507 // because once the main run loop is started, only the delegate calling | |
508 // exit() will end the program. | |
509 [pool drain]; | |
510 return EXIT_FAILURE; | |
511 } | |
OLD | NEW |