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