OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import <Foundation/Foundation.h> | 5 #import <Foundation/Foundation.h> |
6 #include <asl.h> | 6 #include <asl.h> |
7 #include <libgen.h> | 7 #include <libgen.h> |
8 #include <stdarg.h> | 8 #include <stdarg.h> |
9 #include <stdio.h> | 9 #include <stdio.h> |
10 | 10 |
(...skipping 25 matching lines...) Expand all Loading... | |
36 didStart:(BOOL)started | 36 didStart:(BOOL)started |
37 withError:(NSError*)error; | 37 withError:(NSError*)error; |
38 @end | 38 @end |
39 | 39 |
40 #import "iPhoneSimulatorRemoteClient.h" | 40 #import "iPhoneSimulatorRemoteClient.h" |
41 | 41 |
42 // An undocumented system log key included in messages from launchd. The value | 42 // An undocumented system log key included in messages from launchd. The value |
43 // is the PID of the process the message is about (as opposed to launchd's PID). | 43 // is the PID of the process the message is about (as opposed to launchd's PID). |
44 #define ASL_KEY_REF_PID "RefPID" | 44 #define ASL_KEY_REF_PID "RefPID" |
45 | 45 |
46 // The path within the developer dir of the private Simulator frameworks. | |
47 #define kSimulatorFrameworkPathLeaf \ | |
stuartmorgan
2012/07/30 19:34:21
s/PathLeaf/RelativePath/ ? PathLeaf sounds like it
TVL
2012/07/30 20:10:17
Done.
| |
48 @"Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/iPhon eSimulatorRemoteClient.framework" | |
49 #define kDevToolsFoundationPathLeaf \ | |
50 @"../OtherFrameworks/DevToolsFoundation.framework" | |
51 | |
46 namespace { | 52 namespace { |
47 | 53 |
48 // Name of environment variables that control the user's home directory in the | 54 // Name of environment variables that control the user's home directory in the |
49 // simulator. | 55 // simulator. |
50 const char* const kUserHomeEnvVariable = "CFFIXED_USER_HOME"; | 56 const char* const kUserHomeEnvVariable = "CFFIXED_USER_HOME"; |
51 const char* const kHomeEnvVariable = "HOME"; | 57 const char* const kHomeEnvVariable = "HOME"; |
52 | 58 |
53 // Device family codes for iPhone and iPad. | 59 // Device family codes for iPhone and iPad. |
54 const int kIPhoneFamily = 1; | 60 const int kIPhoneFamily = 1; |
55 const int kIPadFamily = 2; | 61 const int kIPadFamily = 2; |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
241 if (entryFound) { | 247 if (entryFound) { |
242 LogError(@"Simulated app crashed or exited with non-zero status"); | 248 LogError(@"Simulated app crashed or exited with non-zero status"); |
243 exit(EXIT_FAILURE); | 249 exit(EXIT_FAILURE); |
244 } | 250 } |
245 exit(EXIT_SUCCESS); | 251 exit(EXIT_SUCCESS); |
246 } | 252 } |
247 @end | 253 @end |
248 | 254 |
249 namespace { | 255 namespace { |
250 | 256 |
257 // Finds the developer dir via xcode-select or the DEVELOPER_DIR environment | |
258 // variable. | |
259 NSString* FindDeveloperDir() { | |
260 // Check the env first. | |
261 NSDictionary* env = [[NSProcessInfo processInfo] environment]; | |
262 NSString* devDir = [env objectForKey:@"DEVELOPER_DIR"]; | |
stuartmorgan
2012/07/30 19:34:21
developerDir
TVL
2012/07/30 20:10:17
Done.
| |
263 if ([devDir length] > 0) { | |
264 return devDir; | |
265 } | |
stuartmorgan
2012/07/30 19:34:21
No {} for one-line statements, per Chromium style.
TVL
2012/07/30 20:10:17
Done.
| |
266 | |
267 // Go look for it via xcode-select. | |
268 NSTask* task; | |
stuartmorgan
2012/07/30 19:34:21
xcodeSelectTask
TVL
2012/07/30 20:10:17
Done.
| |
269 task = [[[NSTask alloc] init] autorelease]; | |
270 [task setLaunchPath:@"/usr/bin/xcode-select"]; | |
271 [task setArguments:[NSArray arrayWithObject:@"-print-path"]]; | |
272 | |
273 NSPipe* pipe = [NSPipe pipe]; | |
stuartmorgan
2012/07/30 19:34:21
outputPipe
TVL
2012/07/30 20:10:17
Done.
| |
274 [task setStandardOutput: pipe]; | |
275 NSFileHandle* file = [pipe fileHandleForReading]; | |
stuartmorgan
2012/07/30 19:34:21
outputFile
TVL
2012/07/30 20:10:17
Done.
| |
276 | |
277 [task launch]; | |
278 NSData* output = [file readDataToEndOfFile]; | |
279 [task terminate]; | |
280 | |
281 NSString* outputStr = | |
stuartmorgan
2012/07/30 19:34:21
Seems like outputData and then output would make m
TVL
2012/07/30 20:10:17
Done.
| |
282 [[[NSString alloc] initWithData:output | |
283 encoding:NSUTF8StringEncoding] autorelease]; | |
284 NSCharacterSet* wsNLSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; | |
stuartmorgan
2012/07/30 19:34:21
Ugh. Inlining the char set would be much less ugly
TVL
2012/07/30 20:10:17
Done.
| |
285 outputStr = [outputStr stringByTrimmingCharactersInSet:wsNLSet]; | |
286 if ([outputStr length] == 0) { | |
287 outputStr = nil; | |
288 } | |
stuartmorgan
2012/07/30 19:34:21
No {}
TVL
2012/07/30 20:10:17
Done.
| |
289 return outputStr; | |
290 } | |
291 | |
292 // Loads the Simulator framework from the given developer dir. | |
293 NSBundle* LoadSimulatorFramework(NSString* developerDir) { | |
294 // The Simulator framework depends on some of the other Xcode private | |
295 // frameworks, manually load them first so everything can be linked up. | |
stuartmorgan
2012/07/30 19:34:21
s/,/;/
TVL
2012/07/30 20:10:17
Done.
| |
296 NSString* devToolsFoundationPath = | |
297 [developerDir stringByAppendingPathComponent:kDevToolsFoundationPathLeaf]; | |
298 NSBundle* devToolsFoundationBundle = | |
299 [NSBundle bundleWithPath:devToolsFoundationPath]; | |
300 if (![devToolsFoundationBundle load]) | |
301 return nil; | |
302 NSString* simBundlePath = | |
303 [developerDir stringByAppendingPathComponent:kSimulatorFrameworkPathLeaf]; | |
304 NSBundle* simBundle = [NSBundle bundleWithPath:simBundlePath]; | |
305 if (![simBundle load]) | |
306 return nil; | |
307 return simBundle; | |
308 } | |
309 | |
310 // Helper to find a class by name and die if it isn't found. | |
311 Class FindClassByName(NSString* clazzName) { | |
stuartmorgan
2012/07/30 19:34:21
Is className as an identifier really protected? I
TVL
2012/07/30 20:10:17
I thought i'd hit it before. renamed.
| |
312 Class theClass = NSClassFromString(clazzName); | |
313 if (!theClass) { | |
314 LogError(@"Failed to find class %@ at runtime.", clazzName); | |
315 exit(EXIT_FAILURE); | |
316 } | |
317 return theClass; | |
318 } | |
319 | |
251 // Converts the given app path to an application spec, which requires an | 320 // Converts the given app path to an application spec, which requires an |
252 // absolute path. | 321 // absolute path. |
253 DTiPhoneSimulatorApplicationSpecifier* BuildAppSpec(NSString* appPath) { | 322 DTiPhoneSimulatorApplicationSpecifier* BuildAppSpec(NSString* appPath) { |
323 Class applicationSpecifierClass = | |
324 FindClassByName(@"DTiPhoneSimulatorApplicationSpecifier"); | |
254 if (![appPath isAbsolutePath]) { | 325 if (![appPath isAbsolutePath]) { |
255 NSString* cwd = [[NSFileManager defaultManager] currentDirectoryPath]; | 326 NSString* cwd = [[NSFileManager defaultManager] currentDirectoryPath]; |
256 appPath = [cwd stringByAppendingPathComponent:appPath]; | 327 appPath = [cwd stringByAppendingPathComponent:appPath]; |
257 } | 328 } |
258 appPath = [appPath stringByStandardizingPath]; | 329 appPath = [appPath stringByStandardizingPath]; |
259 return [DTiPhoneSimulatorApplicationSpecifier | 330 return [applicationSpecifierClass specifierWithApplicationPath:appPath]; |
260 specifierWithApplicationPath:appPath]; | |
261 } | 331 } |
262 | 332 |
263 // Returns the system root for the given SDK version. If sdkVersion is nil, the | 333 // Returns the system root for the given SDK version. If sdkVersion is nil, the |
264 // default system root is returned. Will return nil if the sdkVersion is not | 334 // default system root is returned. Will return nil if the sdkVersion is not |
265 // valid. | 335 // valid. |
266 DTiPhoneSimulatorSystemRoot* BuildSystemRoot(NSString* sdkVersion) { | 336 DTiPhoneSimulatorSystemRoot* BuildSystemRoot(NSString* sdkVersion) { |
267 DTiPhoneSimulatorSystemRoot* systemRoot = | 337 Class systemRootClass = FindClassByName(@"DTiPhoneSimulatorSystemRoot"); |
268 [DTiPhoneSimulatorSystemRoot defaultRoot]; | 338 DTiPhoneSimulatorSystemRoot* systemRoot = [systemRootClass defaultRoot]; |
269 if (sdkVersion) | 339 if (sdkVersion) |
270 systemRoot = [DTiPhoneSimulatorSystemRoot rootWithSDKVersion:sdkVersion]; | 340 systemRoot = [systemRootClass rootWithSDKVersion:sdkVersion]; |
271 | 341 |
272 return systemRoot; | 342 return systemRoot; |
273 } | 343 } |
274 | 344 |
275 // Builds a config object for starting the specified app. | 345 // Builds a config object for starting the specified app. |
276 DTiPhoneSimulatorSessionConfig* BuildSessionConfig( | 346 DTiPhoneSimulatorSessionConfig* BuildSessionConfig( |
277 DTiPhoneSimulatorApplicationSpecifier* appSpec, | 347 DTiPhoneSimulatorApplicationSpecifier* appSpec, |
278 DTiPhoneSimulatorSystemRoot* systemRoot, | 348 DTiPhoneSimulatorSystemRoot* systemRoot, |
279 NSString* stdoutPath, | 349 NSString* stdoutPath, |
280 NSString* stderrPath, | 350 NSString* stderrPath, |
281 NSArray* appArgs, | 351 NSArray* appArgs, |
282 NSDictionary* appEnv, | 352 NSDictionary* appEnv, |
283 NSNumber* deviceFamily) { | 353 NSNumber* deviceFamily) { |
354 Class sessionConfigClass = FindClassByName(@"DTiPhoneSimulatorSessionConfig"); | |
284 DTiPhoneSimulatorSessionConfig* sessionConfig = | 355 DTiPhoneSimulatorSessionConfig* sessionConfig = |
285 [[[DTiPhoneSimulatorSessionConfig alloc] init] autorelease]; | 356 [[[sessionConfigClass alloc] init] autorelease]; |
286 sessionConfig.applicationToSimulateOnStart = appSpec; | 357 sessionConfig.applicationToSimulateOnStart = appSpec; |
287 sessionConfig.simulatedSystemRoot = systemRoot; | 358 sessionConfig.simulatedSystemRoot = systemRoot; |
288 sessionConfig.localizedClientName = @"chromium"; | 359 sessionConfig.localizedClientName = @"chromium"; |
289 sessionConfig.simulatedApplicationStdErrPath = stderrPath; | 360 sessionConfig.simulatedApplicationStdErrPath = stderrPath; |
290 sessionConfig.simulatedApplicationStdOutPath = stdoutPath; | 361 sessionConfig.simulatedApplicationStdOutPath = stdoutPath; |
291 sessionConfig.simulatedApplicationLaunchArgs = appArgs; | 362 sessionConfig.simulatedApplicationLaunchArgs = appArgs; |
292 sessionConfig.simulatedApplicationLaunchEnvironment = appEnv; | 363 sessionConfig.simulatedApplicationLaunchEnvironment = appEnv; |
293 sessionConfig.simulatedDeviceFamily = deviceFamily; | 364 sessionConfig.simulatedDeviceFamily = deviceFamily; |
294 return sessionConfig; | 365 return sessionConfig; |
295 } | 366 } |
296 | 367 |
297 // Builds a simulator session that will use the given delegate. | 368 // Builds a simulator session that will use the given delegate. |
298 DTiPhoneSimulatorSession* BuildSession(SimulatorDelegate* delegate) { | 369 DTiPhoneSimulatorSession* BuildSession(SimulatorDelegate* delegate) { |
370 Class sessionClass = FindClassByName(@"DTiPhoneSimulatorSession"); | |
299 DTiPhoneSimulatorSession* session = | 371 DTiPhoneSimulatorSession* session = |
300 [[[DTiPhoneSimulatorSession alloc] init] autorelease]; | 372 [[[sessionClass alloc] init] autorelease]; |
301 session.delegate = delegate; | 373 session.delegate = delegate; |
302 return session; | 374 return session; |
303 } | 375 } |
304 | 376 |
305 // Creates a temporary directory with a unique name based on the provided | 377 // Creates a temporary directory with a unique name based on the provided |
306 // template. The template should not contain any path separators and be suffixed | 378 // template. The template should not contain any path separators and be suffixed |
307 // with X's, which will be substituted with a unique alphanumeric string (see | 379 // with X's, which will be substituted with a unique alphanumeric string (see |
308 // 'man mkdtemp' for details). The directory will be created as a subdirectory | 380 // 'man mkdtemp' for details). The directory will be created as a subdirectory |
309 // of NSTemporaryDirectory(). For example, if dirNameTemplate is 'test-XXX', | 381 // of NSTemporaryDirectory(). For example, if dirNameTemplate is 'test-XXX', |
310 // this method would return something like '/path/to/tempdir/test-3n2'. | 382 // this method would return something like '/path/to/tempdir/test-3n2'. |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
385 " Will create a new directory if not specified.\n" | 457 " Will create a new directory if not specified.\n" |
386 " -e Specifies an environment key=value pair that will be" | 458 " -e Specifies an environment key=value pair that will be" |
387 " set in the simulated application's environment.\n"); | 459 " set in the simulated application's environment.\n"); |
388 } | 460 } |
389 | 461 |
390 } // namespace | 462 } // namespace |
391 | 463 |
392 int main(int argc, char* const argv[]) { | 464 int main(int argc, char* const argv[]) { |
393 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; | 465 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; |
394 | 466 |
467 // basename() may modify the passed in string and it returns a pointer to an | |
468 // internal buffer. Give it a copy to modify, and copy what it returns. | |
stuartmorgan
2012/07/30 19:34:21
You say "give it a copy", but you don't give it th
TVL
2012/07/30 20:10:17
doh. fixed.
| |
469 char* worker = strdup(argv[0]); | |
395 char* toolName = basename(argv[0]); | 470 char* toolName = basename(argv[0]); |
396 if (toolName != NULL) | 471 if (toolName != NULL) { |
397 gToolName = toolName; | 472 toolName = strdup(toolName); |
473 if (toolName != NULL) | |
stuartmorgan
2012/07/30 19:34:21
Do we really need to handle OOM gracefully here? S
TVL
2012/07/30 20:10:17
I'll remove them if you want, was just trying to b
| |
474 gToolName = toolName; | |
475 } | |
476 if (worker != NULL) | |
stuartmorgan
2012/07/30 19:34:21
Same here.
| |
477 free(worker); | |
398 | 478 |
399 NSString* appPath = nil; | 479 NSString* appPath = nil; |
400 NSString* appName = nil; | 480 NSString* appName = nil; |
401 NSString* sdkVersion = nil; | 481 NSString* sdkVersion = nil; |
402 NSString* deviceName = @"iPhone"; | 482 NSString* deviceName = @"iPhone"; |
403 NSString* simHomePath = nil; | 483 NSString* simHomePath = nil; |
404 NSMutableArray* appArgs = [NSMutableArray array]; | 484 NSMutableArray* appArgs = [NSMutableArray array]; |
405 NSMutableDictionary* appEnv = [NSMutableDictionary dictionary]; | 485 NSMutableDictionary* appEnv = [NSMutableDictionary dictionary]; |
406 | 486 |
407 // Parse the optional arguments | 487 // Parse the optional arguments |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
449 appName = [appPath lastPathComponent]; | 529 appName = [appPath lastPathComponent]; |
450 while (++optind < argc) { | 530 while (++optind < argc) { |
451 [appArgs addObject:[NSString stringWithUTF8String:argv[optind]]]; | 531 [appArgs addObject:[NSString stringWithUTF8String:argv[optind]]]; |
452 } | 532 } |
453 } else { | 533 } else { |
454 LogError(@"Unable to parse command line arguments."); | 534 LogError(@"Unable to parse command line arguments."); |
455 PrintUsage(); | 535 PrintUsage(); |
456 exit(EXIT_FAILURE); | 536 exit(EXIT_FAILURE); |
457 } | 537 } |
458 | 538 |
539 // Figure out the developer dir. | |
stuartmorgan
2012/07/30 19:34:21
Seems like these two comments don't add much, sinc
TVL
2012/07/30 20:10:17
removed. Was just following the style of the next
| |
540 NSString* developerDir = FindDeveloperDir(); | |
541 if (!developerDir) { | |
542 LogError(@"Unable to find developer directory."); | |
543 exit(EXIT_FAILURE); | |
544 } | |
545 | |
546 // Load the Simulator Client framework. | |
547 NSBundle* simulatorFramework = LoadSimulatorFramework(developerDir); | |
548 if (!simulatorFramework) { | |
549 LogError(@"Failed to load the Simulator Framework."); | |
550 exit(EXIT_FAILURE); | |
551 } | |
552 | |
459 // Make sure the app path provided is legit. | 553 // Make sure the app path provided is legit. |
460 DTiPhoneSimulatorApplicationSpecifier* appSpec = BuildAppSpec(appPath); | 554 DTiPhoneSimulatorApplicationSpecifier* appSpec = BuildAppSpec(appPath); |
461 if (!appSpec) { | 555 if (!appSpec) { |
462 LogError(@"Invalid app path: %@", appPath); | 556 LogError(@"Invalid app path: %@", appPath); |
463 exit(EXIT_FAILURE); | 557 exit(EXIT_FAILURE); |
464 } | 558 } |
465 | 559 |
466 // Make sure the SDK path provided is legit (or nil). | 560 // Make sure the SDK path provided is legit (or nil). |
467 DTiPhoneSimulatorSystemRoot* systemRoot = BuildSystemRoot(sdkVersion); | 561 DTiPhoneSimulatorSystemRoot* systemRoot = BuildSystemRoot(sdkVersion); |
468 if (!systemRoot) { | 562 if (!systemRoot) { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
506 | 600 |
507 // Create the config and simulator session. | 601 // Create the config and simulator session. |
508 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec, | 602 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec, |
509 systemRoot, | 603 systemRoot, |
510 stdioPath, | 604 stdioPath, |
511 stdioPath, | 605 stdioPath, |
512 appArgs, | 606 appArgs, |
513 appEnv, | 607 appEnv, |
514 deviceFamily); | 608 deviceFamily); |
515 SimulatorDelegate* delegate = | 609 SimulatorDelegate* delegate = |
516 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath] autorelease]; | 610 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath] autorelease]; |
517 DTiPhoneSimulatorSession* session = BuildSession(delegate); | 611 DTiPhoneSimulatorSession* session = BuildSession(delegate); |
518 | 612 |
519 // Start the simulator session. | 613 // Start the simulator session. |
520 NSError* error; | 614 NSError* error; |
521 BOOL started = [session requestStartWithConfig:config | 615 BOOL started = [session requestStartWithConfig:config |
522 timeout:kSessionStartTimeoutSeconds | 616 timeout:kSessionStartTimeoutSeconds |
523 error:&error]; | 617 error:&error]; |
524 | 618 |
525 // Spin the runtime indefinitely. When the delegate gets the message that the | 619 // Spin the runtime indefinitely. When the delegate gets the message that the |
526 // app has quit it will exit this program. | 620 // app has quit it will exit this program. |
527 if (started) | 621 if (started) |
528 [[NSRunLoop mainRunLoop] run]; | 622 [[NSRunLoop mainRunLoop] run]; |
529 else | 623 else |
530 LogError(@"Simulator failed to start: %@", [error localizedDescription]); | 624 LogError(@"Simulator failed to start: %@", [error localizedDescription]); |
531 | 625 |
532 // Note that this code is only executed if the simulator fails to start | 626 // Note that this code is only executed if the simulator fails to start |
533 // because once the main run loop is started, only the delegate calling | 627 // because once the main run loop is started, only the delegate calling |
534 // exit() will end the program. | 628 // exit() will end the program. |
535 [pool drain]; | 629 [pool drain]; |
536 return EXIT_FAILURE; | 630 return EXIT_FAILURE; |
537 } | 631 } |
OLD | NEW |