diff --git a/Tools/GNUmakefile b/Tools/GNUmakefile index 837e8f74f..afc0e07fc 100644 --- a/Tools/GNUmakefile +++ b/Tools/GNUmakefile @@ -69,9 +69,9 @@ MAN8_PAGES = gdomap.8 # The application to be compiled ifeq ($(add),yes) -TOOL_NAME = autogsdoc cvtenc plmerge sfparse xmlparse +TOOL_NAME = autogsdoc classes cvtenc plmerge sfparse xmlparse else -TOOL_NAME = autogsdoc cvtenc gdnc gspath defaults pl plmerge plutil \ +TOOL_NAME = autogsdoc classes cvtenc gdnc gspath defaults pl plmerge plutil \ plparse sfparse pldes plget plser pl2link xmlparse HTMLLinker # gdomap is currently not supported on Windows MSVC or Android (which # can be either "linux-android" or "linux-androideabi") @@ -88,6 +88,7 @@ TEST_TOOL_NAME = locale_alias # The source files to be compiled autogsdoc_OBJC_FILES = autogsdoc.m AGSParser.m AGSOutput.m AGSIndex.m AGSHtml.m +classes_OBJC_FILES = classes.m cvtenc_OBJC_FILES = cvtenc.m gdomap_C_FILES = gdomap.c gdnc_OBJC_FILES = gdnc.m diff --git a/Tools/classes.m b/Tools/classes.m new file mode 100644 index 000000000..b692f6e7f --- /dev/null +++ b/Tools/classes.m @@ -0,0 +1,513 @@ +/* Crude tool to compare classes in different version of library code to + * look for possible compatiblity issues. + * Only for internal/test use at the momement. + */ +#import "Foundation/Foundation.h" +#import "GNUstepBase/GSObjCRuntime.h" + +static NSSet * +publicClasses() +{ + static NSSet *set = nil; + + if (nil == set) + { + static char *base[] = { + "NSAffineTransform", + "NSAppleEventDescriptor", + "NSAppleEventManager", + "NSAppleScript", + "NSArchiver", + "NSArray", + "NSAssertionHandler", + "NSAttributedString", + "NSAutoreleasePool", + "NSBackgroundActivityScheduler", + "NSBlockOperation", + "NSBundle", + "NSByteCountFormatter", + "NSCache", + "NSCachedURLResponse", + "NSCalendar", + "NSCalendarDate", + "NSCharacterSet", + "NSClassDescription", + "NSCoder", + "NSComparisonPredicate", + "NSCompoundPredicate", + "NSCondition", + "NSConditionLock", + "NSConnection", + "NSCountedSet", + "NSData", + "NSDate", + "NSDateComponents", + "NSDateComponentsFormatter", + "NSDateFormatter", + "NSDateInterval", + "NSDateIntervalFormatter", + "NSDecimalNumber", + "NSDecimalNumberHandler", + "NSDeserializer", + "NSDictionary", + "NSDimension", + "NSDirectoryEnumerator", + "NSDistantObject", + "NSDistributedLock", + "NSDistributedNotificationCenter", + "NSEnergyFormatter", + "NSEnumerator", + "NSError", + "NSException", + "NSExpression", + "NSExtensionContext", + "NSExtensionItem", + "NSFileAccessIntent", + "NSFileCoordinator", + "NSFileHandle", + "NSFileManager", + "NSFileVersion", + "NSFileWrapper", + "NSFormatter", + "NSGarbageCollector", + "NSHTTPCookie", + "NSHTTPCookieStorage", + "NSHTTPURLResponse", + "NSHashTable", + "NSHost", + "NSISO8601DateFormatter", + "NSIndexPath", + "NSIndexSet", + "NSInputStream", + "NSInvocation", + "NSInvocationOperation", + "NSItemProvider", + "NSItemProviderReadingWriting", + "NSJSONSerialization", + "NSKeyedArchiver", + "NSKeyedUnarchiver", + "NSLengthFormatter", + "NSLinguisticTagger", + "NSLocale", + "NSLock", + "NSMapTable", + "NSMassFormatter", + "NSMeasurement", + "NSMeasurementFormatter", + "NSMessagePort", + "NSMessagePortNameServer", + "NSMetadataItem", + "NSMetadataQuery", + "NSMetadataQueryAttributeValueTuple", + "NSMetadataQueryResultGroup", + "NSMethodSignature", + "NSMutableArray", + "NSMutableAttributedString", + "NSMutableCharacterSet", + "NSMutableData", + "NSMutableDictionary", + "NSMutableIndexSet", + "NSMutableOrderedSet", + "NSMutableSet", + "NSMutableString", + "NSMutableURLRequest", + "NSNetService", + "NSNetServiceBrowser", + "NSNotification", + "NSNotificationCenter", + "NSNotificationQueue", + "NSNull", + "NSNumber", + "NSNumberFormatter", + "NSObject", + "NSObjectScripting", + "NSOperation", + "NSOperationQueue", + "NSOrderedSet", + "NSOrthography", + "NSOutputStream", + "NSPersonNameComponents", + "NSPersonNameComponentsFormatter", + "NSPipe", + "NSPointerArray", + "NSPointerFunctions", + "NSPort", + "NSPortCoder", + "NSPortMessage", + "NSPortNameServer", + "NSPredicate", + "NSProcessInfo", + "NSProgress", + "NSPropertyListSerialization", + "NSProtocolChecker", + "NSProxy", + "NSRecursiveLock", + "NSRegularExpression", + "NSRunLoop", + "NSScanner", + "NSScriptClassDescription", + "NSScriptCoercionHandler", + "NSScriptCommand", + "NSScriptCommandDescription", + "NSScriptExecutionContext", + "NSScriptKeyValueCoding", + "NSScriptObjectSpecifiers", + "NSScriptStandardSuiteCommands", + "NSScriptSuiteRegistry", + "NSSerializer", + "NSSet", + "NSSocketPort", + "NSSocketPortNameServer", + "NSSortDescriptor", + "NSSpellServer", + "NSStream", + "NSString", + "NSTask", + "NSTextCheckingResult", + "NSThread", + "NSTimeZone", + "NSTimeZoneDetail", + "NSTimer", + "NSURL", + "NSURLAuthenticationChallenge", + "NSURLCache", + "NSURLComponents", + "NSURLConnection", + "NSURLCredential", + "NSURLCredentialStorage", + "NSURLDownload", + "NSURLHandle", + "NSURLProtectionSpace", + "NSURLProtocol", + "NSURLQueryItem", + "NSURLRequest", + "NSURLResponse", + "NSURLSession", + "NSURLSessionConfiguration", + "NSURLSessionDataTask", + "NSURLSessionDownloadTask", + "NSURLSessionStreamTask", + "NSURLSessionTask", + "NSURLSessionUploadTask", + "NSUUID", + "NSUbiquitousKeyValueStore", + "NSUnarchiver", + "NSUndoManager", + "NSUnit", + "NSUnitAcceleration", + "NSUnitAngle", + "NSUnitArea", + "NSUnitConcentrationMass", + "NSUnitConverter", + "NSUnitConverterLinear", + "NSUnitDispersion", + "NSUnitDuration", + "NSUnitElectricCharge", + "NSUnitElectricCurrent", + "NSUnitElectricPotentialDifference", + "NSUnitElectricResistance", + "NSUnitEnergy", + "NSUnitFrequency", + "NSUnitFuelEfficiency", + "NSUnitIlluminance", + "NSUnitLength", + "NSUnitMass", + "NSUnitPower", + "NSUnitPressure", + "NSUnitSpeed", + "NSUnitTemperature", + "NSUnitVolume", + "NSUserActivity", + "NSUserDefaults", + "NSUserNotification", + "NSUserNotificationCenter", + "NSUserScriptTask", + "NSValue", + "NSValueTransformer", + "NSXMLDTD", + "NSXMLDTDNode", + "NSXMLDocument", + "NSXMLElement", + "NSXMLNode", + "NSXMLParser", + "NSXPCConnection", + "NSXPCInterface", + "NSXPCListener", + "NSXPCListenerEndpoint" + }; + unsigned count = sizeof(base)/sizeof(*base); + unsigned index; + NSMutableSet *m; + + ENTER_POOL + m = [NSMutableSet setWithCapacity: count]; + for (index = 0; index < count; index++) + { + [m addObject: [NSString stringWithUTF8String: base[index]]]; + } + ASSIGNCOPY(set, m); + LEAVE_POOL + } + return set; +} + +static BOOL +findClassMethod(NSString *mName, NSString *cName, NSDictionary *all) +{ + while (cName != nil) + { + NSDictionary *info = [all objectForKey: cName]; + + if ([[info objectForKey: @"classmethods"] containsObject: mName]) + { + return YES; + } + cName = [info objectForKey: @"superclass"]; + } + return NO; +} + +static BOOL +findInstanceMethod(NSString *mName, NSString *cName, NSDictionary *all) +{ + while (cName != nil) + { + NSDictionary *info = [all objectForKey: cName]; + + if ([[info objectForKey: @"instancemethods"] containsObject: mName]) + { + return YES; + } + cName = [info objectForKey: @"superclass"]; + } + return NO; +} + +static BOOL +doCompare(NSString *name, NSDictionary *oinfo, NSDictionary *all) +{ + NSDictionary *ninfo = [all objectForKey: name]; + NSEnumerator *e; + id n; + id o; + BOOL ok = YES; + + if (nil == ninfo) + { + NSLog(@"Class '%@' removed\n", name); + return NO; + } + o = [oinfo objectForKey: @"superclass"]; + n = [ninfo objectForKey: @"superclass"]; + if (o != n && NO == [o isEqual: n]) + { + NSLog(@"Class '%@' superclass changed from %@ to %@\n", name, o, n); + ok = NO; + } + + e = [[oinfo objectForKey: @"classmethods"] objectEnumerator]; + while ((o = [e nextObject]) != nil) + { + if (NO == findClassMethod(o, name, all)) + { + NSLog(@"Class '%@' class method '%@' removed\n", name, o); + ok = NO; + } + } + + e = [[oinfo objectForKey: @"instancemethods"] objectEnumerator]; + while ((o = [e nextObject]) != nil) + { + if (NO == findInstanceMethod(o, name, all)) + { + NSLog(@"Class '%@' instance method '%@' removed\n", name, o); + ok = NO; + } + } + return ok; +} + +static void +doMethods(Class class, NSMutableDictionary *info, BOOL instance) +{ + NSMutableArray *ma = [NSMutableArray array]; + Method *methods; + unsigned int count; + + if (instance) + { + methods = class_copyMethodList(class, &count); + } + else + { + methods = class_copyMethodList(object_getClass(class), &count); + } + if (methods) + { + NSString *name; + unsigned i; + + for (i = 0; i < count; i++) + { + SEL sel = method_getName(methods[i]); + const char *n = sel_getName(sel); + + if (0 == n || '_' == *n) + { + continue; + } + name = [NSString stringWithUTF8String: n]; + if ([ma containsObject: name] == NO) + { + [ma addObject: name]; + } + } + free(methods); + } + + if ([ma count]) + { + [ma sortUsingSelector: @selector(compare:)]; + if (instance) + { + [info setObject: ma forKey: @"instancemethods"]; + } + else + { + [info setObject: ma forKey: @"classmethods"]; + } + } +} + +int +main(int argc, char *argv[]) +{ + ENTER_POOL + NSDictionary *oldClasses; + NSMutableDictionary *newClasses; +// Protocol **protocols; + NSString *signature; + int classCount; + NSDictionary *locale; + NSString *name; + Class class; + NSSet *set; + + locale = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]; + + oldClasses = [NSDictionary dictionaryWithContentsOfFile: @"OldClasses.plist"]; + + set = publicClasses(); + classCount = [set count]; + if (0 == classCount) + { + /* No expected classes ... try to get all classes instead. + */ + classCount = objc_getClassList(NULL, 0); + set = nil; + } + if (classCount > 0) + { + Class buf[classCount]; + int ci; + + if (set) + { + NSEnumerator *e = [set objectEnumerator]; + + ci = 0; + while ((name = [e nextObject]) != nil) + { + class = NSClassFromString(name); + if (Nil == class) + { + NSLog(@"Expected class '%@' not found.", name); + } + else + { + buf[ci++] = class; + } + } + } + else + { + ci = objc_getClassList(buf, classCount); + NSCAssert(ci == classCount, NSInternalInconsistencyException); + } + classCount = ci; + + newClasses = [NSMutableDictionary dictionaryWithCapacity: ci]; + for (ci = 0; ci < classCount; ci++) + { + Class superClass; + NSMutableDictionary *classDescription; + const char *n; + + class = buf[ci]; + if (class_isMetaClass(class)) + { + continue; + } + n = class_getName(class); + if (0 == n || '_' == *n) + { + continue; + } + name = [NSString stringWithUTF8String: class_getName(class)]; + if (set && nil == [set member: name]) + { + NSLog(@"Unexpected class '%@' ignored", name); + continue; + } + + classDescription = [NSMutableDictionary dictionaryWithCapacity: 4]; + [newClasses setObject: classDescription forKey: name]; + + superClass = class_getSuperclass(class); + if (superClass != Nil) + { + n = class_getName(superClass); + name = [NSString stringWithUTF8String: n]; + [classDescription setObject: name forKey: @"superclass"]; + } + + doMethods(class, classDescription, YES); + doMethods(class, classDescription, NO); + } + } + else + { + NSLog(@"Failed to find any classes"); + exit(1); + } + signature = [newClasses descriptionWithLocale: locale indent: 0]; + [signature writeToFile: @"NewClasses.plist" atomically: NO]; + + if (oldClasses) + { + NSEnumerator *e = [oldClasses keyEnumerator]; + BOOL ok = YES; + + while ((name = [e nextObject]) != nil) + { + NSDictionary *oinfo = [oldClasses objectForKey: name]; + + if (NO == doCompare(name, oinfo, newClasses)) + { + ok = NO; + } + } + if (ok) + { + NSLog(@"Old and new class signature match."); + } + else + { + NSLog(@"Old and new class signature differ."); + } + } + + LEAVE_POOL + return 0; +} +