Created
February 9, 2010 18:50
-
-
Save atr000/299513 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* git-update-infoplist.m | |
* git-update-infoplist | |
* | |
* Created by Ofri Wolfus on 25/05/07. | |
* Copyright 2007 Ofri Wolfus. All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without modification, | |
* are permitted provided that the following conditions are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* 3. Neither the name of Ofri Wolfus nor the names of his contributors | |
* may be used to endorse or promote products derived from this software | |
* without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
* | |
* | |
* v0.3: | |
* - We now check the contents of the repository directory for a git repo layout. | |
* - The passed paths are now standardized before using. | |
* - Paths relative to the current directory are now valid. | |
* - Added -[NSString absolutePath]. | |
* - Added +[NSTask fullPathToExecutable:additionalSearchPaths:]. | |
* - A help is now displayed if no arguments are available, or if '-h' or '--help' | |
* were passed. | |
* | |
* v0.2: | |
* - Rewrote +[NSTask fullPathToExecutable:] with pure Cocoa. | |
* - Added custom executable paths. | |
* | |
* v0.1: | |
* - Initial release | |
*/ | |
#import <Foundation/Foundation.h> | |
@interface NSString (DPExtensions) | |
/*! | |
* @abstract A convenient methods for creating an autoreleased string | |
* from an NSData instance. | |
* | |
* @discussion This is the equivalent to <code>[[[NSString alloc] initWithData:data encoding:enc] autorelease]</code>. | |
*/ | |
+ (id)stringWithData:(NSData *)data encoding:(NSStringEncoding)enc; | |
/*! | |
* @abstract Returns an absolute path from the receiver. | |
* @discussion The path is made by standardizing the receiver and appending it to the current directory if needed. | |
*/ | |
- (NSString *)absolutePath; | |
@end | |
@implementation NSString (DPExtensions) | |
+ (id)stringWithData:(NSData *)data encoding:(NSStringEncoding)enc { | |
return [[[NSString alloc] initWithData:data | |
encoding:enc] autorelease]; | |
} | |
- (NSString *)absolutePath { | |
self = [self stringByStandardizingPath]; | |
if (![self hasPrefix:@"/"]) | |
self = [[[NSFileManager defaultManager] currentDirectoryPath] stringByAppendingPathComponent:self]; | |
return self; | |
} | |
@end | |
@interface NSTask (DPExtensions) | |
+ (NSString *)fullPathToExecutable:(NSString *)execName; | |
+ (NSString *)fullPathToExecutable:(NSString *)execName additionalSearchPaths:(NSArray *)paths; | |
@end | |
@implementation NSTask (DPExtensions) | |
+ (NSString *)fullPathToExecutable:(NSString *)execName { | |
return [self fullPathToExecutable:execName | |
additionalSearchPaths:[NSArray arrayWithObjects:@"/usr/local/bin", | |
@"/usr/local/sbin", | |
@"/opt/local/bin", | |
@"/opt/local/sbin", nil]]; | |
} | |
+ (NSString *)fullPathToExecutable:(NSString *)execName additionalSearchPaths:(NSArray *)paths { | |
NSString *result = nil; | |
NSPipe *pipe = [NSPipe pipe]; | |
NSTask *task = [[NSTask alloc] init]; | |
NSMutableDictionary *env = [[[NSProcessInfo processInfo] environment] mutableCopy]; | |
NSMutableString *path_var = [[env objectForKey:@"PATH"] mutableCopy]; | |
NSEnumerator *enumerator = [paths objectEnumerator]; | |
NSString *searchPath; | |
// Add any additional search paths | |
while ((searchPath = [enumerator nextObject])) { | |
if ([path_var rangeOfString:searchPath].location == NSNotFound) | |
[path_var appendFormat:@":%@", searchPath]; | |
} | |
// Set the new PATH variable | |
[env setObject:path_var forKey:@"PATH"]; | |
// Initialize our task | |
[task setLaunchPath:@"/usr/bin/which"]; | |
[task setEnvironment:env]; | |
[task setArguments:[NSArray arrayWithObject:execName]]; | |
[task setStandardOutput:pipe]; | |
// Launch and wait | |
[task launch]; | |
[task waitUntilExit]; | |
if ([task terminationStatus] == 0) { | |
result = [NSString stringWithData:[[pipe fileHandleForReading] readDataToEndOfFile] | |
encoding:NSUTF8StringEncoding]; | |
result = [result stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; | |
// Some error occured, and we didn't get a full path | |
if (![result isAbsolutePath]) | |
result = nil; | |
} | |
// Clean up | |
[path_var release]; | |
[env release]; | |
[task release]; | |
return result; | |
} | |
@end | |
BOOL directoryIsGitRepo(NSString *path) { | |
NSArray *contents = [[NSFileManager defaultManager] directoryContentsAtPath:path]; | |
if (contents && | |
[contents containsObject:@"HEAD"] && | |
[contents containsObject:@"branches"] && | |
[contents containsObject:@"config"] && | |
[contents containsObject:@"description"] && | |
[contents containsObject:@"hooks"] && | |
[contents containsObject:@"info"] && | |
[contents containsObject:@"objects"] && | |
[contents containsObject:@"refs"]) | |
{ | |
return YES; | |
} | |
return NO; | |
} | |
void printHelp(void) { | |
puts("usage: git-info-plist REPO_PATH PLIST_PATH\n\n" | |
"The first argument is a path to the Git repository. It may point to a bare repository,\n" | |
"a \"regular\" repository or to the .git repository inside a repository.\n" | |
"The second argument is a path to a plist file to which git-update-infoplist will write\n" | |
"the commit ID.\n\n" | |
"git-info-plist also accepts two optional environment variables:\n" | |
" COMMIT_KEY_NAME The name of the key to write the commit ID.\n" | |
" The default is \"DPGitCommit\"\n" | |
" GIT_LOG_PATH A full path to the git-log executable. If git-info-plist fails to\n" | |
" locate the git-log executable, use this variable to point it to it.\n" | |
" Alternatively, this can be used to force the use of a given executable\n" | |
" if more than one are available. Note: The default search paths together\n" | |
" with /usr/local/bin, /usr/local/sbin, /opt/local/bin and \n" | |
" /opt/local/sbin are searched for the git-log executable.\n"); | |
} | |
int main (int argc, const char * argv[]) { | |
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; | |
NSProcessInfo *pinfo = [NSProcessInfo processInfo]; | |
NSArray *args = [pinfo arguments]; | |
if ([args count] < 3 || [args containsObject:@"-h"] || [args containsObject:@"--help"]) { | |
printHelp(); | |
return 0; | |
} | |
NSString *repoPath = [[args objectAtIndex:1] absolutePath]; | |
NSString *plistPath = [[args objectAtIndex:2] absolutePath]; | |
NSMutableDictionary *plist = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath]; | |
// Make sure our repository is a real repo | |
repoPath = directoryIsGitRepo(repoPath) ? repoPath | |
: [repoPath stringByAppendingPathComponent:@".git"]; | |
if (!directoryIsGitRepo(repoPath)) { | |
printf("Error: The passed repository does not appear to be a git repository."); | |
return EXIT_FAILURE; | |
} | |
// Something went wrong with loading our plist | |
if (!plist) { | |
printf("Error: Can't load plist from %s.\n", [plistPath UTF8String]); | |
return EXIT_FAILURE; | |
} | |
NSPipe *pipe = [NSPipe pipe]; | |
NSTask *gitLog = [[NSTask alloc] init]; | |
NSMutableDictionary *env = [NSMutableDictionary dictionaryWithDictionary:[pinfo environment]]; | |
NSString *gitLogPath = [env objectForKey:@"GIT_LOG_PATH"] ?: [NSTask fullPathToExecutable:@"git-log"]; | |
// Make sure we found git-log | |
if (!gitLogPath) { | |
printf("Error: Can't find the git-log exectable.\n"); | |
return EXIT_FAILURE; | |
} | |
// Let git-log know where the repo is located | |
[env setObject:repoPath forKey:@"GIT_DIR"]; | |
// Initialize our task | |
[gitLog setLaunchPath:gitLogPath]; | |
[gitLog setArguments:[NSArray arrayWithObjects:@"-1", | |
@"--pretty=format:%H", nil]]; | |
[gitLog setEnvironment:env]; | |
[gitLog setStandardOutput:pipe]; | |
[gitLog setStandardError:pipe]; | |
// Launch and wait for its termination | |
[gitLog launch]; | |
[gitLog waitUntilExit]; | |
// Get the output of git-log | |
NSString *output = [NSString stringWithData:[[pipe fileHandleForReading] readDataToEndOfFile] | |
encoding:NSUTF8StringEncoding]; | |
// An error had occured and git-log exited with non-zero value | |
if ([gitLog terminationStatus] != 0) { | |
// Log the error message of git-log if one was provided | |
if ([output length] > 0) | |
printf("%s", [output UTF8String]); | |
return EXIT_FAILURE; | |
} | |
// Modify the dictionary, | |
[plist setObject:[output stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] | |
forKey:[env objectForKey:@"COMMIT_KEY_NAME"] ?: @"DPGitCommit"]; | |
// and write it back to disk | |
if (![plist writeToFile:plistPath atomically:YES]) { | |
printf("Error: Can't write plist to %s.\n", [plistPath UTF8String]); | |
return EXIT_FAILURE; | |
} | |
[pool release]; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment