From 580cb5fbdaca71a82b26cdf5c869a3fdc2ca005c Mon Sep 17 00:00:00 2001 From: rfm Date: Fri, 5 May 2006 11:06:57 +0000 Subject: [PATCH] NSBundle improvements for bundles created using bundleForLibrary: or bundleForClass: git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@22859 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 11 ++ Headers/Foundation/NSBundle.h | 17 +-- Source/NSBundle.m | 199 +++++++++++++++++++++++++--------- 3 files changed, 166 insertions(+), 61 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8282afa1a..555017069 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2006-05-05 Richard Frith-Macdonald + + * Headers/Foundation/NSBundle.h: fix type of _bundleClasses + * Source/NSBundle.m: Try to make bundles for libraries behave as + well as possible with respect to the code in the libraries. + Rather depends on the Info.plist in the resource directory + specifying an NSPrincipalClass so that we can look up that + class at runtime and determine the file it came from. + Added documentation to mmake it clear that this is not + reliable (eg impossible for static or non-existent libraries). + 2006-04-30 David Ayers * Source/NSString: Define _GNU_SOURCE to make protoype of fwprintf diff --git a/Headers/Foundation/NSBundle.h b/Headers/Foundation/NSBundle.h index cc5b300b9..db8a432f7 100644 --- a/Headers/Foundation/NSBundle.h +++ b/Headers/Foundation/NSBundle.h @@ -33,6 +33,7 @@ @class NSString; @class NSArray; @class NSDictionary; +@class NSMutableArray; @class NSMutableDictionary; /** @@ -91,15 +92,15 @@ GS_EXPORT NSString* const NSLoadedClasses; */ @interface NSBundle : NSObject { - NSString *_path; - NSArray *_bundleClasses; - Class _principalClass; - NSDictionary *_infoDict; + NSString *_path; + NSMutableArray *_bundleClasses; + Class _principalClass; + NSDictionary *_infoDict; NSMutableDictionary *_localizations; - unsigned _bundleType; - BOOL _codeLoaded; - unsigned _version; - NSString *_frameworkVersion; + unsigned _bundleType; + BOOL _codeLoaded; + unsigned _version; + NSString *_frameworkVersion; } /** Return an array enumerating all the bundles in the application. This diff --git a/Source/NSBundle.m b/Source/NSBundle.m index 6d5ee3c31..0d583fcd9 100644 --- a/Source/NSBundle.m +++ b/Source/NSBundle.m @@ -64,7 +64,10 @@ @end typedef enum { - NSBUNDLE_BUNDLE = 1, NSBUNDLE_APPLICATION, NSBUNDLE_FRAMEWORK + NSBUNDLE_BUNDLE = 1, + NSBUNDLE_APPLICATION, + NSBUNDLE_FRAMEWORK, + NSBUNDLE_LIBRARY } bundle_t; /* Class variables - We keep track of all the bundles */ @@ -378,7 +381,6 @@ _find_framework(NSString *name) @interface NSBundle (Private) + (NSString *) _absolutePathOfExecutable: (NSString *)path; + (void) _addFrameworkFromClass: (Class)frameworkClass; -- (NSArray *) _bundleClasses; + (NSString*) _gnustep_target_cpu; + (NSString*) _gnustep_target_dir; + (NSString*) _gnustep_target_os; @@ -604,7 +606,7 @@ _find_framework(NSString *name) value = [NSValue valueWithNonretainedObject: class]; - [(NSMutableArray *)[bundle _bundleClasses] addObject: value]; + [bundle->_bundleClasses addObject: value]; fmClasses++; } @@ -620,17 +622,12 @@ _find_framework(NSString *name) */ if (_loadingBundle != nil && _loadingBundle != bundle) { - [(NSMutableArray *)[_loadingBundle _bundleClasses] - removeObjectsInArray: [bundle _bundleClasses]]; + [_loadingBundle->_bundleClasses + removeObjectsInArray: bundle->_bundleClasses]; } } } -- (NSArray *) _bundleClasses -{ - return _bundleClasses; -} - + (NSString*) _gnustep_target_cpu { return gnustep_target_cpu; @@ -662,6 +659,10 @@ _find_framework(NSString *name) */ +typedef struct { + @defs(NSBundle) +} *bptr; + void _bundle_load_callback(Class theClass, struct objc_category *theCategory) { @@ -695,14 +696,14 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) } /* Store classes (but don't store categories) */ - [(NSMutableArray *)[_loadingBundle _bundleClasses] addObject: - [NSValue valueWithNonretainedObject: (id)theClass]]; + [((bptr)_loadingBundle)->_bundleClasses addObject: + [NSValue valueWithNonretainedObject: (id)theClass]]; } @implementation NSBundle -+ (void)initialize ++ (void) initialize { if (self == [NSBundle class]) { @@ -1004,7 +1005,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) while (NSNextMapEnumeratorPair(&enumerate, &key, (void **)&bundle)) { int i, j; - NSArray *bundleClasses = [bundle _bundleClasses]; + NSArray *bundleClasses = bundle->_bundleClasses; BOOL found = NO; j = [bundleClasses count]; @@ -1020,13 +1021,61 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) bundle = nil; } - [load_lock unlock]; - if (!bundle) + + if (bundle == nil) { - /* Is it in the main bundle? */ + /* Is it in the main bundle or a library? */ if (class_is_class(aClass)) - bundle = [self mainBundle]; + { + NSString *lib; + + /* + * Take the path to the binary containing the class and + * convert it to the format for a library name as used + * for obtaining a library resource bundle. + */ + lib = objc_get_symbol_path (aClass, NULL); + if ([lib isEqual: ExecutablePath()] == YES) + { + lib = nil; // In program, not library. + } + lib = [lib lastPathComponent]; + do + { + lib = [lib stringByDeletingPathExtension]; + } + while ([[lib pathExtension] length] > 0); + if ([lib hasPrefix: @"lib"] == YES) + { + lib = [lib substringFromIndex: 3]; + } + + /* + * Get the library bundle ... if there wasn't one + * then we will assume the class was in the program + * executable and return the mainBundle instead. + */ + bundle = [NSBundle bundleForLibrary: lib]; + if (bundle == nil) + { + bundle = [self mainBundle]; + } + + /* + * Add the class to the list of classes known to be in + * the library or executable. We didn't find it there + * to start with, so we know it's safe to add now. + */ + if (bundle->_bundleClasses == nil) + { + bundle->_bundleClasses + = [[NSMutableArray alloc] initWithCapacity: 2]; + } + [bundle->_bundleClasses addObject: + [NSValue valueWithNonretainedObject: aClass]]; + } } + [load_lock unlock]; return bundle; } @@ -1132,7 +1181,13 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) [super dealloc]; } -- (NSString *) bundlePath +- (NSString*) description +{ + return [[super description] stringByAppendingFormat: + @" <%@>%@", [self bundlePath], [self isLoaded] ? @" (loaded)" : @""]; +} + +- (NSString*) bundlePath { return _path; } @@ -1162,6 +1217,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) BOOL found = NO; theClass = NSClassFromString(className); + [load_lock lock]; j = [_bundleClasses count]; for (i = 0; i < j && found == NO; i++) @@ -1173,6 +1229,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) found = YES; } } + [load_lock unlock]; if (found == NO) { @@ -1185,39 +1242,38 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) - (Class) principalClass { - NSString* class_name; + NSString *class_name; if (_principalClass) { return _principalClass; } - class_name = [[self infoDictionary] objectForKey: @"NSPrincipalClass"]; - - if (self == _mainBundle || self == _gnustep_bundle) - { - _codeLoaded = YES; - if (class_name) - { - _principalClass = NSClassFromString(class_name); - } - return _principalClass; - } - if ([self load] == NO) { return Nil; } + class_name = [[self infoDictionary] objectForKey: @"NSPrincipalClass"]; + if (class_name) { _principalClass = NSClassFromString(class_name); } - - if (!_principalClass && [_bundleClasses count]) + else if (self == _gnustep_bundle) { - _principalClass = [[_bundleClasses objectAtIndex: 0] - nonretainedObjectValue]; + _principalClass = [NSObject class]; + } + + if (_principalClass == nil) + { + [load_lock lock]; + if (_principalClass == nil && [_bundleClasses count] > 0) + { + _principalClass = [[_bundleClasses objectAtIndex: 0] + nonretainedObjectValue]; + } + [load_lock unlock]; } return _principalClass; } @@ -1232,11 +1288,12 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) - (BOOL) load { - if (self == _mainBundle || self == _gnustep_bundle) + if (self == _mainBundle || self ->_bundleType == NSBUNDLE_LIBRARY) { _codeLoaded = YES; return YES; } + [load_lock lock]; if (!_codeLoaded) @@ -1253,7 +1310,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) return NO; } _loadingBundle = self; - _bundleClasses = RETAIN([NSMutableArray arrayWithCapacity: 2]); + _bundleClasses = [[NSMutableArray alloc] initWithCapacity: 2]; _loadingFrameworks = RETAIN([NSMutableArray arrayWithCapacity: 2]); /* This code is executed twice if a class linked in the bundle call a @@ -1295,8 +1352,8 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) classEnumerator = [_bundleClasses objectEnumerator]; while ((class = [classEnumerator nextObject]) != nil) { - [classNames addObject: NSStringFromClass([class - nonretainedObjectValue])]; + [classNames addObject: + NSStringFromClass([class nonretainedObjectValue])]; } [load_lock unlock]; @@ -1615,7 +1672,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) return dict; } -- (NSArray *)localizations +- (NSArray *) localizations { NSString *locale; NSArray *localizations; @@ -1633,7 +1690,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) return [array makeImmutableCopyOnFail: NO]; } -- (NSArray *)preferredLocalizations +- (NSArray *) preferredLocalizations { return [NSBundle preferredLocalizationsFromArray: [self localizations]]; } @@ -1796,6 +1853,10 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) { return ExecutablePath(); } + if (self->_bundleType == NSBUNDLE_LIBRARY) + { + return objc_get_symbol_path ([self principalClass], NULL); + } object = [[self infoDictionary] objectForKey: @"NSExecutable"]; if (object == nil || [object length] == 0) { @@ -1884,7 +1945,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) return _infoDict; } -- (NSString *)builtInPlugInsPath +- (NSString *) builtInPlugInsPath { NSString *version = _frameworkVersion; @@ -1907,17 +1968,17 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) } } -- (NSString *)bundleIdentifier +- (NSString*) bundleIdentifier { return [[self infoDictionary] objectForKey:@"CFBundleIdentifier"]; } -- (unsigned)bundleVersion +- (unsigned) bundleVersion { return _version; } -- (void)setBundleVersion:(unsigned)version +- (void) setBundleVersion: (unsigned)version { _version = version; } @@ -1926,11 +1987,30 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) @implementation NSBundle (GNUstep) -/** Return a bundle which accesses the first existing directory from the list - GNUSTEP_USER_ROOT/Libraries/Resources/libraryName/ - GNUSTEP_NETWORK_ROOT/Libraries/Resources/libraryName/ - GNUSTEP_LOCAL_ROOT/Libraries/Resources/libraryName/ - GNUSTEP_SYSTEM_ROOT/Libraries/Resources/libraryName/ +/** + *

Return a bundle which accesses the first existing directory from the list + * GNUSTEP_USER_ROOT/Libraries/Resources/libraryName/ + * GNUSTEP_NETWORK_ROOT/Libraries/Resources/libraryName/ + * GNUSTEP_LOCAL_ROOT/Libraries/Resources/libraryName/ + * GNUSTEP_SYSTEM_ROOT/Libraries/Resources/libraryName/
+ * Where libraryName is the name of a library without the lib + * prefix or any extensions. + *

+ *

This method exists to provide resource bundles for libraries and hos no + * particular relationship to the library code itsself. The named library + * could be a dynamic library linked in to the running program, a static + * library (whose code may not even exist on the host machine except where + * it is linked in to the program), or even a library which is not linked + * into the program at all (eg. where you want to share resources provided + * for a library you do not actually use). + *

+ *

The bundle for the library gnustep-base is a special case ... + * for this bundle the -principalClass method returns [NSObject] and the + * -executablePath method returns the path to the gnustep-base dynamic + * library (if it can be found). As a general rule, library bundles are + * not guaranteed to return values for these methods as the library may + * not exist on disk. + *

*/ + (NSBundle *) bundleForLibrary: (NSString *)libraryName { @@ -1940,7 +2020,14 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) NSString *tail; NSFileManager *fm = [NSFileManager defaultManager]; - if (libraryName == nil) + libraryName = [libraryName lastPathComponent]; + do + { + libraryName = [libraryName stringByDeletingPathExtension]; + } + while ([[libraryName pathExtension] length] > 0); + + if ([libraryName length] == 0) { return nil; } @@ -1958,7 +2045,13 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory) if ([fm fileExistsAtPath: path isDirectory: &isDir] && isDir) { - return [self bundleWithPath: path]; + NSBundle *b = [self bundleWithPath: path]; + + if (b != nil && b->_bundleType == NSBUNDLE_BUNDLE) + { + b->_bundleType = NSBUNDLE_LIBRARY; + } + return b; } }