Commits

Augie Fackler committed 9e2222f

Initial import of shepherd into a public repository.

Comments (0)

Files changed (9)

+syntax: regexp
+
+\.xcodeproj/.*\.mode1$
+\.xcodeproj/.*\.mode1v3$
+\.xcodeproj/.*\.pbxuser$
+\.xcodeproj/.*\.perspectivev3$
+
+syntax: glob
+
+build
+ddcli
+.DS_Store
+*.~.nib
+/*
+ *  RAFShepherd.h
+ *  shepherd
+ *
+ *
+ * Copyright 2008 Augie Fackler.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#import <Cocoa/Cocoa.h>
+#import "DDCommandLineInterface.h"
+
+#define CURRENT_MARKETING_VERSION (0.1)
+
+@interface RAFShepherd : NSObject <DDCliApplicationDelegate> {
+    NSString *_command;
+    NSString *_extension;
+    NSString *_filter;
+    NSString *_dir;
+    NSString *_watchdir;
+    double _wait;
+    BOOL _version;
+    BOOL _help;
+
+	NSString *tempFilePath;
+	NSTask *testRun;
+}
+
+- (void) runTestCommand;
+- (void)handleCallbackForStream:(ConstFSEventStreamRef)streamRef 
+                           size:(size_t)numEvents 
+                         events:(NSArray *)eventPaths
+                          flags:(const FSEventStreamEventFlags *)eventFlags
+                            ids:(const FSEventStreamEventId *)eventIds;
+- (void) printUsage: (FILE *) stream;
+- (void) printVersion;
+- (void)startWatching;
+
+@end
+/*
+ *  RAFShepherd.m
+ *  shepherd
+ *
+ *
+ * Copyright 2008 Augie Fackler.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import "RAFShepherd.h"
+
+#define TEMPFILE_TEMPLATE "/tmp/shepherd_XXXXXXXX.sh"
+
+void streamEventCallback(ConstFSEventStreamRef streamRef,
+						 RAFShepherd *clientCallBackInfo,
+						 size_t numEvents,
+						 NSArray *eventPaths,
+						 const FSEventStreamEventFlags eventFlags[],
+						 const FSEventStreamEventId eventIds[])
+{
+    [clientCallBackInfo handleCallbackForStream:streamRef size:numEvents events:eventPaths flags:eventFlags ids:eventIds];
+}
+
+#pragma mark -
+
+
+@implementation RAFShepherd
+
+- (void) runTestCommand;
+{
+	if (! tempFilePath ) {
+		char *temp_file = malloc(strlen(TEMPFILE_TEMPLATE)+1);
+		strncpy(temp_file, TEMPFILE_TEMPLATE, strlen(TEMPFILE_TEMPLATE)+1);
+		int result = mkstemps(temp_file, 3);
+		NSAssert(result != -1, @"Couldn't make a temporary file.");
+		tempFilePath = [NSString stringWithCString:temp_file];
+	}
+	if (testRun && [testRun isRunning]) {
+		[testRun terminate];
+		ddprintf(@"Noticed edits during a test run, restarting tests...\n");
+	}
+	// hack alert - we let sh read the script in, then the script starts by rm'ing itself.
+	NSString *evilScript = [NSString stringWithFormat:@"rm %@\n%@", tempFilePath, _command];
+	[evilScript writeToFile:tempFilePath	atomically:YES encoding:NSUTF8StringEncoding error:NULL];
+	testRun = [NSTask launchedTaskWithLaunchPath:@"/bin/sh" 
+										arguments:[NSArray arrayWithObjects:tempFilePath, nil]];
+}
+
+- (void)handleCallbackForStream:(ConstFSEventStreamRef)streamRef 
+                           size:(size_t)numEvents 
+                         events:(NSArray *)eventPaths
+                          flags:(const FSEventStreamEventFlags *)eventFlags
+                            ids:(const FSEventStreamEventId *)eventIds;
+{	
+    NSDate *now = [NSDate date];
+    NSEnumerator *pathEnum = [eventPaths objectEnumerator];
+	NSString *path;
+	BOOL runCommand = NO;
+    NSArray *filters = [_filter componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@","]];
+	while ((!runCommand && (path = [pathEnum nextObject]))) {
+		BOOL filt = NO;
+		NSString *obj;
+		NSEnumerator *filtEnum = [filters objectEnumerator];
+		while ((!filt && (obj = [filtEnum nextObject]))) {
+			if ([path rangeOfString:obj].length != 0)
+				filt = YES;
+		}
+		if (!filt) {
+			NSError *theErr = nil;
+			NSArray *files = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&theErr];
+			if (theErr)
+				exit(1);
+			NSEnumerator *fileEnum = [files objectEnumerator];
+			NSString *file;
+			while ((!runCommand && (file = [fileEnum nextObject]))) {
+				if ([file hasSuffix:_extension]) {
+					NSDictionary *d = [[NSFileManager defaultManager] attributesOfItemAtPath:[path stringByAppendingPathComponent:file]
+                                                                                       error:&theErr];
+					if (theErr)
+						exit(1);
+					NSDate *mtime = [d objectForKey:NSFileModificationDate];
+                    /* we add a constant to _wait here because otherwise we have a slight delay
+                     * and without the fuzz the tests sometimes never run.
+                     */
+					if ([mtime timeIntervalSinceDate:now] > -(_wait + 1))
+						runCommand = YES;
+				}
+			}
+		}
+	}
+	if (runCommand) {
+		[self runTestCommand];
+	}
+}    
+
+- (void)startWatching;
+{
+    ddprintf(@"Watching %@, interval %f\n", _watchdir, _wait);
+    FSEventStreamContext *streamContext = malloc(sizeof(FSEventStreamContext));
+    // this needs all its fields zeroed, do it the lazy way
+    for (unsigned i = 0; i < sizeof(FSEventStreamContext); i++)
+        ((char *)streamContext)[i] = 0;
+    streamContext->info = self;
+
+    [self runTestCommand];
+	FSEventStreamRef stream = FSEventStreamCreate(NULL, (FSEventStreamCallback)streamEventCallback, 
+												  streamContext, (CFArrayRef)[NSArray arrayWithObject:_watchdir],
+												  kFSEventStreamEventIdSinceNow,
+												  _wait, kFSEventStreamCreateFlagUseCFTypes);
+	if (stream) {
+		FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+		FSEventStreamStart(stream);
+		CFRunLoopRun();
+	}
+}
+
+- (void) printUsage: (FILE *) stream;
+{
+    ddfprintf(stream, @"%@: Usage [OPTIONS] <argument> [...]\n", DDCliApp);
+}
+
+- (void) printHelp;
+{
+    [self printUsage: stdout];
+    printf("\n"
+           "  -f, --filter LIST             Exclude files whose path contain one of\n"
+           "                                these comma separated values.\n"
+           "  -e, --extension EXT           Match this extension\n"
+           "  -c, --command COMMAND         Command to run (executed by system())\n"
+           "  -d, --directory DIR           Run tests in directory DIR\n"
+           "  -w, --watchdir DIR            Watch files in DIR and subdirs\n"
+           "      --wait INT                Run tests no more often than INT seconds\n"
+           "      --version                 Display version and exit\n"
+           "  -h, --help                    Display this help and exit\n"
+           "\n"
+           "Re-runs tests automatically using given command by watching files\n"
+           "matching EXT using FSEvents.\n"
+           "Written by Augie Fackler\n");
+}
+
+- (void) printVersion;
+{
+    ddprintf(@"%@ version %s\n", DDCliApp, CURRENT_MARKETING_VERSION);
+}
+
+- (void) application: (DDCliApplication *) app
+    willParseOptions: (DDGetoptLongParser *) optionsParser;
+{
+    DDGetoptOption optionTable[] = 
+    {
+        // Long         Short   Argument options
+        {@"filter",    'f',    DDGetoptRequiredArgument},
+        {@"extension", 'e',    DDGetoptRequiredArgument},
+        {@"command",   'c',    DDGetoptRequiredArgument},
+        {@"directory", 'd',    DDGetoptRequiredArgument},
+        {@"watchdir",  'w',    DDGetoptRequiredArgument},
+        {@"wait",       0,      DDGetoptRequiredArgument},
+        {@"version",    0,      DDGetoptNoArgument},
+        {@"help",       'h',    DDGetoptNoArgument},
+        {nil,           0,      0},
+    };
+    [optionsParser addOptionsFromTable: optionTable];
+}
+
+- (int)application: (DDCliApplication *) app
+   runWithArguments: (NSArray *) arguments;
+{
+	tempFilePath = nil;
+	testRun = nil;
+    if (_help)
+    {
+        [self printHelp];
+        return EXIT_SUCCESS;
+    }
+    
+    if (_version)
+    {
+        [self printVersion];
+        return EXIT_SUCCESS;
+    }
+    
+    if (!_watchdir) {
+        const char *wd = getcwd(NULL, 0);
+        _watchdir = [NSString stringWithCString:wd];
+        free((void *)wd);
+    }
+    
+    if (_wait <= 0)
+        _wait = 0.5;
+    
+    if (_command && _extension)
+        [self startWatching];
+    else {
+        ddprintf(@"You must specify -e and -c\n");
+        return EXIT_FAILURE;
+    }
+    return EXIT_SUCCESS;
+}
+
+@end
+Growl notification on test failure.
+#!/bin/zsh
+if [ ! -e ddcli ] ; then
+	hg clone http://www.dribin.org/dave/hg/ddcli/ ddcli
+else
+	echo "You appear to already have a copy of ddcli."
+fi
+.\"Modified from man(1) of FreeBSD, the NetBSD mdoc.template, and mdoc.samples.
+.\"See Also:
+.\"man mdoc.samples for a complete listing of options
+.\"man mdoc for the short list of editing options
+.\"/usr/share/misc/mdoc.template
+.Dd 4/18/08               \" DATE 
+.Dt shepherd 1      \" Program name and manual section number 
+.Os Darwin
+.Sh NAME                 \" Section Header - required - don't modify 
+.Nm shepherd,
+.\" The following lines are read in generating the apropos(man -k) database. Use only key
+.\" words here as the database is built based on the words here and in the .ND line. 
+.Nm Other_name_for_same_program(),
+.Nm Yet another name for the same program.
+.\" Use .Nm macro to designate other names for the documented program.
+.Nd This line parsed for whatis database.
+.Sh SYNOPSIS             \" Section Header - required - don't modify
+.Nm
+.Op Fl abcd              \" [-abcd]
+.Op Fl a Ar path         \" [-a path] 
+.Op Ar file              \" [file]
+.Op Ar                   \" [file ...]
+.Ar arg0                 \" Underlined argument - use .Ar anywhere to underline
+arg2 ...                 \" Arguments
+.Sh DESCRIPTION          \" Section Header - required - don't modify
+Use the .Nm macro to refer to your program throughout the man page like such:
+.Nm
+Underlining is accomplished with the .Ar macro like this:
+.Ar underlined text .
+.Pp                      \" Inserts a space
+A list of items with descriptions:
+.Bl -tag -width -indent  \" Begins a tagged list 
+.It item a               \" Each item preceded by .It macro
+Description of item a
+.It item b
+Description of item b
+.El                      \" Ends the list
+.Pp
+A list of flags and their descriptions:
+.Bl -tag -width -indent  \" Differs from above in tag removed 
+.It Fl a                 \"-a flag as a list item
+Description of -a flag
+.It Fl b
+Description of -b flag
+.El                      \" Ends the list
+.Pp
+.\" .Sh ENVIRONMENT      \" May not be needed
+.\" .Bl -tag -width "ENV_VAR_1" -indent \" ENV_VAR_1 is width of the string ENV_VAR_1
+.\" .It Ev ENV_VAR_1
+.\" Description of ENV_VAR_1
+.\" .It Ev ENV_VAR_2
+.\" Description of ENV_VAR_2
+.\" .El                      
+.Sh FILES                \" File used or created by the topic of the man page
+.Bl -tag -width "/Users/joeuser/Library/really_long_file_name" -compact
+.It Pa /usr/share/file_name
+FILE_1 description
+.It Pa /Users/joeuser/Library/really_long_file_name
+FILE_2 description
+.El                      \" Ends the list
+.\" .Sh DIAGNOSTICS       \" May not be needed
+.\" .Bl -diag
+.\" .It Diagnostic Tag
+.\" Diagnostic informtion here.
+.\" .It Diagnostic Tag
+.\" Diagnostic informtion here.
+.\" .El
+.Sh SEE ALSO 
+.\" List links in ascending order by section, alphabetically within a section.
+.\" Please do not reference files that do not exist without filing a bug report
+.Xr a 1 , 
+.Xr b 1 ,
+.Xr c 1 ,
+.Xr a 2 ,
+.Xr b 2 ,
+.Xr a 3 ,
+.Xr b 3 
+.\" .Sh BUGS              \" Document known, unremedied bugs 
+.\" .Sh HISTORY           \" Document history if command behaves in a unique manner
+/*
+ *  shepherd.m
+ *  shepherd
+ *
+ * Copyright 2008 Augie Fackler.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <Foundation/Foundation.h>
+#import <CoreServices/CoreServices.h>
+#import "RAFShepherd.h"
+
+
+int main (int argc, char * const * argv)
+{
+    return DDCliAppRunWithClass([RAFShepherd class]);
+}

shepherd.xcodeproj/project.pbxproj

+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 45;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		11982D310DB9AC42009A55EF /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 11982D300DB9AC42009A55EF /* CoreServices.framework */; };
+		8DD76F9A0486AA7600D96B5E /* shepherd.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* shepherd.m */; settings = {ATTRIBUTES = (); }; };
+		8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
+		8DD76F9F0486AA7600D96B5E /* shepherd.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C6859EA3029092ED04C91782 /* shepherd.1 */; };
+		DED26B430E26673F00AEC961 /* DDCliApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = DED26B3B0E26673F00AEC961 /* DDCliApplication.m */; };
+		DED26B440E26673F00AEC961 /* DDCliParseException.m in Sources */ = {isa = PBXBuildFile; fileRef = DED26B3D0E26673F00AEC961 /* DDCliParseException.m */; };
+		DED26B450E26673F00AEC961 /* DDCliUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = DED26B3F0E26673F00AEC961 /* DDCliUtil.m */; };
+		DED26B460E26673F00AEC961 /* DDGetoptLongParser.m in Sources */ = {isa = PBXBuildFile; fileRef = DED26B420E26673F00AEC961 /* DDGetoptLongParser.m */; };
+		DED26B4A0E2667A400AEC961 /* RAFShepherd.m in Sources */ = {isa = PBXBuildFile; fileRef = DED26B490E2667A400AEC961 /* RAFShepherd.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		8DD76F9E0486AA7600D96B5E /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 8;
+			dstPath = /usr/share/man/man1/;
+			dstSubfolderSpec = 0;
+			files = (
+				8DD76F9F0486AA7600D96B5E /* shepherd.1 in CopyFiles */,
+			);
+			runOnlyForDeploymentPostprocessing = 1;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		08FB7796FE84155DC02AAC07 /* shepherd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = shepherd.m; sourceTree = "<group>"; };
+		08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+		11982D300DB9AC42009A55EF /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = /System/Library/Frameworks/CoreServices.framework; sourceTree = "<absolute>"; };
+		32A70AAB03705E1F00C91783 /* shepherd_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shepherd_Prefix.pch; sourceTree = "<group>"; };
+		8DD76FA10486AA7600D96B5E /* shepherd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = shepherd; sourceTree = BUILT_PRODUCTS_DIR; };
+		C6859EA3029092ED04C91782 /* shepherd.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = shepherd.1; sourceTree = "<group>"; };
+		DED26B3A0E26673F00AEC961 /* DDCliApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DDCliApplication.h; path = ddcli/lib/DDCliApplication.h; sourceTree = "<group>"; };
+		DED26B3B0E26673F00AEC961 /* DDCliApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DDCliApplication.m; path = ddcli/lib/DDCliApplication.m; sourceTree = "<group>"; };
+		DED26B3C0E26673F00AEC961 /* DDCliParseException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DDCliParseException.h; path = ddcli/lib/DDCliParseException.h; sourceTree = "<group>"; };
+		DED26B3D0E26673F00AEC961 /* DDCliParseException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DDCliParseException.m; path = ddcli/lib/DDCliParseException.m; sourceTree = "<group>"; };
+		DED26B3E0E26673F00AEC961 /* DDCliUtil.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DDCliUtil.h; path = ddcli/lib/DDCliUtil.h; sourceTree = "<group>"; };
+		DED26B3F0E26673F00AEC961 /* DDCliUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DDCliUtil.m; path = ddcli/lib/DDCliUtil.m; sourceTree = "<group>"; };
+		DED26B400E26673F00AEC961 /* DDCommandLineInterface.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DDCommandLineInterface.h; path = ddcli/lib/DDCommandLineInterface.h; sourceTree = "<group>"; };
+		DED26B410E26673F00AEC961 /* DDGetoptLongParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DDGetoptLongParser.h; path = ddcli/lib/DDGetoptLongParser.h; sourceTree = "<group>"; };
+		DED26B420E26673F00AEC961 /* DDGetoptLongParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DDGetoptLongParser.m; path = ddcli/lib/DDGetoptLongParser.m; sourceTree = "<group>"; };
+		DED26B480E2667A400AEC961 /* RAFShepherd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RAFShepherd.h; sourceTree = "<group>"; };
+		DED26B490E2667A400AEC961 /* RAFShepherd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RAFShepherd.m; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		8DD76F9B0486AA7600D96B5E /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */,
+				11982D310DB9AC42009A55EF /* CoreServices.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		08FB7794FE84155DC02AAC07 /* shepherd */ = {
+			isa = PBXGroup;
+			children = (
+				08FB7795FE84155DC02AAC07 /* Source */,
+				C6859EA2029092E104C91782 /* Documentation */,
+				08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */,
+				1AB674ADFE9D54B511CA2CBB /* Products */,
+			);
+			name = shepherd;
+			sourceTree = "<group>";
+		};
+		08FB7795FE84155DC02AAC07 /* Source */ = {
+			isa = PBXGroup;
+			children = (
+				DED26B380E26672F00AEC961 /* External Sources */,
+				32A70AAB03705E1F00C91783 /* shepherd_Prefix.pch */,
+				08FB7796FE84155DC02AAC07 /* shepherd.m */,
+				DED26B480E2667A400AEC961 /* RAFShepherd.h */,
+				DED26B490E2667A400AEC961 /* RAFShepherd.m */,
+			);
+			name = Source;
+			sourceTree = "<group>";
+		};
+		08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = {
+			isa = PBXGroup;
+			children = (
+				11982D300DB9AC42009A55EF /* CoreServices.framework */,
+				08FB779EFE84155DC02AAC07 /* Foundation.framework */,
+			);
+			name = "External Frameworks and Libraries";
+			sourceTree = "<group>";
+		};
+		1AB674ADFE9D54B511CA2CBB /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				8DD76FA10486AA7600D96B5E /* shepherd */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		C6859EA2029092E104C91782 /* Documentation */ = {
+			isa = PBXGroup;
+			children = (
+				C6859EA3029092ED04C91782 /* shepherd.1 */,
+			);
+			name = Documentation;
+			sourceTree = "<group>";
+		};
+		DED26B380E26672F00AEC961 /* External Sources */ = {
+			isa = PBXGroup;
+			children = (
+				DED26B390E26673700AEC961 /* ddcli */,
+			);
+			name = "External Sources";
+			sourceTree = "<group>";
+		};
+		DED26B390E26673700AEC961 /* ddcli */ = {
+			isa = PBXGroup;
+			children = (
+				DED26B3A0E26673F00AEC961 /* DDCliApplication.h */,
+				DED26B3B0E26673F00AEC961 /* DDCliApplication.m */,
+				DED26B3C0E26673F00AEC961 /* DDCliParseException.h */,
+				DED26B3D0E26673F00AEC961 /* DDCliParseException.m */,
+				DED26B3E0E26673F00AEC961 /* DDCliUtil.h */,
+				DED26B3F0E26673F00AEC961 /* DDCliUtil.m */,
+				DED26B400E26673F00AEC961 /* DDCommandLineInterface.h */,
+				DED26B410E26673F00AEC961 /* DDGetoptLongParser.h */,
+				DED26B420E26673F00AEC961 /* DDGetoptLongParser.m */,
+			);
+			name = ddcli;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		8DD76F960486AA7600D96B5E /* shepherd */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "shepherd" */;
+			buildPhases = (
+				1131EFA70E3965D500E78E7D /* ShellScript */,
+				8DD76F990486AA7600D96B5E /* Sources */,
+				8DD76F9B0486AA7600D96B5E /* Frameworks */,
+				8DD76F9E0486AA7600D96B5E /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = shepherd;
+			productInstallPath = "$(HOME)/bin";
+			productName = shepherd;
+			productReference = 8DD76FA10486AA7600D96B5E /* shepherd */;
+			productType = "com.apple.product-type.tool";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		08FB7793FE84155DC02AAC07 /* Project object */ = {
+			isa = PBXProject;
+			buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "shepherd" */;
+			compatibilityVersion = "Xcode 3.1";
+			hasScannedForEncodings = 1;
+			mainGroup = 08FB7794FE84155DC02AAC07 /* shepherd */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				8DD76F960486AA7600D96B5E /* shepherd */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		1131EFA70E3965D500E78E7D /* ShellScript */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "zsh fetch_deps.sh\n";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		8DD76F990486AA7600D96B5E /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				8DD76F9A0486AA7600D96B5E /* shepherd.m in Sources */,
+				DED26B430E26673F00AEC961 /* DDCliApplication.m in Sources */,
+				DED26B440E26673F00AEC961 /* DDCliParseException.m in Sources */,
+				DED26B450E26673F00AEC961 /* DDCliUtil.m in Sources */,
+				DED26B460E26673F00AEC961 /* DDGetoptLongParser.m in Sources */,
+				DED26B4A0E2667A400AEC961 /* RAFShepherd.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		1DEB927508733DD40010E9CD /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				COPY_PHASE_STRIP = NO;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_ENABLE_FIX_AND_CONTINUE = YES;
+				GCC_MODEL_TUNING = G5;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = shepherd_Prefix.pch;
+				INSTALL_PATH = /usr/local/bin;
+				PRODUCT_NAME = shepherd;
+			};
+			name = Debug;
+		};
+		1DEB927608733DD40010E9CD /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				GCC_MODEL_TUNING = G5;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = shepherd_Prefix.pch;
+				INSTALL_PATH = /usr/local/bin;
+				PRODUCT_NAME = shepherd;
+			};
+			name = Release;
+		};
+		1DEB927908733DD40010E9CD /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				PREBINDING = NO;
+				SDKROOT = macosx10.5;
+			};
+			name = Debug;
+		};
+		1DEB927A08733DD40010E9CD /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+				GCC_C_LANGUAGE_STANDARD = c99;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				PREBINDING = NO;
+				SDKROOT = macosx10.5;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "shepherd" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1DEB927508733DD40010E9CD /* Debug */,
+				1DEB927608733DD40010E9CD /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "shepherd" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				1DEB927908733DD40010E9CD /* Debug */,
+				1DEB927A08733DD40010E9CD /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
+}

shepherd_Prefix.pch

+//
+// Prefix header for all source files of the 'shepherd' target in the 'shepherd' project.
+//
+
+#ifdef __OBJC__
+    #import <Foundation/Foundation.h>
+#endif
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.