1# $Id: CheckLib.pm,v 1.1 2008/02/24 10:17:48 richdawe Exp $ 2 3package Devel::CheckLib; 4 5use strict; 6use vars qw($VERSION @ISA @EXPORT); 7$VERSION = '0.3'; 8use Config; 9 10use File::Spec; 11use File::Temp; 12 13require Exporter; 14@ISA = qw(Exporter); 15@EXPORT = qw(assert_lib check_lib_or_exit); 16 17# localising prevents the warningness leaking out of this module 18local $^W = 1; # use warnings is a 5.6-ism 19 20_findcc(); # bomb out early if there's no compiler 21 22=head1 NAME 23 24Devel::CheckLib - check that a library is available 25 26=head1 DESCRIPTION 27 28Devel::CheckLib is a perl module that checks whether a particular C 29library is available, and dies if it is not. 30 31=head1 SYNOPSIS 32 33 # in a Makefile.PL or Build.PL 34 use lib qw(inc); 35 use Devel::CheckLib; 36 37 check_lib_or_exit( lib => 'jpeg' ); 38 check_lib_or_exit( lib => [ 'iconv', 'jpeg' ] ); 39 40 # or prompt for path to library and then do this: 41 check_lib_or_exit( lib => 'jpeg', libpath => $additional_path ); 42 43=head1 HOW IT WORKS 44 45You pass named parameters to a function 46describing how to build and link to the library. Currently the only 47parameter supported is 'lib', which can be a string or an arrayref of 48several libraries. In the future, expect us to add something for 49checking that header files are available as well. 50 51It works by trying to compile this: 52 53 int main(void) { return 0; } 54 55and linking it to the specified libraries. If something pops out the end 56which looks executable, then we know that it worked. 57 58=head1 FUNCTIONS 59 60All of these take the same named parameters and are exported by default. 61To avoid exporting them, C<use Devel::CheckLib ()>. 62 63=head2 assert_lib 64 65Takes several named parameters. 66 67The value of C<lib> must be either a string with the name of a single 68library or a reference to an array of strings of library names. Depending 69on the compiler found, library names will be fed to the compiler either as 70C<-l> arguments or as C<.lib> file names. (E.g. C<-ljpeg> or C<jpeg.lib>) 71 72Likewise, C<libpath> must if provided either be a string or an array of strings 73representing additional paths to search for libraries. 74 75C<LIBS> must be a C<ExtUtils::MakeMaker>-style space-seperated list of 76libraries (each preceded by '-l') and directories (preceded by '-L'). 77 78This will die with an error message if any of the libraries listed can 79not be found. B<Note>: dying in a Makefile.PL or Build.PL may provoke 80a 'FAIL' report from CPAN Testers' automated smoke testers. Use 81C<check_lib_or_exit> instead. 82 83=head2 check_lib_or_exit 84 85This behaves exactly the same as C<assert_lib()> except that instead of 86dieing, it warns (with exactly the same error message) and exits. 87This is intended for use in Makefile.PL / Build.PL 88when you might want to prompt the user for various paths and 89things before checking that what they've told you is sane. 90 91If a library isn't found, it exits with an exit value of 0 to avoid 92causing a CPAN Testers 'FAIL' report. CPAN Testers should ignore this 93result -- which is what you want if an external library dependency is not 94available. 95 96=cut 97 98sub check_lib_or_exit { 99 eval 'assert_lib(@_)'; 100 if($@) { 101 warn $@; 102 exit; 103 } 104} 105 106sub assert_lib { 107 my %args = @_; 108 my (@libs, @libpaths); 109 110 @libs = (ref($args{lib}) ? @{$args{lib}} : $args{lib}) 111 if $args{lib}; 112 @libpaths = (ref($args{libpath}) ? @{$args{libpath}} : $args{libpath}) 113 if $args{libpath}; 114 115 # work-a-like for Makefile.PL's "LIBS" argument 116 if(defined($args{LIBS})) { 117 foreach my $arg (split(/\s+/, $args{LIBS})) { 118 die("LIBS argument badly-formed: $arg\n") unless($arg =~ /^-l/i); 119 push @{$arg =~ /^-l/ ? \@libs : \@libpaths}, substr($arg, 2); 120 } 121 } 122 123 my @cc = _findcc(); 124 my($ch, $cfile) = File::Temp::tempfile( 125 'assertlibXXXXXXXX', SUFFIX => '.c', UNLINK => 1 126 ); 127 print $ch "int main(void) { return 0; }\n"; 128 close($ch); 129 130 my @missing; 131 for my $lib ( @libs ) { 132 my $exefile = File::Temp::mktemp( 'assertlibXXXXXXXX' ) . $Config{_exe}; 133 my @sys_cmd; 134 if ( $Config{cc} eq 'cl' ) { # Microsoft compiler 135 require Win32; 136 my @libpath = map { 137 q{/libpath:} . Win32::GetShortPathName($_) 138 } @libpaths; 139 @sys_cmd = (@cc, $cfile, "${lib}.lib", "/Fe$exefile", 140 "/link", @libpath 141 ); 142 } elsif($Config{cc} =~ /bcc32(\.exe)?/) { # Borland 143 my @libpath = map { "-L$_" } @libpaths; 144 @sys_cmd = (@cc, "-o$exefile", "-l$lib", @libpath, $cfile); 145 } else { # Unix-ish 146 # gcc, Sun, AIX (gcc, cc) 147 my @libpath = map { "-L$_" } @libpaths; 148 @sys_cmd = (@cc, $cfile, "-o", "$exefile", "-l$lib", @libpath); 149 } 150 warn "# @sys_cmd\n" if $args{debug}; 151 my $rv = $args{debug} ? system(@sys_cmd) : _quiet_system(@sys_cmd); 152 push @missing, $lib if $rv != 0 || ! -x $exefile; 153 _cleanup_exe($exefile); 154 } 155 156 unlink $cfile; 157 my $miss_string = join( q{, }, map { qq{'$_'} } @missing ); 158 die("Can't build and link to $miss_string\n") if @missing; 159} 160 161sub _cleanup_exe { 162 my ($exefile) = @_; 163 my $ofile = $exefile; 164 $ofile =~ s/$Config{_exe}$/$Config{_o}/; 165 unlink $exefile if -f $exefile; 166 unlink $ofile if -f $ofile; 167 unlink "$exefile\.manifest" if -f "$exefile\.manifest"; 168 return 169} 170 171sub _findcc { 172 my @paths = split(/$Config{path_sep}/, $ENV{PATH}); 173 my @cc = split(/\s+/, $Config{cc}); 174 return @cc if -x $cc[0]; 175 foreach my $path (@paths) { 176 my $compiler = File::Spec->catfile($path, $cc[0]) . $Config{_exe}; 177 return ($compiler, @cc[1 .. $#cc]) if -x $compiler; 178 } 179 die("Couldn't find your C compiler\n"); 180} 181 182# code substantially borrowed from IPC::Run3 183sub _quiet_system { 184 my (@cmd) = @_; 185 186 # save handles 187 local *STDOUT_SAVE; 188 local *STDERR_SAVE; 189 open STDOUT_SAVE, ">&STDOUT" or die "CheckLib: $! saving STDOUT"; 190 open STDERR_SAVE, ">&STDERR" or die "CheckLib: $! saving STDERR"; 191 192 # redirect to nowhere 193 local *DEV_NULL; 194 open DEV_NULL, ">" . File::Spec->devnull 195 or die "CheckLib: $! opening handle to null device"; 196 open STDOUT, ">&" . fileno DEV_NULL 197 or die "CheckLib: $! redirecting STDOUT to null handle"; 198 open STDERR, ">&" . fileno DEV_NULL 199 or die "CheckLib: $! redirecting STDERR to null handle"; 200 201 # run system command 202 my $rv = system(@cmd); 203 204 # restore handles 205 open STDOUT, ">&" . fileno STDOUT_SAVE 206 or die "CheckLib: $! restoring STDOUT handle"; 207 open STDERR, ">&" . fileno STDERR_SAVE 208 or die "CheckLib: $! restoring STDERR handle"; 209 210 return $rv; 211} 212 213=head1 PLATFORMS SUPPORTED 214 215You must have a C compiler installed. We check for C<$Config{cc}>, 216both literally as it is in Config.pm and also in the $PATH. 217 218It has been tested with varying degrees on rigourousness on: 219 220=over 221 222=item gcc (on Linux, *BSD, Solaris, Cygwin) 223 224=item Sun's compiler tools on Solaris 225 226=item IBM's tools on AIX 227 228=item Microsoft's tools on Windows 229 230=item MinGW on Windows (with Strawberry Perl) 231 232=item Borland's tools on Windows 233 234=back 235 236=head1 WARNINGS, BUGS and FEEDBACK 237 238This is a very early release intended primarily for feedback from 239people who have discussed it. The interface may change and it has 240not been adequately tested. 241 242Feedback is most welcome, including constructive criticism. 243Bug reports should be made using L<http://rt.cpan.org/> or by email. 244 245When submitting a bug report, please include the output from running: 246 247 perl -V 248 perl -MDevel::CheckLib 249 250=head1 SEE ALSO 251 252L<Devel::CheckOS> 253 254=head1 AUTHORS 255 256David Cantrell E<lt>david@cantrell.org.ukE<gt> 257 258David Golden E<lt>dagolden@cpan.orgE<gt> 259 260Thanks to the cpan-testers-discuss mailing list for prompting us to write it 261in the first place; 262 263to Chris Williams for help with Borland support. 264 265=head1 COPYRIGHT and LICENCE 266 267Copyright 2007 David Cantrell. Portions copyright 2007 David Golden. 268 269This module is free-as-in-speech software, and may be used, distributed, 270and modified under the same conditions as perl itself. 271 272=head1 CONSPIRACY 273 274This module is also free-as-in-mason software. 275 276=cut 277 2781; 279