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