diff --git a/BuildMeUp.xcodeproj/project.pbxproj b/BuildMeUp.xcodeproj/project.pbxproj new file mode 100644 index 0000000..eff6f42 --- /dev/null +++ b/BuildMeUp.xcodeproj/project.pbxproj @@ -0,0 +1,315 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + BF5300A21CC61B760019B30E /* readme.md in Sources */ = {isa = PBXBuildFile; fileRef = BF5300A11CC61B760019B30E /* readme.md */; }; + BFC6C2FA1CC5ED8C00FA2490 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFC6C2F91CC5ED8C00FA2490 /* AppKit.framework */; }; + BFC6C2FC1CC5ED8C00FA2490 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFC6C2FB1CC5ED8C00FA2490 /* Foundation.framework */; }; + BFC6C3001CC5ED8C00FA2490 /* BuildMeUp.xcscheme in Resources */ = {isa = PBXBuildFile; fileRef = BFC6C2FF1CC5ED8C00FA2490 /* BuildMeUp.xcscheme */; }; + BFC6C3031CC5ED8C00FA2490 /* BuildMeUp.m in Sources */ = {isa = PBXBuildFile; fileRef = BFC6C3021CC5ED8C00FA2490 /* BuildMeUp.m */; }; + BFC6C3061CC5ED8C00FA2490 /* NSObject_Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = BFC6C3051CC5ED8C00FA2490 /* NSObject_Extension.m */; }; + BFC6C30F1CC5F38F00FA2490 /* JRSwizzle.m in Sources */ = {isa = PBXBuildFile; fileRef = BFC6C30E1CC5F38F00FA2490 /* JRSwizzle.m */; }; + BFC6C3151CC6146A00FA2490 /* NSUserDefaults+Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = BFC6C3141CC6146A00FA2490 /* NSUserDefaults+Extension.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + BF5300A11CC61B760019B30E /* readme.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = readme.md; sourceTree = ""; }; + BF5300A31CC61B940019B30E /* license.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = license.md; sourceTree = ""; }; + BF5300A41CC61C340019B30E /* JRSwizzle_license.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = JRSwizzle_license.md; sourceTree = ""; }; + BF5300A71CC61CED0019B30E /* screenshot.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = screenshot.png; sourceTree = ""; }; + BFC6C2F61CC5ED8C00FA2490 /* BuildMeUp.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BuildMeUp.xcplugin; sourceTree = BUILT_PRODUCTS_DIR; }; + BFC6C2F91CC5ED8C00FA2490 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + BFC6C2FB1CC5ED8C00FA2490 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + BFC6C2FF1CC5ED8C00FA2490 /* BuildMeUp.xcscheme */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = BuildMeUp.xcscheme; path = BuildMeUp.xcodeproj/xcshareddata/xcschemes/BuildMeUp.xcscheme; sourceTree = SOURCE_ROOT; }; + BFC6C3011CC5ED8C00FA2490 /* BuildMeUp.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BuildMeUp.h; sourceTree = ""; }; + BFC6C3021CC5ED8C00FA2490 /* BuildMeUp.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BuildMeUp.m; sourceTree = ""; }; + BFC6C3041CC5ED8C00FA2490 /* NSObject_Extension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSObject_Extension.h; sourceTree = ""; }; + BFC6C3051CC5ED8C00FA2490 /* NSObject_Extension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSObject_Extension.m; sourceTree = ""; }; + BFC6C3071CC5ED8C00FA2490 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BFC6C30D1CC5F38F00FA2490 /* JRSwizzle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JRSwizzle.h; sourceTree = ""; }; + BFC6C30E1CC5F38F00FA2490 /* JRSwizzle.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JRSwizzle.m; sourceTree = ""; }; + BFC6C3131CC6146A00FA2490 /* NSUserDefaults+Extension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSUserDefaults+Extension.h"; sourceTree = ""; }; + BFC6C3141CC6146A00FA2490 /* NSUserDefaults+Extension.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSUserDefaults+Extension.m"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + BFC6C2F41CC5ED8C00FA2490 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BFC6C2FA1CC5ED8C00FA2490 /* AppKit.framework in Frameworks */, + BFC6C2FC1CC5ED8C00FA2490 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + BFC6C2ED1CC5ED8C00FA2490 = { + isa = PBXGroup; + children = ( + BF5300A11CC61B760019B30E /* readme.md */, + BF5300A31CC61B940019B30E /* license.md */, + BF5300A71CC61CED0019B30E /* screenshot.png */, + BFC6C2FD1CC5ED8C00FA2490 /* BuildMeUp */, + BFC6C2F81CC5ED8C00FA2490 /* Frameworks */, + BFC6C2F71CC5ED8C00FA2490 /* Products */, + ); + sourceTree = ""; + }; + BFC6C2F71CC5ED8C00FA2490 /* Products */ = { + isa = PBXGroup; + children = ( + BFC6C2F61CC5ED8C00FA2490 /* BuildMeUp.xcplugin */, + ); + name = Products; + sourceTree = ""; + }; + BFC6C2F81CC5ED8C00FA2490 /* Frameworks */ = { + isa = PBXGroup; + children = ( + BFC6C2F91CC5ED8C00FA2490 /* AppKit.framework */, + BFC6C2FB1CC5ED8C00FA2490 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + BFC6C2FD1CC5ED8C00FA2490 /* BuildMeUp */ = { + isa = PBXGroup; + children = ( + BFC6C30D1CC5F38F00FA2490 /* JRSwizzle.h */, + BFC6C30E1CC5F38F00FA2490 /* JRSwizzle.m */, + BF5300A41CC61C340019B30E /* JRSwizzle_license.md */, + BFC6C3011CC5ED8C00FA2490 /* BuildMeUp.h */, + BFC6C3021CC5ED8C00FA2490 /* BuildMeUp.m */, + BFC6C3131CC6146A00FA2490 /* NSUserDefaults+Extension.h */, + BFC6C3141CC6146A00FA2490 /* NSUserDefaults+Extension.m */, + BFC6C3041CC5ED8C00FA2490 /* NSObject_Extension.h */, + BFC6C3051CC5ED8C00FA2490 /* NSObject_Extension.m */, + BFC6C3071CC5ED8C00FA2490 /* Info.plist */, + BFC6C2FE1CC5ED8C00FA2490 /* Supporting Files */, + ); + path = BuildMeUp; + sourceTree = ""; + }; + BFC6C2FE1CC5ED8C00FA2490 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + BFC6C2FF1CC5ED8C00FA2490 /* BuildMeUp.xcscheme */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + BFC6C2F51CC5ED8C00FA2490 /* BuildMeUp */ = { + isa = PBXNativeTarget; + buildConfigurationList = BFC6C30A1CC5ED8C00FA2490 /* Build configuration list for PBXNativeTarget "BuildMeUp" */; + buildPhases = ( + BFC6C2F21CC5ED8C00FA2490 /* Sources */, + BFC6C2F31CC5ED8C00FA2490 /* Resources */, + BFC6C2F41CC5ED8C00FA2490 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = BuildMeUp; + productName = BuildMeUp; + productReference = BFC6C2F61CC5ED8C00FA2490 /* BuildMeUp.xcplugin */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BFC6C2EE1CC5ED8C00FA2490 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0730; + ORGANIZATIONNAME = "BlackDog Foundry Pty Ltd"; + TargetAttributes = { + BFC6C2F51CC5ED8C00FA2490 = { + CreatedOnToolsVersion = 7.3; + }; + }; + }; + buildConfigurationList = BFC6C2F11CC5ED8C00FA2490 /* Build configuration list for PBXProject "BuildMeUp" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = BFC6C2ED1CC5ED8C00FA2490; + productRefGroup = BFC6C2F71CC5ED8C00FA2490 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + BFC6C2F51CC5ED8C00FA2490 /* BuildMeUp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + BFC6C2F31CC5ED8C00FA2490 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BFC6C3001CC5ED8C00FA2490 /* BuildMeUp.xcscheme in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + BFC6C2F21CC5ED8C00FA2490 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BFC6C3031CC5ED8C00FA2490 /* BuildMeUp.m in Sources */, + BFC6C3151CC6146A00FA2490 /* NSUserDefaults+Extension.m in Sources */, + BFC6C30F1CC5F38F00FA2490 /* JRSwizzle.m in Sources */, + BF5300A21CC61B760019B30E /* readme.md in Sources */, + BFC6C3061CC5ED8C00FA2490 /* NSObject_Extension.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + BFC6C3081CC5ED8C00FA2490 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + }; + name = Debug; + }; + BFC6C3091CC5ED8C00FA2490 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + }; + name = Release; + }; + BFC6C30B1CC5ED8C00FA2490 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DEPLOYMENT_LOCATION = YES; + DSTROOT = "$(HOME)"; + INFOPLIST_FILE = BuildMeUp/Info.plist; + INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + PRODUCT_BUNDLE_IDENTIFIER = com.blackdogfoundry.BuildMeUp; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = xcplugin; + }; + name = Debug; + }; + BFC6C30C1CC5ED8C00FA2490 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DEPLOYMENT_LOCATION = YES; + DSTROOT = "$(HOME)"; + INFOPLIST_FILE = BuildMeUp/Info.plist; + INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; + MACOSX_DEPLOYMENT_TARGET = 10.11; + PRODUCT_BUNDLE_IDENTIFIER = com.blackdogfoundry.BuildMeUp; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = xcplugin; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + BFC6C2F11CC5ED8C00FA2490 /* Build configuration list for PBXProject "BuildMeUp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BFC6C3081CC5ED8C00FA2490 /* Debug */, + BFC6C3091CC5ED8C00FA2490 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BFC6C30A1CC5ED8C00FA2490 /* Build configuration list for PBXNativeTarget "BuildMeUp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BFC6C30B1CC5ED8C00FA2490 /* Debug */, + BFC6C30C1CC5ED8C00FA2490 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BFC6C2EE1CC5ED8C00FA2490 /* Project object */; +} diff --git a/BuildMeUp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/BuildMeUp.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..3946fe9 --- /dev/null +++ b/BuildMeUp.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/BuildMeUp.xcodeproj/xcshareddata/xcschemes/BuildMeUp.xcscheme b/BuildMeUp.xcodeproj/xcshareddata/xcschemes/BuildMeUp.xcscheme new file mode 100755 index 0000000..7c92dc9 --- /dev/null +++ b/BuildMeUp.xcodeproj/xcshareddata/xcschemes/BuildMeUp.xcscheme @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BuildMeUp/BuildMeUp.h b/BuildMeUp/BuildMeUp.h new file mode 100755 index 0000000..0ada64b --- /dev/null +++ b/BuildMeUp/BuildMeUp.h @@ -0,0 +1,22 @@ +// +// BuildMeUp.h +// BuildMeUp +// +// Created by Craig Edwards on 19/04/2016. +// Copyright © 2016 BlackDog Foundry Pty Ltd. All rights reserved. +// + +#import + +@class BuildMeUp; + +static BuildMeUp *sharedPlugin; + +@interface BuildMeUp : NSObject + +@property (nonatomic, strong) NSDate *start; + ++ (instancetype)sharedPlugin; +- (id)initWithBundle:(NSBundle *)plugin; + +@end \ No newline at end of file diff --git a/BuildMeUp/BuildMeUp.m b/BuildMeUp/BuildMeUp.m new file mode 100755 index 0000000..ff7d820 --- /dev/null +++ b/BuildMeUp/BuildMeUp.m @@ -0,0 +1,75 @@ +// +// BuildMeUp.m +// BuildMeUp +// +// Created by Craig Edwards on 19/04/2016. +// Copyright © 2016 BlackDog Foundry Pty Ltd. All rights reserved. +// + +#import "BuildMeUp.h" +#import "JRSwizzle.h" +#import "NSUserDefaults+Extension.h" + +@interface IDEBuildSystemActivityReporter +-(NSAttributedString *)attributedResultStringForBuildOperation:(id)object; +@end + +@implementation NSObject (SwizzledMethods) + +-(NSAttributedString *)bmu_attributedResultStringForBuildOperation:(id)object { + // ask the original method for the string to display + NSAttributedString *original = [self bmu_attributedResultStringForBuildOperation:object]; + + // generate some info to append to it + NSTimeInterval buildTime = [[NSUserDefaults standardUserDefaults] bmu_buildTime]; + NSString *timing = [NSString stringWithFormat:@" [%.2f secs]", buildTime]; + + // append and return + NSMutableAttributedString *modified = [original mutableCopy]; + [modified appendAttributedString:[[NSAttributedString alloc] initWithString:timing]]; + return modified; +} + +@end + +@implementation BuildMeUp + ++(instancetype)sharedPlugin { + return sharedPlugin; +} + +-(id)initWithBundle:(NSBundle *)plugin { + if (self = [super init]) { + // these are the two events that signify that a build is starting / ending + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didStartBuild:) name:@"IDEBuildOperationWillStartNotification" object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didStopBuild:) name:@"IDEBuildOperationDidStopNotification" object:nil]; + + // we need to swizzle the -[IDEBuildSystemActivityReporter attributedResultStringForBuildOperation:] + // method as that is the one that displays the results in the activity view + NSError *error = nil; + Class clazz = NSClassFromString(@"IDEBuildSystemActivityReporter"); + if (![clazz jr_swizzleMethod:@selector(attributedResultStringForBuildOperation:) withMethod:@selector(bmu_attributedResultStringForBuildOperation:) error:&error]) { + NSLog(@"Unable to swizzle title method: %@", error); + } + } + return self; +} + +-(void)didStartBuild:(NSNotification*)notification { + // start the timer + self.start = [NSDate date]; +} + +-(void)didStopBuild:(NSNotification*)notification { + // finish the timer, and add the difference to the previous total + NSDate *now = [NSDate date]; + NSTimeInterval diff = [now timeIntervalSinceDate:self.start]; + NSTimeInterval total = [[NSUserDefaults standardUserDefaults] bmu_buildTime]; + [[NSUserDefaults standardUserDefaults] bmu_setBuildTime:total+diff]; +} + +-(void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +@end diff --git a/BuildMeUp/Info.plist b/BuildMeUp/Info.plist new file mode 100644 index 0000000..f389f58 --- /dev/null +++ b/BuildMeUp/Info.plist @@ -0,0 +1,51 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + DVTPlugInCompatibilityUUIDs + + C4A681B0-4A26-480E-93EC-1218098B9AA0 + F41BD31E-2683-44B8-AE7F-5F09E919790E + AD68E85B-441B-4301-B564-A45E4919A6AD + A16FF353-8441-459E-A50C-B071F53F51B7 + 9F75337B-21B4-4ADC-B558-F9CADF7073A7 + E969541F-E6F9-4D25-8158-72DC3545A6C6 + 8DC44374-2B35-4C57-A6FE-2AD66A36AAD9 + AABB7188-E14E-4433-AD3B-5CD791EAD9A3 + 8DC44374-2B35-4C57-A6FE-2AD66A36AAD9 + AABB7188-E14E-4433-AD3B-5CD791EAD9A3 + 7FDF5C7A-131F-4ABB-9EDC-8C5F8F0B8A90 + 0420B86A-AA43-4792-9ED0-6FE0F2B16A13 + 7265231C-39B4-402C-89E1-16167C4CC990 + ACA8656B-FEA8-4B6D-8E4A-93F4C95C362C + + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSPrincipalClass + BuildMeUp + XC4Compatible + + XCPluginHasUI + + + diff --git a/BuildMeUp/JRSwizzle.h b/BuildMeUp/JRSwizzle.h new file mode 100755 index 0000000..7d29bc2 --- /dev/null +++ b/BuildMeUp/JRSwizzle.h @@ -0,0 +1,13 @@ +// JRSwizzle.h semver:1.0 +// Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com +// Some rights reserved: http://opensource.org/licenses/MIT +// https://github.com/rentzsch/jrswizzle + +#import + +@interface NSObject (JRSwizzle) + ++ (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_; ++ (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_; + +@end diff --git a/BuildMeUp/JRSwizzle.m b/BuildMeUp/JRSwizzle.m new file mode 100755 index 0000000..4e582bf --- /dev/null +++ b/BuildMeUp/JRSwizzle.m @@ -0,0 +1,134 @@ +// JRSwizzle.m semver:1.0 +// Copyright (c) 2007-2011 Jonathan 'Wolf' Rentzsch: http://rentzsch.com +// Some rights reserved: http://opensource.org/licenses/MIT +// https://github.com/rentzsch/jrswizzle + +#import "JRSwizzle.h" + +#if TARGET_OS_IPHONE + #import + #import +#else + #import +#endif + +#define SetNSErrorFor(FUNC, ERROR_VAR, FORMAT,...) \ + if (ERROR_VAR) { \ + NSString *errStr = [NSString stringWithFormat:@"%s: " FORMAT,FUNC,##__VA_ARGS__]; \ + *ERROR_VAR = [NSError errorWithDomain:@"NSCocoaErrorDomain" \ + code:-1 \ + userInfo:[NSDictionary dictionaryWithObject:errStr forKey:NSLocalizedDescriptionKey]]; \ + } +#define SetNSError(ERROR_VAR, FORMAT,...) SetNSErrorFor(__func__, ERROR_VAR, FORMAT, ##__VA_ARGS__) + +#if OBJC_API_VERSION >= 2 +#define GetClass(obj) object_getClass(obj) +#else +#define GetClass(obj) (obj ? obj->isa : Nil) +#endif + +@implementation NSObject (JRSwizzle) + ++ (BOOL)jr_swizzleMethod:(SEL)origSel_ withMethod:(SEL)altSel_ error:(NSError**)error_ { +#if OBJC_API_VERSION >= 2 + Method origMethod = class_getInstanceMethod(self, origSel_); + if (!origMethod) { +#if TARGET_OS_IPHONE + SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self class]); +#else + SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]); +#endif + return NO; + } + + Method altMethod = class_getInstanceMethod(self, altSel_); + if (!altMethod) { +#if TARGET_OS_IPHONE + SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self class]); +#else + SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]); +#endif + return NO; + } + + class_addMethod(self, + origSel_, + class_getMethodImplementation(self, origSel_), + method_getTypeEncoding(origMethod)); + class_addMethod(self, + altSel_, + class_getMethodImplementation(self, altSel_), + method_getTypeEncoding(altMethod)); + + method_exchangeImplementations(class_getInstanceMethod(self, origSel_), class_getInstanceMethod(self, altSel_)); + return YES; +#else + // Scan for non-inherited methods. + Method directOriginalMethod = NULL, directAlternateMethod = NULL; + + void *iterator = NULL; + struct objc_method_list *mlist = class_nextMethodList(self, &iterator); + while (mlist) { + int method_index = 0; + for (; method_index < mlist->method_count; method_index++) { + if (mlist->method_list[method_index].method_name == origSel_) { + assert(!directOriginalMethod); + directOriginalMethod = &mlist->method_list[method_index]; + } + if (mlist->method_list[method_index].method_name == altSel_) { + assert(!directAlternateMethod); + directAlternateMethod = &mlist->method_list[method_index]; + } + } + mlist = class_nextMethodList(self, &iterator); + } + + // If either method is inherited, copy it up to the target class to make it non-inherited. + if (!directOriginalMethod || !directAlternateMethod) { + Method inheritedOriginalMethod = NULL, inheritedAlternateMethod = NULL; + if (!directOriginalMethod) { + inheritedOriginalMethod = class_getInstanceMethod(self, origSel_); + if (!inheritedOriginalMethod) { + SetNSError(error_, @"original method %@ not found for class %@", NSStringFromSelector(origSel_), [self className]); + return NO; + } + } + if (!directAlternateMethod) { + inheritedAlternateMethod = class_getInstanceMethod(self, altSel_); + if (!inheritedAlternateMethod) { + SetNSError(error_, @"alternate method %@ not found for class %@", NSStringFromSelector(altSel_), [self className]); + return NO; + } + } + + int hoisted_method_count = !directOriginalMethod && !directAlternateMethod ? 2 : 1; + struct objc_method_list *hoisted_method_list = malloc(sizeof(struct objc_method_list) + (sizeof(struct objc_method)*(hoisted_method_count-1))); + hoisted_method_list->obsolete = NULL; // soothe valgrind - apparently ObjC runtime accesses this value and it shows as uninitialized in valgrind + hoisted_method_list->method_count = hoisted_method_count; + Method hoisted_method = hoisted_method_list->method_list; + + if (!directOriginalMethod) { + bcopy(inheritedOriginalMethod, hoisted_method, sizeof(struct objc_method)); + directOriginalMethod = hoisted_method++; + } + if (!directAlternateMethod) { + bcopy(inheritedAlternateMethod, hoisted_method, sizeof(struct objc_method)); + directAlternateMethod = hoisted_method; + } + class_addMethods(self, hoisted_method_list); + } + + // Swizzle. + IMP temp = directOriginalMethod->method_imp; + directOriginalMethod->method_imp = directAlternateMethod->method_imp; + directAlternateMethod->method_imp = temp; + + return YES; +#endif +} + ++ (BOOL)jr_swizzleClassMethod:(SEL)origSel_ withClassMethod:(SEL)altSel_ error:(NSError**)error_ { + return [GetClass((id)self) jr_swizzleMethod:origSel_ withMethod:altSel_ error:error_]; +} + +@end diff --git a/BuildMeUp/JRSwizzle_license.md b/BuildMeUp/JRSwizzle_license.md new file mode 100755 index 0000000..07b17dc --- /dev/null +++ b/BuildMeUp/JRSwizzle_license.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2007-2015 Jonathan 'Wolf' Rentzsch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/BuildMeUp/NSObject_Extension.h b/BuildMeUp/NSObject_Extension.h new file mode 100644 index 0000000..03aaea8 --- /dev/null +++ b/BuildMeUp/NSObject_Extension.h @@ -0,0 +1,16 @@ +// +// NSObject_Extension.h +// BuildMeUp +// +// Created by Craig Edwards on 19/04/2016. +// Copyright © 2016 BlackDog Foundry Pty Ltd. All rights reserved. +// + +#import + +@interface NSObject (Xcode_Plugin_Template_Extension) + ++ (void)pluginDidLoad:(NSBundle *)plugin; + + +@end diff --git a/BuildMeUp/NSObject_Extension.m b/BuildMeUp/NSObject_Extension.m new file mode 100644 index 0000000..bf7a59d --- /dev/null +++ b/BuildMeUp/NSObject_Extension.m @@ -0,0 +1,25 @@ +// +// NSObject_Extension.m +// BuildMeUp +// +// Created by Craig Edwards on 19/04/2016. +// Copyright © 2016 BlackDog Foundry Pty Ltd. All rights reserved. +// + + +#import "NSObject_Extension.h" +#import "BuildMeUp.h" + +@implementation NSObject (Xcode_Plugin_Template_Extension) + ++ (void)pluginDidLoad:(NSBundle *)plugin +{ + static dispatch_once_t onceToken; + NSString *currentApplicationName = [[NSBundle mainBundle] infoDictionary][@"CFBundleName"]; + if ([currentApplicationName isEqual:@"Xcode"]) { + dispatch_once(&onceToken, ^{ + sharedPlugin = [[BuildMeUp alloc] initWithBundle:plugin]; + }); + } +} +@end diff --git a/BuildMeUp/NSUserDefaults+Extension.h b/BuildMeUp/NSUserDefaults+Extension.h new file mode 100644 index 0000000..62a3ca1 --- /dev/null +++ b/BuildMeUp/NSUserDefaults+Extension.h @@ -0,0 +1,16 @@ +// +// NSUserDefaults+Extension.h +// BuildMeUp +// +// Created by Craig Edwards on 19/04/2016. +// Copyright © 2016 BlackDog Foundry Pty Ltd. All rights reserved. +// + +#import + +@interface NSUserDefaults (Extension) + +-(NSTimeInterval)bmu_buildTime; +-(void)bmu_setBuildTime:(NSTimeInterval)time; + +@end diff --git a/BuildMeUp/NSUserDefaults+Extension.m b/BuildMeUp/NSUserDefaults+Extension.m new file mode 100644 index 0000000..72dc761 --- /dev/null +++ b/BuildMeUp/NSUserDefaults+Extension.m @@ -0,0 +1,50 @@ +// +// NSUserDefaults+Extension.m +// BuildMeUp +// +// Created by Craig Edwards on 19/04/2016. +// Copyright © 2016 BlackDog Foundry Pty Ltd. All rights reserved. +// + +#import +#import "NSUserDefaults+Extension.h" + +@implementation NSUserDefaults (Extension) + +-(NSTimeInterval)bmu_buildTime { + return [self bmu_timeForField:@"build"]; +} +-(void)bmu_setBuildTime:(NSTimeInterval)time { + [self bmu_setTime:time forField:@"build"]; +} + +# +#pragma mark - Private methods +# +/** Figures out the project name of the current (focused) project */ +-(NSString *)projectName { + NSArray *workspaceWindowControllers = [NSClassFromString(@"IDEWorkspaceWindowController") valueForKey:@"workspaceWindowControllers"]; + id workspace = nil; + for (id controller in workspaceWindowControllers) { + if ([[controller valueForKey:@"window"] isEqual:[NSApp keyWindow]]) { + workspace = [controller valueForKey:@"_workspace"]; + } + } + return [workspace valueForKey:@"name"]; +} + +/** Returns a time value for a specific field name */ +-(NSTimeInterval)bmu_timeForField:(NSString *)fieldName { + NSString *projectName = [self projectName]; + NSString *key = [NSString stringWithFormat:@"BuildMeUp_%@_%@", projectName, fieldName]; + return [self doubleForKey:key]; +} + +/** Stores a time value for a specific field name */ +-(void)bmu_setTime:(NSTimeInterval)time forField:(NSString *)fieldName { + NSString *projectName = [self projectName]; + NSString *key = [NSString stringWithFormat:@"BuildMeUp_%@_%@", projectName, fieldName]; + [self setValue:@(time) forKey:key]; +} + +@end diff --git a/license.md b/license.md new file mode 100644 index 0000000..5a19bf2 --- /dev/null +++ b/license.md @@ -0,0 +1,16 @@ +Copyright (c) 2016 Craig Edwards. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and +associated documentation files (the "Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial +portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT +LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..82dda1c --- /dev/null +++ b/readme.md @@ -0,0 +1,27 @@ +## Build Me Up +This is a simple Xcode plugin that keeps track of how long Xcode takes to build your project(s). It accumulates +the total time and displays in the Activity View in the Xcode toolbar. + + + +It keeps a different running total for each Xcode project. + +### Where is the data stored? +It is basically stored in the user defaults for Xcode. To list stored values, you can use the following +command: + + defaults read com.apple.dt.xcode | grep BuildMeUp + +If you want to clear the total, you can use the +following command from the terminal: + + defaults delete com.apple.dt.Xcode BuildMeUp_XXXX_total + +where `XXXX` is your project name. + +### Important Notes +If you have multiple projects open and are flicking between them, things may get a bit confused. BuildMeUp +uses the currently focused Xcode window to determine which project is being built. + +### License +Released under the MIT license. diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..9c13f5c Binary files /dev/null and b/screenshot.png differ