简体   繁体   中英

Running xcodebuild twice from a Cocoa app (via NSTask) silently fails

I have a Cocoa app that does a number of things, but among them, uses ' xcodebuild ' to clean and re-build a iOS project in XCode. The Cocoa app was developed using XCode 4.1, and runs only on OS X 10.7 (because of the NSTask termination handlers). I hadn't touched it for a few months, and after I had upgraded to XCode 4.2 I found that only the first NSTask that ran xcodebuild actually did anything. Subsequent calls do nothing.

Some details:

  • The iOS project being built sits within the resource bundle of the Cocoa app. So basically, the Cocoa app is a wrapper for an iOS project. Imagine a GUI with a big red button that says: "Build me an iOS app!"
  • This code worked perfectly in XCode 4.1. Task 1 executed, then in the termination handler for task 1, task 2 was called, and ran just fine. The problem is that task 2 no longer runs at all.
  • I have since upgraded to xcode 4.3, which required me to also install the standalone Xcode command line tools. The code still doesn't work properly though.
  • I can run both the ' xcodebuild clean ' and ' xcodebuild ' commands in Terminal with no problems.

Below is a (paraphrased) version of the offending code.

#import "AppDelegate.h"

@implementation AppDelegate
@synthesize window = _window;

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
    [self runTaskOne];
}

// Clean the iOS Project
- (void) runTaskOne
{
NSTask *task = [[NSTask alloc] init];
[task setCurrentDirectoryPath:@".../myproject/DerivedData/myproject/Build/Products/Debug/myproject.app/Contents/Resources/iOSProjectFolder"];
[task setLaunchPath:@"/usr/bin/xcodebuild"];
[task setArguments:[NSArray arrayWithObjects:@"clean", nil]];
[task setTerminationHandler:^(NSTask *task)
 {
     [self runTaskTwo];
 }];
[task launch];

}

// Build the same iOS project that was just cleaned
- (void) runTaskTwo;
{
NSTask *task = [[NSTask alloc] init];
[task setCurrentDirectoryPath:@".../myproject/DerivedData/myproject/Build/Products/Debug/myproject.app/Contents/Resources/iOSProjectFolder"];
[task setLaunchPath:@"/usr/bin/xcodebuild"];
[task launch];
} 

@end

So just to re-iterate: Task 1 (the clean) works fine. Task 2 (the build) doesn't even seem launch. This behaviour is only observed after upgrading to XCode 4.2. I must be doing something wrong, but what?

It seems as though I ran into a bug in Xcode, something similar to this: NSTask NSPipe - objective c command line help

Basically, Xcode will hold on to the output of the NSTask if NSTask has already been launched once.

The solution was to switch the Task's standard input thusly:

[task setStandardInput:[NSPipe pipe]];

As soon as I put that into the runTaskOne method, everything started working nicely.

If your project uses ARC (Automatic Reference Counting), the NSTask instance is automatically released at the end of the runTaskOne method. So the completion handler is never invoked because the object that is supposed to observe the process no longer exists.

You need to keep a reference to the task by storing it in an instance variable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM