Improvements for github issue 352

This commit is contained in:
rfm 2023-11-23 16:39:58 +00:00
parent c1c17df624
commit 07fed93200
4 changed files with 157 additions and 140 deletions

View file

@ -1,3 +1,13 @@
2023-11-23 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSBundle.m:
* Source/GSPrivate.h:
* Headers/Foundation/NSBundle.m:
Make +bundleForLibrary:version: behavior match documenation.
Make +bundleForClass: call new private method to get library name
and version, warnign if version cannot be determined.
Mark +bundleForLibrary: as deprecated since we want a version.
2023-11-21 Richard Frith-Macdonald <rfm@gnu.org>
* Source/objc-load.m: Fix to get library path in standardised form to

View file

@ -530,13 +530,12 @@ GS_EXPORT_CLASS
* not exist on disk.
* </p>
*/
+ (NSBundle *) bundleForLibrary: (NSString *)libraryName
version: (NSString *)interfaceVersion;
+ (NSBundle *) bundleForLibrary: (NSString*)libraryName
version: (NSString*)interfaceVersion;
/** This method is a equivalent to bundleForLibrary:version: with a nil
* version.
/** Use +bundleForLibrary:version: instead.
*/
+ (NSBundle *) bundleForLibrary: (NSString *)libraryName;
+ (NSBundle*) bundleForLibrary: (NSString*)libraryName NS_DEPRECATED();

View file

