DynaLoader: Introduce d_libname_unique
authorBrian Fraser <fraserbn@gmail.com>
Wed, 15 May 2013 11:52:18 +0000 (08:52 -0300)
committerBrian Fraser <fraserbn@gmail.com>
Fri, 3 Jan 2014 22:45:06 +0000 (19:45 -0300)
Android's linker has some unusual behavior, in that it only uses
the basename of a library in its cache.  That means that, as far as
dlopen() is concerned, the libraries for Hash::Util and List::Util,
both of which are called Util.so, are the same.

This commit teaches DynaLoader about d_libname_unique.  When
defined, it signals DynaLoader define a mod2fname sub that renames
the .so files to something "unique" -- so for example,
Hash/Util/Util.so becomes Hash/Util/PL_Hash__Util.so.

dist/XSLoader/XSLoader_pm.PL
ext/DynaLoader/DynaLoader_pm.PL
ext/DynaLoader/t/DynaLoader.t

index fb5707c..e382058 100644 (file)
@@ -1,5 +1,7 @@
 use strict;
 use Config;
+# We require DynaLoader to make sure that mod2fname is loaded
+eval { require DynaLoader };
 
 1 while unlink "XSLoader.pm";
 open OUT, ">XSLoader.pm" or die $!;
@@ -8,7 +10,7 @@ print OUT <<'EOT';
 
 package XSLoader;
 
-$VERSION = "0.16";
+$VERSION = "0.17";
 
 #use strict;
 
@@ -201,7 +203,7 @@ XSLoader - Dynamically load C libraries into Perl code
 
 =head1 VERSION
 
-Version 0.16
+Version 0.17
 
 =head1 SYNOPSIS
 
index f0139f9..6c2a3e6 100644 (file)
@@ -85,7 +85,7 @@ package DynaLoader;
 # Tim.Bunce@ig.co.uk, August 1994
 
 BEGIN {
-    $VERSION = '1.22';
+    $VERSION = '1.23';
 }
 
 use Config;
@@ -235,6 +235,32 @@ if ($ENV{PERL_BUILD_EXPAND_CONFIG_VARS} && $ENV{PERL_BUILD_EXPAND_ENV_VARS}) {
 EOT
 }
 
+if ( $Config::Config{d_libname_unique} ) {
+    print OUT <<'EOT';
+sub mod2fname {
+    my $parts = shift;
+    my $so_len = length($Config::Config{dlext})+1;
+    my $name_max = 255; # No easy way to get this here
+    
+    my $libname = "PL_" .  join("__", @$parts);
+    
+    return $libname if (length($libname)+$so_len) <= $name_max;
+    
+    # It's too darned big, so we need to go strip. We use the same
+    # algorithm as xsubpp does. First, strip out doubled __
+    $libname =~ s/__/_/g;
+    return $libname if (length($libname)+$so_len) <= $name_max;
+    
+    # Strip duplicate letters
+    1 while $libname =~ s/(.)\1/\U$1/i;
+    return $libname if (length($libname)+$so_len) <= $name_max;
+    
+    # Still too long. Truncate.
+    $libname = substr($libname, 0, $name_max - $so_len);
+    return $libname;
+}
+EOT
+}
 
 # following long string contains $^O-specific stuff, which is factored out
 print OUT expand_os_specific(<<'EOT');
@@ -314,7 +340,7 @@ sub bootstrap {
        next unless -d $dir; # skip over uninteresting directories
        
        # check for common cases to avoid autoload of dl_findfile
-       my $try = "$dir/$modfname.$dl_dlext";
+        my $try = "$dir/$modfname.$dl_dlext";
        last if $file = ($do_expand) ? dl_expandspec($try) : ((-f $try) && $try);
        
        # no luck here, save dir for possible later dl_findfile search
index 1aa8c4e..a95287a 100644 (file)
@@ -26,7 +26,7 @@ BEGIN {
     'Time::HiRes'=> q| ::is( ref Time::HiRes->can('usleep'),'CODE' ) |,  # 5.7.3
 );
 
-plan tests => 22 + keys(%modules) * 3;
+plan tests => 26 + keys(%modules) * 3;
 
 
 # Try to load the module
@@ -155,3 +155,32 @@ for my $libref (reverse @DynaLoader::dl_librefs) {
         }
     }
 }
+
+SKIP: {
+    skip "mod2fname not defined on this platform", 4
+        unless defined &DynaLoader::mod2fname && $Config{d_libname_unique};
+
+    is(
+        DynaLoader::mod2fname(["Hash", "Util"]),
+        "PL_Hash__Util",
+        "mod2fname + libname_unique works"
+    );
+
+    is(
+        DynaLoader::mod2fname([("Hash", "Util") x 25]),
+        "PL_" . join("_", ("Hash", "Util")x25),
+        "mod2fname + libname_unique collapses double __'s for long names"
+    );
+
+    is(
+        DynaLoader::mod2fname([("Haash", "Uttil") x 25]),
+        "PL_" . join("_", ("HAsh", "UTil")x25),
+        "mod2fname + libname_unique collapses repeated characters for long names"
+    );
+
+    is(
+        DynaLoader::mod2fname([("Hash", "Util")x30]),
+        substr(("PL_" . join("_", ("Hash", "Util")x30)), 0, 255 - (length($Config::Config{dlext})+1)),
+        "mod2fname + libname_unique correctly truncates long names"
+    );
+}