Implemented locating frameworks which are linked into the application (or

into a bundle, or another framework, which is loaded dynamically) by using
objc_get_symbol_path() if available.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@14880 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Nicola Pero 2002-10-30 23:04:06 +00:00
parent 21474cad2a
commit b2b6c3d947
2 changed files with 171 additions and 60 deletions

View file

@ -1,3 +1,10 @@
Wed Oct 30 23:42:50 2002 Nicola Pero <n.pero@mi.flashnet.it>
* Source/NSBundle.m ([+_addFrameworkFromClass:]): New code
locating linked frameworks magically - no matter where they are on
disk - by using objc_get_symbol_path(). If that fails or is not
available, fall back on the existing code. Fixed NSLog() message.
2002-10-30 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSThread.m: Implemented and documented new MacOS-X methods -

View file

@ -229,7 +229,7 @@ _bundle_name_first_match(NSString* directory, NSString* name)
@implementation NSBundle (Private)
/* Nicola:
/* Nicola & Mirko:
Frameworks can be used in an application in two different ways:
@ -247,17 +247,23 @@ _bundle_name_first_match(NSString* directory, NSString* name)
disk, and we have no way of knowing what the class list is, or the
version. So the trick we use in this case to work around those
problems is that gnustep-make generates a 'NSFramework_xxx' class
and compiles it into each framework. By asking the class, we can
get the version information, the list of classes which were
compiled into the framework, and the path were the framework bundle
was supposed to be installed (this is not completely reliable as
the programmer might change it later on by specifying a different
and compiles it into each framework. By asking to the class, we
can get the version information and the list of classes which were
compiled into the framework. To get the location of the framework
on disk, we try using advanced dynamic linker features to get the
shared object file on disk from which the NSFramework_xxx class was
loaded. If that doesn't work, because the dynamic linker can't
provide this information on this platform (or maybe because the
framework was statically linked into the application), we have a
fallback trick :-) we can ask to the NSFramework_xxx class the path
to were the framework bundle was supposed to be installed (this is
recorded the first time when the NSFramework_xxx class is generated
at compile time; it's not completely reliable - for example the
programmer might change it later on by specifying a different
GNUSTEP_INSTALLATION_DIR when installing ... (TODO - modify
gnustep-make so that it issues a warning if you do that, suggesting
you to recompile before installing!)). (FIXME/TODO: If the linker
supports it, we should be asking the path on disk to the shared
object containing the NSFramework_xxx class ... and we can very
reliably guess the framework path from that path!!!!).
you to recompile before installing!)), and search for the framework
there.
So at startup, we scan all classes which were compiled into the
application. For each NSFramework_ class, we call the following
@ -265,17 +271,18 @@ _bundle_name_first_match(NSString* directory, NSString* name)
the classes belonging to it, and tries to determine the path
on disk to the framework bundle.
Mirko:
Bundles (ie frameworks) could depend to other frameworks (linked
togheter on platform that supports this behaviour) so in
_bundle_load_callback() we construct a list of all NSFramework_* classes
loaded and call this method to build the correct list of bundles.
Bundles (and frameworks if dynamically loaded as bundles) could
depend on other frameworks (linked togheter on platform that
supports this behaviour) so whenever we dynamically load a bundle,
we need to spot out any additional NSFramework_* classes which are
loaded, and call this method (exactly as for frameworks linked into
the main application) to record them, and try finding the path on
disk to those framework bundles.
*/
+ (void) _addFrameworkFromClass: (Class)frameworkClass
{
NSBundle *bundle;
NSBundle *bundle = nil;
NSString **fmClasses;
NSString *bundlePath = nil;
int len;
@ -290,63 +297,150 @@ _bundle_name_first_match(NSString* directory, NSString* name)
if (len > 12 * sizeof(char)
&& !strncmp("NSFramework_", frameworkClass->name, 12))
{
NSString *varEnv, *path, *name;
/* The name of the framework. */
name = [NSString stringWithCString: &frameworkClass->name[12]];
/* Create the framework bundle if the bundle was linked into the
application. */
NSString *name = [NSString stringWithCString: &frameworkClass->name[12]];
/* NICOLA: I think if we could get the path to the NSFramework_
class from the linker, then we could build the framework path
reliably. I consider the following only a hack. */
/* Try getting the path to the framework using the dynamic
* linker. When it works it's really cool :-) This is the only
* really universal way of getting the framework path ... we can
* locate the framework no matter where it is on disk!
*/
bundlePath = objc_get_symbol_path (frameworkClass, NULL);
/* varEnv is something like GNUSTEP_LOCAL_ROOT. */
varEnv = [frameworkClass frameworkEnv];
if (varEnv != nil && [varEnv length] > 0)
if ([bundlePath isEqualToString: _executable_path])
{
/* FIXME - I don't think we should be reading it from
the environment directly! */
bundlePath = [[[NSProcessInfo processInfo] environment]
objectForKey: varEnv];
/* Ops ... the NSFramework_xxx class is linked in the main
* executable. Maybe the framework was statically linked
* into the application ... resort to searching the
* framework bundle on the filesystem manually.
*/
bundlePath = nil;
}
/* FIXME - path is something like ?. */
path = [frameworkClass frameworkPath];
if (path && [path length])
if (bundlePath != nil)
{
if (bundlePath)
NSString *pathComponent;
/* Dereference symlinks, and standardize path. This will
* only work properly if the original bundlePath is
* absolute. This should normally be the case if, as
* recommended, you use only absolute paths in
* LD_LIBRARY_PATH.
*/
bundlePath = [bundlePath stringByStandardizingPath];
/* We now have the location of the shared library object
* file inside the framework directory. We need to walk up
* the directory tree up to the top of the framework. To do
* so, we need to chop off the extra subdirectories, the
* library combo and the target cpu/os if they exist. The
* framework and this library should match so we can use the
* compiled-in settings.
*/
/* library name */
bundlePath = [bundlePath stringByDeletingLastPathComponent];
/* library combo */
pathComponent = [bundlePath lastPathComponent];
if ([pathComponent isEqual: library_combo])
{
bundlePath = [bundlePath stringByAppendingPathComponent: path];
bundlePath = [bundlePath stringByDeletingLastPathComponent];
}
/* target os */
pathComponent = [bundlePath lastPathComponent];
if ([pathComponent isEqual: gnustep_target_os])
{
bundlePath = [bundlePath stringByDeletingLastPathComponent];
}
/* target cpu */
pathComponent = [bundlePath lastPathComponent];
if ([pathComponent isEqual: gnustep_target_cpu])
{
bundlePath = [bundlePath stringByDeletingLastPathComponent];
}
/* version name */
bundlePath = [bundlePath stringByDeletingLastPathComponent];
pathComponent = [bundlePath lastPathComponent];
if ([pathComponent isEqual: @"Versions"])
{
bundlePath = [bundlePath stringByDeletingLastPathComponent];
pathComponent = [bundlePath lastPathComponent];
if ([pathComponent isEqualToString:
[NSString stringWithFormat: @"%@%@",
name, @".framework"]])
{
/* Try creating the bundle. */
bundle = [[self alloc] initWithPath: bundlePath];
}
}
/* Failed - buu - try the fallback trick. */
if (bundle == nil)
{
bundlePath = nil;
}
}
if (bundlePath == nil)
{
/* NICOLA: In an ideal world, the following is just a hack
* for when objc_get_symbol_path() fails! But in real life
* objc_get_symbol_path() is risky (some platforms don't
* have it at all!), so this hack might be used a lot! It
* must be quite robust. We try to look for the framework
* in the standard GNUstep installation dirs. This should
* be reasonably safe if the user is not being too clever
* ... :-)
*/
NSString *varEnv, *path;
/* varEnv is something like GNUSTEP_LOCAL_ROOT. */
varEnv = [frameworkClass frameworkEnv];
if (varEnv != nil && [varEnv length] > 0)
{
/* FIXME - I don't think we should be reading it from
the environment directly! */
bundlePath = [[[NSProcessInfo processInfo] environment]
objectForKey: varEnv];
}
/* FIXME - path is something like ?. */
path = [frameworkClass frameworkPath];
if (path && [path length])
{
if (bundlePath)
{
bundlePath = [bundlePath stringByAppendingPathComponent:
path];
}
else
{
bundlePath = path;
}
}
else
{
bundlePath = path;
bundlePath = [bundlePath stringByAppendingPathComponent:
@"Library/Frameworks"];
}
}
else
{
bundlePath = [bundlePath stringByAppendingPathComponent:
@"Library/Frameworks"];
}
bundlePath = [bundlePath stringByAppendingPathComponent:
[NSString stringWithFormat:
@"%@.framework",
name]];
/* Try creating the bundle. */
bundle = [[self alloc] initWithPath: bundlePath];
bundlePath = [bundlePath stringByAppendingPathComponent:
[NSString stringWithFormat:
@"%@.framework",
name]];
/* Try creating the bundle. */
bundle = [[self alloc] initWithPath: bundlePath];
}
if (bundle == nil)
{
/* TODO: We couldn't locate the framework in the expected
location. NICOLA: We should be trying to locate it in
some other obvious location, iterating over
user/network/local/system dirs ... TODO. */
NSLog (@"Problem locating framework locating for framework %@",
name);
NSLog (@"Problem locating framework %@", name);
return;
}
@ -372,9 +466,18 @@ _bundle_name_first_match(NSString* directory, NSString* name)
fmClasses++;
}
if (_loadingBundle)
[(NSMutableArray *)[_loadingBundle _bundleClasses]
removeObjectsInArray: [bundle _bundleClasses]];
/* If _loadingBundle is not nil, it means we reached this point
* while loading a bundle. This can happen if the framework is
* linked into the bundle (then, the dynamic linker
* automatically drags in the framework when the bundle is
* loaded). But then, the classes in the framework should be
* removed from the list of classes in the bundle.
*/
if (_loadingBundle != nil)
{
[(NSMutableArray *)[_loadingBundle _bundleClasses]
removeObjectsInArray: [bundle _bundleClasses]];
}
}
}
@ -665,7 +768,8 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
&& ([s hasSuffix: @".debug"] == NO)
&& ([s hasSuffix: @".profile"] == NO))
// GNUstep Web
&& (([s hasSuffix: @".gswa"] == NO) && ([s hasSuffix: @".woa"] == NO)))
&& (([s hasSuffix: @".gswa"] == NO)
&& ([s hasSuffix: @".woa"] == NO)))
{
isApplication = NO;
}
@ -756,7 +860,7 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
/* Check if we were already initialized for this directory */
[load_lock lock];
if (_bundles)
if (_bundles)
{
NSBundle* bundle = (NSBundle *)NSMapGet(_bundles, path);
if (bundle)