@ -279,6 +279,7 @@ typedef enum {
+ (NSString*) _gnustep_target_dir;
+ (NSString*) _gnustep_target_os;
+ (NSString*) _library_combo;
+ (NSString*) _versionForLibrary: (NSString**)path;
@end
/**

View file

@ -986,6 +986,115 @@ _find_main_bundle_for_tool(NSString *toolName)
return library_combo;
}
+ (NSString*) _versionForLibrary: (NSString**)name
{
NSString *libraryName;
NSString *ver = nil;
NSRange r;
if (NULL == name || nil == (libraryName = *name))
{
return nil;
}
/*
* Eliminate any base path or extensions.
*/
libraryName = [libraryName lastPathComponent];
#if defined(_WIN32)
/* A dll is usually of the form 'xxx-maj_min.dll'
* so we can extract the version info and use it.
*/
if ([[libraryName pathExtension] isEqual: @"dll"])
{
libraryName = [libraryName stringByDeletingPathExtension];
r = [libraryName rangeOfString: @"-" options: NSBackwardsSearch];
if (r.length > 0)
{
ver = [[libraryName substringFromIndex: NSMaxRange(r)]
stringByReplacingString: @"_" withString: @"."];
}
}
#elif defined(__APPLE__)
/* A .dylib is usually of the form 'libxxx.maj.min.sub.dylib',
* but GNUstep-make installs them with 'libxxx.dylib.maj.min.sub'.
* For maximum compatibility with support both forms here.
*/
if ([[libraryName pathExtension] isEqual: @"dylib"])
{
NSString *s = [libraryName stringByDeletingPathExtension];
NSArray *a = [s componentsSeparatedByString: @"."];
if ([a count] > 1)
{
libraryName = [a objectAtIndex: 0];
if ([a count] >= 3)
{
ver = [NSString stringWithFormat: @"%@.%@",
[a objectAtIndex: 1], [a objectAtIndex: 2]];
}
}
}
else
{
r = [libraryName rangeOfString: @".dylib."];
if (r.length > 0)
{
NSString *s = [libraryName substringFromIndex: NSMaxRange(r)];
NSArray *a = [s componentsSeparatedByString: @"."];
libraryName = [libraryName substringToIndex: r.location];
if ([a count] >= 2)
{
ver = [NSString stringWithFormat: @"%@.%@",
[a objectAtIndex: 0], [a objectAtIndex: 1]];
}
}
}
#else
/* A .so is usually of the form 'libxxx.so.maj.min.sub'
* so we can extract the version info and use it.
*/
r = [libraryName rangeOfString: @".so."];
if (r.length > 0)
{
NSString *s = [libraryName substringFromIndex: NSMaxRange(r)];
NSArray *a = [s componentsSeparatedByString: @"."];
libraryName = [libraryName substringToIndex: r.location];
if ([a count] >= 2)
{
ver = [NSString stringWithFormat: @"%@.%@",
[a objectAtIndex: 0], [a objectAtIndex: 1]];
}
}
#endif
while ([[libraryName pathExtension] length] > 0)
{
libraryName = [libraryName stringByDeletingPathExtension];
}
/*
* Discard leading 'lib'
*/
if ([libraryName hasPrefix: @"lib"] == YES)
{
libraryName = [libraryName substringFromIndex: 3];
}
if (0 == [libraryName length])
{
libraryName = nil;
}
if (name)
{
*name = libraryName;
}
return ver;
}
@end
/*
@ -1556,28 +1665,36 @@ _bundle_load_callback(Class theClass, struct objc_category *theCategory)
/* Is it in the main bundle or a library? */
if (!class_isMetaClass(aClass))
{
NSString *lib;
NSString *path;
/*
* 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.
/* If the class is defined in a file other than the executable
* it must be in a library or framework/bundle.
*/
lib = GSPrivateSymbolPath(aClass);
if ([lib isEqual: GSPrivateExecutablePath()] == YES)
path = GSPrivateSymbolPath(aClass);
if ([path isEqual: GSPrivateExecutablePath()] == NO)
{
lib = nil; // In program, not library.
}
NSString *libraryName = path;
NSString *ver = [self _versionForLibrary: &libraryName];
/*
* Get the library bundle ... if there wasn't one then we
* will check to see if it's in a newly loaded framework
* and if not, assume the class was in the program executable
* and return the mainBundle instead.
*/
bundle = [NSBundle bundleForLibrary: lib];
NSDebugLLog(@"NSBundle", @"NSBundle bundleForClass: looking up %@ in bundle %@", lib, bundle);
if (nil == bundle && [[self _addFrameworks] count] > 0)
if (nil == ver)
{
NSLog(@"Warning: [%@+%@] unable to determine"
@" version of library '%@' containing '%@'",
NSStringFromClass(self), NSStringFromSelector(_cmd),
path, NSStringFromClass(aClass));
}
/* Get the library bundle ... if there wasn't one then we
* will check to see if it's in a newly loaded framework
* and if not, assume the class was in the program executable
* and return the mainBundle instead.
*/
bundle = [NSBundle bundleForLibrary: libraryName
version: ver];
}
NSDebugLLog(@"NSBundle",
@"NSBundle bundleForClass: looking up %@ in bundle %@",
NSStringFromClass(aClass), bundle);
if (nil == bundle && [[self _addFrameworks] count] > 0)
{
bundle = (NSBundle *)NSMapGet(_byClass, aClass);
if ((id)bundle == (id)[NSNull null])
@ -2977,13 +3094,15 @@ IF_NO_ARC(
@implementation NSBundle (GNUstep)
+ (NSBundle *) bundleForLibrary: (NSString *)libraryName
+ (NSBundle*) bundleForLibrary: (NSString*)libraryName
{
return [self bundleForLibrary: libraryName version: nil];
NSString *ver = [self _versionForLibrary: &libraryName];
return [self bundleForLibrary: libraryName version: ver];
}
+ (NSBundle *) bundleForLibrary: (NSString *)libraryName
version: (NSString *)interfaceVersion
+ (NSBundle*) bundleForLibrary: (NSString*)libraryName
version: (NSString*)interfaceVersion
{
/* Important: if you change this code, make sure to also
* change NSUserDefault's manual gnustep-base resource
@ -2993,118 +3112,6 @@ IF_NO_ARC(
NSEnumerator *enumerator;
NSString *path = libraryName;
NSFileManager *fm = manager();
NSRange r;
if ([libraryName length] == 0)
{
return nil;
}
/*
* Eliminate any base path or extensions.
*/
libraryName = [libraryName lastPathComponent];
#if defined(_WIN32)
/* A dll is usually of the form 'xxx-maj_min.dll'
* so we can extract the version info and use it.
*/
if ([[libraryName pathExtension] isEqual: @"dll"])
{
libraryName = [libraryName stringByDeletingPathExtension];
r = [libraryName rangeOfString: @"-" options: NSBackwardsSearch];
if (r.length > 0)
{
NSString *ver;
ver = [[libraryName substringFromIndex: NSMaxRange(r)]
stringByReplacingString: @"_" withString: @"."];
libraryName = [libraryName substringToIndex: r.location];
if (interfaceVersion == nil)
{
interfaceVersion = ver;
}
}
}
#elif defined(__APPLE__)
/* A .dylib is usually of the form 'libxxx.maj.min.sub.dylib',
* but GNUstep-make installs them with 'libxxx.dylib.maj.min.sub'.
* For maximum compatibility with support both forms here.
*/
if ([[libraryName pathExtension] isEqual: @"dylib"])
{
NSString *s = [libraryName stringByDeletingPathExtension];
NSArray *a = [s componentsSeparatedByString: @"."];
if ([a count] > 1)
{
libraryName = [a objectAtIndex: 0];
if (interfaceVersion == nil && [a count] >= 3)
{
interfaceVersion = [NSString stringWithFormat: @"%@.%@",
[a objectAtIndex: 1], [a objectAtIndex: 2]];
}
}
}
else
{
r = [libraryName rangeOfString: @".dylib."];
if (r.length > 0)
{
NSString *s = [libraryName substringFromIndex: NSMaxRange(r)];
NSArray *a = [s componentsSeparatedByString: @"."];
libraryName = [libraryName substringToIndex: r.location];
if (interfaceVersion == nil && [a count] >= 2)
{
interfaceVersion = [NSString stringWithFormat: @"%@.%@",
[a objectAtIndex: 0], [a objectAtIndex: 1]];
}
}
}
#else
/* A .so is usually of the form 'libxxx.so.maj.min.sub'
* so we can extract the version info and use it.
*/
r = [libraryName rangeOfString: @".so."];
if (r.length > 0)
{
NSString *s = [libraryName substringFromIndex: NSMaxRange(r)];
NSArray *a = [s componentsSeparatedByString: @"."];
libraryName = [libraryName substringToIndex: r.location];
if (interfaceVersion == nil && [a count] >= 2)
{
interfaceVersion = [NSString stringWithFormat: @"%@.%@",
[a objectAtIndex: 0], [a objectAtIndex: 1]];
}
}
#endif
if (nil == interfaceVersion
&& NO == [libraryName isEqualToString: @"gnustep-base"])
{
NSLog(@"Warning: [%@+%@] called without version and unable to"
@" infer version from library name (%@).",
NSStringFromClass(self), NSStringFromSelector(_cmd), path);
}
while ([[libraryName pathExtension] length] > 0)
{
libraryName = [libraryName stringByDeletingPathExtension];
}
/*
* Discard leading 'lib'
*/
if ([libraryName hasPrefix: @"lib"] == YES)
{
libraryName = [libraryName substringFromIndex: 3];
}
if ([libraryName length] == 0)
{
return nil;
}
/*
* We expect to find the library resources in the GNUSTEP_LIBRARY domain in:
@ -3118,7 +3125,7 @@ IF_NO_ARC(
* Libraries/Resources/<libraryName>/
*
*/
paths = NSSearchPathForDirectoriesInDomains (NSLibraryDirectory,
paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSAllDomainsMask, YES);
enumerator = [paths objectEnumerator];