| Marc Kupietz | e20daf1 | 2026-07-31 09:55:55 +0900 | [diff] [blame] | 1 | # $Id: CheckLib.pm,v 1.25 2008/10/27 12:16:23 drhyde Exp $ |
| 2 | |
| 3 | package # |
| 4 | Devel::CheckLib; |
| 5 | |
| 6 | use 5.00405; #postfix foreach |
| 7 | use strict; |
| 8 | use vars qw($VERSION @ISA @EXPORT); |
| 9 | $VERSION = '1.16'; |
| 10 | use Config qw(%Config); |
| 11 | use Text::ParseWords qw(quotewords shellwords); |
| 12 | |
| 13 | use File::Spec; |
| 14 | use File::Temp; |
| 15 | |
| 16 | require Exporter; |
| 17 | @ISA = qw(Exporter); |
| 18 | @EXPORT = qw(assert_lib check_lib_or_exit check_lib); |
| 19 | |
| 20 | # localising prevents the warningness leaking out of this module |
| 21 | local $^W = 1; # use warnings is a 5.6-ism |
| 22 | |
| 23 | _findcc(); # bomb out early if there's no compiler |
| 24 | |
| 25 | =head1 NAME |
| 26 | |
| 27 | Devel::CheckLib - check that a library is available |
| 28 | |
| 29 | =head1 DESCRIPTION |
| 30 | |
| 31 | Devel::CheckLib is a perl module that checks whether a particular C |
| 32 | library and its headers are available. |
| 33 | |
| 34 | =head1 SYNOPSIS |
| 35 | |
| 36 | use Devel::CheckLib; |
| 37 | |
| 38 | check_lib_or_exit( lib => 'jpeg', header => 'jpeglib.h' ); |
| 39 | check_lib_or_exit( lib => [ 'iconv', 'jpeg' ] ); |
| 40 | |
| 41 | # or prompt for path to library and then do this: |
| 42 | check_lib_or_exit( lib => 'jpeg', libpath => $additional_path ); |
| 43 | |
| 44 | =head1 USING IT IN Makefile.PL or Build.PL |
| 45 | |
| 46 | If you want to use this from Makefile.PL or Build.PL, do |
| 47 | not simply copy the module into your distribution as this may cause |
| 48 | problems when PAUSE and search.cpan.org index the distro. Instead, use |
| 49 | the use-devel-checklib script. |
| 50 | |
| 51 | =head1 HOW IT WORKS |
| 52 | |
| 53 | You pass named parameters to a function, describing to it how to build |
| 54 | and link to the libraries. |
| 55 | |
| 56 | It works by trying to compile some code - which defaults to this: |
| 57 | |
| 58 | int main(int argc, char *argv[]) { return 0; } |
| 59 | |
| 60 | and linking it to the specified libraries. If something pops out the end |
| 61 | which looks executable, it gets executed, and if main() returns 0 we know |
| 62 | that it worked. That tiny program is |
| 63 | built once for each library that you specify, and (without linking) once |
| 64 | for each header file. |
| 65 | |
| 66 | If you want to check for the presence of particular functions in a |
| 67 | library, or even that those functions return particular results, then |
| 68 | you can pass your own function body for main() thus: |
| 69 | |
| 70 | check_lib_or_exit( |
| 71 | function => 'foo();if(libversion() > 5) return 0; else return 1;' |
| 72 | incpath => ... |
| 73 | libpath => ... |
| 74 | lib => ... |
| 75 | header => ... |
| 76 | ); |
| 77 | |
| 78 | In that case, it will fail to build if either foo() or libversion() don't |
| 79 | exist, and main() will return the wrong value if libversion()'s return |
| 80 | value isn't what you want. |
| 81 | |
| 82 | =head1 FUNCTIONS |
| 83 | |
| 84 | All of these take the same named parameters and are exported by default. |
| 85 | To avoid exporting them, C<use Devel::CheckLib ()>. |
| 86 | |
| 87 | =head2 assert_lib |
| 88 | |
| 89 | This takes several named parameters, all of which are optional, and dies |
| 90 | with an error message if any of the libraries listed can |
| 91 | not be found. B<Note>: dying in a Makefile.PL or Build.PL may provoke |
| 92 | a 'FAIL' report from CPAN Testers' automated smoke testers. Use |
| 93 | C<check_lib_or_exit> instead. |
| 94 | |
| 95 | The named parameters are: |
| 96 | |
| 97 | =over |
| 98 | |
| 99 | =item lib |
| 100 | |
| 101 | Must be either a string with the name of a single |
| 102 | library or a reference to an array of strings of library names. Depending |
| 103 | on the compiler found, library names will be fed to the compiler either as |
| 104 | C<-l> arguments or as C<.lib> file names. (E.g. C<-ljpeg> or C<jpeg.lib>) |
| 105 | |
| 106 | =item libpath |
| 107 | |
| 108 | a string or an array of strings |
| 109 | representing additional paths to search for libraries. |
| 110 | |
| 111 | =item LIBS |
| 112 | |
| 113 | a C<ExtUtils::MakeMaker>-style space-separated list of |
| 114 | libraries (each preceded by '-l') and directories (preceded by '-L'). |
| 115 | |
| 116 | This can also be supplied on the command-line. |
| 117 | |
| 118 | =item debug |
| 119 | |
| 120 | If true - emit information during processing that can be used for |
| 121 | debugging. |
| 122 | |
| 123 | =back |
| 124 | |
| 125 | And libraries are no use without header files, so ... |
| 126 | |
| 127 | =over |
| 128 | |
| 129 | =item header |
| 130 | |
| 131 | Must be either a string with the name of a single |
| 132 | header file or a reference to an array of strings of header file names. |
| 133 | |
| 134 | =item incpath |
| 135 | |
| 136 | a string or an array of strings |
| 137 | representing additional paths to search for headers. |
| 138 | |
| 139 | =item INC |
| 140 | |
| 141 | a C<ExtUtils::MakeMaker>-style space-separated list of |
| 142 | incpaths, each preceded by '-I'. |
| 143 | |
| 144 | This can also be supplied on the command-line. |
| 145 | |
| 146 | =item ccflags |
| 147 | |
| 148 | Extra flags to pass to the compiler. |
| 149 | |
| 150 | =item ldflags |
| 151 | |
| 152 | Extra flags to pass to the linker. |
| 153 | |
| 154 | =item analyze_binary |
| 155 | |
| 156 | a callback function that will be invoked in order to perform custom |
| 157 | analysis of the generated binary. The callback arguments are the |
| 158 | library name and the path to the binary just compiled. |
| 159 | |
| 160 | It is possible to use this callback, for instance, to inspect the |
| 161 | binary for further dependencies. |
| 162 | |
| 163 | =item not_execute |
| 164 | |
| 165 | Do not try to execute generated binary. Only check that compilation has not failed. |
| 166 | |
| 167 | =back |
| 168 | |
| 169 | =head2 check_lib_or_exit |
| 170 | |
| 171 | This behaves exactly the same as C<assert_lib()> except that instead of |
| 172 | dieing, it warns (with exactly the same error message) and exits. |
| 173 | This is intended for use in Makefile.PL / Build.PL |
| 174 | when you might want to prompt the user for various paths and |
| 175 | things before checking that what they've told you is sane. |
| 176 | |
| 177 | If any library or header is missing, it exits with an exit value of 0 to avoid |
| 178 | causing a CPAN Testers 'FAIL' report. CPAN Testers should ignore this |
| 179 | result -- which is what you want if an external library dependency is not |
| 180 | available. |
| 181 | |
| 182 | =head2 check_lib |
| 183 | |
| 184 | This behaves exactly the same as C<assert_lib()> except that it is silent, |
| 185 | returning false instead of dieing, or true otherwise. |
| 186 | |
| 187 | =cut |
| 188 | |
| 189 | sub check_lib_or_exit { |
| 190 | eval 'assert_lib(@_)'; |
| 191 | if($@) { |
| 192 | warn $@; |
| 193 | exit; |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | sub check_lib { |
| 198 | eval 'assert_lib(@_)'; |
| 199 | return $@ ? 0 : 1; |
| 200 | } |
| 201 | |
| 202 | # borrowed from Text::ParseWords |
| 203 | sub _parse_line { |
| 204 | my($delimiter, $keep, $line) = @_; |
| 205 | my($word, @pieces); |
| 206 | |
| 207 | no warnings 'uninitialized'; # we will be testing undef strings |
| 208 | |
| 209 | while (length($line)) { |
| 210 | # This pattern is optimised to be stack conservative on older perls. |
| 211 | # Do not refactor without being careful and testing it on very long strings. |
| 212 | # See Perl bug #42980 for an example of a stack busting input. |
| 213 | $line =~ s/^ |
| 214 | (?: |
| 215 | # double quoted string |
| 216 | (") # $quote |
| 217 | ((?>[^\\"]*(?:\\.[^\\"]*)*))" # $quoted |
| 218 | | # --OR-- |
| 219 | # singe quoted string |
| 220 | (') # $quote |
| 221 | ((?>[^\\']*(?:\\.[^\\']*)*))' # $quoted |
| 222 | | # --OR-- |
| 223 | # unquoted string |
| 224 | ( # $unquoted |
| 225 | (?:\\.|[^\\"'])*? |
| 226 | ) |
| 227 | # followed by |
| 228 | ( # $delim |
| 229 | \Z(?!\n) # EOL |
| 230 | | # --OR-- |
| 231 | (?-x:$delimiter) # delimiter |
| 232 | | # --OR-- |
| 233 | (?!^)(?=["']) # a quote |
| 234 | ) |
| 235 | )//xs or return; # extended layout |
| 236 | my ($quote, $quoted, $unquoted, $delim) = (($1 ? ($1,$2) : ($3,$4)), $5, $6); |
| 237 | |
| 238 | return() unless( defined($quote) || length($unquoted) || length($delim)); |
| 239 | |
| 240 | if ($keep) { |
| 241 | $quoted = "$quote$quoted$quote"; |
| 242 | } |
| 243 | else { |
| 244 | $unquoted =~ s/\\(.)/$1/sg; |
| 245 | if (defined $quote) { |
| 246 | $quoted =~ s/\\(.)/$1/sg if ($quote eq '"'); |
| 247 | } |
| 248 | } |
| 249 | $word .= substr($line, 0, 0); # leave results tainted |
| 250 | $word .= defined $quote ? $quoted : $unquoted; |
| 251 | |
| 252 | if (length($delim)) { |
| 253 | push(@pieces, $word); |
| 254 | push(@pieces, $delim) if ($keep eq 'delimiters'); |
| 255 | undef $word; |
| 256 | } |
| 257 | if (!length($line)) { |
| 258 | push(@pieces, $word); |
| 259 | } |
| 260 | } |
| 261 | return(@pieces); |
| 262 | } |
| 263 | |
| 264 | sub _parsewords { |
| 265 | return shellwords @_ if $^O ne 'MSWin32'; |
| 266 | # for Win32, take off "" but leave \ |
| 267 | map { my $s=$_; $s =~ s/^"(.*)"$/$1/; $s } grep defined && length, quotewords '\s+', 1, @_; |
| 268 | } |
| 269 | |
| 270 | sub _compile_cmd { |
| 271 | my ($Config_cc, $cc, $cfile, $exefile, $incpaths, $ld, $Config_libs, $lib, $libpaths) = @_; |
| 272 | my @sys_cmd = @$cc; |
| 273 | if ( $Config_cc eq 'cl' ) { # Microsoft compiler |
| 274 | # this is horribly sensitive to the order of arguments |
| 275 | push @sys_cmd, |
| 276 | $cfile, |
| 277 | (defined $lib ? "${lib}.lib" : ()), |
| 278 | "/Fe$exefile", |
| 279 | (map '/I'.$_, @$incpaths), |
| 280 | "/link", |
| 281 | @$ld, |
| 282 | _parsewords($Config_libs), |
| 283 | (defined $lib ? map '/libpath:'.$_, @$libpaths : ()), |
| 284 | ; |
| 285 | } elsif($Config_cc =~ /bcc32(\.exe)?/) { # Borland |
| 286 | push @sys_cmd, |
| 287 | @$ld, |
| 288 | (map "-I$_", @$incpaths), |
| 289 | "-o$exefile", |
| 290 | (defined $lib ? ((map "-L$_", @$libpaths), "-l$lib") : ()), |
| 291 | $cfile, |
| 292 | ; |
| 293 | } else { # Unix-ish: gcc, Sun, AIX (gcc, cc), ... |
| 294 | push @sys_cmd, |
| 295 | (map "-I$_", @$incpaths), |
| 296 | $cfile, |
| 297 | (!defined $lib ? () : ( |
| 298 | (map "-L$_", @$libpaths), |
| 299 | ($^O eq 'darwin' ? (map { "-Wl,-rpath,$_" } @$libpaths) : ()), |
| 300 | "-l$lib", |
| 301 | )), |
| 302 | @$ld, |
| 303 | "-o", $exefile, |
| 304 | ; |
| 305 | } |
| 306 | @sys_cmd; |
| 307 | } |
| 308 | |
| 309 | sub _make_cfile { |
| 310 | my ($use_headers, $function, $debug) = @_; |
| 311 | my $code = ''; |
| 312 | $code .= qq{#include <$_>\n} for @$use_headers; |
| 313 | $code .= "int main(int argc, char *argv[]) { ".($function || 'return 0;')." }\n"; |
| 314 | if ($debug) { |
| 315 | (my $c = $code) =~ s:^:# :gm; |
| 316 | warn "# Code:\n$c\n"; |
| 317 | } |
| 318 | my ($ch, $cfile) = File::Temp::tempfile( |
| 319 | 'assertlibXXXXXXXX', SUFFIX => '.c' |
| 320 | ); |
| 321 | print $ch $code; |
| 322 | close $ch; |
| 323 | (my $ofile = $cfile) =~ s/\.c$/$Config{_o}/; |
| 324 | ($cfile, $ofile); |
| 325 | } |
| 326 | |
| 327 | sub assert_lib { |
| 328 | my %args = @_; |
| 329 | $args{$_} = [$args{$_}] |
| 330 | for grep $args{$_} && !ref($args{$_}), qw(lib libpath header incpath); |
| 331 | my @libs = @{$args{lib} || []}; |
| 332 | my @libpaths = @{$args{libpath} || []}; |
| 333 | my @headers = @{$args{header} || []}; |
| 334 | my @incpaths = @{$args{incpath} || []}; |
| 335 | my $analyze_binary = $args{analyze_binary}; |
| 336 | my $execute = !$args{not_execute}; |
| 337 | |
| 338 | my @argv = @ARGV; |
| 339 | push @argv, _parse_line('\s+', 0, $ENV{PERL_MM_OPT}||''); |
| 340 | |
| 341 | # work-a-like for Makefile.PL's LIBS and INC arguments |
| 342 | # if given as command-line argument, append to %args |
| 343 | for my $arg (@argv) { |
| 344 | for my $mm_attr_key (qw(LIBS INC)) { |
| 345 | if (my ($mm_attr_value) = $arg =~ /\A $mm_attr_key = (.*)/x) { |
| 346 | # it is tempting to put some \s* into the expression, but the |
| 347 | # MM command-line parser only accepts LIBS etc. followed by =, |
| 348 | # so we should not be any more lenient with whitespace than that |
| 349 | $args{$mm_attr_key} .= " $mm_attr_value"; |
| 350 | } |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | if(defined($args{LIBS})) { |
| 355 | foreach my $arg (_parsewords($args{LIBS})) { |
| 356 | die("LIBS argument badly-formed: $arg\n") unless($arg =~ /^-[lLR]/); |
| 357 | push @{$arg =~ /^-l/ ? \@libs : \@libpaths}, substr($arg, 2); |
| 358 | } |
| 359 | } |
| 360 | if(defined($args{INC})) { |
| 361 | foreach my $arg (_parsewords($args{INC})) { |
| 362 | die("INC argument badly-formed: $arg\n") unless($arg =~ /^-I/); |
| 363 | push @incpaths, substr($arg, 2); |
| 364 | } |
| 365 | } |
| 366 | |
| 367 | my ($cc, $ld) = _findcc($args{debug}, $args{ccflags}, $args{ldflags}); |
| 368 | my @missing; |
| 369 | my @wrongresult; |
| 370 | my @wronganalysis; |
| 371 | my @use_headers; |
| 372 | |
| 373 | # first figure out which headers we can't find ... |
| 374 | for my $header (@headers) { |
| 375 | push @use_headers, $header; |
| 376 | my ($cfile, $ofile) = _make_cfile(\@use_headers, '', $args{debug}); |
| 377 | my $exefile = File::Temp::mktemp( 'assertlibXXXXXXXX' ) . $Config{_exe}; |
| 378 | my @sys_cmd = _compile_cmd($Config{cc}, $cc, $cfile, $exefile, \@incpaths, $ld, $Config{libs}); |
| 379 | warn "# @sys_cmd\n" if $args{debug}; |
| 380 | my $rv = $args{debug} ? system(@sys_cmd) : _quiet_system(@sys_cmd); |
| 381 | push @missing, $header if $rv != 0 || ! -f $exefile; |
| 382 | _cleanup_exe($exefile); |
| 383 | unlink $cfile; |
| 384 | } |
| 385 | |
| 386 | # now do each library in turn with headers |
| 387 | my ($cfile, $ofile) = _make_cfile(\@use_headers, @args{qw(function debug)}); |
| 388 | for my $lib ( @libs ) { |
| 389 | last if $Config{cc} eq 'CC/DECC'; # VMS |
| 390 | my $exefile = File::Temp::mktemp( 'assertlibXXXXXXXX' ) . $Config{_exe}; |
| 391 | my @sys_cmd = _compile_cmd($Config{cc}, $cc, $cfile, $exefile, \@incpaths, $ld, $Config{libs}, $lib, \@libpaths); |
| 392 | warn "# @sys_cmd\n" if $args{debug}; |
| 393 | local $ENV{LD_RUN_PATH} = join(":", grep $_, @libpaths, $ENV{LD_RUN_PATH}) unless $^O eq 'MSWin32' or $^O eq 'darwin'; |
| 394 | local $ENV{PATH} = join(";", @libpaths).";".$ENV{PATH} if $^O eq 'MSWin32'; |
| 395 | my $rv = $args{debug} ? system(@sys_cmd) : _quiet_system(@sys_cmd); |
| 396 | if ($rv != 0 || ! -f $exefile) { |
| 397 | push @missing, $lib; |
| 398 | } |
| 399 | else { |
| 400 | chmod 0755, $exefile; |
| 401 | my $absexefile = File::Spec->rel2abs($exefile); |
| 402 | $absexefile = '"'.$absexefile.'"' if $absexefile =~ m/\s/; |
| 403 | warn "# Execute($execute): $absexefile\n" if $args{debug}; |
| 404 | if ($execute) { |
| 405 | my $retval = system($absexefile); |
| 406 | warn "# return value: $retval\n" if $args{debug}; |
| 407 | push @wrongresult, $lib if $retval != 0; |
| 408 | } |
| 409 | push @wronganalysis, $lib |
| 410 | if $analyze_binary and !$analyze_binary->($lib, $exefile); |
| 411 | } |
| 412 | _cleanup_exe($exefile); |
| 413 | } |
| 414 | unlink $cfile; |
| 415 | |
| 416 | my $miss_string = join( q{, }, map qq{'$_'}, @missing ); |
| 417 | die("Can't link/include C library $miss_string, aborting.\n") if @missing; |
| 418 | my $wrong_string = join( q{, }, map qq{'$_'}, @wrongresult); |
| 419 | die("wrong result: $wrong_string\n") if @wrongresult; |
| 420 | my $analysis_string = join(q{, }, map qq{'$_'}, @wronganalysis ); |
| 421 | die("wrong analysis: $analysis_string") if @wronganalysis; |
| 422 | } |
| 423 | |
| 424 | sub _cleanup_exe { |
| 425 | my ($exefile) = @_; |
| 426 | my $ofile = $exefile; |
| 427 | $ofile =~ s/$Config{_exe}$/$Config{_o}/; |
| 428 | # List of files to remove |
| 429 | my @rmfiles; |
| 430 | push @rmfiles, $exefile, $ofile, "$exefile\.manifest"; |
| 431 | if ( $Config{cc} eq 'cl' ) { |
| 432 | # MSVC also creates foo.ilk and foo.pdb |
| 433 | my $ilkfile = $exefile; |
| 434 | $ilkfile =~ s/$Config{_exe}$/.ilk/; |
| 435 | my $pdbfile = $exefile; |
| 436 | $pdbfile =~ s/$Config{_exe}$/.pdb/; |
| 437 | push @rmfiles, $ilkfile, $pdbfile; |
| 438 | } |
| 439 | foreach (grep -f, @rmfiles) { |
| 440 | unlink $_ or warn "Could not remove $_: $!"; |
| 441 | } |
| 442 | return |
| 443 | } |
| 444 | |
| 445 | # return ($cc, $ld) |
| 446 | # where $cc is an array ref of compiler name, compiler flags |
| 447 | # where $ld is an array ref of linker flags |
| 448 | sub _findcc { |
| 449 | my ($debug, $user_ccflags, $user_ldflags) = @_; |
| 450 | # Need to use $keep=1 to work with MSWin32 backslashes and quotes |
| 451 | my $Config_ccflags = $Config{ccflags}; # use copy so ASPerl will compile |
| 452 | $Config_ccflags =~ s:-O\S*::; # stop GCC optimising away test code |
| 453 | my @Config_ldflags = (); |
| 454 | for my $config_val ( @Config{qw(ldflags)} ){ |
| 455 | push @Config_ldflags, $config_val if ( $config_val =~ /\S/ ); |
| 456 | } |
| 457 | my @ccflags = grep { length } _parsewords($Config_ccflags||'', $user_ccflags||''); |
| 458 | my @ldflags = grep { length && $_ !~ m/^-Wl/ } _parsewords(@Config_ldflags, $user_ldflags||''); |
| 459 | my @paths = split(/$Config{path_sep}/, $ENV{PATH}); |
| 460 | my @cc = _parsewords($Config{cc}); |
| 461 | if (check_compiler ($cc[0], $debug)) { |
| 462 | return ( [ @cc, @ccflags ], \@ldflags ); |
| 463 | } |
| 464 | # Find the extension for executables. |
| 465 | my $exe = $Config{_exe}; |
| 466 | if ($^O eq 'cygwin') { |
| 467 | $exe = ''; |
| 468 | } |
| 469 | foreach my $path (@paths) { |
| 470 | # Look for "$path/$cc[0].exe" |
| 471 | my $compiler = File::Spec->catfile($path, $cc[0]) . $exe; |
| 472 | if (check_compiler ($compiler, $debug)) { |
| 473 | return ([ $compiler, @cc[1 .. $#cc], @ccflags ], \@ldflags) |
| 474 | } |
| 475 | next if ! $exe; |
| 476 | # Look for "$path/$cc[0]" without the .exe, if necessary. |
| 477 | $compiler = File::Spec->catfile($path, $cc[0]); |
| 478 | if (check_compiler ($compiler, $debug)) { |
| 479 | return ([ $compiler, @cc[1 .. $#cc], @ccflags ], \@ldflags) |
| 480 | } |
| 481 | } |
| 482 | die("Couldn't find your C compiler.\n"); |
| 483 | } |
| 484 | |
| 485 | sub check_compiler |
| 486 | { |
| 487 | my ($compiler, $debug) = @_; |
| 488 | if (-f $compiler && -x $compiler) { |
| 489 | warn "# Compiler seems to be $compiler\n" if $debug; |
| 490 | return 1; |
| 491 | } |
| 492 | warn "# Compiler was not $compiler\n" if $debug; |
| 493 | return ''; |
| 494 | } |
| 495 | |
| 496 | |
| 497 | # code substantially borrowed from IPC::Run3 |
| 498 | sub _quiet_system { |
| 499 | my (@cmd) = @_; |
| 500 | |
| 501 | # save handles |
| 502 | local *STDOUT_SAVE; |
| 503 | local *STDERR_SAVE; |
| 504 | open STDOUT_SAVE, ">&STDOUT" or die "CheckLib: $! saving STDOUT"; |
| 505 | open STDERR_SAVE, ">&STDERR" or die "CheckLib: $! saving STDERR"; |
| 506 | |
| 507 | # redirect to nowhere |
| 508 | local *DEV_NULL; |
| 509 | open DEV_NULL, ">" . File::Spec->devnull |
| 510 | or die "CheckLib: $! opening handle to null device"; |
| 511 | open STDOUT, ">&" . fileno DEV_NULL |
| 512 | or die "CheckLib: $! redirecting STDOUT to null handle"; |
| 513 | open STDERR, ">&" . fileno DEV_NULL |
| 514 | or die "CheckLib: $! redirecting STDERR to null handle"; |
| 515 | |
| 516 | # run system command |
| 517 | my $rv = system(@cmd); |
| 518 | |
| 519 | # restore handles |
| 520 | open STDOUT, ">&" . fileno STDOUT_SAVE |
| 521 | or die "CheckLib: $! restoring STDOUT handle"; |
| 522 | open STDERR, ">&" . fileno STDERR_SAVE |
| 523 | or die "CheckLib: $! restoring STDERR handle"; |
| 524 | |
| 525 | return $rv; |
| 526 | } |
| 527 | |
| 528 | =head1 PLATFORMS SUPPORTED |
| 529 | |
| 530 | You must have a C compiler installed. We check for C<$Config{cc}>, |
| 531 | both literally as it is in Config.pm and also in the $PATH. |
| 532 | |
| 533 | It has been tested with varying degrees of rigorousness on: |
| 534 | |
| 535 | =over |
| 536 | |
| 537 | =item gcc (on Linux, *BSD, Mac OS X, Solaris, Cygwin) |
| 538 | |
| 539 | =item Sun's compiler tools on Solaris |
| 540 | |
| 541 | =item IBM's tools on AIX |
| 542 | |
| 543 | =item SGI's tools on Irix 6.5 |
| 544 | |
| 545 | =item Microsoft's tools on Windows |
| 546 | |
| 547 | =item MinGW on Windows (with Strawberry Perl) |
| 548 | |
| 549 | =item Borland's tools on Windows |
| 550 | |
| 551 | =item QNX |
| 552 | |
| 553 | =back |
| 554 | |
| 555 | =head1 WARNINGS, BUGS and FEEDBACK |
| 556 | |
| 557 | This is a very early release intended primarily for feedback from |
| 558 | people who have discussed it. The interface may change and it has |
| 559 | not been adequately tested. |
| 560 | |
| 561 | Feedback is most welcome, including constructive criticism. |
| 562 | Bug reports should be made using L<http://rt.cpan.org/> or by email. |
| 563 | |
| 564 | When submitting a bug report, please include the output from running: |
| 565 | |
| 566 | perl -V |
| 567 | perl -MDevel::CheckLib -e0 |
| 568 | |
| 569 | =head1 SEE ALSO |
| 570 | |
| 571 | L<Devel::CheckOS> |
| 572 | |
| 573 | L<Probe::Perl> |
| 574 | |
| 575 | =head1 AUTHORS |
| 576 | |
| 577 | David Cantrell E<lt>david@cantrell.org.ukE<gt> |
| 578 | |
| 579 | David Golden E<lt>dagolden@cpan.orgE<gt> |
| 580 | |
| 581 | Yasuhiro Matsumoto E<lt>mattn@cpan.orgE<gt> |
| 582 | |
| 583 | Thanks to the cpan-testers-discuss mailing list for prompting us to write it |
| 584 | in the first place; |
| 585 | |
| 586 | to Chris Williams for help with Borland support; |
| 587 | |
| 588 | to Tony Cook for help with Microsoft compiler command-line options |
| 589 | |
| 590 | =head1 COPYRIGHT and LICENCE |
| 591 | |
| 592 | Copyright 2007 David Cantrell. Portions copyright 2007 David Golden. |
| 593 | |
| 594 | This module is free-as-in-speech software, and may be used, distributed, |
| 595 | and modified under the same conditions as perl itself. |
| 596 | |
| 597 | =head1 CONSPIRACY |
| 598 | |
| 599 | This module is also free-as-in-mason software. |
| 600 | |
| 601 | =cut |
| 602 | |
| 603 | 1; |