From 8b4e133fc10add22468671aad546017fa560054d Mon Sep 17 00:00:00 2001 From: Tony Cook Date: Tue, 22 Nov 2011 20:21:07 +1100 Subject: [PATCH] update to a less ancient Devel::CheckLib --- Makefile.PL | 11 +-- inc/Devel/CheckLib.pm | 167 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 136 insertions(+), 42 deletions(-) diff --git a/Makefile.PL b/Makefile.PL index 2417bd9..3508f70 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -207,7 +207,7 @@ sub find_lib { } } if (@found) { - push @lflags, "-L$_" for grep !$seen_libdir{$_}, @found; + push @lflags, _quote_spaces("-L$_") for grep !$seen_libdir{$_}, @found; @seen_libdir{@found} = (1) x @found; } else { @@ -217,12 +217,3 @@ sub find_lib { @found; } -# wrapper around assert lib that doesn't exit and doesn't die -sub check_lib { - my (%opts) = @_; - - my $title = delete $opts{title}; - $title and print "Looking even harder for $title\n"; - - return eval { assert_lib(%opts); 1 }; -} diff --git a/inc/Devel/CheckLib.pm b/inc/Devel/CheckLib.pm index 5d6ec52..9bd00d0 100644 --- a/inc/Devel/CheckLib.pm +++ b/inc/Devel/CheckLib.pm @@ -1,19 +1,23 @@ # $Id: CheckLib.pm,v 1.25 2008/10/27 12:16:23 drhyde Exp $ +# This is a modified version of Devel::CheckLib 0.93 including the patches from +# RT issues 60176 and 61645 package # Devel::CheckLib; +use 5.00405; #postfix foreach use strict; use vars qw($VERSION @ISA @EXPORT); -$VERSION = '0.699_001'; -use Config; +$VERSION = '0.93_001'; +use Config qw(%Config); +use Text::ParseWords 'quotewords'; use File::Spec; use File::Temp; require Exporter; @ISA = qw(Exporter); -@EXPORT = qw(assert_lib check_lib_or_exit); +@EXPORT = qw(assert_lib check_lib_or_exit check_lib); # localising prevents the warningness leaking out of this module local $^W = 1; # use warnings is a 5.6-ism @@ -138,6 +142,23 @@ This can also be supplied on the command-line. =back +If you need to perform know more than "does it link?" you can provide +code to be compiled and run: + +=over + +=item function + +the body of the function. If not provided C is +used. + +=item prologue + +code to insert between the C<#include> of the headers and the +definition of main. + +=back + =head2 check_lib_or_exit This behaves exactly the same as C except that instead of @@ -151,6 +172,11 @@ causing a CPAN Testers 'FAIL' report. CPAN Testers should ignore this result -- which is what you want if an external library dependency is not available. +=head2 check_lib + +This behaves exactly the same as C except that it is silent, +returning false instead of dieing, or true otherwise. + =cut sub check_lib_or_exit { @@ -161,6 +187,11 @@ sub check_lib_or_exit { } } +sub check_lib { + eval 'assert_lib(@_)'; + return $@ ? 0 : 1; +} + sub assert_lib { my %args = @_; my (@libs, @libpaths, @headers, @incpaths); @@ -175,10 +206,15 @@ sub assert_lib { @incpaths = (ref($args{incpath}) ? @{$args{incpath}} : $args{incpath}) if $args{incpath}; + # each entry is an arrayref containing: + # 0: arrayref of library search paths + # 1: arrayref of library names + my @link_cfgs = map [ \@libpaths, [ $_ ] ], @libs; + # work-a-like for Makefile.PL's LIBS and INC arguments # if given as command-line argument, append to %args for my $arg (@ARGV) { - for my $mm_attr_key qw(LIBS INC) { + for my $mm_attr_key (qw(LIBS INC)) { if (my ($mm_attr_value) = $arg =~ /\A $mm_attr_key = (.*)/x) { # it is tempting to put some \s* into the expression, but the # MM command-line parser only accepts LIBS etc. followed by =, @@ -190,28 +226,44 @@ sub assert_lib { # using special form of split to trim whitespace if(defined($args{LIBS})) { - foreach my $arg (split(' ', $args{LIBS})) { - die("LIBS argument badly-formed: $arg\n") unless($arg =~ /^-l/i); - push @{$arg =~ /^-l/ ? \@libs : \@libpaths}, substr($arg, 2); - } + if (ref $args{LIBS}) { + foreach my $arg (@{$args{LIBS}}) { + my @sep = split ' ', $arg; + my @libs = map { /^-l(.+)$/ ? $1 : () } @sep; + my @paths = map { /^-L(.+)$/ ? $1 : () } @sep; + push @link_cfgs, [ \@paths, \@libs ]; + } + } + else { + my @libs; + my @paths; + foreach my $arg (split(' ', $args{LIBS})) { + die("LIBS argument badly-formed: $arg\n") unless($arg =~ /^-l/i); + push @{$arg =~ /^-l/ ? \@libs : \@paths}, substr($arg, 2); + } + push @link_cfgs, map [ \@paths, [ $_ ] ], @libs; + } } if(defined($args{INC})) { - foreach my $arg (split(' ', $args{INC})) { + foreach my $arg (_shellwords($args{INC})) { die("INC argument badly-formed: $arg\n") unless($arg =~ /^-I/); push @incpaths, substr($arg, 2); } } - my @cc = _findcc(); + my ($cc, $ld) = _findcc(); my @missing; my @wrongresult; + my @use_headers; # first figure out which headers we can't find ... for my $header (@headers) { + push @use_headers, $header; my($ch, $cfile) = File::Temp::tempfile( 'assertlibXXXXXXXX', SUFFIX => '.c' ); - print $ch qq{#include <$header>\nint main(void) { return 0; }\n}; + print $ch qq{#include <$_>\n} for @use_headers; + print $ch qq{int main(void) { return 0; }\n}; close($ch); my $exefile = File::Temp::mktemp( 'assertlibXXXXXXXX' ) . $Config{_exe}; my @sys_cmd; @@ -219,21 +271,25 @@ sub assert_lib { if ( $Config{cc} eq 'cl' ) { # Microsoft compiler require Win32; @sys_cmd = ( - @cc, + @$cc, $cfile, "/Fe$exefile", - (map { '/I'.Win32::GetShortPathName($_) } @incpaths) + (map { '/I'.Win32::GetShortPathName($_) } @incpaths), + "/link", + @$ld ); } elsif($Config{cc} =~ /bcc32(\.exe)?/) { # Borland @sys_cmd = ( - @cc, + @$cc, (map { "-I$_" } @incpaths), "-o$exefile", - $cfile + $cfile, + @$ld ); } else { # Unix-ish: gcc, Sun, AIX (gcc, cc), ... @sys_cmd = ( - @cc, + @$cc, + @$ld, $cfile, (map { "-I$_" } @incpaths), "-o", "$exefile" @@ -251,9 +307,11 @@ sub assert_lib { 'assertlibXXXXXXXX', SUFFIX => '.c' ); print $ch qq{#include <$_>\n} foreach (@headers); + print $ch "\n$args{prologue}\n" if $args{prologue}; print $ch "int main(void) { ".($args{function} || 'return 0;')." }\n"; close($ch); - for my $lib ( @libs ) { + for my $link_cfg ( @link_cfgs ) { + my ($paths, $libs) = @$link_cfg; my $exefile = File::Temp::mktemp( 'assertlibXXXXXXXX' ) . $Config{_exe}; my @sys_cmd; if ( $Config{cc} eq 'cl' ) { # Microsoft compiler @@ -261,39 +319,45 @@ sub assert_lib { my @libpath = map { q{/libpath:} . Win32::GetShortPathName($_) } @libpaths; + # this is horribly sensitive to the order of arguments @sys_cmd = ( - @cc, + @$cc, $cfile, - "${lib}.lib", + ( map { "$_.lib" } @$libs ), "/Fe$exefile", (map { '/I'.Win32::GetShortPathName($_) } @incpaths), "/link", - (map {'/libpath:'.Win32::GetShortPathName($_)} @libpaths), + @$ld, + (map {'/libpath:'.Win32::GetShortPathName($_)} @$paths), ); } elsif($Config{cc} eq 'CC/DECC') { # VMS } elsif($Config{cc} =~ /bcc32(\.exe)?/) { # Borland @sys_cmd = ( - @cc, + @$cc, + @$ld, "-o$exefile", - "-l$lib", + (map { "-l$_" } @$libs ), (map { "-I$_" } @incpaths), - (map { "-L$_" } @libpaths), + (map { "-L$_" } @$paths), $cfile); } else { # Unix-ish # gcc, Sun, AIX (gcc, cc) @sys_cmd = ( - @cc, + @$cc, + @$ld, $cfile, "-o", "$exefile", - "-l$lib", + (map { "-l$_" } @$libs ), (map { "-I$_" } @incpaths), - (map { "-L$_" } @libpaths) + (map { "-L$_" } @$paths) ); } warn "# @sys_cmd\n" if $args{debug}; my $rv = $args{debug} ? system(@sys_cmd) : _quiet_system(@sys_cmd); - push @missing, $lib if $rv != 0 || ! -x $exefile; - push @wrongresult, $lib if $rv == 0 && -x $exefile && system(File::Spec->rel2abs($exefile)) != 0; + push @missing, @$libs if $rv != 0 || ! -x $exefile; + my $absexefile = File::Spec->rel2abs($exefile); + $absexefile = '"'.$absexefile.'"' if $absexefile =~ m/\s/; + push @wrongresult, @$libs if $rv == 0 && -x $exefile && system($absexefile) != 0; _cleanup_exe($exefile); } unlink $cfile; @@ -311,20 +375,55 @@ sub _cleanup_exe { unlink $exefile if -f $exefile; unlink $ofile if -f $ofile; unlink "$exefile\.manifest" if -f "$exefile\.manifest"; + if ( $Config{cc} eq 'cl' ) { + # MSVC also creates foo.ilk and foo.pdb + my $ilkfile = $exefile; + $ilkfile =~ s/$Config{_exe}$/.ilk/; + my $pdbfile = $exefile; + $pdbfile =~ s/$Config{_exe}$/.pdb/; + unlink $ilkfile if -f $ilkfile; + unlink $pdbfile if -f $pdbfile; + } return } - + +# return ($cc, $ld) +# where $cc is an array ref of compiler name, compiler flags +# where $ld is an array ref of linker flags sub _findcc { + # Need to use $keep=1 to work with MSWin32 backslashes and quotes + my $Config_ccflags = $Config{ccflags}; # use copy so ASPerl will compile + my @Config_ldflags = @Config{qw(ldflags perllibs)}; + my @ccflags = grep { length } quotewords('\s+', 1, $Config_ccflags); + my @ldflags = grep { length } quotewords('\s+', 1, @Config_ldflags); my @paths = split(/$Config{path_sep}/, $ENV{PATH}); my @cc = split(/\s+/, $Config{cc}); - return @cc if -x $cc[0]; + return ( [ @cc, @ccflags ], \@ldflags ) if -x $cc[0]; foreach my $path (@paths) { my $compiler = File::Spec->catfile($path, $cc[0]) . $Config{_exe}; - return ($compiler, @cc[1 .. $#cc]) if -x $compiler; + return ([ $compiler, @cc[1 .. $#cc], @ccflags ], \@ldflags) + if -x $compiler; } die("Couldn't find your C compiler\n"); } +sub _shellwords { + my $line = shift; + + if ($^O eq "MSWin32") { + my @elements; + $line =~ s/^\s+//; + while ($line =~ s/^"([^"]*)"// || $line =~ s/^(\S+)//) { + push @elements, $1; + $line =~ s/^\s+//; + } + return @elements; + } + else { + return grep defined && /\S/, quotewords('\s+', 0, $line); + } +} + # code substantially borrowed from IPC::Run3 sub _quiet_system { my (@cmd) = @_; @@ -409,10 +508,14 @@ David Cantrell Edavid@cantrell.org.ukE David Golden Edagolden@cpan.orgE +Yasuhiro Matsumoto Emattn@cpan.orgE + Thanks to the cpan-testers-discuss mailing list for prompting us to write it in the first place; -to Chris Williams for help with Borland support. +to Chris Williams for help with Borland support; + +to Tony Cook for help with Microsoft compiler command-line options =head1 COPYRIGHT and LICENCE -- 2.30.2