unbreak library probes on non-Win32
[imager.git] / GIF / inc / Devel / CheckLib.pm
1 # $Id: CheckLib.pm,v 1.25 2008/10/27 12:16:23 drhyde Exp $
2 # This is a modified version of Devel::CheckLib 0.93 including the patches from
3 # RT issues 60176 and 61645
4
5 package #
6 Devel::CheckLib;
7
8 use 5.00405; #postfix foreach
9 use strict;
10 use vars qw($VERSION @ISA @EXPORT);
11 $VERSION = '0.93_001';
12 use Config qw(%Config);
13 use Text::ParseWords 'quotewords';
14
15 use File::Spec;
16 use File::Temp;
17
18 require 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
23 local $^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
29 Devel::CheckLib - check that a library is available
30
31 =head1 DESCRIPTION
32
33 Devel::CheckLib is a perl module that checks whether a particular C
34 library 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
48 If you want to use this from Makefile.PL or Build.PL, do
49 not simply copy the module into your distribution as this may cause
50 problems when PAUSE and search.cpan.org index the distro.  Instead, use
51 the use-devel-checklib script.
52
53 =head1 HOW IT WORKS
54
55 You pass named parameters to a function, describing to it how to build
56 and link to the libraries.
57
58 It works by trying to compile some code - which defaults to this:
59
60     int main(void) { return 0; }
61
62 and linking it to the specified libraries.  If something pops out the end
63 which looks executable, it gets executed, and if main() returns 0 we know
64 that it worked.  That tiny program is
65 built once for each library that you specify, and (without linking) once
66 for each header file.
67
68 If you want to check for the presence of particular functions in a
69 library, or even that those functions return particular results, then
70 you 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
80 In that case, it will fail to build if either foo() or libversion() don't
81 exist, and main() will return the wrong value if libversion()'s return
82 value isn't what you want.
83
84 =head1 FUNCTIONS
85
86 All of these take the same named parameters and are exported by default.
87 To avoid exporting them, C<use Devel::CheckLib ()>.
88
89 =head2 assert_lib
90
91 This takes several named parameters, all of which are optional, and dies
92 with an error message if any of the libraries listed can
93 not be found.  B<Note>: dying in a Makefile.PL or Build.PL may provoke
94 a 'FAIL' report from CPAN Testers' automated smoke testers.  Use 
95 C<check_lib_or_exit> instead.
96
97 The named parameters are:
98
99 =over
100
101 =item lib
102
103 Must be either a string with the name of a single 
104 library or a reference to an array of strings of library names.  Depending
105 on the compiler found, library names will be fed to the compiler either as
106 C<-l> arguments or as C<.lib> file names.  (E.g. C<-ljpeg> or C<jpeg.lib>)
107
108 =item libpath
109
110 a string or an array of strings
111 representing additional paths to search for libraries.
112
113 =item LIBS
114
115 a C<ExtUtils::MakeMaker>-style space-seperated list of
116 libraries (each preceded by '-l') and directories (preceded by '-L').
117
118 This can also be supplied on the command-line.
119
120 =back
121
122 And libraries are no use without header files, so ...
123
124 =over
125
126 =item header
127
128 Must be either a string with the name of a single 
129 header file or a reference to an array of strings of header file names.
130
131 =item incpath
132
133 a string or an array of strings
134 representing additional paths to search for headers.
135
136 =item INC
137
138 a C<ExtUtils::MakeMaker>-style space-seperated list of
139 incpaths, each preceded by '-I'.
140
141 This can also be supplied on the command-line.
142
143 =back
144
145 If you need to perform know more than "does it link?" you can provide
146 code to be compiled and run:
147
148 =over
149
150 =item function
151
152 the body of the <main()> function.  If not provided C<return 0;> is
153 used.
154
155 =item prologue
156
157 code to insert between the C<#include> of the headers and the
158 definition of main.
159
160 =back
161
162 =head2 check_lib_or_exit
163
164 This behaves exactly the same as C<assert_lib()> except that instead of
165 dieing, it warns (with exactly the same error message) and exits.
166 This is intended for use in Makefile.PL / Build.PL
167 when you might want to prompt the user for various paths and
168 things before checking that what they've told you is sane.
169
170 If any library or header is missing, it exits with an exit value of 0 to avoid
171 causing a CPAN Testers 'FAIL' report.  CPAN Testers should ignore this
172 result -- which is what you want if an external library dependency is not
173 available.
174
175 =head2 check_lib
176
177 This behaves exactly the same as C<assert_lib()> except that it is silent,
178 returning false instead of dieing, or true otherwise.
179
180 =cut
181
182 sub check_lib_or_exit {
183     eval 'assert_lib(@_)';
184     if($@) {
185         warn $@;
186         exit;
187     }
188 }
189
190 sub check_lib {
191     eval 'assert_lib(@_)';
192     return $@ ? 0 : 1;
193 }
194
195 sub 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) {
217         for my $mm_attr_key (qw(LIBS INC)) {
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     }
226
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})) {
248         foreach my $arg (_shellwords($args{INC})) {
249             die("INC argument badly-formed: $arg\n") unless($arg =~ /^-I/);
250             push @incpaths, substr($arg, 2);
251         }
252     }
253
254     my ($cc, $ld) = _findcc();
255     my @missing;
256     my @wrongresult;
257     my @use_headers;
258
259     # first figure out which headers we can't find ...
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 = (
274                 @$cc,
275                 $cfile,
276                 "/Fe$exefile",
277                 (map { '/I'.Win32::GetShortPathName($_) } @incpaths),
278                 "/link",
279                 @$ld
280             );
281         } elsif($Config{cc} =~ /bcc32(\.exe)?/) {    # Borland
282             @sys_cmd = (
283                 @$cc,
284                 (map { "-I$_" } @incpaths),
285                 "-o$exefile",
286                 $cfile,
287                 @$ld
288             );
289         } else { # Unix-ish: gcc, Sun, AIX (gcc, cc), ...
290             @sys_cmd = (
291                 @$cc,
292                 @$ld,
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 = (
324                 @$cc,
325                 $cfile,
326                 ( map { "$_.lib" } @$libs ),
327                 "/Fe$exefile", 
328                 (map { '/I'.Win32::GetShortPathName($_) } @incpaths),
329                 "/link",
330                 @$ld,
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 = (
336                 @$cc,
337                 @$ld,
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 = (
346                 @$cc,
347                 @$ld,
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;
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;
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
371 sub _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";
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     }
387     return
388 }
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
393 sub _findcc {
394     # Need to use $keep=1 to work with MSWin32 backslashes and quotes
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);
399     my @paths = split(/$Config{path_sep}/, $ENV{PATH});
400     my @cc = split(/\s+/, $Config{cc});
401     return ( [ @cc, @ccflags ], \@ldflags ) if -x $cc[0];
402     foreach my $path (@paths) {
403         my $compiler = File::Spec->catfile($path, $cc[0]) . $Config{_exe};
404         return ([ $compiler, @cc[1 .. $#cc], @ccflags ], \@ldflags)
405             if -x $compiler;
406     }
407     die("Couldn't find your C compiler\n");
408 }
409
410 sub _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 {
423         return grep defined && /\S/, quotewords('\s+', 0, $line);
424     } 
425 }
426
427 # code substantially borrowed from IPC::Run3
428 sub _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
460 You must have a C compiler installed.  We check for C<$Config{cc}>,
461 both literally as it is in Config.pm and also in the $PATH.
462
463 It 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
487 This is a very early release intended primarily for feedback from
488 people who have discussed it.  The interface may change and it has
489 not been adequately tested.
490
491 Feedback is most welcome, including constructive criticism.
492 Bug reports should be made using L<http://rt.cpan.org/> or by email.
493
494 When 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
501 L<Devel::CheckOS>
502
503 L<Probe::Perl>
504
505 =head1 AUTHORS
506
507 David Cantrell E<lt>david@cantrell.org.ukE<gt>
508
509 David Golden E<lt>dagolden@cpan.orgE<gt>
510
511 Yasuhiro Matsumoto E<lt>mattn@cpan.orgE<gt>
512
513 Thanks to the cpan-testers-discuss mailing list for prompting us to write it
514 in the first place;
515
516 to Chris Williams for help with Borland support;
517
518 to Tony Cook for help with Microsoft compiler command-line options
519
520 =head1 COPYRIGHT and LICENCE
521
522 Copyright 2007 David Cantrell. Portions copyright 2007 David Golden.
523
524 This module is free-as-in-speech software, and may be used, distributed,
525 and modified under the same conditions as perl itself.
526
527 =head1 CONSPIRACY
528
529 This module is also free-as-in-mason software.
530
531 =cut
532
533 1;