Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(78)

Side by Side Diff: testing/iossim/iossim.mm

Issue 10828070: Don't directly link to the private Xcode framework, instead find it at runtime (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « testing/iossim/iossim.gyp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
63 // If this timeout occurs iossim will likely exit with non-zero status; the 63 // If this timeout occurs iossim will likely exit with non-zero status; the
64 // exception being if the app is invoked and completes execution before the 64 // exception being if the app is invoked and completes execution before the
65 // session is started (this case is handled in session:didStart:withError). 65 // session is started (this case is handled in session:didStart:withError).
66 const NSTimeInterval kSessionStartTimeoutSeconds = 30; 66 const NSTimeInterval kSessionStartTimeoutSeconds = 30;
67 67
68 // While the simulated app is running, its stdout is redirected to a file which 68 // While the simulated app is running, its stdout is redirected to a file which
69 // is polled by iossim and written to iossim's stdout using the following 69 // is polled by iossim and written to iossim's stdout using the following
70 // polling interval. 70 // polling interval.
71 const NSTimeInterval kOutputPollIntervalSeconds = 0.1; 71 const NSTimeInterval kOutputPollIntervalSeconds = 0.1;
72 72
73 // The path within the developer dir of the private Simulator frameworks.
74 NSString * const kSimulatorFrameworkRelativePath =
stuartmorgan 2012/07/30 21:12:25 NSString* to match the rest of the file.
TVL 2012/07/30 21:17:28 Done.
75 @"Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/iPh oneSimulatorRemoteClient.framework";
76 NSString * const kDevToolsFoundationRelativePath =
77 @"../OtherFrameworks/DevToolsFoundation.framework";
78
73 const char* gToolName = "iossim"; 79 const char* gToolName = "iossim";
74 80
75 void LogError(NSString* format, ...) { 81 void LogError(NSString* format, ...) {
76 va_list list; 82 va_list list;
77 va_start(list, format); 83 va_start(list, format);
78 84
79 NSString* message = 85 NSString* message =
80 [[[NSString alloc] initWithFormat:format arguments:list] autorelease]; 86 [[[NSString alloc] initWithFormat:format arguments:list] autorelease];
81 87
82 fprintf(stderr, "%s: ERROR: %s\n", gToolName, [message UTF8String]); 88 fprintf(stderr, "%s: ERROR: %s\n", gToolName, [message UTF8String]);
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
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* developerDir = [env objectForKey:@"DEVELOPER_DIR"];
263 if ([developerDir length] > 0)
264 return developerDir;
265
266 // Go look for it via xcode-select.
267 NSTask* xcodeSelectTask = [[[NSTask alloc] init] autorelease];
268 [xcodeSelectTask setLaunchPath:@"/usr/bin/xcode-select"];
269 [xcodeSelectTask setArguments:[NSArray arrayWithObject:@"-print-path"]];
270
271 NSPipe* outputPipe = [NSPipe pipe];
272 [xcodeSelectTask setStandardOutput:outputPipe];
273 NSFileHandle* outputFile = [outputPipe fileHandleForReading];
274
275 [xcodeSelectTask launch];
276 NSData* outputData = [outputFile readDataToEndOfFile];
277 [xcodeSelectTask terminate];
278
279 NSString* output =
280 [[[NSString alloc] initWithData:outputData
281 encoding:NSUTF8StringEncoding] autorelease];
282 output =
283 [output stringByTrimmingCharactersInSet:
stuartmorgan 2012/07/30 21:12:25 Pull this up to the previous line (and then just i
TVL 2012/07/30 21:17:28 Done.
284 [NSCharacterSet whitespaceAndNewlineCharacterSet]];
285 if ([output length] == 0)
286 output = nil;
287 return output;
288 }
289
290 // Loads the Simulator framework from the given developer dir.
291 NSBundle* LoadSimulatorFramework(NSString* developerDir) {
292 // The Simulator framework depends on some of the other Xcode private
293 // frameworks; manually load them first so everything can be linked up.
294 NSString* devToolsFoundationPath =
295 [developerDir
stuartmorgan 2012/07/30 21:12:25 Same
TVL 2012/07/30 21:17:28 Done.
296 stringByAppendingPathComponent:kDevToolsFoundationRelativePath];
297 NSBundle* devToolsFoundationBundle =
298 [NSBundle bundleWithPath:devToolsFoundationPath];
299 if (![devToolsFoundationBundle load])
300 return nil;
301 NSString* simBundlePath =
302 [developerDir
stuartmorgan 2012/07/30 21:12:25 Same
TVL 2012/07/30 21:17:28 Done.
303 stringByAppendingPathComponent:kSimulatorFrameworkRelativePath];
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* nameOfClass) {
312 Class theClass = NSClassFromString(nameOfClass);
313 if (!theClass) {
314 LogError(@"Failed to find class %@ at runtime.", nameOfClass);
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
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
395 char* toolName = basename(argv[0]); 467 // basename() may modify the passed in string and it returns a pointer to an
396 if (toolName != NULL) 468 // internal buffer. Give it a copy to modify, and copy what it returns.
397 gToolName = toolName; 469 char* worker = strdup(argv[0]);
470 char* toolName = basename(worker);
471 if (toolName != NULL) {
472 toolName = strdup(toolName);
473 if (toolName != NULL)
474 gToolName = toolName;
475 }
476 if (worker != NULL)
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
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 NSString* developerDir = FindDeveloperDir();
540 if (!developerDir) {
541 LogError(@"Unable to find developer directory.");
542 exit(EXIT_FAILURE);
543 }
544
545 NSBundle* simulatorFramework = LoadSimulatorFramework(developerDir);
546 if (!simulatorFramework) {
547 LogError(@"Failed to load the Simulator Framework.");
548 exit(EXIT_FAILURE);
549 }
550
459 // Make sure the app path provided is legit. 551 // Make sure the app path provided is legit.
460 DTiPhoneSimulatorApplicationSpecifier* appSpec = BuildAppSpec(appPath); 552 DTiPhoneSimulatorApplicationSpecifier* appSpec = BuildAppSpec(appPath);
461 if (!appSpec) { 553 if (!appSpec) {
462 LogError(@"Invalid app path: %@", appPath); 554 LogError(@"Invalid app path: %@", appPath);
463 exit(EXIT_FAILURE); 555 exit(EXIT_FAILURE);
464 } 556 }
465 557
466 // Make sure the SDK path provided is legit (or nil). 558 // Make sure the SDK path provided is legit (or nil).
467 DTiPhoneSimulatorSystemRoot* systemRoot = BuildSystemRoot(sdkVersion); 559 DTiPhoneSimulatorSystemRoot* systemRoot = BuildSystemRoot(sdkVersion);
468 if (!systemRoot) { 560 if (!systemRoot) {
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
506 598
507 // Create the config and simulator session. 599 // Create the config and simulator session.
508 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec, 600 DTiPhoneSimulatorSessionConfig* config = BuildSessionConfig(appSpec,
509 systemRoot, 601 systemRoot,
510 stdioPath, 602 stdioPath,
511 stdioPath, 603 stdioPath,
512 appArgs, 604 appArgs,
513 appEnv, 605 appEnv,
514 deviceFamily); 606 deviceFamily);
515 SimulatorDelegate* delegate = 607 SimulatorDelegate* delegate =
516 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath] autorelease]; 608 [[[SimulatorDelegate alloc] initWithStdioPath:stdioPath] autorelease];
517 DTiPhoneSimulatorSession* session = BuildSession(delegate); 609 DTiPhoneSimulatorSession* session = BuildSession(delegate);
518 610
519 // Start the simulator session. 611 // Start the simulator session.
520 NSError* error; 612 NSError* error;
521 BOOL started = [session requestStartWithConfig:config 613 BOOL started = [session requestStartWithConfig:config
522 timeout:kSessionStartTimeoutSeconds 614 timeout:kSessionStartTimeoutSeconds
523 error:&error]; 615 error:&error];
524 616
525 // Spin the runtime indefinitely. When the delegate gets the message that the 617 // Spin the runtime indefinitely. When the delegate gets the message that the
526 // app has quit it will exit this program. 618 // app has quit it will exit this program.
527 if (started) 619 if (started)
528 [[NSRunLoop mainRunLoop] run]; 620 [[NSRunLoop mainRunLoop] run];
529 else 621 else
530 LogError(@"Simulator failed to start: %@", [error localizedDescription]); 622 LogError(@"Simulator failed to start: %@", [error localizedDescription]);
531 623
532 // Note that this code is only executed if the simulator fails to start 624 // 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 625 // because once the main run loop is started, only the delegate calling
534 // exit() will end the program. 626 // exit() will end the program.
535 [pool drain]; 627 [pool drain];
536 return EXIT_FAILURE; 628 return EXIT_FAILURE;
537 } 629 }
OLDNEW
« no previous file with comments | « testing/iossim/iossim.gyp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698