| Devel-CheckLib documentation | Contained in the Devel-CheckLib distribution. |
Devel::CheckLib - check that a library is available
Devel::CheckLib is a perl module that checks whether a particular C library and its headers are available.
use Devel::CheckLib;
check_lib_or_exit( lib => 'jpeg', header => 'jpeglib.h' );
check_lib_or_exit( lib => [ 'iconv', 'jpeg' ] );
# or prompt for path to library and then do this:
check_lib_or_exit( lib => 'jpeg', libpath => $additional_path );
If you want to use this from Makefile.PL or Build.PL, do not simply copy the module into your distribution as this may cause problems when PAUSE and search.cpan.org index the distro. Instead, use the use-devel-checklib script.
You pass named parameters to a function, describing to it how to build and link to the libraries.
It works by trying to compile some code - which defaults to this:
int main(void) { return 0; }
and linking it to the specified libraries. If something pops out the end which looks executable, it gets executed, and if main() returns 0 we know that it worked. That tiny program is built once for each library that you specify, and (without linking) once for each header file.
If you want to check for the presence of particular functions in a library, or even that those functions return particular results, then you can pass your own function body for main() thus:
check_lib_or_exit(
function => 'foo();if(libversion() > 5) return 0; else return 1;'
incpath => ...
libpath => ...
lib => ...
header => ...
);
In that case, it will fail to build if either foo() or libversion() don't exist, and main() will return the wrong value if libversion()'s return value isn't what you want.
All of these take the same named parameters and are exported by default.
To avoid exporting them, use Devel::CheckLib ().
This takes several named parameters, all of which are optional, and dies
with an error message if any of the libraries listed can
not be found. Note: dying in a Makefile.PL or Build.PL may provoke
a 'FAIL' report from CPAN Testers' automated smoke testers. Use
check_lib_or_exit instead.
The named parameters are:
Must be either a string with the name of a single
library or a reference to an array of strings of library names. Depending
on the compiler found, library names will be fed to the compiler either as
-l arguments or as .lib file names. (E.g. -ljpeg or jpeg.lib)
a string or an array of strings representing additional paths to search for libraries.
a ExtUtils::MakeMaker-style space-seperated list of
libraries (each preceded by '-l') and directories (preceded by '-L').
This can also be supplied on the command-line.
And libraries are no use without header files, so ...
Must be either a string with the name of a single header file or a reference to an array of strings of header file names.
a string or an array of strings representing additional paths to search for headers.
a ExtUtils::MakeMaker-style space-seperated list of
incpaths, each preceded by '-I'.
This can also be supplied on the command-line.
This behaves exactly the same as assert_lib() except that instead of
dieing, it warns (with exactly the same error message) and exits.
This is intended for use in Makefile.PL / Build.PL
when you might want to prompt the user for various paths and
things before checking that what they've told you is sane.
If any library or header is missing, it exits with an exit value of 0 to avoid 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.
This behaves exactly the same as assert_lib() except that it is silent,
returning false instead of dieing, or true otherwise.
You must have a C compiler installed. We check for $Config{cc},
both literally as it is in Config.pm and also in the $PATH.
It has been tested with varying degrees on rigourousness on:
This is a very early release intended primarily for feedback from people who have discussed it. The interface may change and it has not been adequately tested.
Feedback is most welcome, including constructive criticism. Bug reports should be made using http://rt.cpan.org/ or by email.
When submitting a bug report, please include the output from running:
perl -V
perl -MDevel::CheckLib -e0
David Cantrell <david@cantrell.org.uk>
David Golden <dagolden@cpan.org>
Yasuhiro Matsumoto <mattn@cpan.org>
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 Tony Cook for help with Microsoft compiler command-line options
Copyright 2007 David Cantrell. Portions copyright 2007 David Golden.
This module is free-as-in-speech software, and may be used, distributed, and modified under the same conditions as perl itself.
This module is also free-as-in-mason software.
| Devel-CheckLib documentation | Contained in the Devel-CheckLib distribution. |
# $Id: CheckLib.pm,v 1.25 2008/10/27 12:16:23 drhyde Exp $ package Devel::CheckLib; use 5.00405; #postfix foreach use strict; use vars qw($VERSION @ISA @EXPORT); $VERSION = '0.93'; 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 check_lib); # localising prevents the warningness leaking out of this module local $^W = 1; # use warnings is a 5.6-ism _findcc(); # bomb out early if there's no compiler
sub check_lib_or_exit { eval 'assert_lib(@_)'; if($@) { warn $@; exit; } } sub check_lib { eval 'assert_lib(@_)'; return $@ ? 0 : 1; } sub assert_lib { my %args = @_; my (@libs, @libpaths, @headers, @incpaths); # FIXME: these four just SCREAM "refactor" at me @libs = (ref($args{lib}) ? @{$args{lib}} : $args{lib}) if $args{lib}; @libpaths = (ref($args{libpath}) ? @{$args{libpath}} : $args{libpath}) if $args{libpath}; @headers = (ref($args{header}) ? @{$args{header}} : $args{header}) if $args{header}; @incpaths = (ref($args{incpath}) ? @{$args{incpath}} : $args{incpath}) if $args{incpath}; # 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)) { 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 =, # so we should not be any more lenient with whitespace than that $args{$mm_attr_key} .= " $mm_attr_value"; } } } # 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(defined($args{INC})) { foreach my $arg (split(' ', $args{INC})) { die("INC argument badly-formed: $arg\n") unless($arg =~ /^-I/); push @incpaths, substr($arg, 2); } } my @cc = _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 <$_>\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; # FIXME: re-factor - almost identical code later when linking if ( $Config{cc} eq 'cl' ) { # Microsoft compiler require Win32; @sys_cmd = ( @cc, $cfile, "/Fe$exefile", (map { '/I'.Win32::GetShortPathName($_) } @incpaths) ); } elsif($Config{cc} =~ /bcc32(\.exe)?/) { # Borland @sys_cmd = ( @cc, (map { "-I$_" } @incpaths), "-o$exefile", $cfile ); } else { # Unix-ish: gcc, Sun, AIX (gcc, cc), ... @sys_cmd = ( @cc, $cfile, (map { "-I$_" } @incpaths), "-o", "$exefile" ); } warn "# @sys_cmd\n" if $args{debug}; my $rv = $args{debug} ? system(@sys_cmd) : _quiet_system(@sys_cmd); push @missing, $header if $rv != 0 || ! -x $exefile; _cleanup_exe($exefile); unlink $cfile; } # now do each library in turn with headers my($ch, $cfile) = File::Temp::tempfile( 'assertlibXXXXXXXX', SUFFIX => '.c' ); print $ch qq{#include <$_>\n} foreach (@headers); print $ch "int main(void) { ".($args{function} || 'return 0;')." }\n"; close($ch); for my $lib ( @libs ) { my $exefile = File::Temp::mktemp( 'assertlibXXXXXXXX' ) . $Config{_exe}; my @sys_cmd; if ( $Config{cc} eq 'cl' ) { # Microsoft compiler require Win32; my @libpath = map { q{/libpath:} . Win32::GetShortPathName($_) } @libpaths; # this is horribly sensitive to the order of arguments @sys_cmd = ( @cc, $cfile, "${lib}.lib", "/Fe$exefile", (map { '/I'.Win32::GetShortPathName($_) } @incpaths), "/link", (map {'/libpath:'.Win32::GetShortPathName($_)} @libpaths), ); } elsif($Config{cc} eq 'CC/DECC') { # VMS } elsif($Config{cc} =~ /bcc32(\.exe)?/) { # Borland @sys_cmd = ( @cc, "-o$exefile", "-l$lib", (map { "-I$_" } @incpaths), (map { "-L$_" } @libpaths), $cfile); } else { # Unix-ish # gcc, Sun, AIX (gcc, cc) @sys_cmd = ( @cc, $cfile, "-o", "$exefile", "-l$lib", (map { "-I$_" } @incpaths), (map { "-L$_" } @libpaths) ); } 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; my $absexefile = File::Spec->rel2abs($exefile); $absexefile = '"'.$absexefile.'"' if $absexefile =~ m/\s/; push @wrongresult, $lib if $rv == 0 && -x $exefile && system($absexefile) != 0; _cleanup_exe($exefile); } unlink $cfile; my $miss_string = join( q{, }, map { qq{'$_'} } @missing ); die("Can't link/include $miss_string\n") if @missing; my $wrong_string = join( q{, }, map { qq{'$_'} } @wrongresult); die("wrong result: $wrong_string\n") if @wrongresult; } sub _cleanup_exe { my ($exefile) = @_; my $ofile = $exefile; $ofile =~ s/$Config{_exe}$/$Config{_o}/; unlink $exefile if -f $exefile; unlink $ofile if -f $ofile; unlink "$exefile\.manifest" if -f "$exefile\.manifest"; return } sub _findcc { # Need to use $keep=1 to work with MSWin32 backslashes and quotes my @Config_ccflags_ldflags = @Config{qw(ccflags ldflags)}; # use copy so ASPerl will compile my @flags = grep { length } map { quotewords('\s+', 1, $_ || ()) } @Config_ccflags_ldflags; my @paths = split(/$Config{path_sep}/, $ENV{PATH}); my @cc = split(/\s+/, $Config{cc}); return (@cc, @flags) if -x $cc[0]; foreach my $path (@paths) { my $compiler = File::Spec->catfile($path, $cc[0]) . $Config{_exe}; return ($compiler, @cc[1 .. $#cc], @flags) if -x $compiler; } die("Couldn't find your C compiler\n"); } # code substantially borrowed from IPC::Run3 sub _quiet_system { my (@cmd) = @_; # save handles local *STDOUT_SAVE; local *STDERR_SAVE; open STDOUT_SAVE, ">&STDOUT" or die "CheckLib: $! saving STDOUT"; open STDERR_SAVE, ">&STDERR" or die "CheckLib: $! saving STDERR"; # redirect to nowhere local *DEV_NULL; open DEV_NULL, ">" . File::Spec->devnull or die "CheckLib: $! opening handle to null device"; open STDOUT, ">&" . fileno DEV_NULL or die "CheckLib: $! redirecting STDOUT to null handle"; open STDERR, ">&" . fileno DEV_NULL or die "CheckLib: $! redirecting STDERR to null handle"; # run system command my $rv = system(@cmd); # restore handles open STDOUT, ">&" . fileno STDOUT_SAVE or die "CheckLib: $! restoring STDOUT handle"; open STDERR, ">&" . fileno STDERR_SAVE or die "CheckLib: $! restoring STDERR handle"; return $rv; }
1;