Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 365 lines (324 sloc) 11.901 kb
27f8fa0 Colin Wheeler MIT License is on all source code files now
authored
1 /*
2010a62 Colin Wheeler adding CWTask to Zangetsu
authored
2 // CWTask.m
27f8fa0 Colin Wheeler MIT License is on all source code files now
authored
3 // Zangetsu
2010a62 Colin Wheeler adding CWTask to Zangetsu
authored
4 //
5 // Created by Colin Wheeler on 8/30/10.
6 // Copyright 2010. All rights reserved.
7 //
27f8fa0 Colin Wheeler MIT License is on all source code files now
authored
8
9 Copyright (c) 2011 Colin Wheeler
10
11 Permission is hereby granted, free of charge, to any person obtaining a copy
12 of this software and associated documentation files (the "Software"), to deal
13 in the Software without restriction, including without limitation the rights
14 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 copies of the Software, and to permit persons to whom the Software is
16 furnished to do so, subject to the following conditions:
17
18 The above copyright notice and this permission notice shall be included in
19 all copies or substantial portions of the Software.
20
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27 THE SOFTWARE.
28 */
2010a62 Colin Wheeler adding CWTask to Zangetsu
authored
29
b464885 Colin Wheeler CWTask now properly uses new Objective-C 2.0 in LLVM features to hide p...
authored
30 #import "CWTask.h"
9af7672 Colin Wheeler beginning to completely rewrite CWTask with a much better design
authored
31
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
32 @interface CWTask ()
33 // Publicly declared
34 @property (nonatomic, readwrite, retain) NSString * executable;
35 @property (nonatomic, readwrite, retain) NSArray * arguments;
36 @property (nonatomic, readwrite, retain) NSString * directoryPath;
37 @property (nonatomic, readwrite, assign) NSInteger successCode;
38 // Privately Declared
39 @property (nonatomic, readwrite, assign) BOOL taskHasRun;
40 @property (nonatomic, readwrite, assign) BOOL inAsynchronous;
41 @property (nonatomic, readwrite, retain) NSPipe * pipe;
42 @property (nonatomic, readwrite, retain) NSTask * cwTask;
43 // Private Methods
44 - (void) _configureTask;
45 - (BOOL) _validateTask:(NSError **)error;
46 - (void) _performPostRunActionsIfApplicable;
9085ed4 Colin Wheeler _resultsStringFromLaunchedTask now can write to an NSError to report exc...
authored
47 - (NSString *) _resultsStringFromLaunchedTask:(NSError **)error;
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
48 - (BOOL) _validateExecutable:(NSError **)error;
49 - (BOOL) _validateDirectoryPathIfApplicable:(NSError **)error;
50 - (BOOL) _validateTaskHasRun:(NSError **)error;
9af7672 Colin Wheeler beginning to completely rewrite CWTask with a much better design
authored
51 @end
2010a62 Colin Wheeler adding CWTask to Zangetsu
authored
52
53 @implementation CWTask
54
ea55540 Colin Wheeler making ivars private to access on CWTask
authored
55 @synthesize executable = _executable;
56 @synthesize arguments = _arguments;
57 @synthesize directoryPath = _directoryPath;
58 @synthesize successCode = _successCode;
59 @synthesize completionBlock = _completionBlock;
60 @synthesize taskHasRun = _taskHasRun;
61 @synthesize inAsynchronous = _inAsynchronous;
62 @synthesize cwTask = _cwTask;
63 @synthesize pipe = _pipe;
9af7672 Colin Wheeler beginning to completely rewrite CWTask with a much better design
authored
64
318b5e1 Colin Wheeler replacing all instances of '#pragma mark' with '//MARK:'
authored
65 //MARK: -
66 //MARK: Public API
2010a62 Colin Wheeler adding CWTask to Zangetsu
authored
67
fd3214b Colin Wheeler documenting CWTask and adding parameter asserts
authored
68 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
69 * designated initializer
70 *
71 * @param exec a string containing the full path to the executable to be launched
72 * @param execArgs a NSArray of NSString objects containing arguments for the executable (optional)
73 * @param path a string containing the full path you want the executable the be launched at (optional)
74 * @return a CWTask Object
fd3214b Colin Wheeler documenting CWTask and adding parameter asserts
authored
75 */
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
76 - (id) initWithExecutable:(NSString *)exec
687d02f Colin Wheeler minor formatting updates
authored
77 andArguments:(NSArray *)execArgs
78 atDirectory:(NSString *)path
79 {
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
80 self = [super init];
81 if (self) {
ea55540 Colin Wheeler making ivars private to access on CWTask
authored
82 _executable = exec;
83 _arguments = execArgs;
84 _directoryPath = path;
85 _successCode = kCWTaskNotLaunched;
86 _taskHasRun = NO;
87 _inAsynchronous = NO;
88 _cwTask = [[NSTask alloc] init];
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
89 }
90 return self;
e8f6d0a Colin Wheeler updating documentation on CWTask and updating _validateExecutable
authored
91 }
92
93 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
94 * default implementation so if someone calls this and then
95 * tries to launch the task the method will immediately see
96 * that executable == nil and therefore will return immediatly
97 * with an error about the executable.
98 *
99 * @return an invalid CWTask object
e8f6d0a Colin Wheeler updating documentation on CWTask and updating _validateExecutable
authored
100 */
9cdcd60 Colin Wheeler massive code cleanup
authored
101 - (id) init
102 {
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
103 self = [super init];
104 if (self) {
ea55540 Colin Wheeler making ivars private to access on CWTask
authored
105 _executable = nil;
106 _arguments = nil;
107 _directoryPath = nil;
108 _successCode = kCWTaskNotLaunched;
109 _taskHasRun = NO;
110 _inAsynchronous = NO;
111 _cwTask = nil;
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
112 }
113 return self;
2010a62 Colin Wheeler adding CWTask to Zangetsu
authored
114 }
115
6a1d3fb Colin Wheeler more detailed documentation enhanced for Doxygen in CWTask
authored
116 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
117 * Description for debug information
6a1d3fb Colin Wheeler more detailed documentation enhanced for Doxygen in CWTask
authored
118 */
687d02f Colin Wheeler minor formatting updates
authored
119 - (NSString *) description
120 {
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
121 NSString * desc = [NSString stringWithFormat:@"CWTask::Executable('%@')\nArguements: %@\nDirectory Path:%@",
ea55540 Colin Wheeler making ivars private to access on CWTask
authored
122 _executable, _arguments, _directoryPath];
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
123
124 return desc;
1103a5e Colin Wheeler adding a description method to CWTask
authored
125 }
126
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
127 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
128 * Any arguments to the task are set here
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
129 */
687d02f Colin Wheeler minor formatting updates
authored
130 - (void) _configureTask
131 {
571f687 Colin Wheeler refactoring code in CWTask
authored
132 [[self cwTask] setLaunchPath:[self executable]];
133 [self setPipe:[NSPipe pipe]];
134 [[self cwTask] setStandardOutput:[self pipe]];
ea55540 Colin Wheeler making ivars private to access on CWTask
authored
135 if ([_arguments count] > 0) {
136 [_cwTask setArguments:[self arguments]];
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
137 }
571f687 Colin Wheeler refactoring code in CWTask
authored
138 if ([self directoryPath]) {
ea55540 Colin Wheeler making ivars private to access on CWTask
authored
139 [_cwTask setCurrentDirectoryPath:[self directoryPath]];
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
140 }
572e5ef Colin Wheeler just refactoring the shit out of this code to make it more readable and ...
authored
141 }
142
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
143 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
144 * Runs all the validation methods and returns NO if any of them fail,
145 * returns YES otherwise
146 *
147 * @param error a NSError object to be written to if something fails
148 * @return (BOOL) NO if the task fails any validation test, YES otherwise
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
149 */
687d02f Colin Wheeler minor formatting updates
authored
150 - (BOOL) _validateTask:(NSError **)error
151 {
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
152 if (![self _validateExecutable:error] ||
153 ![self _validateDirectoryPathIfApplicable:error] ||
154 ![self _validateTaskHasRun:error]) {
155 return NO;
156 }
157 return YES;
3e50542 Colin Wheeler further refactoring on CWTask
authored
158 }
159
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
160 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
161 * Checks for a non nil value of executable and checks that the executable actually exists
162 * if either fail it writes out a kCWTaskInvalidExecutable error to the NSError pointer and
163 * returns NO
164 *
165 * @param error a NSError object to be written to if something fails
166 * @return (BOOL) NO is the executable specified doesn't exist otherwise returns YES
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
167 */
687d02f Colin Wheeler minor formatting updates
authored
168 - (BOOL) _validateExecutable:(NSError **)error
169 {
571f687 Colin Wheeler refactoring code in CWTask
authored
170 if (([self executable] == nil) || ![[NSFileManager defaultManager] fileExistsAtPath:[self executable]]) {
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
171 if (*error) {
61f6c91 Colin Wheeler refactoring code to match the new CWCreateError() argument order
authored
172 *error = CWCreateError(kCWTaskErrorDomain, kCWTaskInvalidExecutable, @"Executable Path provided doesn't exist");
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
173 }
174 return NO;
175 }
176 return YES;
3e50542 Colin Wheeler further refactoring on CWTask
authored
177 }
178
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
179 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
180 * if there is a non nil directory path provided it validates that it actually exists
181 * if that fails it writes out a kCWTaskInvalidDirectory error and returns NO
182 *
183 * @param error a NSError object to be written to if something fails
184 * @return (BOOL) YES if the directory path exists otherwise returns NO
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
185 */
687d02f Colin Wheeler minor formatting updates
authored
186 - (BOOL) _validateDirectoryPathIfApplicable:(NSError **)error
187 {
571f687 Colin Wheeler refactoring code in CWTask
authored
188 if ([self directoryPath]) {
189 if (![[NSFileManager defaultManager] fileExistsAtPath:[self directoryPath]]) {
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
190 if (*error) {
61f6c91 Colin Wheeler refactoring code to match the new CWCreateError() argument order
authored
191 *error = CWCreateError(kCWTaskErrorDomain, kCWTaskInvalidDirectory, @"The Directory Specified does not exist & is invalid");
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
192 }
193 return NO;
194 }
195 }
196 return YES;
3e50542 Colin Wheeler further refactoring on CWTask
authored
197 }
198
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
199 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
200 * CWTask behaves just like NSTask in that each task object may only run once. This
201 * checks to see if it has already run and if it has write out a kCWTaskAlreadyRun error
202 * to the error pointer and then returns NO
203 *
204 * @param error a NSError object to be written to if something fails
205 * @return (BOOL) YES if the task has not been run, otherwise returns NO
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
206 */
687d02f Colin Wheeler minor formatting updates
authored
207 - (BOOL) _validateTaskHasRun:(NSError **)error
208 {
571f687 Colin Wheeler refactoring code in CWTask
authored
209 if ([self taskHasRun] == YES) {
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
210 if (*error) {
61f6c91 Colin Wheeler refactoring code to match the new CWCreateError() argument order
authored
211 *error = CWCreateError(kCWTaskErrorDomain, kCWTaskAlreadyRun, @"CWTask Object has already been run");
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
212 }
213 return NO;
214 }
215 return YES;
572e5ef Colin Wheeler just refactoring the shit out of this code to make it more readable and ...
authored
216 }
217
fd3214b Colin Wheeler documenting CWTask and adding parameter asserts
authored
218 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
219 * Launches the task. Performs validation on the task to make sure
220 * all passed in parameters are good, configures the internal task
221 * object and then runs it and gets back the returned data from the
222 * task as a NSString object and then performs post run operations
223 * if necessary.
224 *
225 * @param error a NSError object to be written to if something fails
226 * @return a NSString with the output of the launched task if successful, otherwise the error reference is written to
fd3214b Colin Wheeler documenting CWTask and adding parameter asserts
authored
227 */
687d02f Colin Wheeler minor formatting updates
authored
228 - (NSString *) launchTask:(NSError **)error
229 {
6d5061e Colin Wheeler lots of minor reformatting
authored
230 if ([self _validateTask:error] == NO) { return nil; }
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
231
232 NSString * resultsString = nil;
233
571f687 Colin Wheeler refactoring code in CWTask
authored
234 if ([self taskHasRun] == NO) {
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
235 [self _configureTask];
9085ed4 Colin Wheeler _resultsStringFromLaunchedTask now can write to an NSError to report exc...
authored
236 resultsString = [self _resultsStringFromLaunchedTask:error];
571f687 Colin Wheeler refactoring code in CWTask
authored
237 [self setTaskHasRun:YES];
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
238 [self _performPostRunActionsIfApplicable];
239 }
240 return resultsString;
2010a62 Colin Wheeler adding CWTask to Zangetsu
authored
241 }
242
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
243 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
244 * actual launching of the task and extracting the results from
245 * the NSPipe into a NSString object occur here
246 *
247 * @return a NSString object with the contents of the lauched tasks output
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
248 */
687d02f Colin Wheeler minor formatting updates
authored
249 - (NSString *) _resultsStringFromLaunchedTask:(NSError **)error
250 {
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
251 NSData * returnedData = nil;
252 NSString * taskOutput = nil;
253
254 @try {
ea55540 Colin Wheeler making ivars private to access on CWTask
authored
255 [_cwTask launch];
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
256 }
257 @catch (NSException * e) {
258 CWDebugLog(@"caught exception: %@", e);
4c46c4d Colin Wheeler replacing all magic string instances in CWCreateError() calls with stati...
authored
259 *error = CWCreateError(kCWTaskErrorDomain, kCWTaskEncounteredExceptionOnRun, [e description]);
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
260 }
261
571f687 Colin Wheeler refactoring code in CWTask
authored
262 returnedData = [[[self pipe] fileHandleForReading] readDataToEndOfFile];
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
263 if (returnedData) {
264 taskOutput = [[NSString alloc] initWithData:returnedData encoding:NSUTF8StringEncoding];
265 }
266
267 return taskOutput;
572e5ef Colin Wheeler just refactoring the shit out of this code to make it more readable and ...
authored
268 }
269
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
270 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
271 * any post run actions after the task have been launched occurr here
272 *
273 * @param error a NSError object to be written to if something fails
ce92243 Colin Wheeler documenting any undocumented CWTask methods
authored
274 */
687d02f Colin Wheeler minor formatting updates
authored
275 - (void) _performPostRunActionsIfApplicable
276 {
ea55540 Colin Wheeler making ivars private to access on CWTask
authored
277 if (![_cwTask isRunning]) {
278 [self setSuccessCode:[_cwTask terminationStatus]];
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
279 }
571f687 Colin Wheeler refactoring code in CWTask
authored
280 if (([self inAsynchronous] == NO) && [self completionBlock]) {
ec80b7d Colin Wheeler minor update & fix
authored
281 [self completionBlock]();
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
282 }
572e5ef Colin Wheeler just refactoring the shit out of this code to make it more readable and ...
authored
283 }
284
0b2c42a Colin Wheeler better documenting -launchTaskWithResult
authored
285 /**
286 Launches the task on a private queue and returns the result in a completion block
287
288 This is exactly similar to using -launchTaskonGCDQueue except CWTask is creating the private queue to use
289
290 @param block the completion block to be called when the task has results to show. this must not be nil.
291 */
9275ca1 Colin Wheeler new api -[CWTask launchTaskWithResult:] uses a private queue to launch t...
authored
292 -(void)launchTaskWithResult:(void (^)(NSString *output, NSError *error))block
293 {
4c7c65f Colin Wheeler making sure CWTask uses a unique queue label
authored
294 const char *uniqueLabel = [[NSString stringWithFormat:@"com.CWTask.%@_%@",[self executable],[NSString cw_uuidString]] UTF8String];
295 dispatch_queue_t queue = dispatch_queue_create(uniqueLabel, DISPATCH_QUEUE_SERIAL);
9275ca1 Colin Wheeler new api -[CWTask launchTaskWithResult:] uses a private queue to launch t...
authored
296 [self setInAsynchronous:YES];
297 dispatch_async(queue, ^{
298 NSError * taskError;
299 NSString * resultsString = nil;
300
301 resultsString = [self launchTask:&taskError];
302
303 dispatch_async(dispatch_get_main_queue(), ^{
304 block(resultsString,taskError);
305 });
306 });
307 dispatch_release(queue);
308 }
309
fd3214b Colin Wheeler documenting CWTask and adding parameter asserts
authored
310 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
311 * adds a operation to the passed in NSOperationQueue and calls
312 * -launchTask: then when it is done returns back to the main
313 * thread via NSOperationQueue mainQueue and executes the block
314 *
315 * @param queue a NSOperationQueue to launch the task on
316 * @param output passed to you in the completion block with the contents of the launched tasks output
317 * @param error a NSError object with a error if something went wrong
fd3214b Colin Wheeler documenting CWTask and adding parameter asserts
authored
318 */
687d02f Colin Wheeler minor formatting updates
authored
319 - (void) launchTaskOnQueue:(NSOperationQueue *)queue
320 withCompletionBlock:(void (^)(NSString * output, NSError * error))block
321 {
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
322 NSParameterAssert(queue);
571f687 Colin Wheeler refactoring code in CWTask
authored
323 [self setInAsynchronous:YES];
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
324
325 [queue addOperationWithBlock:^{
326 NSError * taskError;
327 NSString * resultsString = nil;
328
329 resultsString = [self launchTask:&taskError];
330
331 [[NSOperationQueue mainQueue] addOperationWithBlock:^{
332 block (resultsString, taskError);
333 }];
334 }];
fe50b91 Colin Wheeler adding experimental asynchronous launch methods to CWTask
authored
335 }
336
fd3214b Colin Wheeler documenting CWTask and adding parameter asserts
authored
337 /**
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
338 * adds a operation to the passed in gcd dispatch_qeueue_t queue and calls
339 * -launchTask: then when it is done returns back to the main
340 * thread via dispatch_get_main_queue() and executes the block
341 *
342 * @param queue a Grand Central Dispatch Queue (dispatch_queue_t) to launch the task on
343 * @param output passed to you in the completion block with the contents of the launched tasks output
344 * @param error a NSError object with a error if something went wrong
fd3214b Colin Wheeler documenting CWTask and adding parameter asserts
authored
345 */
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
346 - (void) launchTaskOnGCDQueue:(dispatch_queue_t)queue
687d02f Colin Wheeler minor formatting updates
authored
347 withCompletionBlock:(void (^)(NSString * output, NSError * error))block
348 {
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
349 NSParameterAssert(queue);
571f687 Colin Wheeler refactoring code in CWTask
authored
350 [self setInAsynchronous:YES];
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
351
352 dispatch_async(queue, ^{
353 NSError * taskError;
354 NSString * resultsString = nil;
355
fe50b91 Colin Wheeler adding experimental asynchronous launch methods to CWTask
authored
356 resultsString = [self launchTask:&taskError];
bd15496 Colin Wheeler cleaning up formatting of CWTask
authored
357
358 dispatch_async (dispatch_get_main_queue (), ^{
359 block (resultsString, taskError);
fe50b91 Colin Wheeler adding experimental asynchronous launch methods to CWTask
authored
360 });
361 });
362 }
363
2010a62 Colin Wheeler adding CWTask to Zangetsu
authored
364 @end
Something went wrong with that request. Please try again.