forked from robbiehanson/XcodeColors
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improved Xcode4/5 support, improved loader, install routine, code cle…
…anup. Moved loading routies to more reliable XCFixin model. Eliminated unneccesary and non-functional install scripts with simple compiler directives and removed unneccesary version.plist . Tidied codebase. Small edit to Readme regarding ~/.MacOSX/environment cariable used in info.plist. Changed schemes.. one for xcode4, one for xcode5 vs. debug/release.
- Loading branch information
1 parent
ad35c47
commit 7a978b7
Showing
10 changed files
with
374 additions
and
404 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
#import <Foundation/Foundation.h> | ||
|
||
#define XCFixinPreflight() \ | ||
if (!XCFixinShouldLoad()) \ | ||
return; \ | ||
\ | ||
static NSUInteger loadAttempt = 0; \ | ||
loadAttempt++; \ | ||
NSLog(@"%@ initialization attempt %ju/%ju...", \ | ||
NSStringFromClass([self class]), \ | ||
(uintmax_t)loadAttempt, \ | ||
(uintmax_t)XCFixinMaxLoadAttempts); | ||
|
||
#define XCFixinPostflight() \ | ||
NSLog(@"%@ initialization successful!", NSStringFromClass([self class])); \ | ||
return; \ | ||
failed: \ | ||
{ \ | ||
NSLog(@"%@ initialization failed.", NSStringFromClass([self class])); \ | ||
\ | ||
if (loadAttempt < XCFixinMaxLoadAttempts) \ | ||
{ \ | ||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(), \ | ||
^(void) \ | ||
{ \ | ||
[self pluginDidLoad: plugin]; \ | ||
}); \ | ||
} \ | ||
\ | ||
else NSLog(@"%@ failing permanently. :(", NSStringFromClass([self class])); \ | ||
} | ||
|
||
#define XCFixinAssertMessageFormat @"Assertion failed (file: %s, function: %s, line: %u): %s\n" | ||
#define XCFixinNoOp (void)0 | ||
|
||
#define XCFixinAssertOrPerform(condition, action) \ | ||
({ \ | ||
bool __evaluated_condition = false; \ | ||
\ | ||
__evaluated_condition = (condition); \ | ||
\ | ||
if (!__evaluated_condition) \ | ||
{ \ | ||
NSLog(XCFixinAssertMessageFormat, __FILE__, __PRETTY_FUNCTION__, __LINE__, (#condition)); \ | ||
action; \ | ||
} \ | ||
}) | ||
|
||
#define XCFixinAssertOrRaise(condition) XCFixinAssertOrPerform((condition), [NSException raise: NSGenericException format: @"An XCFixin exception occurred"]) | ||
|
||
#define XCFixinConfirmOrPerform(condition, action) \ | ||
({ \ | ||
if (!(condition)) \ | ||
{ \ | ||
action; \ | ||
} \ | ||
}) | ||
|
||
BOOL XCFixinShouldLoad(void); | ||
extern const NSUInteger XCFixinMaxLoadAttempts; | ||
|
||
/* This function overrides a method at the given class level, and returns the old implementation. If no method existed at | ||
the given class' level, a new method is created at that level, and the superclass' (or super-superclass', and so on) | ||
implementation is returned. | ||
This function returns nil on failure. */ | ||
IMP XCFixinOverrideMethod(Class class, SEL selector, IMP newImplementation); | ||
#define XCFixinOverrideMethodString(className, selector, newImplementation) XCFixinOverrideMethod(NSClassFromString(className), selector, newImplementation) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
#import "XCFixin.h" | ||
#import <objc/runtime.h> | ||
|
||
BOOL XCFixinShouldLoad(void) | ||
{ | ||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; | ||
BOOL result = NO; | ||
|
||
/* Prevent our plugins from loading in non-IDE processes, like xcodebuild. */ | ||
NSString *processName = [[NSProcessInfo processInfo] processName]; | ||
XCFixinConfirmOrPerform([processName caseInsensitiveCompare: @"xcode"] == NSOrderedSame, goto cleanup); | ||
|
||
/* Prevent our plugins from loading in Xcode versions < 4. */ | ||
NSArray *versionComponents = [[[NSBundle mainBundle] objectForInfoDictionaryKey: @"CFBundleShortVersionString"] componentsSeparatedByString: @"."]; | ||
XCFixinConfirmOrPerform(versionComponents && [versionComponents count], goto cleanup); | ||
NSInteger xcodeMajorVersion = [[versionComponents objectAtIndex: 0] integerValue]; | ||
XCFixinConfirmOrPerform(xcodeMajorVersion >= 4, goto cleanup); | ||
|
||
result = YES; | ||
|
||
cleanup: | ||
{ | ||
[pool release], | ||
pool = nil; | ||
} | ||
|
||
return result; | ||
} | ||
|
||
const NSUInteger XCFixinMaxLoadAttempts = 3; | ||
IMP XCFixinOverrideMethod(Class class, SEL selector, IMP newImplementation) | ||
{ | ||
Method *classMethods = nil; | ||
IMP result = nil; | ||
|
||
XCFixinAssertOrPerform(class, goto cleanup); | ||
XCFixinAssertOrPerform(selector, goto cleanup); | ||
XCFixinAssertOrPerform(newImplementation, goto cleanup); | ||
|
||
Method originalMethod = class_getInstanceMethod(class, selector); | ||
XCFixinAssertOrPerform(originalMethod, goto cleanup); | ||
|
||
IMP originalImplementation = method_getImplementation(originalMethod); | ||
unsigned int classMethodsCount = 0; | ||
classMethods = class_copyMethodList(class, &classMethodsCount); | ||
XCFixinAssertOrPerform(classMethods, goto cleanup); | ||
|
||
/* Check to see if the method is defined at the level of 'class', rather than at a super class' level. */ | ||
BOOL methodDefined = NO; | ||
for (unsigned int i = 0; i < classMethodsCount; i++) | ||
{ | ||
if (classMethods[i] == originalMethod) | ||
{ | ||
methodDefined = YES; | ||
break; | ||
} | ||
} | ||
|
||
/* If the method's defined at the level of 'class', then we'll just set its implementation. */ | ||
if (methodDefined) | ||
{ | ||
IMP setImplementationResult = method_setImplementation(originalMethod, newImplementation); | ||
XCFixinAssertOrPerform(setImplementationResult, goto cleanup); | ||
} | ||
|
||
/* If the method isn't defined at the level of 'class' (and therefore it's defined at a superclass' level), then | ||
we need to add a method to the level of 'class'. */ | ||
else | ||
{ | ||
/* Use the return/argument types for the existing method. */ | ||
const char *types = method_getTypeEncoding(originalMethod); | ||
XCFixinAssertOrPerform(types, goto cleanup); | ||
|
||
BOOL addMethodResult = class_addMethod(class, selector, newImplementation, types); | ||
XCFixinAssertOrPerform(addMethodResult, goto cleanup); | ||
} | ||
|
||
result = originalImplementation; | ||
|
||
cleanup: | ||
{ | ||
if (classMethods) | ||
free(classMethods), | ||
classMethods = nil; | ||
} | ||
|
||
return result; | ||
} |
This file contains 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
Oops, something went wrong.