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