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