]> git.imager.perl.org - imager.git/blob - TIFF/inc/Devel/CheckLib.pm
Devel::CheckLib: handle the gcc-4 symlink on Cygwin
[imager.git] / TIFF / 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 and other changes I need to backport
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_002';
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]) . ($^O eq 'cygwin' ? '' : $Config{_exe});
410         return ([ $compiler, @cc[1 .. $#cc], @ccflags ], \@ldflags)
411             if -x $compiler;
412         next if ! length $Config{_exe};
413         $compiler = File::Spec->catfile($path, $cc[0]);
414         return ([ $compiler, @cc[1 .. $#cc], @ccflags ], \@ldflags)
415             if -x $compiler;
416     }
417     die("Couldn't find your C compiler\n");
418 }
419
420 sub _shellwords {
421     my $line = shift;
422
423     if ($^O eq "MSWin32") {
424         my @elements;
425         $line =~ s/^\s+//;
426         while ($line =~ s/^"([^"]*)"// || $line =~ s/^(\S+)//) {
427           push @elements, $1;
428           $line =~ s/^\s+//;
429         }
430         return @elements;
431     }
432     else {
433         return grep defined && /\S/, quotewords('\s+', 0, $line);
434     } 
435 }
436
437 # code substantially borrowed from IPC::Run3
438 sub _quiet_system {
439     my (@cmd) = @_;
440
441     # save handles
442     local *STDOUT_SAVE;
443     local *STDERR_SAVE;
444     open STDOUT_SAVE, ">&STDOUT" or die "CheckLib: $! saving STDOUT";
445     open STDERR_SAVE, ">&STDERR" or die "CheckLib: $! saving STDERR";
446     
447     # redirect to nowhere
448     local *DEV_NULL;
449     open DEV_NULL, ">" . File::Spec->devnull 
450         or die "CheckLib: $! opening handle to null device";
451     open STDOUT, ">&" . fileno DEV_NULL
452         or die "CheckLib: $! redirecting STDOUT to null handle";
453     open STDERR, ">&" . fileno DEV_NULL
454         or die "CheckLib: $! redirecting STDERR to null handle";
455
456     # run system command
457     my $rv = system(@cmd);
458
459     # restore handles
460     open STDOUT, ">&" . fileno STDOUT_SAVE
461         or die "CheckLib: $! restoring STDOUT handle";
462     open STDERR, ">&" . fileno STDERR_SAVE
463         or die "CheckLib: $! restoring STDERR handle";
464
465     return $rv;
466 }
467
468 =head1 PLATFORMS SUPPORTED
469
470 You must have a C compiler installed.  We check for C<$Config{cc}>,
471 both literally as it is in Config.pm and also in the $PATH.
472
473 It has been tested with varying degrees on rigourousness on:
474
475 =over
476
477 =item gcc (on Linux, *BSD, Mac OS X, Solaris, Cygwin)
478
479 =item Sun's compiler tools on Solaris
480
481 =item IBM's tools on AIX
482
483 =item SGI's tools on Irix 6.5
484
485 =item Microsoft's tools on Windows
486
487 =item MinGW on Windows (with Strawberry Perl)
488
489 =item Borland's tools on Windows
490
491 =item QNX
492
493 =back
494
495 =head1 WARNINGS, BUGS and FEEDBACK
496
497 This is a very early release intended primarily for feedback from
498 people who have discussed it.  The interface may change and it has
499 not been adequately tested.
500
501 Feedback is most welcome, including constructive criticism.
502 Bug reports should be made using L<http://rt.cpan.org/> or by email.
503
504 When submitting a bug report, please include the output from running:
505
506     perl -V
507     perl -MDevel::CheckLib -e0
508
509 =head1 SEE ALSO
510
511 L<Devel::CheckOS>
512
513 L<Probe::Perl>
514
515 =head1 AUTHORS
516
517 David Cantrell E<lt>david@cantrell.org.ukE<gt>
518
519 David Golden E<lt>dagolden@cpan.orgE<gt>
520
521 Yasuhiro Matsumoto E<lt>mattn@cpan.orgE<gt>
522
523 Thanks to the cpan-testers-discuss mailing list for prompting us to write it
524 in the first place;
525
526 to Chris Williams for help with Borland support;
527
528 to Tony Cook for help with Microsoft compiler command-line options
529
530 =head1 COPYRIGHT and LICENCE
531
532 Copyright 2007 David Cantrell. Portions copyright 2007 David Golden.
533
534 This module is free-as-in-speech software, and may be used, distributed,
535 and modified under the same conditions as perl itself.
536
537 =head1 CONSPIRACY
538
539 This module is also free-as-in-mason software.
540
541 =cut
542
543 1;