从 Cocoa 应用程序(通过 NSTask)静默运行 xcodebuild 两次失败
Posted
技术标签:
【中文标题】从 Cocoa 应用程序(通过 NSTask)静默运行 xcodebuild 两次失败【英文标题】:Running xcodebuild twice from a Cocoa app (via NSTask) silently fails 【发布时间】:2012-02-27 20:52:18 【问题描述】:我有一个 Cocoa 应用程序,它可以做很多事情,但其中使用“xcodebuild
”在 XCode 中清理和重新构建一个 ios 项目。 Cocoa 应用程序是使用 XCode 4.1 开发的,并且仅在 OS X 10.7 上运行(因为 NSTask 终止处理程序)。我已经有几个月没有碰它了,在我升级到 XCode 4.2 之后,我发现只有第一个运行 xcodebuild 的 NSTask 真正做了任何事情。后续调用什么都不做。
一些细节:
正在构建的 iOS 项目位于 Cocoa 应用程序的资源包中。所以基本上,Cocoa 应用程序是一个 iOS 项目的包装器。想象一个带有红色大按钮的 GUI,上面写着:“为我构建一个 iOS 应用!” 此代码在 XCode 4.1 中完美运行。任务 1 执行,然后在任务 1 的终止处理程序中,任务 2 被调用,并且运行得很好。问题是任务 2 根本不再运行。 我已经升级到 xcode 4.3,这要求我还安装独立的 Xcode 命令行工具。代码仍然无法正常工作。 我可以毫无问题地在终端中运行“xcodebuild clean
”和“xcodebuild
”命令。
以下是违规代码的(转述)版本。
#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
所以只是重复一遍:任务 1(清洁)工作正常。任务 2(构建)甚至没有启动。仅在升级到 XCode 4.2 后才会观察到此行为。我一定是做错了什么,但是什么?
【问题讨论】:
确实如此。实际上,我刚刚注意到任务 1 的终止处理程序从未运行。 ... Aaand 经过一番搜索,我找到了答案。现在就写吧。 【参考方案1】:好像我在 Xcode 中遇到了一个错误,类似于以下内容: NSTask NSPipe - objective c command line help
基本上,如果 NSTask 已经启动过一次,Xcode 将保留 NSTask 的输出。
解决方案是这样切换任务的标准输入:
[task setStandardInput:[NSPipe pipe]];
我将它放入 runTaskOne 方法后,一切都开始运行良好。
【讨论】:
【参考方案2】:如果您的项目使用 ARC(自动引用计数),NSTask
实例会在 runTaskOne
方法结束时自动释放。所以完成处理程序永远不会被调用,因为应该观察进程的对象不再存在。
您需要通过将任务存储在实例变量中来保留对任务的引用。
【讨论】:
感谢尼古拉斯的回复。我发现通过将管道作为标准输入添加到任务中,例如:[task setStandardInput:[NSPipe pipe]];
可以解决问题。终止处理程序已运行,一切正常。以上是关于从 Cocoa 应用程序(通过 NSTask)静默运行 xcodebuild 两次失败的主要内容,如果未能解决你的问题,请参考以下文章