Skip to content
This repository
Newer
Older
100644 364 lines (324 sloc) 11.901 kb
27f8fa06 »
2011-09-01 MIT License is on all source code files now
1 /*
2010a62c »
2010-11-03 adding CWTask to Zangetsu
2 // CWTask.m
27f8fa06 »
2011-09-01 MIT License is on all source code files now
3 // Zangetsu
2010a62c »
2010-11-03 adding CWTask to Zangetsu
4 //
5 // Created by Colin Wheeler on 8/30/10.
6 // Copyright 2010. All rights reserved.
7 //
27f8fa06 »
2011-09-01 MIT License is on all source code files now
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 */
2010a62c »
2010-11-03 adding CWTask to Zangetsu
29
b4648853 »
2011-02-12 CWTask now properly uses new Objective-C 2.0 in LLVM features to hide…
30 #import "CWTask.h"
9af76727 »
2010-12-31 beginning to completely rewrite CWTask with a much better design
31
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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;
9085ed4f »
2011-09-06 _resultsStringFromLaunchedTask now can write to an NSError to report …
47 - (NSString *) _resultsStringFromLaunchedTask:(NSError **)error;
bd15496f »
2011-04-01 cleaning up formatting of CWTask
48 - (BOOL) _validateExecutable:(NSError **)error;
49 - (BOOL) _validateDirectoryPathIfApplicable:(NSError **)error;
50 - (BOOL) _validateTaskHasRun:(NSError **)error;
9af76727 »
2010-12-31 beginning to completely rewrite CWTask with a much better design
51 @end
2010a62c »
2010-11-03 adding CWTask to Zangetsu
52
53 @implementation CWTask
54
ea555409 »
2012-01-28 making ivars private to access on CWTask
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;
9af76727 »
2010-12-31 beginning to completely rewrite CWTask with a much better design
64
318b5e12 »
2011-10-03 replacing all instances of '#pragma mark' with '//MARK:'
65 //MARK: -
66 //MARK: Public API
2010a62c »
2010-11-03 adding CWTask to Zangetsu
67
fd3214b0 »
2011-01-08 documenting CWTask and adding parameter asserts
68 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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
fd3214b0 »
2011-01-08 documenting CWTask and adding parameter asserts
75 */
bd15496f »
2011-04-01 cleaning up formatting of CWTask
76 - (id) initWithExecutable:(NSString *)exec
687d02f5 »
2012-02-16 minor formatting updates
77 andArguments:(NSArray *)execArgs
78 atDirectory:(NSString *)path
79 {
bd15496f »
2011-04-01 cleaning up formatting of CWTask
80 self = [super init];
81 if (self) {
ea555409 »
2012-01-28 making ivars private to access on CWTask
82 _executable = exec;
83 _arguments = execArgs;
84 _directoryPath = path;
85 _successCode = kCWTaskNotLaunched;
86 _taskHasRun = NO;
87 _inAsynchronous = NO;
88 _cwTask = [[NSTask alloc] init];
bd15496f »
2011-04-01 cleaning up formatting of CWTask
89 }
90 return self;
e8f6d0a5 »
2011-02-15 updating documentation on CWTask and updating _validateExecutable
91 }
92
93 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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
e8f6d0a5 »
2011-02-15 updating documentation on CWTask and updating _validateExecutable
100 */
9cdcd601 »
2012-03-23 massive code cleanup
101 - (id) init
102 {
bd15496f »
2011-04-01 cleaning up formatting of CWTask
103 self = [super init];
104 if (self) {
ea555409 »
2012-01-28 making ivars private to access on CWTask
105 _executable = nil;
106 _arguments = nil;
107 _directoryPath = nil;
108 _successCode = kCWTaskNotLaunched;
109 _taskHasRun = NO;
110 _inAsynchronous = NO;
111 _cwTask = nil;
bd15496f »
2011-04-01 cleaning up formatting of CWTask
112 }
113 return self;
2010a62c »
2010-11-03 adding CWTask to Zangetsu
114 }
115
6a1d3fbf »
2011-02-26 more detailed documentation enhanced for Doxygen in CWTask
116 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
117 * Description for debug information
6a1d3fbf »
2011-02-26 more detailed documentation enhanced for Doxygen in CWTask
118 */
687d02f5 »
2012-02-16 minor formatting updates
119 - (NSString *) description
120 {
bd15496f »
2011-04-01 cleaning up formatting of CWTask
121 NSString * desc = [NSString stringWithFormat:@"CWTask::Executable('%@')\nArguements: %@\nDirectory Path:%@",
ea555409 »
2012-01-28 making ivars private to access on CWTask
122 _executable, _arguments, _directoryPath];
bd15496f »
2011-04-01 cleaning up formatting of CWTask
123
124 return desc;
1103a5ed »
2011-01-28 adding a description method to CWTask
125 }
126
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
127 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
128 * Any arguments to the task are set here
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
129 */
687d02f5 »
2012-02-16 minor formatting updates
130 - (void) _configureTask
131 {
571f6872 »
2012-01-19 refactoring code in CWTask
132 [[self cwTask] setLaunchPath:[self executable]];
133 [self setPipe:[NSPipe pipe]];
134 [[self cwTask] setStandardOutput:[self pipe]];
ea555409 »
2012-01-28 making ivars private to access on CWTask
135 if ([_arguments count] > 0) {
136 [_cwTask setArguments:[self arguments]];
bd15496f »
2011-04-01 cleaning up formatting of CWTask
137 }
571f6872 »
2012-01-19 refactoring code in CWTask
138 if ([self directoryPath]) {
ea555409 »
2012-01-28 making ivars private to access on CWTask
139 [_cwTask setCurrentDirectoryPath:[self directoryPath]];
bd15496f »
2011-04-01 cleaning up formatting of CWTask
140 }
572e5efc »
2011-02-13 just refactoring the shit out of this code to make it more readable a…
141 }
142
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
143 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
149 */
687d02f5 »
2012-02-16 minor formatting updates
150 - (BOOL) _validateTask:(NSError **)error
151 {
bd15496f »
2011-04-01 cleaning up formatting of CWTask
152 if (![self _validateExecutable:error] ||
153 ![self _validateDirectoryPathIfApplicable:error] ||
154 ![self _validateTaskHasRun:error]) {
155 return NO;
156 }
157 return YES;
3e505421 »
2011-02-14 further refactoring on CWTask
158 }
159
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
160 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
167 */
687d02f5 »
2012-02-16 minor formatting updates
168 - (BOOL) _validateExecutable:(NSError **)error
169 {
571f6872 »
2012-01-19 refactoring code in CWTask
170 if (([self executable] == nil) || ![[NSFileManager defaultManager] fileExistsAtPath:[self executable]]) {
bd15496f »
2011-04-01 cleaning up formatting of CWTask
171 if (*error) {
61f6c91d »
2012-01-04 refactoring code to match the new CWCreateError() argument order
172 *error = CWCreateError(kCWTaskErrorDomain, kCWTaskInvalidExecutable, @"Executable Path provided doesn't exist");
bd15496f »
2011-04-01 cleaning up formatting of CWTask
173 }
174 return NO;
175 }
176 return YES;
3e505421 »
2011-02-14 further refactoring on CWTask
177 }
178
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
179 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
185 */
687d02f5 »
2012-02-16 minor formatting updates
186 - (BOOL) _validateDirectoryPathIfApplicable:(NSError **)error
187 {
571f6872 »
2012-01-19 refactoring code in CWTask
188 if ([self directoryPath]) {
189 if (![[NSFileManager defaultManager] fileExistsAtPath:[self directoryPath]]) {
bd15496f »
2011-04-01 cleaning up formatting of CWTask
190 if (*error) {
61f6c91d »
2012-01-04 refactoring code to match the new CWCreateError() argument order
191 *error = CWCreateError(kCWTaskErrorDomain, kCWTaskInvalidDirectory, @"The Directory Specified does not exist & is invalid");
bd15496f »
2011-04-01 cleaning up formatting of CWTask
192 }
193 return NO;
194 }
195 }
196 return YES;
3e505421 »
2011-02-14 further refactoring on CWTask
197 }
198
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
199 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
206 */
687d02f5 »
2012-02-16 minor formatting updates
207 - (BOOL) _validateTaskHasRun:(NSError **)error
208 {
571f6872 »
2012-01-19 refactoring code in CWTask
209 if ([self taskHasRun] == YES) {
bd15496f »
2011-04-01 cleaning up formatting of CWTask
210 if (*error) {
61f6c91d »
2012-01-04 refactoring code to match the new CWCreateError() argument order
211 *error = CWCreateError(kCWTaskErrorDomain, kCWTaskAlreadyRun, @"CWTask Object has already been run");
bd15496f »
2011-04-01 cleaning up formatting of CWTask
212 }
213 return NO;
214 }
215 return YES;
572e5efc »
2011-02-13 just refactoring the shit out of this code to make it more readable a…
216 }
217
fd3214b0 »
2011-01-08 documenting CWTask and adding parameter asserts
218 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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
fd3214b0 »
2011-01-08 documenting CWTask and adding parameter asserts
227 */
687d02f5 »
2012-02-16 minor formatting updates
228 - (NSString *) launchTask:(NSError **)error
229 {
6d5061ef »
2011-11-28 lots of minor reformatting
230 if ([self _validateTask:error] == NO) { return nil; }
bd15496f »
2011-04-01 cleaning up formatting of CWTask
231
232 NSString * resultsString = nil;
233
571f6872 »
2012-01-19 refactoring code in CWTask
234 if ([self taskHasRun] == NO) {
bd15496f »
2011-04-01 cleaning up formatting of CWTask
235 [self _configureTask];
9085ed4f »
2011-09-06 _resultsStringFromLaunchedTask now can write to an NSError to report …
236 resultsString = [self _resultsStringFromLaunchedTask:error];
571f6872 »
2012-01-19 refactoring code in CWTask
237 [self setTaskHasRun:YES];
bd15496f »
2011-04-01 cleaning up formatting of CWTask
238 [self _performPostRunActionsIfApplicable];
239 }
240 return resultsString;
2010a62c »
2010-11-03 adding CWTask to Zangetsu
241 }
242
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
243 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
248 */
687d02f5 »
2012-02-16 minor formatting updates
249 - (NSString *) _resultsStringFromLaunchedTask:(NSError **)error
250 {
bd15496f »
2011-04-01 cleaning up formatting of CWTask
251 NSData * returnedData = nil;
252 NSString * taskOutput = nil;
253
254 @try {
ea555409 »
2012-01-28 making ivars private to access on CWTask
255 [_cwTask launch];
bd15496f »
2011-04-01 cleaning up formatting of CWTask
256 }
257 @catch (NSException * e) {
258 CWDebugLog(@"caught exception: %@", e);
4c46c4d6 »
2012-01-05 replacing all magic string instances in CWCreateError() calls with st…
259 *error = CWCreateError(kCWTaskErrorDomain, kCWTaskEncounteredExceptionOnRun, [e description]);
bd15496f »
2011-04-01 cleaning up formatting of CWTask
260 }
261
571f6872 »
2012-01-19 refactoring code in CWTask
262 returnedData = [[[self pipe] fileHandleForReading] readDataToEndOfFile];
bd15496f »
2011-04-01 cleaning up formatting of CWTask
263 if (returnedData) {
264 taskOutput = [[NSString alloc] initWithData:returnedData encoding:NSUTF8StringEncoding];
265 }
266
267 return taskOutput;
572e5efc »
2011-02-13 just refactoring the shit out of this code to make it more readable a…
268 }
269
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
270 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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
ce922439 »
2011-02-16 documenting any undocumented CWTask methods
274 */
687d02f5 »
2012-02-16 minor formatting updates
275 - (void) _performPostRunActionsIfApplicable
276 {
ea555409 »
2012-01-28 making ivars private to access on CWTask
277 if (![_cwTask isRunning]) {
278 [self setSuccessCode:[_cwTask terminationStatus]];
bd15496f »
2011-04-01 cleaning up formatting of CWTask
279 }
571f6872 »
2012-01-19 refactoring code in CWTask
280 if (([self inAsynchronous] == NO) && [self completionBlock]) {
ec80b7d5 »
2012-02-23 minor update & fix
281 [self completionBlock]();
bd15496f »
2011-04-01 cleaning up formatting of CWTask
282 }
572e5efc »
2011-02-13 just refactoring the shit out of this code to make it more readable a…
283 }
284
0b2c42aa »
2012-02-15 better documenting -launchTaskWithResult
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 */
9275ca15 »
2012-02-14 new api -[CWTask launchTaskWithResult:] uses a private queue to launc…
292 -(void)launchTaskWithResult:(void (^)(NSString *output, NSError *error))block
293 {
4c7c65fe »
2012-03-06 making sure CWTask uses a unique queue label
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);
9275ca15 »
2012-02-14 new api -[CWTask launchTaskWithResult:] uses a private queue to launc…
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
fd3214b0 »
2011-01-08 documenting CWTask and adding parameter asserts
310 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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
fd3214b0 »
2011-01-08 documenting CWTask and adding parameter asserts
318 */
687d02f5 »
2012-02-16 minor formatting updates
319 - (void) launchTaskOnQueue:(NSOperationQueue *)queue
320 withCompletionBlock:(void (^)(NSString * output, NSError * error))block
321 {
bd15496f »
2011-04-01 cleaning up formatting of CWTask
322 NSParameterAssert(queue);
571f6872 »
2012-01-19 refactoring code in CWTask
323 [self setInAsynchronous:YES];
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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 }];
fe50b913 »
2011-01-01 adding experimental asynchronous launch methods to CWTask
335 }
336
fd3214b0 »
2011-01-08 documenting CWTask and adding parameter asserts
337 /**
bd15496f »
2011-04-01 cleaning up formatting of CWTask
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
fd3214b0 »
2011-01-08 documenting CWTask and adding parameter asserts
345 */
bd15496f »
2011-04-01 cleaning up formatting of CWTask
346 - (void) launchTaskOnGCDQueue:(dispatch_queue_t)queue
687d02f5 »
2012-02-16 minor formatting updates
347 withCompletionBlock:(void (^)(NSString * output, NSError * error))block
348 {
bd15496f »
2011-04-01 cleaning up formatting of CWTask
349 NSParameterAssert(queue);
571f6872 »
2012-01-19 refactoring code in CWTask
350 [self setInAsynchronous:YES];
bd15496f »
2011-04-01 cleaning up formatting of CWTask
351
352 dispatch_async(queue, ^{
353 NSError * taskError;
354 NSString * resultsString = nil;
355
fe50b913 »
2011-01-01 adding experimental asynchronous launch methods to CWTask
356 resultsString = [self launchTask:&taskError];
bd15496f »
2011-04-01 cleaning up formatting of CWTask
357
358 dispatch_async (dispatch_get_main_queue (), ^{
359 block (resultsString, taskError);
fe50b913 »
2011-01-01 adding experimental asynchronous launch methods to CWTask
360 });
361 });
362 }
363
2010a62c »
2010-11-03 adding CWTask to Zangetsu
364 @end
Something went wrong with that request. Please try again.