On Fri, Feb 25, 2011 at 6:07 PM, Maurice Mengel <[email protected]> wrote:
> 1) Corrections: XML::LibXSLT does show up in scandeps.pl output
Nevermind, found the problem and fixed it.
> On Fri, Feb 25, 2011 at 3:35 AM, Roderich Schupp
> <[email protected]> wrote:
>> Looking at LibXSLT.pm (line ~26):
>>
>> # avoid possible shared library name conflict on Win32
>> # not using this trick on 5.10.0 (suffering from DynaLoader bug)
>> local $DynaLoader::dl_dlext = "xs.$DynaLoader::dl_dlext" if (($^O eq
>> 'MSWin32') && ($] ne '5.010000'));
That actually causes the problem. XML::LibXSLT localizes
$DynaLoader::dl_dlext to
some non-standard value (would otherwise be the same as $Config::Config{dlext}).
This "tricks" DynaLoader to look for LibXSLT.xs.dll instead of LibXSLT.dll.
(It does that because the Perl module XML::LibXSLT is just a Perl wrapper
for the C library libxslt. Now that shared library might be installed
as libxslt.dll.
Nevermind that on Strawberry it's actually called libxslt1_.dll, just for the
sake of argument assume it were libxslt.dll. Now by convention (implemented
by DynaLoader) the Perl "glue" library of an XS module Foo::Bar would be
called Bar.dll and it would live in .../auto/Foo/Bar/Bar.dll. Because
Windows isn't case sensitive in the case of LibXSLT that would lead
to two libraries called libxslt.dll and LibXSLT.dll - the same name when
case doesn't matter. They would live in different locations, but
still the Windows shared library loader could get confused.)
OK, back to the problem. PAR::Packer (actually it's PAR) also plays
tricks on DynLoader. It does that to intercept the loading of shared
libraries so that they can actually be loaded from a .par archive
(or a packed executable). It duplicates part of DynaLoader's search
algorithm, esp. the above naming convention. But it doesn't use
$DynaLoader::dl_dlext, but $Config::Config{dlext}. So it can't "find"
LibXSLT.xs.dll (even though it was actually packed in).
The solution is easy: use $DynaLoader::dl_dlext in PAR, too.
Can you confirm that the following fixes you problem
(order of the steps below is crucial, so that PAR::Packer picks up
the fixed PAR and then packed executable pick up the fixed PAR::Packer):
(1) unpack a PAR tarball from CPAN and copy the attached file
over lib/PAR/Heavy.pm
(2) build and install PAR (perl Makefile.PL ... ?make install)
(3) rebuild and install PAR::Packer from scratch
(4) re-pack you script and test
Cheers, Roderich
package PAR::Heavy;
$PAR::Heavy::VERSION = '0.12';
=head1 NAME
PAR::Heavy - PAR guts
=head1 SYNOPSIS
(internal use only)
=head1 DESCRIPTION
No user-serviceable parts inside.
=cut
########################################################################
# Dynamic inclusion of XS modules
my ($bootstrap, $dl_findfile); # Caches for code references
my ($cache_key); # The current file to find
my $is_insensitive_fs = (
-s $0
and (-s lc($0) || -1) == (-s uc($0) || -1)
and (-s lc($0) || -1) == -s $0
);
# Adds pre-hooks to Dynaloader's key methods
sub _init_dynaloader {
return if $bootstrap;
return unless eval { require DynaLoader; DynaLoader::dl_findfile(); 1 };
$bootstrap = \&DynaLoader::bootstrap;
$dl_findfile = \&DynaLoader::dl_findfile;
local $^W;
*{'DynaLoader::dl_expandspec'} = sub { return };
*{'DynaLoader::bootstrap'} = \&_bootstrap;
*{'DynaLoader::dl_findfile'} = \&_dl_findfile;
}
# Return the cached location of .dll inside PAR first, if possible.
sub _dl_findfile {
return $FullCache{$cache_key} if exists $FullCache{$cache_key};
if ($is_insensitive_fs) {
# We have a case-insensitive filesystem...
my ($key) = grep { lc($_) eq lc($cache_key) } keys %FullCache;
return $FullCache{$key} if defined $key;
}
return $dl_findfile->(@_);
}
# Find and extract .dll from PAR files for a given dynamic module.
sub _bootstrap {
my (@args) = @_;
my ($module) = $args[0] or return;
my @modparts = split(/::/, $module);
my $modfname = $modparts[-1];
$modfname = &DynaLoader::mod2fname(\@modparts)
if defined &DynaLoader::mod2fname;
if (($^O eq 'NetWare') && (length($modfname) > 8)) {
$modfname = substr($modfname, 0, 8);
}
my $modpname = join((($^O eq 'MacOS') ? ':' : '/'), @modparts);
my $file = $cache_key = "auto/$modpname/$modfname.$DynaLoader::dl_dlext";
if ($FullCache{$file}) {
# TODO: understand
local $DynaLoader::do_expand = 1;
return $bootstrap->(@args);
}
my $member;
# First, try to find things in the peferentially loaded PARs:
$member = PAR::_find_par_internals([@PAR::PAR_INC], undef, $file, 1)
if defined &PAR::_find_par_internals;
# If that failed to find the dll, let DynaLoader (try or) throw an error
unless ($member) {
my $filename = eval { $bootstrap->(@args) };
return $filename if not $@ and defined $filename;
# Now try the fallback pars
$member = PAR::_find_par_internals([@PAR::PAR_INC_LAST], undef, $file, 1)
if defined &PAR::_find_par_internals;
# If that fails, let dynaloader have another go JUST to throw an error
# While this may seem wasteful, nothing really matters once we fail to
# load shared libraries!
unless ($member) {
return $bootstrap->(@args);
}
}
$FullCache{$file} = _dl_extract($member, $file);
# Now extract all associated shared objs in the same auto/ dir
# XXX: shouldn't this also set $FullCache{...} for those files?
my $first = $member->fileName;
my $path_pattern = $first;
$path_pattern =~ s{[^/]*$}{};
if ($PAR::LastAccessedPAR) {
foreach my $member ( $PAR::LastAccessedPAR->members ) {
next if $member->isDirectory;
my $name = $member->fileName;
next if $name eq $first;
next unless $name =~ m{^/?\Q$path_pattern\E\/[^/]*\.\Q$DynaLoader::dl_dlext\E[^/]*$};
$name =~ s{.*/}{};
_dl_extract($member, $file, $name);
}
}
local $DynaLoader::do_expand = 1;
return $bootstrap->(@args);
}
sub _dl_extract {
my ($member, $file, $name) = @_;
require File::Spec;
require File::Temp;
my ($fh, $filename);
# fix borked tempdir from earlier versions
if ($ENV{PAR_TEMP} and -e $ENV{PAR_TEMP} and !-d $ENV{PAR_TEMP}) {
unlink($ENV{PAR_TEMP});
mkdir($ENV{PAR_TEMP}, 0755);
}
if ($ENV{PAR_CLEAN} and !$name) {
($fh, $filename) = File::Temp::tempfile(
DIR => ($ENV{PAR_TEMP} || File::Spec->tmpdir),
SUFFIX => ".$DynaLoader::dl_dlext",
UNLINK => ($^O ne 'MSWin32' and $^O !~ /hpux/),
);
($filename) = $filename =~ /^([\x20-\xff]+)$/;
}
else {
$filename = File::Spec->catfile(
($ENV{PAR_TEMP} || File::Spec->tmpdir),
($name || ($member->crc32String . ".$DynaLoader::dl_dlext"))
);
($filename) = $filename =~ /^([\x20-\xff]+)$/;
open $fh, '>', $filename or die $!
unless -r $filename and -e _
and -s _ == $member->uncompressedSize;
}
if ($fh) {
binmode($fh);
$member->extractToFileHandle($fh);
close $fh;
chmod 0755, $filename;
}
return $filename;
}
1;
=head1 SEE ALSO
L<PAR>
=head1 AUTHORS
Audrey Tang E<lt>[email protected]<gt>
L<http://par.perl.org/> is the official PAR website. You can write
to the mailing list at E<lt>[email protected]<gt>, or send an empty mail to
E<lt>[email protected]<gt> to participate in the discussion.
Please submit bug reports to E<lt>[email protected]<gt>.
=head1 COPYRIGHT
Copyright 2002-2010 by Audrey Tang
E<lt>[email protected]<gt>.
Copyright 2006-2010 by Steffen Mueller
E<lt>[email protected]<gt>.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
See L<http://www.perl.com/perl/misc/Artistic.html>
=cut