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 \ | |
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"]; | |
263 if ([devDir length] > 0) { | |
264 return devDir; | |
265 } | |
266 | |
267 // Go look for it via xcode-select. | |
268 NSTask* task; | |
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]; | |
274 [task setStandardOutput: pipe]; | |
275 NSFileHandle* file = [pipe fileHandleForReading]; | |
276 | |
277 [task launch]; | |
278 NSData* output = [file readDataToEndOfFile]; | |
279 [task terminate]; | |
280 | |
281 NSString* outputStr = | |
282 [[[NSString alloc] initWithData:output | |
lliabraa
2012/07/30 17:18:51
indent by 4
TVL
2012/07/30 17:23:05
Done.
| |
283 encoding:NSUTF8StringEncoding] autorelease]; | |
284 NSCharacterSet* wsNLSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; | |
285 outputStr = [outputStr stringByTrimmingCharactersInSet:wsNLSet]; | |
286 if ([outputStr length] == 0) { | |
287 outputStr = nil; | |
288 } | |
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. | |
296 NSString* devToolsFoundationPath = | |
lliabraa
2012/07/30 17:18:51
indent by 4
TVL
2012/07/30 17:23:05
Done.
| |
297 [developerDir stringByAppendingPathComponent:kDevToolsFoundationPathLeaf]; | |
298 NSBundle* devToolsFoundationBundle = | |
299 [NSBundle bundleWithPath:devToolsFoundationPath]; | |
lliabraa
2012/07/30 17:18:51
indent by 4
TVL
2012/07/30 17:23:05
Done.
| |
300 if (![devToolsFoundationBundle load]) | |
301 return nil; | |
302 NSString* simBundlePath = | |
303 [developerDir stringByAppendingPathComponent:kSimulatorFrameworkPathLeaf]; | |
lliabraa
2012/07/30 17:18:51
indent by 4
TVL
2012/07/30 17:23:05
Done.
| |
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) { | |
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"); | |
lliabraa
2012/07/30 17:18:51
indent by 4
TVL
2012/07/30 17:23:05
Done.
| |
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 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
449 appName = [appPath lastPathComponent]; | 521 appName = [appPath lastPathComponent]; |
450 while (++optind < argc) { | 522 while (++optind < argc) { |
451 [appArgs addObject:[NSString stringWithUTF8String:argv[optind]]]; | 523 [appArgs addObject:[NSString stringWithUTF8String:argv[optind]]]; |
452 } | 524 } |
453 } else { | 525 } else { |
454 LogError(@"Unable to parse command line arguments."); | 526 LogError(@"Unable to parse command line arguments."); |
455 PrintUsage(); | 527 PrintUsage(); |
456 exit(EXIT_FAILURE); | 528 exit(EXIT_FAILURE); |
457 } | 529 } |
458 | 530 |
531 // Figure out the developer dir. | |
532 NSString* developerDir = FindDeveloperDir(); | |
533 if (!developerDir) { | |
534 LogError(@"Unable to find developer directory."); | |
535 exit(EXIT_FAILURE); | |
536 } | |
537 | |
538 // Load the Simulator Client framework. | |
539 NSBundle* simulatorFramework = LoadSimulatorFramework(developerDir); | |
540 if (!simulatorFramework) { | |
541 LogError(@"Failed to load the Simulator Framework."); | |
542 exit(EXIT_FAILURE); | |
543 } | |
544 | |
459 // Make sure the app path provided is legit. | 545 // Make sure the app path provided is legit. |
460 DTiPhoneSimulatorApplicationSpecifier* appSpec = BuildAppSpec(appPath); | 546 DTiPhoneSimulatorApplicationSpecifier* appSpec = BuildAppSpec(appPath); |
461 if (!appSpec) { | 547 if (!appSpec) { |
462 LogError(@"Invalid app path: %@", appPath); | 548 LogError(@"Invalid app path: %@", appPath); |
463 exit(EXIT_FAILURE); | 549 exit(EXIT_FAILURE); |
464 } | 550 } |
465 | 551 |
466 // Make sure the SDK path provided is legit (or nil). | 552 // Make sure the SDK path provided is legit (or nil). |
467 DTiPhoneSimulatorSystemRoot* systemRoot = BuildSystemRoot(sdkVersion); | 553 DTiPhoneSimulatorSystemRoot* systemRoot = BuildSystemRoot(sdkVersion); |
468 if (!systemRoot) { | 554 if (!systemRoot) { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
506 | 592 |
507 // Create the config and simulator session. | 593 // Create the config and simulator session. |
508 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec, | 594 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec, |
509 systemRoot, | 595 systemRoot, |
510 stdioPath, | 596 stdioPath, |
511 stdioPath, | 597 stdioPath, |
512 appArgs, | 598 appArgs, |
513 appEnv, | 599 appEnv, |
514 deviceFamily); | 600 deviceFamily); |
515 SimulatorDelegate* delegate = | 601 SimulatorDelegate* delegate = |
516 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath] autorelease]; | 602 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath] autorelease]; |
517 DTiPhoneSimulatorSession* session = BuildSession(delegate); | 603 DTiPhoneSimulatorSession* session = BuildSession(delegate); |
518 | 604 |
519 // Start the simulator session. | 605 // Start the simulator session. |
520 NSError* error; | 606 NSError* error; |
521 BOOL started = [session requestStartWithConfig:config | 607 BOOL started = [session requestStartWithConfig:config |
522 timeout:kSessionStartTimeoutSeconds | 608 timeout:kSessionStartTimeoutSeconds |
523 error:&error]; | 609 error:&error]; |
524 | 610 |
525 // Spin the runtime indefinitely. When the delegate gets the message that the | 611 // Spin the runtime indefinitely. When the delegate gets the message that the |
526 // app has quit it will exit this program. | 612 // app has quit it will exit this program. |
527 if (started) | 613 if (started) |
528 [[NSRunLoop mainRunLoop] run]; | 614 [[NSRunLoop mainRunLoop] run]; |
529 else | 615 else |
530 LogError(@"Simulator failed to start: %@", [error localizedDescription]); | 616 LogError(@"Simulator failed to start: %@", [error localizedDescription]); |
531 | 617 |
532 // Note that this code is only executed if the simulator fails to start | 618 // 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 | 619 // because once the main run loop is started, only the delegate calling |
534 // exit() will end the program. | 620 // exit() will end the program. |
535 [pool drain]; | 621 [pool drain]; |
536 return EXIT_FAILURE; | 622 return EXIT_FAILURE; |
537 } | 623 } |
OLD | NEW |