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
This commit is contained in:
rfm 2006-05-05 11:06:57 +00:00
parent ba59f28d5d
commit 580cb5fbda
3 changed files with 166 additions and 61 deletions

View file

@ -1,3 +1,14 @@
2006-05-05 Richard Frith-Macdonald <rfm@gnu.org>
* 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 <d.ayers@inode.at> 2006-04-30 David Ayers <d.ayers@inode.at>
* Source/NSString: Define _GNU_SOURCE to make protoype of fwprintf * Source/NSString: Define _GNU_SOURCE to make protoype of fwprintf

View file

@ -33,6 +33,7 @@
@class NSString; @class NSString;
@class NSArray; @class NSArray;
@class NSDictionary; @class NSDictionary;
@class NSMutableArray;
@class NSMutableDictionary; @class NSMutableDictionary;
/** /**
@ -91,15 +92,15 @@ GS_EXPORT NSString* const NSLoadedClasses;
*/ */
@interface NSBundle : NSObject @interface NSBundle : NSObject
{ {
NSString *_path; NSString *_path;
NSArray *_bundleClasses; NSMutableArray *_bundleClasses;
Class _principalClass; Class _principalClass;
NSDictionary *_infoDict; NSDictionary *_infoDict;
NSMutableDictionary *_localizations; NSMutableDictionary *_localizations;
unsigned _bundleType; unsigned _bundleType;
BOOL _codeLoaded; BOOL _codeLoaded;
unsigned _version; unsigned _version;
NSString *_frameworkVersion; NSString *_frameworkVersion;
} }
/** Return an array enumerating all the bundles in the application. This /** Return an array enumerating all the bundles in the application. This

View file

@ -64,7 +64,10 @@
@end @end
typedef enum { typedef enum {
NSBUNDLE_BUNDLE = 1, NSBUNDLE_APPLICATION, NSBUNDLE_FRAMEWORK NSBUNDLE_BUNDLE = 1,
NSBUNDLE_APPLICATION,
NSBUNDLE_FRAMEWORK,
NSBUNDLE_LIBRARY
} bundle_t; } bundle_t;
/* Class variables - We keep track of all the bundles */ /* Class variables - We keep track of all the bundles */
@ -378,7 +381,6 @@ _find_framework(NSString *name)
@interface NSBundle (Private) @interface NSBundle (Private)
+ (NSString *) _absolutePathOfExecutable: (NSString *)path; + (NSString *) _absolutePathOfExecutable: (NSString *)path;
+ (void) _addFrameworkFromClass: (Class)frameworkClass; + (void) _addFrameworkFromClass: (Class)frameworkClass;
- (NSArray *) _bundleClasses;
+ (NSString*) _gnustep_target_cpu; + (NSString*) _gnustep_target_cpu;
+ (NSString*) _gnustep_target_dir; + (NSString*) _gnustep_target_dir;
+ (NSString*) _gnustep_target_os; + (NSString*) _gnustep_target_os;
@ -604,7 +606,7 @@ _find_framework(NSString *name)
value = [NSValue valueWithNonretainedObject: class]; value = [NSValue valueWithNonretainedObject: class];
[(NSMutableArray *)[bundle _bundleClasses] addObject: value]; [bundle->_bundleClasses addObject: value];
fmClasses++; fmClasses++;
} }
@ -620,17 +622,12 @@ _find_framework(NSString *name)
*/ */
if (_loadingBundle != nil && _loadingBundle != bundle) if (_loadingBundle != nil && _loadingBundle != bundle)
{ {
[(NSMutableArray *)[_loadingBundle _bundleClasses] [_loadingBundle->_bundleClasses
removeObjectsInArray: [bundle _bundleClasses]]; removeObjectsInArray: bundle->_bundleClasses];
} }
} }
} }
- (NSArray *) _bundleClasses
{
return _bundleClasses;
}
+ (NSString*) _gnustep_target_cpu + (NSString*) _gnustep_target_cpu
{ {
return gnustep_target_cpu; return gnustep_target_cpu;
@ -662,6 +659,10 @@ _find_framework(NSString *name)
*/ */
typedef struct {
@defs(NSBundle)
} *bptr;
void void
_bundle_load_callback(Class theClass, struct objc_category *theCategory) _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) */ /* Store classes (but don't store categories) */
[(NSMutableArray *)[_loadingBundle _bundleClasses] addObject: [((bptr)_loadingBundle)->_bundleClasses addObject:
[NSValue valueWithNonretainedObject: (id)theClass]]; [NSValue valueWithNonretainedObject: (id)theClass]];
} }
@implementation NSBundle @implementation NSBundle
+ (void)initialize + (void) initialize
{ {
if (self == [NSBundle class]) if (self == [NSBundle class])
{ {
@ -1004,7 +1005,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
while (NSNextMapEnumeratorPair(&enumerate, &key, (void **)&bundle)) while (NSNextMapEnumeratorPair(&enumerate, &key, (void **)&bundle))
{ {
int i, j; int i, j;
NSArray *bundleClasses = [bundle _bundleClasses]; NSArray *bundleClasses = bundle->_bundleClasses;
BOOL found = NO; BOOL found = NO;
j = [bundleClasses count]; j = [bundleClasses count];
@ -1020,13 +1021,61 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
bundle = nil; 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)) 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; return bundle;
} }
@ -1132,7 +1181,13 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
[super dealloc]; [super dealloc];
} }
- (NSString *) bundlePath - (NSString*) description
{
return [[super description] stringByAppendingFormat:
@" <%@>%@", [self bundlePath], [self isLoaded] ? @" (loaded)" : @""];
}
- (NSString*) bundlePath
{ {
return _path; return _path;
} }
@ -1162,6 +1217,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
BOOL found = NO; BOOL found = NO;
theClass = NSClassFromString(className); theClass = NSClassFromString(className);
[load_lock lock];
j = [_bundleClasses count]; j = [_bundleClasses count];
for (i = 0; i < j && found == NO; i++) for (i = 0; i < j && found == NO; i++)
@ -1173,6 +1229,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
found = YES; found = YES;
} }
} }
[load_lock unlock];
if (found == NO) if (found == NO)
{ {
@ -1185,39 +1242,38 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
- (Class) principalClass - (Class) principalClass
{ {
NSString* class_name; NSString *class_name;
if (_principalClass) if (_principalClass)
{ {
return _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) if ([self load] == NO)
{ {
return Nil; return Nil;
} }
class_name = [[self infoDictionary] objectForKey: @"NSPrincipalClass"];
if (class_name) if (class_name)
{ {
_principalClass = NSClassFromString(class_name); _principalClass = NSClassFromString(class_name);
} }
else if (self == _gnustep_bundle)
if (!_principalClass && [_bundleClasses count])
{ {
_principalClass = [[_bundleClasses objectAtIndex: 0] _principalClass = [NSObject class];
nonretainedObjectValue]; }
if (_principalClass == nil)
{
[load_lock lock];
if (_principalClass == nil && [_bundleClasses count] > 0)
{
_principalClass = [[_bundleClasses objectAtIndex: 0]
nonretainedObjectValue];
}
[load_lock unlock];
} }
return _principalClass; return _principalClass;
} }
@ -1232,11 +1288,12 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
- (BOOL) load - (BOOL) load
{ {
if (self == _mainBundle || self == _gnustep_bundle) if (self == _mainBundle || self ->_bundleType == NSBUNDLE_LIBRARY)
{ {
_codeLoaded = YES; _codeLoaded = YES;
return YES; return YES;
} }
[load_lock lock]; [load_lock lock];
if (!_codeLoaded) if (!_codeLoaded)
@ -1253,7 +1310,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
return NO; return NO;
} }
_loadingBundle = self; _loadingBundle = self;
_bundleClasses = RETAIN([NSMutableArray arrayWithCapacity: 2]); _bundleClasses = [[NSMutableArray alloc] initWithCapacity: 2];
_loadingFrameworks = RETAIN([NSMutableArray arrayWithCapacity: 2]); _loadingFrameworks = RETAIN([NSMutableArray arrayWithCapacity: 2]);
/* This code is executed twice if a class linked in the bundle call a /* 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]; classEnumerator = [_bundleClasses objectEnumerator];
while ((class = [classEnumerator nextObject]) != nil) while ((class = [classEnumerator nextObject]) != nil)
{ {
[classNames addObject: NSStringFromClass([class [classNames addObject:
nonretainedObjectValue])]; NSStringFromClass([class nonretainedObjectValue])];
} }
[load_lock unlock]; [load_lock unlock];
@ -1615,7 +1672,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
return dict; return dict;
} }
- (NSArray *)localizations - (NSArray *) localizations
{ {
NSString *locale; NSString *locale;
NSArray *localizations; NSArray *localizations;
@ -1633,7 +1690,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
return [array makeImmutableCopyOnFail: NO]; return [array makeImmutableCopyOnFail: NO];
} }
- (NSArray *)preferredLocalizations - (NSArray *) preferredLocalizations
{ {
return [NSBundle preferredLocalizationsFromArray: [self localizations]]; return [NSBundle preferredLocalizationsFromArray: [self localizations]];
} }
@ -1796,6 +1853,10 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
{ {
return ExecutablePath(); return ExecutablePath();
} }
if (self->_bundleType == NSBUNDLE_LIBRARY)
{
return objc_get_symbol_path ([self principalClass], NULL);
}
object = [[self infoDictionary] objectForKey: @"NSExecutable"]; object = [[self infoDictionary] objectForKey: @"NSExecutable"];
if (object == nil || [object length] == 0) if (object == nil || [object length] == 0)
{ {
@ -1884,7 +1945,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
return _infoDict; return _infoDict;
} }
- (NSString *)builtInPlugInsPath - (NSString *) builtInPlugInsPath
{ {
NSString *version = _frameworkVersion; 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"]; return [[self infoDictionary] objectForKey:@"CFBundleIdentifier"];
} }
- (unsigned)bundleVersion - (unsigned) bundleVersion
{ {
return _version; return _version;
} }
- (void)setBundleVersion:(unsigned)version - (void) setBundleVersion: (unsigned)version
{ {
_version = version; _version = version;
} }
@ -1926,11 +1987,30 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
@implementation NSBundle (GNUstep) @implementation NSBundle (GNUstep)
/** Return a bundle which accesses the first existing directory from the list /**
GNUSTEP_USER_ROOT/Libraries/Resources/libraryName/ * <p>Return a bundle which accesses the first existing directory from the list
GNUSTEP_NETWORK_ROOT/Libraries/Resources/libraryName/ * GNUSTEP_USER_ROOT/Libraries/Resources/libraryName/
GNUSTEP_LOCAL_ROOT/Libraries/Resources/libraryName/ * GNUSTEP_NETWORK_ROOT/Libraries/Resources/libraryName/
GNUSTEP_SYSTEM_ROOT/Libraries/Resources/libraryName/ * GNUSTEP_LOCAL_ROOT/Libraries/Resources/libraryName/
* GNUSTEP_SYSTEM_ROOT/Libraries/Resources/libraryName/<br />
* Where libraryName is the name of a library without the <em>lib</em>
* prefix or any extensions.
* </p>
* <p>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).
* </p>
* <p>The bundle for the library <em>gnustep-base</em> 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.
* </p>
*/ */
+ (NSBundle *) bundleForLibrary: (NSString *)libraryName + (NSBundle *) bundleForLibrary: (NSString *)libraryName
{ {
@ -1940,7 +2020,14 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
NSString *tail; NSString *tail;
NSFileManager *fm = [NSFileManager defaultManager]; 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; return nil;
} }
@ -1958,7 +2045,13 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
if ([fm fileExistsAtPath: path isDirectory: &isDir] && isDir) 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;
} }
} }